##// END OF EJS Templates
merge: merged default into stable
marcink -
r16:170c5398 merge v4.0.1 stable
parent child Browse files
Show More
@@ -0,0 +1,17 b''
1 |RCE| 4.0.1 |RNS|
2 -----------------
3
4 Release Date
5 ^^^^^^^^^^^^
6
7 - 2016-05-25
8
9 Fixes
10 ^^^^^
11
12 - fixed default session to be file based instead of memory which causes
13 problems in multi-worker setup
14 - ui: fixing forks table #3959
15 - ui: fixed gravatars misalignment issues
16 - logging: fixed excesive formatting on auth logging
17 - pull requests: better ref selection when opening PRs from changelog
@@ -1,5 +1,6 b''
1 [bumpversion]
1 [bumpversion]
2 current_version = 4.0.0
2 current_version = 4.0.1
3 message = release: Bump version {current_version} to {new_version}
3 message = release: Bump version {current_version} to {new_version}
4
4
5 [bumpversion:file:rhodecode/VERSION]
5 [bumpversion:file:rhodecode/VERSION]
6
@@ -1,39 +1,47 b''
1
1
2 WEBPACK=./node_modules/webpack/bin/webpack.js
2 WEBPACK=./node_modules/webpack/bin/webpack.js
3 GRUNT=grunt
3 GRUNT=grunt
4 NODE_PATH=./node_modules
4 NODE_PATH=./node_modules
5 FLAKE8=flake8 setup.py pytest_pylons/ rhodecode/ --select=E124 --ignore=E711,E712,E510,E121,E122,E126,E127,E128,E501,F401 --max-line-length=100 --exclude=*rhodecode/lib/dbmigrate/*,*rhodecode/tests/*,*rhodecode/lib/vcs/utils/*
5 FLAKE8=flake8 setup.py pytest_pylons/ rhodecode/ --select=E124 --ignore=E711,E712,E510,E121,E122,E126,E127,E128,E501,F401 --max-line-length=100 --exclude=*rhodecode/lib/dbmigrate/*,*rhodecode/tests/*,*rhodecode/lib/vcs/utils/*
6 CI_PREFIX=enterprise
6 CI_PREFIX=enterprise
7
7
8 .PHONY: help clean test test-clean test-lint test-only
8 .PHONY: docs docs-clean ci-docs clean test test-clean test-lint test-only
9
10
11 docs:
12 (cd docs; nix-build default.nix -o result; make clean html)
9
13
10 help:
14 docs-clean:
11 @echo "TODO: describe Makefile"
15 (cd docs; make clean)
16
17 ci-docs: docs;
18
12
19
13 clean: test-clean
20 clean: test-clean
14 find . -type f \( -iname '*.c' -o -iname '*.pyc' -o -iname '*.so' \) -exec rm '{}' ';'
21 find . -type f \( -iname '*.c' -o -iname '*.pyc' -o -iname '*.so' \) -exec rm '{}' ';'
15
22
16 test: test-clean test-lint test-only
23 test: test-clean test-lint test-only
17
24
18 test-clean:
25 test-clean:
19 rm -rf coverage.xml htmlcov junit.xml pylint.log result
26 rm -rf coverage.xml htmlcov junit.xml pylint.log result
20
27
21 test-lint:
28 test-lint:
22 if [ "$$IN_NIX_SHELL" = "1" ]; then \
29 if [ "$$IN_NIX_SHELL" = "1" ]; then \
23 $(FLAKE8); \
30 $(FLAKE8); \
24 else \
31 else \
25 $(FLAKE8) --format=pylint --exit-zero > pylint.log; \
32 $(FLAKE8) --format=pylint --exit-zero > pylint.log; \
26 fi
33 fi
27
34
28 test-only:
35 test-only:
29 PYTHONHASHSEED=random py.test -vv -r xw --cov=rhodecode --cov-report=term-missing --cov-report=html rhodecode/tests/
36 PYTHONHASHSEED=random py.test -vv -r xw --cov=rhodecode --cov-report=term-missing --cov-report=html rhodecode/tests/
30
37
31 web-build:
38 web-build:
32 NODE_PATH=$(NODE_PATH) $(GRUNT)
39 NODE_PATH=$(NODE_PATH) $(GRUNT)
33
40
34 web-test:
41 web-test:
35 @echo "no test for our javascript, yet!"
42 @echo "no test for our javascript, yet!"
36
43
37 docs-bootstrap:
44 docs-bootstrap:
38 (cd docs; nix-build default.nix -o result)
45 (cd docs; nix-build default.nix -o result)
39 @echo "Please go to docs folder and run make html"
46 @echo "Please go to docs folder and run make html"
47
@@ -1,575 +1,577 b''
1 ################################################################################
1 ################################################################################
2 ################################################################################
2 ################################################################################
3 # RhodeCode Enterprise - configuration file #
3 # RhodeCode Enterprise - configuration file #
4 # Built-in functions and variables #
4 # Built-in functions and variables #
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
8
9 [DEFAULT]
9 [DEFAULT]
10 debug = true
10 debug = true
11 pdebug = false
11 pdebug = false
12 ################################################################################
12 ################################################################################
13 ## Uncomment and replace with the email address which should receive ##
13 ## Uncomment and replace with the email address which should receive ##
14 ## any error reports after an application crash ##
14 ## any error reports after an application crash ##
15 ## Additionally these settings will be used by the RhodeCode mailing system ##
15 ## Additionally these settings will be used by the RhodeCode mailing system ##
16 ################################################################################
16 ################################################################################
17 #email_to = admin@localhost
17 #email_to = admin@localhost
18 #error_email_from = paste_error@localhost
18 #error_email_from = paste_error@localhost
19 #app_email_from = rhodecode-noreply@localhost
19 #app_email_from = rhodecode-noreply@localhost
20 #error_message =
20 #error_message =
21 #email_prefix = [RhodeCode]
21 #email_prefix = [RhodeCode]
22
22
23 #smtp_server = mail.server.com
23 #smtp_server = mail.server.com
24 #smtp_username =
24 #smtp_username =
25 #smtp_password =
25 #smtp_password =
26 #smtp_port =
26 #smtp_port =
27 #smtp_use_tls = false
27 #smtp_use_tls = false
28 #smtp_use_ssl = true
28 #smtp_use_ssl = true
29 ## Specify available auth parameters here (e.g. LOGIN PLAIN CRAM-MD5, etc.)
29 ## Specify available auth parameters here (e.g. LOGIN PLAIN CRAM-MD5, etc.)
30 #smtp_auth =
30 #smtp_auth =
31
31
32 [server:main]
32 [server:main]
33 ## COMMON ##
33 ## COMMON ##
34 host = 127.0.0.1
34 host = 127.0.0.1
35 port = 5000
35 port = 5000
36
36
37 ##########################
37 ##########################
38 ## WAITRESS WSGI SERVER ##
38 ## WAITRESS WSGI SERVER ##
39 ##########################
39 ##########################
40 use = egg:waitress#main
40 use = egg:waitress#main
41 ## number of worker threads
41 ## number of worker threads
42 threads = 5
42 threads = 5
43 ## MAX BODY SIZE 100GB
43 ## MAX BODY SIZE 100GB
44 max_request_body_size = 107374182400
44 max_request_body_size = 107374182400
45 ## Use poll instead of select, fixes file descriptors limits problems.
45 ## Use poll instead of select, fixes file descriptors limits problems.
46 ## May not work on old windows systems.
46 ## May not work on old windows systems.
47 asyncore_use_poll = true
47 asyncore_use_poll = true
48
48
49
49
50 ##########################
50 ##########################
51 ## GUNICORN WSGI SERVER ##
51 ## GUNICORN WSGI SERVER ##
52 ##########################
52 ##########################
53 ## run with gunicorn --log-config <inifile.ini> --paste <inifile.ini>
53 ## run with gunicorn --log-config <inifile.ini> --paste <inifile.ini>
54 #use = egg:gunicorn#main
54 #use = egg:gunicorn#main
55 ## Sets the number of process workers. You must set `instance_id = *`
55 ## Sets the number of process workers. You must set `instance_id = *`
56 ## when this option is set to more than one worker, recommended
56 ## when this option is set to more than one worker, recommended
57 ## value is (2 * NUMBER_OF_CPUS + 1), eg 2CPU = 5 workers
57 ## value is (2 * NUMBER_OF_CPUS + 1), eg 2CPU = 5 workers
58 ## The `instance_id = *` must be set in the [app:main] section below
58 ## The `instance_id = *` must be set in the [app:main] section below
59 #workers = 1
59 #workers = 1
60 ## number of threads for each of the worker, must be set to 1 for gevent
60 ## number of threads for each of the worker, must be set to 1 for gevent
61 ## generally recommened to be at 1
61 ## generally recommened to be at 1
62 #threads = 1
62 #threads = 1
63 ## process name
63 ## process name
64 #proc_name = rhodecode
64 #proc_name = rhodecode
65 ## type of worker class, one of sync, gevent
65 ## type of worker class, one of sync, gevent
66 ## recommended for bigger setup is using of of other than sync one
66 ## recommended for bigger setup is using of of other than sync one
67 #worker_class = sync
67 #worker_class = sync
68 ## The maximum number of simultaneous clients. Valid only for Gevent
69 #worker_connections = 10
68 ## max number of requests that worker will handle before being gracefully
70 ## max number of requests that worker will handle before being gracefully
69 ## restarted, could prevent memory leaks
71 ## restarted, could prevent memory leaks
70 #max_requests = 1000
72 #max_requests = 1000
71 #max_requests_jitter = 30
73 #max_requests_jitter = 30
72 ## ammount of time a worker can spend with handling a request before it
74 ## ammount of time a worker can spend with handling a request before it
73 ## gets killed and restarted. Set to 6hrs
75 ## gets killed and restarted. Set to 6hrs
74 #timeout = 21600
76 #timeout = 21600
75
77
76
78
77 ## prefix middleware for RhodeCode, disables force_https flag.
79 ## prefix middleware for RhodeCode, disables force_https flag.
78 ## allows to set RhodeCode under a prefix in server.
80 ## allows to set RhodeCode under a prefix in server.
79 ## eg https://server.com/<prefix>. Enable `filter-with =` option below as well.
81 ## eg https://server.com/<prefix>. Enable `filter-with =` option below as well.
80 #[filter:proxy-prefix]
82 #[filter:proxy-prefix]
81 #use = egg:PasteDeploy#prefix
83 #use = egg:PasteDeploy#prefix
82 #prefix = /<your-prefix>
84 #prefix = /<your-prefix>
83
85
84 [app:main]
86 [app:main]
85 use = egg:rhodecode-enterprise-ce
87 use = egg:rhodecode-enterprise-ce
86 ## enable proxy prefix middleware, defined below
88 ## enable proxy prefix middleware, defined below
87 #filter-with = proxy-prefix
89 #filter-with = proxy-prefix
88
90
89 # During development the we want to have the debug toolbar enabled
91 # During development the we want to have the debug toolbar enabled
90 pyramid.includes =
92 pyramid.includes =
91 pyramid_debugtoolbar
93 pyramid_debugtoolbar
92 rhodecode.utils.debugtoolbar
94 rhodecode.utils.debugtoolbar
93 rhodecode.lib.middleware.request_wrapper
95 rhodecode.lib.middleware.request_wrapper
94
96
95 pyramid.reload_templates = true
97 pyramid.reload_templates = true
96
98
97 debugtoolbar.hosts = 0.0.0.0/0
99 debugtoolbar.hosts = 0.0.0.0/0
98 debugtoolbar.exclude_prefixes =
100 debugtoolbar.exclude_prefixes =
99 /css
101 /css
100 /fonts
102 /fonts
101 /images
103 /images
102 /js
104 /js
103
105
104 ## RHODECODE PLUGINS ##
106 ## RHODECODE PLUGINS ##
105 rhodecode.includes =
107 rhodecode.includes =
106 rhodecode.api
108 rhodecode.api
107
109
108
110
109 # api prefix url
111 # api prefix url
110 rhodecode.api.url = /_admin/api
112 rhodecode.api.url = /_admin/api
111
113
112
114
113 ## END RHODECODE PLUGINS ##
115 ## END RHODECODE PLUGINS ##
114
116
115 full_stack = true
117 full_stack = true
116
118
117 ## Serve static files via RhodeCode, disable to serve them via HTTP server
119 ## Serve static files via RhodeCode, disable to serve them via HTTP server
118 static_files = true
120 static_files = true
119
121
120 ## Optional Languages
122 ## Optional Languages
121 ## en(default), be, de, es, fr, it, ja, pl, pt, ru, zh
123 ## en(default), be, de, es, fr, it, ja, pl, pt, ru, zh
122 lang = en
124 lang = en
123
125
124 ## perform a full repository scan on each server start, this should be
126 ## perform a full repository scan on each server start, this should be
125 ## set to false after first startup, to allow faster server restarts.
127 ## set to false after first startup, to allow faster server restarts.
126 startup.import_repos = false
128 startup.import_repos = false
127
129
128 ## Uncomment and set this path to use archive download cache.
130 ## Uncomment and set this path to use archive download cache.
129 ## Once enabled, generated archives will be cached at this location
131 ## Once enabled, generated archives will be cached at this location
130 ## and served from the cache during subsequent requests for the same archive of
132 ## and served from the cache during subsequent requests for the same archive of
131 ## the repository.
133 ## the repository.
132 #archive_cache_dir = /tmp/tarballcache
134 #archive_cache_dir = /tmp/tarballcache
133
135
134 ## change this to unique ID for security
136 ## change this to unique ID for security
135 app_instance_uuid = rc-production
137 app_instance_uuid = rc-production
136
138
137 ## cut off limit for large diffs (size in bytes)
139 ## cut off limit for large diffs (size in bytes)
138 cut_off_limit_diff = 1024000
140 cut_off_limit_diff = 1024000
139 cut_off_limit_file = 256000
141 cut_off_limit_file = 256000
140
142
141 ## use cache version of scm repo everywhere
143 ## use cache version of scm repo everywhere
142 vcs_full_cache = true
144 vcs_full_cache = true
143
145
144 ## force https in RhodeCode, fixes https redirects, assumes it's always https
146 ## force https in RhodeCode, fixes https redirects, assumes it's always https
145 ## Normally this is controlled by proper http flags sent from http server
147 ## Normally this is controlled by proper http flags sent from http server
146 force_https = false
148 force_https = false
147
149
148 ## use Strict-Transport-Security headers
150 ## use Strict-Transport-Security headers
149 use_htsts = false
151 use_htsts = false
150
152
151 ## number of commits stats will parse on each iteration
153 ## number of commits stats will parse on each iteration
152 commit_parse_limit = 25
154 commit_parse_limit = 25
153
155
154 ## git rev filter option, --all is the default filter, if you need to
156 ## git rev filter option, --all is the default filter, if you need to
155 ## hide all refs in changelog switch this to --branches --tags
157 ## hide all refs in changelog switch this to --branches --tags
156 git_rev_filter = --branches --tags
158 git_rev_filter = --branches --tags
157
159
158 # Set to true if your repos are exposed using the dumb protocol
160 # Set to true if your repos are exposed using the dumb protocol
159 git_update_server_info = false
161 git_update_server_info = false
160
162
161 ## RSS/ATOM feed options
163 ## RSS/ATOM feed options
162 rss_cut_off_limit = 256000
164 rss_cut_off_limit = 256000
163 rss_items_per_page = 10
165 rss_items_per_page = 10
164 rss_include_diff = false
166 rss_include_diff = false
165
167
166 ## gist URL alias, used to create nicer urls for gist. This should be an
168 ## gist URL alias, used to create nicer urls for gist. This should be an
167 ## url that does rewrites to _admin/gists/<gistid>.
169 ## url that does rewrites to _admin/gists/<gistid>.
168 ## example: http://gist.rhodecode.org/{gistid}. Empty means use the internal
170 ## example: http://gist.rhodecode.org/{gistid}. Empty means use the internal
169 ## RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/<gistid>
171 ## RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/<gistid>
170 gist_alias_url =
172 gist_alias_url =
171
173
172 ## List of controllers (using glob pattern syntax) that AUTH TOKENS could be
174 ## List of controllers (using glob pattern syntax) that AUTH TOKENS could be
173 ## used for access.
175 ## used for access.
174 ## Adding ?auth_token = <token> to the url authenticates this request as if it
176 ## Adding ?auth_token = <token> to the url authenticates this request as if it
175 ## came from the the logged in user who own this authentication token.
177 ## came from the the logged in user who own this authentication token.
176 ##
178 ##
177 ## Syntax is <ControllerClass>:<function_pattern>.
179 ## Syntax is <ControllerClass>:<function_pattern>.
178 ## To enable access to raw_files put `FilesController:raw`.
180 ## To enable access to raw_files put `FilesController:raw`.
179 ## To enable access to patches add `ChangesetController:changeset_patch`.
181 ## To enable access to patches add `ChangesetController:changeset_patch`.
180 ## The list should be "," separated and on a single line.
182 ## The list should be "," separated and on a single line.
181 ##
183 ##
182 ## Recommended controllers to enable:
184 ## Recommended controllers to enable:
183 # ChangesetController:changeset_patch,
185 # ChangesetController:changeset_patch,
184 # ChangesetController:changeset_raw,
186 # ChangesetController:changeset_raw,
185 # FilesController:raw,
187 # FilesController:raw,
186 # FilesController:archivefile,
188 # FilesController:archivefile,
187 # GistsController:*,
189 # GistsController:*,
188 api_access_controllers_whitelist =
190 api_access_controllers_whitelist =
189
191
190 ## default encoding used to convert from and to unicode
192 ## default encoding used to convert from and to unicode
191 ## can be also a comma separated list of encoding in case of mixed encodings
193 ## can be also a comma separated list of encoding in case of mixed encodings
192 default_encoding = UTF-8
194 default_encoding = UTF-8
193
195
194 ## instance-id prefix
196 ## instance-id prefix
195 ## a prefix key for this instance used for cache invalidation when running
197 ## a prefix key for this instance used for cache invalidation when running
196 ## multiple instances of rhodecode, make sure it's globally unique for
198 ## multiple instances of rhodecode, make sure it's globally unique for
197 ## all running rhodecode instances. Leave empty if you don't use it
199 ## all running rhodecode instances. Leave empty if you don't use it
198 instance_id =
200 instance_id =
199
201
200 ## alternative return HTTP header for failed authentication. Default HTTP
202 ## alternative return HTTP header for failed authentication. Default HTTP
201 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
203 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
202 ## handling that causing a series of failed authentication calls.
204 ## handling that causing a series of failed authentication calls.
203 ## Set this variable to 403 to return HTTPForbidden, or any other HTTP code
205 ## Set this variable to 403 to return HTTPForbidden, or any other HTTP code
204 ## This will be served instead of default 401 on bad authnetication
206 ## This will be served instead of default 401 on bad authnetication
205 auth_ret_code =
207 auth_ret_code =
206
208
207 ## use special detection method when serving auth_ret_code, instead of serving
209 ## use special detection method when serving auth_ret_code, instead of serving
208 ## ret_code directly, use 401 initially (Which triggers credentials prompt)
210 ## ret_code directly, use 401 initially (Which triggers credentials prompt)
209 ## and then serve auth_ret_code to clients
211 ## and then serve auth_ret_code to clients
210 auth_ret_code_detection = false
212 auth_ret_code_detection = false
211
213
212 ## locking return code. When repository is locked return this HTTP code. 2XX
214 ## locking return code. When repository is locked return this HTTP code. 2XX
213 ## codes don't break the transactions while 4XX codes do
215 ## codes don't break the transactions while 4XX codes do
214 lock_ret_code = 423
216 lock_ret_code = 423
215
217
216 ## allows to change the repository location in settings page
218 ## allows to change the repository location in settings page
217 allow_repo_location_change = true
219 allow_repo_location_change = true
218
220
219 ## allows to setup custom hooks in settings page
221 ## allows to setup custom hooks in settings page
220 allow_custom_hooks_settings = true
222 allow_custom_hooks_settings = true
221
223
222 ## generated license token, goto license page in RhodeCode settings to obtain
224 ## generated license token, goto license page in RhodeCode settings to obtain
223 ## new token
225 ## new token
224 license_token =
226 license_token =
225
227
226 ## supervisor connection uri, for managing supervisor and logs.
228 ## supervisor connection uri, for managing supervisor and logs.
227 supervisor.uri =
229 supervisor.uri =
228 ## supervisord group name/id we only want this RC instance to handle
230 ## supervisord group name/id we only want this RC instance to handle
229 supervisor.group_id = dev
231 supervisor.group_id = dev
230
232
231 ## Display extended labs settings
233 ## Display extended labs settings
232 labs_settings_active = true
234 labs_settings_active = true
233
235
234 ####################################
236 ####################################
235 ### CELERY CONFIG ####
237 ### CELERY CONFIG ####
236 ####################################
238 ####################################
237 use_celery = false
239 use_celery = false
238 broker.host = localhost
240 broker.host = localhost
239 broker.vhost = rabbitmqhost
241 broker.vhost = rabbitmqhost
240 broker.port = 5672
242 broker.port = 5672
241 broker.user = rabbitmq
243 broker.user = rabbitmq
242 broker.password = qweqwe
244 broker.password = qweqwe
243
245
244 celery.imports = rhodecode.lib.celerylib.tasks
246 celery.imports = rhodecode.lib.celerylib.tasks
245
247
246 celery.result.backend = amqp
248 celery.result.backend = amqp
247 celery.result.dburi = amqp://
249 celery.result.dburi = amqp://
248 celery.result.serialier = json
250 celery.result.serialier = json
249
251
250 #celery.send.task.error.emails = true
252 #celery.send.task.error.emails = true
251 #celery.amqp.task.result.expires = 18000
253 #celery.amqp.task.result.expires = 18000
252
254
253 celeryd.concurrency = 2
255 celeryd.concurrency = 2
254 #celeryd.log.file = celeryd.log
256 #celeryd.log.file = celeryd.log
255 celeryd.log.level = debug
257 celeryd.log.level = debug
256 celeryd.max.tasks.per.child = 1
258 celeryd.max.tasks.per.child = 1
257
259
258 ## tasks will never be sent to the queue, but executed locally instead.
260 ## tasks will never be sent to the queue, but executed locally instead.
259 celery.always.eager = false
261 celery.always.eager = false
260
262
261 ####################################
263 ####################################
262 ### BEAKER CACHE ####
264 ### BEAKER CACHE ####
263 ####################################
265 ####################################
264 # default cache dir for templates. Putting this into a ramdisk
266 # default cache dir for templates. Putting this into a ramdisk
265 ## can boost performance, eg. %(here)s/data_ramdisk
267 ## can boost performance, eg. %(here)s/data_ramdisk
266 cache_dir = %(here)s/data
268 cache_dir = %(here)s/data
267
269
268 ## locking and default file storage for Beaker. Putting this into a ramdisk
270 ## locking and default file storage for Beaker. Putting this into a ramdisk
269 ## can boost performance, eg. %(here)s/data_ramdisk/cache/beaker_data
271 ## can boost performance, eg. %(here)s/data_ramdisk/cache/beaker_data
270 beaker.cache.data_dir = %(here)s/data/cache/beaker_data
272 beaker.cache.data_dir = %(here)s/data/cache/beaker_data
271 beaker.cache.lock_dir = %(here)s/data/cache/beaker_lock
273 beaker.cache.lock_dir = %(here)s/data/cache/beaker_lock
272
274
273 beaker.cache.regions = super_short_term, short_term, long_term, sql_cache_short, auth_plugins, repo_cache_long
275 beaker.cache.regions = super_short_term, short_term, long_term, sql_cache_short, auth_plugins, repo_cache_long
274
276
275 beaker.cache.super_short_term.type = memory
277 beaker.cache.super_short_term.type = memory
276 beaker.cache.super_short_term.expire = 10
278 beaker.cache.super_short_term.expire = 10
277 beaker.cache.super_short_term.key_length = 256
279 beaker.cache.super_short_term.key_length = 256
278
280
279 beaker.cache.short_term.type = memory
281 beaker.cache.short_term.type = memory
280 beaker.cache.short_term.expire = 60
282 beaker.cache.short_term.expire = 60
281 beaker.cache.short_term.key_length = 256
283 beaker.cache.short_term.key_length = 256
282
284
283 beaker.cache.long_term.type = memory
285 beaker.cache.long_term.type = memory
284 beaker.cache.long_term.expire = 36000
286 beaker.cache.long_term.expire = 36000
285 beaker.cache.long_term.key_length = 256
287 beaker.cache.long_term.key_length = 256
286
288
287 beaker.cache.sql_cache_short.type = memory
289 beaker.cache.sql_cache_short.type = memory
288 beaker.cache.sql_cache_short.expire = 10
290 beaker.cache.sql_cache_short.expire = 10
289 beaker.cache.sql_cache_short.key_length = 256
291 beaker.cache.sql_cache_short.key_length = 256
290
292
291 # default is memory cache, configure only if required
293 # default is memory cache, configure only if required
292 # using multi-node or multi-worker setup
294 # using multi-node or multi-worker setup
293 #beaker.cache.auth_plugins.type = ext:database
295 #beaker.cache.auth_plugins.type = ext:database
294 #beaker.cache.auth_plugins.lock_dir = %(here)s/data/cache/auth_plugin_lock
296 #beaker.cache.auth_plugins.lock_dir = %(here)s/data/cache/auth_plugin_lock
295 #beaker.cache.auth_plugins.url = postgresql://postgres:secret@localhost/rhodecode
297 #beaker.cache.auth_plugins.url = postgresql://postgres:secret@localhost/rhodecode
296 #beaker.cache.auth_plugins.url = mysql://root:secret@127.0.0.1/rhodecode
298 #beaker.cache.auth_plugins.url = mysql://root:secret@127.0.0.1/rhodecode
297 #beaker.cache.auth_plugins.sa.pool_recycle = 3600
299 #beaker.cache.auth_plugins.sa.pool_recycle = 3600
298 #beaker.cache.auth_plugins.sa.pool_size = 10
300 #beaker.cache.auth_plugins.sa.pool_size = 10
299 #beaker.cache.auth_plugins.sa.max_overflow = 0
301 #beaker.cache.auth_plugins.sa.max_overflow = 0
300
302
301 beaker.cache.repo_cache_long.type = memorylru_base
303 beaker.cache.repo_cache_long.type = memorylru_base
302 beaker.cache.repo_cache_long.max_items = 4096
304 beaker.cache.repo_cache_long.max_items = 4096
303 beaker.cache.repo_cache_long.expire = 2592000
305 beaker.cache.repo_cache_long.expire = 2592000
304
306
305 # default is memorylru_base cache, configure only if required
307 # default is memorylru_base cache, configure only if required
306 # using multi-node or multi-worker setup
308 # using multi-node or multi-worker setup
307 #beaker.cache.repo_cache_long.type = ext:memcached
309 #beaker.cache.repo_cache_long.type = ext:memcached
308 #beaker.cache.repo_cache_long.url = localhost:11211
310 #beaker.cache.repo_cache_long.url = localhost:11211
309 #beaker.cache.repo_cache_long.expire = 1209600
311 #beaker.cache.repo_cache_long.expire = 1209600
310 #beaker.cache.repo_cache_long.key_length = 256
312 #beaker.cache.repo_cache_long.key_length = 256
311
313
312 ####################################
314 ####################################
313 ### BEAKER SESSION ####
315 ### BEAKER SESSION ####
314 ####################################
316 ####################################
315
317
316 ## .session.type is type of storage options for the session, current allowed
318 ## .session.type is type of storage options for the session, current allowed
317 ## types are file, ext:memcached, ext:database, and memory(default).
319 ## types are file, ext:memcached, ext:database, and memory(default).
318 beaker.session.type = file
320 beaker.session.type = file
319 beaker.session.data_dir = %(here)s/data/sessions/data
321 beaker.session.data_dir = %(here)s/data/sessions/data
320
322
321 ## db based session, fast, and allows easy management over logged in users ##
323 ## db based session, fast, and allows easy management over logged in users ##
322 #beaker.session.type = ext:database
324 #beaker.session.type = ext:database
323 #beaker.session.table_name = db_session
325 #beaker.session.table_name = db_session
324 #beaker.session.sa.url = postgresql://postgres:secret@localhost/rhodecode
326 #beaker.session.sa.url = postgresql://postgres:secret@localhost/rhodecode
325 #beaker.session.sa.url = mysql://root:secret@127.0.0.1/rhodecode
327 #beaker.session.sa.url = mysql://root:secret@127.0.0.1/rhodecode
326 #beaker.session.sa.pool_recycle = 3600
328 #beaker.session.sa.pool_recycle = 3600
327 #beaker.session.sa.echo = false
329 #beaker.session.sa.echo = false
328
330
329 beaker.session.key = rhodecode
331 beaker.session.key = rhodecode
330 beaker.session.secret = develop-rc-uytcxaz
332 beaker.session.secret = develop-rc-uytcxaz
331 beaker.session.lock_dir = %(here)s/data/sessions/lock
333 beaker.session.lock_dir = %(here)s/data/sessions/lock
332
334
333 ## Secure encrypted cookie. Requires AES and AES python libraries
335 ## Secure encrypted cookie. Requires AES and AES python libraries
334 ## you must disable beaker.session.secret to use this
336 ## you must disable beaker.session.secret to use this
335 #beaker.session.encrypt_key = <key_for_encryption>
337 #beaker.session.encrypt_key = <key_for_encryption>
336 #beaker.session.validate_key = <validation_key>
338 #beaker.session.validate_key = <validation_key>
337
339
338 ## sets session as invalid(also logging out user) if it haven not been
340 ## sets session as invalid(also logging out user) if it haven not been
339 ## accessed for given amount of time in seconds
341 ## accessed for given amount of time in seconds
340 beaker.session.timeout = 2592000
342 beaker.session.timeout = 2592000
341 beaker.session.httponly = true
343 beaker.session.httponly = true
342 #beaker.session.cookie_path = /<your-prefix>
344 #beaker.session.cookie_path = /<your-prefix>
343
345
344 ## uncomment for https secure cookie
346 ## uncomment for https secure cookie
345 beaker.session.secure = false
347 beaker.session.secure = false
346
348
347 ## auto save the session to not to use .save()
349 ## auto save the session to not to use .save()
348 beaker.session.auto = false
350 beaker.session.auto = false
349
351
350 ## default cookie expiration time in seconds, set to `true` to set expire
352 ## default cookie expiration time in seconds, set to `true` to set expire
351 ## at browser close
353 ## at browser close
352 #beaker.session.cookie_expires = 3600
354 #beaker.session.cookie_expires = 3600
353
355
354 ###################################
356 ###################################
355 ## SEARCH INDEXING CONFIGURATION ##
357 ## SEARCH INDEXING CONFIGURATION ##
356 ###################################
358 ###################################
357
359
358 search.module = rhodecode.lib.index.whoosh
360 search.module = rhodecode.lib.index.whoosh
359 search.location = %(here)s/data/index
361 search.location = %(here)s/data/index
360
362
361 ###################################
363 ###################################
362 ## ERROR AND LOG HANDLING SYSTEM ##
364 ## ERROR AND LOG HANDLING SYSTEM ##
363 ###################################
365 ###################################
364
366
365 ## Appenlight is tailored to work with RhodeCode, see
367 ## Appenlight is tailored to work with RhodeCode, see
366 ## http://appenlight.com for details how to obtain an account
368 ## http://appenlight.com for details how to obtain an account
367
369
368 ## appenlight integration enabled
370 ## appenlight integration enabled
369 appenlight = false
371 appenlight = false
370
372
371 appenlight.server_url = https://api.appenlight.com
373 appenlight.server_url = https://api.appenlight.com
372 appenlight.api_key = YOUR_API_KEY
374 appenlight.api_key = YOUR_API_KEY
373 ;appenlight.transport_config = https://api.appenlight.com?threaded=1&timeout=5
375 ;appenlight.transport_config = https://api.appenlight.com?threaded=1&timeout=5
374
376
375 # used for JS client
377 # used for JS client
376 appenlight.api_public_key = YOUR_API_PUBLIC_KEY
378 appenlight.api_public_key = YOUR_API_PUBLIC_KEY
377
379
378 ## TWEAK AMOUNT OF INFO SENT HERE
380 ## TWEAK AMOUNT OF INFO SENT HERE
379
381
380 ## enables 404 error logging (default False)
382 ## enables 404 error logging (default False)
381 appenlight.report_404 = false
383 appenlight.report_404 = false
382
384
383 ## time in seconds after request is considered being slow (default 1)
385 ## time in seconds after request is considered being slow (default 1)
384 appenlight.slow_request_time = 1
386 appenlight.slow_request_time = 1
385
387
386 ## record slow requests in application
388 ## record slow requests in application
387 ## (needs to be enabled for slow datastore recording and time tracking)
389 ## (needs to be enabled for slow datastore recording and time tracking)
388 appenlight.slow_requests = true
390 appenlight.slow_requests = true
389
391
390 ## enable hooking to application loggers
392 ## enable hooking to application loggers
391 appenlight.logging = true
393 appenlight.logging = true
392
394
393 ## minimum log level for log capture
395 ## minimum log level for log capture
394 appenlight.logging.level = WARNING
396 appenlight.logging.level = WARNING
395
397
396 ## send logs only from erroneous/slow requests
398 ## send logs only from erroneous/slow requests
397 ## (saves API quota for intensive logging)
399 ## (saves API quota for intensive logging)
398 appenlight.logging_on_error = false
400 appenlight.logging_on_error = false
399
401
400 ## list of additonal keywords that should be grabbed from environ object
402 ## list of additonal keywords that should be grabbed from environ object
401 ## can be string with comma separated list of words in lowercase
403 ## can be string with comma separated list of words in lowercase
402 ## (by default client will always send following info:
404 ## (by default client will always send following info:
403 ## 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
405 ## 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
404 ## start with HTTP* this list be extended with additional keywords here
406 ## start with HTTP* this list be extended with additional keywords here
405 appenlight.environ_keys_whitelist =
407 appenlight.environ_keys_whitelist =
406
408
407 ## list of keywords that should be blanked from request object
409 ## list of keywords that should be blanked from request object
408 ## can be string with comma separated list of words in lowercase
410 ## can be string with comma separated list of words in lowercase
409 ## (by default client will always blank keys that contain following words
411 ## (by default client will always blank keys that contain following words
410 ## 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
412 ## 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
411 ## this list be extended with additional keywords set here
413 ## this list be extended with additional keywords set here
412 appenlight.request_keys_blacklist =
414 appenlight.request_keys_blacklist =
413
415
414 ## list of namespaces that should be ignores when gathering log entries
416 ## list of namespaces that should be ignores when gathering log entries
415 ## can be string with comma separated list of namespaces
417 ## can be string with comma separated list of namespaces
416 ## (by default the client ignores own entries: appenlight_client.client)
418 ## (by default the client ignores own entries: appenlight_client.client)
417 appenlight.log_namespace_blacklist =
419 appenlight.log_namespace_blacklist =
418
420
419
421
420 ################################################################################
422 ################################################################################
421 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
423 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
422 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
424 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
423 ## execute malicious code after an exception is raised. ##
425 ## execute malicious code after an exception is raised. ##
424 ################################################################################
426 ################################################################################
425 #set debug = false
427 #set debug = false
426
428
427
429
428 ##############
430 ##############
429 ## STYLING ##
431 ## STYLING ##
430 ##############
432 ##############
431 debug_style = true
433 debug_style = true
432
434
433 #########################################################
435 #########################################################
434 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
436 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
435 #########################################################
437 #########################################################
436 sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30
438 sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30
437 #sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode
439 #sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode
438 #sqlalchemy.db1.url = mysql://root:qweqwe@localhost/rhodecode
440 #sqlalchemy.db1.url = mysql://root:qweqwe@localhost/rhodecode
439
441
440 # see sqlalchemy docs for other advanced settings
442 # see sqlalchemy docs for other advanced settings
441
443
442 ## print the sql statements to output
444 ## print the sql statements to output
443 sqlalchemy.db1.echo = false
445 sqlalchemy.db1.echo = false
444 ## recycle the connections after this ammount of seconds
446 ## recycle the connections after this ammount of seconds
445 sqlalchemy.db1.pool_recycle = 3600
447 sqlalchemy.db1.pool_recycle = 3600
446 sqlalchemy.db1.convert_unicode = true
448 sqlalchemy.db1.convert_unicode = true
447
449
448 ## the number of connections to keep open inside the connection pool.
450 ## the number of connections to keep open inside the connection pool.
449 ## 0 indicates no limit
451 ## 0 indicates no limit
450 #sqlalchemy.db1.pool_size = 5
452 #sqlalchemy.db1.pool_size = 5
451
453
452 ## the number of connections to allow in connection pool "overflow", that is
454 ## the number of connections to allow in connection pool "overflow", that is
453 ## connections that can be opened above and beyond the pool_size setting,
455 ## connections that can be opened above and beyond the pool_size setting,
454 ## which defaults to five.
456 ## which defaults to five.
455 #sqlalchemy.db1.max_overflow = 10
457 #sqlalchemy.db1.max_overflow = 10
456
458
457
459
458 ##################
460 ##################
459 ### VCS CONFIG ###
461 ### VCS CONFIG ###
460 ##################
462 ##################
461 vcs.server.enable = true
463 vcs.server.enable = true
462 vcs.server = localhost:9900
464 vcs.server = localhost:9900
463 # Available protocols: pyro4, http
465 # Available protocols: pyro4, http
464 vcs.server.protocol = pyro4
466 vcs.server.protocol = pyro4
465
467
466 # available impl:
468 # available impl:
467 # vcsserver.scm_app (EE only, for testing),
469 # vcsserver.scm_app (EE only, for testing),
468 # rhodecode.lib.middleware.utils.scm_app_http
470 # rhodecode.lib.middleware.utils.scm_app_http
469 # pyro4
471 # pyro4
470 #vcs.scm_app_implementation = rhodecode.lib.middleware.utils.scm_app_http
472 #vcs.scm_app_implementation = rhodecode.lib.middleware.utils.scm_app_http
471
473
472 vcs.server.log_level = debug
474 vcs.server.log_level = debug
473 vcs.start_server = true
475 vcs.start_server = true
474 vcs.backends = hg, git, svn
476 vcs.backends = hg, git, svn
475 vcs.connection_timeout = 3600
477 vcs.connection_timeout = 3600
476 ## Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
478 ## Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
477 ## Available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible
479 ## Available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible
478 #vcs.svn.compatible_version = pre-1.8-compatible
480 #vcs.svn.compatible_version = pre-1.8-compatible
479
481
480 ################################
482 ################################
481 ### LOGGING CONFIGURATION ####
483 ### LOGGING CONFIGURATION ####
482 ################################
484 ################################
483 [loggers]
485 [loggers]
484 keys = root, routes, rhodecode, sqlalchemy, beaker, pyro4, templates, whoosh_indexer
486 keys = root, routes, rhodecode, sqlalchemy, beaker, pyro4, templates, whoosh_indexer
485
487
486 [handlers]
488 [handlers]
487 keys = console, console_sql
489 keys = console, console_sql
488
490
489 [formatters]
491 [formatters]
490 keys = generic, color_formatter, color_formatter_sql
492 keys = generic, color_formatter, color_formatter_sql
491
493
492 #############
494 #############
493 ## LOGGERS ##
495 ## LOGGERS ##
494 #############
496 #############
495 [logger_root]
497 [logger_root]
496 level = NOTSET
498 level = NOTSET
497 handlers = console
499 handlers = console
498
500
499 [logger_routes]
501 [logger_routes]
500 level = DEBUG
502 level = DEBUG
501 handlers =
503 handlers =
502 qualname = routes.middleware
504 qualname = routes.middleware
503 ## "level = DEBUG" logs the route matched and routing variables.
505 ## "level = DEBUG" logs the route matched and routing variables.
504 propagate = 1
506 propagate = 1
505
507
506 [logger_beaker]
508 [logger_beaker]
507 level = DEBUG
509 level = DEBUG
508 handlers =
510 handlers =
509 qualname = beaker.container
511 qualname = beaker.container
510 propagate = 1
512 propagate = 1
511
513
512 [logger_pyro4]
514 [logger_pyro4]
513 level = DEBUG
515 level = DEBUG
514 handlers =
516 handlers =
515 qualname = Pyro4
517 qualname = Pyro4
516 propagate = 1
518 propagate = 1
517
519
518 [logger_templates]
520 [logger_templates]
519 level = INFO
521 level = INFO
520 handlers =
522 handlers =
521 qualname = pylons.templating
523 qualname = pylons.templating
522 propagate = 1
524 propagate = 1
523
525
524 [logger_rhodecode]
526 [logger_rhodecode]
525 level = DEBUG
527 level = DEBUG
526 handlers =
528 handlers =
527 qualname = rhodecode
529 qualname = rhodecode
528 propagate = 1
530 propagate = 1
529
531
530 [logger_sqlalchemy]
532 [logger_sqlalchemy]
531 level = INFO
533 level = INFO
532 handlers = console_sql
534 handlers = console_sql
533 qualname = sqlalchemy.engine
535 qualname = sqlalchemy.engine
534 propagate = 0
536 propagate = 0
535
537
536 [logger_whoosh_indexer]
538 [logger_whoosh_indexer]
537 level = DEBUG
539 level = DEBUG
538 handlers =
540 handlers =
539 qualname = whoosh_indexer
541 qualname = whoosh_indexer
540 propagate = 1
542 propagate = 1
541
543
542 ##############
544 ##############
543 ## HANDLERS ##
545 ## HANDLERS ##
544 ##############
546 ##############
545
547
546 [handler_console]
548 [handler_console]
547 class = StreamHandler
549 class = StreamHandler
548 args = (sys.stderr,)
550 args = (sys.stderr,)
549 level = DEBUG
551 level = DEBUG
550 formatter = color_formatter
552 formatter = color_formatter
551
553
552 [handler_console_sql]
554 [handler_console_sql]
553 class = StreamHandler
555 class = StreamHandler
554 args = (sys.stderr,)
556 args = (sys.stderr,)
555 level = DEBUG
557 level = DEBUG
556 formatter = color_formatter_sql
558 formatter = color_formatter_sql
557
559
558 ################
560 ################
559 ## FORMATTERS ##
561 ## FORMATTERS ##
560 ################
562 ################
561
563
562 [formatter_generic]
564 [formatter_generic]
563 class = rhodecode.lib.logging_formatter.Pyro4AwareFormatter
565 class = rhodecode.lib.logging_formatter.Pyro4AwareFormatter
564 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
566 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
565 datefmt = %Y-%m-%d %H:%M:%S
567 datefmt = %Y-%m-%d %H:%M:%S
566
568
567 [formatter_color_formatter]
569 [formatter_color_formatter]
568 class = rhodecode.lib.logging_formatter.ColorFormatter
570 class = rhodecode.lib.logging_formatter.ColorFormatter
569 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
571 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
570 datefmt = %Y-%m-%d %H:%M:%S
572 datefmt = %Y-%m-%d %H:%M:%S
571
573
572 [formatter_color_formatter_sql]
574 [formatter_color_formatter_sql]
573 class = rhodecode.lib.logging_formatter.ColorFormatterSql
575 class = rhodecode.lib.logging_formatter.ColorFormatterSql
574 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
576 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
575 datefmt = %Y-%m-%d %H:%M:%S
577 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,541 +1,551 b''
1 ################################################################################
1 ################################################################################
2 ################################################################################
2 ################################################################################
3 # RhodeCode Enterprise - configuration file #
3 # RhodeCode Enterprise - configuration file #
4 # Built-in functions and variables #
4 # Built-in functions and variables #
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
8
9 [DEFAULT]
9 [DEFAULT]
10 debug = true
10 debug = true
11 pdebug = false
11 pdebug = false
12 ################################################################################
12 ################################################################################
13 ## Uncomment and replace with the email address which should receive ##
13 ## Uncomment and replace with the email address which should receive ##
14 ## any error reports after an application crash ##
14 ## any error reports after an application crash ##
15 ## Additionally these settings will be used by the RhodeCode mailing system ##
15 ## Additionally these settings will be used by the RhodeCode mailing system ##
16 ################################################################################
16 ################################################################################
17 #email_to = admin@localhost
17 #email_to = admin@localhost
18 #error_email_from = paste_error@localhost
18 #error_email_from = paste_error@localhost
19 #app_email_from = rhodecode-noreply@localhost
19 #app_email_from = rhodecode-noreply@localhost
20 #error_message =
20 #error_message =
21 #email_prefix = [RhodeCode]
21 #email_prefix = [RhodeCode]
22
22
23 #smtp_server = mail.server.com
23 #smtp_server = mail.server.com
24 #smtp_username =
24 #smtp_username =
25 #smtp_password =
25 #smtp_password =
26 #smtp_port =
26 #smtp_port =
27 #smtp_use_tls = false
27 #smtp_use_tls = false
28 #smtp_use_ssl = true
28 #smtp_use_ssl = true
29 ## Specify available auth parameters here (e.g. LOGIN PLAIN CRAM-MD5, etc.)
29 ## Specify available auth parameters here (e.g. LOGIN PLAIN CRAM-MD5, etc.)
30 #smtp_auth =
30 #smtp_auth =
31
31
32 [server:main]
32 [server:main]
33 ## COMMON ##
33 ## COMMON ##
34 host = 127.0.0.1
34 host = 127.0.0.1
35 port = 5000
35 port = 5000
36
36
37 ##########################
37 ##########################
38 ## WAITRESS WSGI SERVER ##
38 ## WAITRESS WSGI SERVER ##
39 ##########################
39 ##########################
40 use = egg:waitress#main
40 use = egg:waitress#main
41 ## number of worker threads
41 ## number of worker threads
42 threads = 5
42 threads = 5
43 ## MAX BODY SIZE 100GB
43 ## MAX BODY SIZE 100GB
44 max_request_body_size = 107374182400
44 max_request_body_size = 107374182400
45 ## Use poll instead of select, fixes file descriptors limits problems.
45 ## Use poll instead of select, fixes file descriptors limits problems.
46 ## May not work on old windows systems.
46 ## May not work on old windows systems.
47 asyncore_use_poll = true
47 asyncore_use_poll = true
48
48
49
49
50 ##########################
50 ##########################
51 ## GUNICORN WSGI SERVER ##
51 ## GUNICORN WSGI SERVER ##
52 ##########################
52 ##########################
53 ## run with gunicorn --log-config <inifile.ini> --paste <inifile.ini>
53 ## run with gunicorn --log-config <inifile.ini> --paste <inifile.ini>
54 #use = egg:gunicorn#main
54 #use = egg:gunicorn#main
55 ## Sets the number of process workers. You must set `instance_id = *`
55 ## Sets the number of process workers. You must set `instance_id = *`
56 ## when this option is set to more than one worker, recommended
56 ## when this option is set to more than one worker, recommended
57 ## value is (2 * NUMBER_OF_CPUS + 1), eg 2CPU = 5 workers
57 ## value is (2 * NUMBER_OF_CPUS + 1), eg 2CPU = 5 workers
58 ## The `instance_id = *` must be set in the [app:main] section below
58 ## The `instance_id = *` must be set in the [app:main] section below
59 #workers = 1
59 #workers = 1
60 ## number of threads for each of the worker, must be set to 1 for gevent
60 ## number of threads for each of the worker, must be set to 1 for gevent
61 ## generally recommened to be at 1
61 ## generally recommened to be at 1
62 #threads = 1
62 #threads = 1
63 ## process name
63 ## process name
64 #proc_name = rhodecode
64 #proc_name = rhodecode
65 ## type of worker class, one of sync, gevent
65 ## type of worker class, one of sync, gevent
66 ## recommended for bigger setup is using of of other than sync one
66 ## recommended for bigger setup is using of of other than sync one
67 #worker_class = sync
67 #worker_class = sync
68 ## The maximum number of simultaneous clients. Valid only for Gevent
69 #worker_connections = 10
68 ## max number of requests that worker will handle before being gracefully
70 ## max number of requests that worker will handle before being gracefully
69 ## restarted, could prevent memory leaks
71 ## restarted, could prevent memory leaks
70 #max_requests = 1000
72 #max_requests = 1000
71 #max_requests_jitter = 30
73 #max_requests_jitter = 30
72 ## ammount of time a worker can spend with handling a request before it
74 ## ammount of time a worker can spend with handling a request before it
73 ## gets killed and restarted. Set to 6hrs
75 ## gets killed and restarted. Set to 6hrs
74 #timeout = 21600
76 #timeout = 21600
75
77
76
78
77 ## prefix middleware for RhodeCode, disables force_https flag.
79 ## prefix middleware for RhodeCode, disables force_https flag.
78 ## allows to set RhodeCode under a prefix in server.
80 ## allows to set RhodeCode under a prefix in server.
79 ## eg https://server.com/<prefix>. Enable `filter-with =` option below as well.
81 ## eg https://server.com/<prefix>. Enable `filter-with =` option below as well.
80 #[filter:proxy-prefix]
82 #[filter:proxy-prefix]
81 #use = egg:PasteDeploy#prefix
83 #use = egg:PasteDeploy#prefix
82 #prefix = /<your-prefix>
84 #prefix = /<your-prefix>
83
85
84 [app:main]
86 [app:main]
85 use = egg:rhodecode-enterprise-ce
87 use = egg:rhodecode-enterprise-ce
86 ## enable proxy prefix middleware, defined below
88 ## enable proxy prefix middleware, defined below
87 #filter-with = proxy-prefix
89 #filter-with = proxy-prefix
88
90
89 full_stack = true
91 full_stack = true
90
92
91 ## Serve static files via RhodeCode, disable to serve them via HTTP server
93 ## Serve static files via RhodeCode, disable to serve them via HTTP server
92 static_files = true
94 static_files = true
93
95
94 ## Optional Languages
96 ## Optional Languages
95 ## en(default), be, de, es, fr, it, ja, pl, pt, ru, zh
97 ## en(default), be, de, es, fr, it, ja, pl, pt, ru, zh
96 lang = en
98 lang = en
97
99
98 ## perform a full repository scan on each server start, this should be
100 ## perform a full repository scan on each server start, this should be
99 ## set to false after first startup, to allow faster server restarts.
101 ## set to false after first startup, to allow faster server restarts.
100 startup.import_repos = false
102 startup.import_repos = false
101
103
102 ## Uncomment and set this path to use archive download cache.
104 ## Uncomment and set this path to use archive download cache.
103 ## Once enabled, generated archives will be cached at this location
105 ## Once enabled, generated archives will be cached at this location
104 ## and served from the cache during subsequent requests for the same archive of
106 ## and served from the cache during subsequent requests for the same archive of
105 ## the repository.
107 ## the repository.
106 #archive_cache_dir = /tmp/tarballcache
108 #archive_cache_dir = /tmp/tarballcache
107
109
108 ## change this to unique ID for security
110 ## change this to unique ID for security
109 app_instance_uuid = rc-production
111 app_instance_uuid = rc-production
110
112
111 ## cut off limit for large diffs (size in bytes)
113 ## cut off limit for large diffs (size in bytes)
112 cut_off_limit_diff = 1024000
114 cut_off_limit_diff = 1024000
113 cut_off_limit_file = 256000
115 cut_off_limit_file = 256000
114
116
115 ## use cache version of scm repo everywhere
117 ## use cache version of scm repo everywhere
116 vcs_full_cache = true
118 vcs_full_cache = true
117
119
118 ## force https in RhodeCode, fixes https redirects, assumes it's always https
120 ## force https in RhodeCode, fixes https redirects, assumes it's always https
119 ## Normally this is controlled by proper http flags sent from http server
121 ## Normally this is controlled by proper http flags sent from http server
120 force_https = false
122 force_https = false
121
123
122 ## use Strict-Transport-Security headers
124 ## use Strict-Transport-Security headers
123 use_htsts = false
125 use_htsts = false
124
126
125 ## number of commits stats will parse on each iteration
127 ## number of commits stats will parse on each iteration
126 commit_parse_limit = 25
128 commit_parse_limit = 25
127
129
128 ## git rev filter option, --all is the default filter, if you need to
130 ## git rev filter option, --all is the default filter, if you need to
129 ## hide all refs in changelog switch this to --branches --tags
131 ## hide all refs in changelog switch this to --branches --tags
130 git_rev_filter = --branches --tags
132 git_rev_filter = --branches --tags
131
133
132 # Set to true if your repos are exposed using the dumb protocol
134 # Set to true if your repos are exposed using the dumb protocol
133 git_update_server_info = false
135 git_update_server_info = false
134
136
135 ## RSS/ATOM feed options
137 ## RSS/ATOM feed options
136 rss_cut_off_limit = 256000
138 rss_cut_off_limit = 256000
137 rss_items_per_page = 10
139 rss_items_per_page = 10
138 rss_include_diff = false
140 rss_include_diff = false
139
141
140 ## gist URL alias, used to create nicer urls for gist. This should be an
142 ## gist URL alias, used to create nicer urls for gist. This should be an
141 ## url that does rewrites to _admin/gists/<gistid>.
143 ## url that does rewrites to _admin/gists/<gistid>.
142 ## example: http://gist.rhodecode.org/{gistid}. Empty means use the internal
144 ## example: http://gist.rhodecode.org/{gistid}. Empty means use the internal
143 ## RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/<gistid>
145 ## RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/<gistid>
144 gist_alias_url =
146 gist_alias_url =
145
147
146 ## List of controllers (using glob pattern syntax) that AUTH TOKENS could be
148 ## List of controllers (using glob pattern syntax) that AUTH TOKENS could be
147 ## used for access.
149 ## used for access.
148 ## Adding ?auth_token = <token> to the url authenticates this request as if it
150 ## Adding ?auth_token = <token> to the url authenticates this request as if it
149 ## came from the the logged in user who own this authentication token.
151 ## came from the the logged in user who own this authentication token.
150 ##
152 ##
151 ## Syntax is <ControllerClass>:<function_pattern>.
153 ## Syntax is <ControllerClass>:<function_pattern>.
152 ## To enable access to raw_files put `FilesController:raw`.
154 ## To enable access to raw_files put `FilesController:raw`.
153 ## To enable access to patches add `ChangesetController:changeset_patch`.
155 ## To enable access to patches add `ChangesetController:changeset_patch`.
154 ## The list should be "," separated and on a single line.
156 ## The list should be "," separated and on a single line.
155 ##
157 ##
156 ## Recommended controllers to enable:
158 ## Recommended controllers to enable:
157 # ChangesetController:changeset_patch,
159 # ChangesetController:changeset_patch,
158 # ChangesetController:changeset_raw,
160 # ChangesetController:changeset_raw,
159 # FilesController:raw,
161 # FilesController:raw,
160 # FilesController:archivefile,
162 # FilesController:archivefile,
161 # GistsController:*,
163 # GistsController:*,
162 api_access_controllers_whitelist =
164 api_access_controllers_whitelist =
163
165
164 ## default encoding used to convert from and to unicode
166 ## default encoding used to convert from and to unicode
165 ## can be also a comma separated list of encoding in case of mixed encodings
167 ## can be also a comma separated list of encoding in case of mixed encodings
166 default_encoding = UTF-8
168 default_encoding = UTF-8
167
169
168 ## instance-id prefix
170 ## instance-id prefix
169 ## a prefix key for this instance used for cache invalidation when running
171 ## a prefix key for this instance used for cache invalidation when running
170 ## multiple instances of rhodecode, make sure it's globally unique for
172 ## multiple instances of rhodecode, make sure it's globally unique for
171 ## all running rhodecode instances. Leave empty if you don't use it
173 ## all running rhodecode instances. Leave empty if you don't use it
172 instance_id =
174 instance_id =
173
175
174 ## alternative return HTTP header for failed authentication. Default HTTP
176 ## alternative return HTTP header for failed authentication. Default HTTP
175 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
177 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
176 ## handling that causing a series of failed authentication calls.
178 ## handling that causing a series of failed authentication calls.
177 ## Set this variable to 403 to return HTTPForbidden, or any other HTTP code
179 ## Set this variable to 403 to return HTTPForbidden, or any other HTTP code
178 ## This will be served instead of default 401 on bad authnetication
180 ## This will be served instead of default 401 on bad authnetication
179 auth_ret_code =
181 auth_ret_code =
180
182
181 ## use special detection method when serving auth_ret_code, instead of serving
183 ## use special detection method when serving auth_ret_code, instead of serving
182 ## ret_code directly, use 401 initially (Which triggers credentials prompt)
184 ## ret_code directly, use 401 initially (Which triggers credentials prompt)
183 ## and then serve auth_ret_code to clients
185 ## and then serve auth_ret_code to clients
184 auth_ret_code_detection = false
186 auth_ret_code_detection = false
185
187
186 ## locking return code. When repository is locked return this HTTP code. 2XX
188 ## locking return code. When repository is locked return this HTTP code. 2XX
187 ## codes don't break the transactions while 4XX codes do
189 ## codes don't break the transactions while 4XX codes do
188 lock_ret_code = 423
190 lock_ret_code = 423
189
191
190 ## allows to change the repository location in settings page
192 ## allows to change the repository location in settings page
191 allow_repo_location_change = true
193 allow_repo_location_change = true
192
194
193 ## allows to setup custom hooks in settings page
195 ## allows to setup custom hooks in settings page
194 allow_custom_hooks_settings = true
196 allow_custom_hooks_settings = true
195
197
196 ## generated license token, goto license page in RhodeCode settings to obtain
198 ## generated license token, goto license page in RhodeCode settings to obtain
197 ## new token
199 ## new token
198 license_token =
200 license_token =
199
201
200 ## supervisor connection uri, for managing supervisor and logs.
202 ## supervisor connection uri, for managing supervisor and logs.
201 supervisor.uri =
203 supervisor.uri =
202 ## supervisord group name/id we only want this RC instance to handle
204 ## supervisord group name/id we only want this RC instance to handle
203 supervisor.group_id = prod
205 supervisor.group_id = prod
204
206
205 ## Display extended labs settings
207 ## Display extended labs settings
206 labs_settings_active = true
208 labs_settings_active = true
207
209
208 ####################################
210 ####################################
209 ### CELERY CONFIG ####
211 ### CELERY CONFIG ####
210 ####################################
212 ####################################
211 use_celery = false
213 use_celery = false
212 broker.host = localhost
214 broker.host = localhost
213 broker.vhost = rabbitmqhost
215 broker.vhost = rabbitmqhost
214 broker.port = 5672
216 broker.port = 5672
215 broker.user = rabbitmq
217 broker.user = rabbitmq
216 broker.password = qweqwe
218 broker.password = qweqwe
217
219
218 celery.imports = rhodecode.lib.celerylib.tasks
220 celery.imports = rhodecode.lib.celerylib.tasks
219
221
220 celery.result.backend = amqp
222 celery.result.backend = amqp
221 celery.result.dburi = amqp://
223 celery.result.dburi = amqp://
222 celery.result.serialier = json
224 celery.result.serialier = json
223
225
224 #celery.send.task.error.emails = true
226 #celery.send.task.error.emails = true
225 #celery.amqp.task.result.expires = 18000
227 #celery.amqp.task.result.expires = 18000
226
228
227 celeryd.concurrency = 2
229 celeryd.concurrency = 2
228 #celeryd.log.file = celeryd.log
230 #celeryd.log.file = celeryd.log
229 celeryd.log.level = debug
231 celeryd.log.level = debug
230 celeryd.max.tasks.per.child = 1
232 celeryd.max.tasks.per.child = 1
231
233
232 ## tasks will never be sent to the queue, but executed locally instead.
234 ## tasks will never be sent to the queue, but executed locally instead.
233 celery.always.eager = false
235 celery.always.eager = false
234
236
235 ####################################
237 ####################################
236 ### BEAKER CACHE ####
238 ### BEAKER CACHE ####
237 ####################################
239 ####################################
238 # default cache dir for templates. Putting this into a ramdisk
240 # default cache dir for templates. Putting this into a ramdisk
239 ## can boost performance, eg. %(here)s/data_ramdisk
241 ## can boost performance, eg. %(here)s/data_ramdisk
240 cache_dir = %(here)s/data
242 cache_dir = %(here)s/data
241
243
242 ## locking and default file storage for Beaker. Putting this into a ramdisk
244 ## locking and default file storage for Beaker. Putting this into a ramdisk
243 ## can boost performance, eg. %(here)s/data_ramdisk/cache/beaker_data
245 ## can boost performance, eg. %(here)s/data_ramdisk/cache/beaker_data
244 beaker.cache.data_dir = %(here)s/data/cache/beaker_data
246 beaker.cache.data_dir = %(here)s/data/cache/beaker_data
245 beaker.cache.lock_dir = %(here)s/data/cache/beaker_lock
247 beaker.cache.lock_dir = %(here)s/data/cache/beaker_lock
246
248
247 beaker.cache.regions = super_short_term, short_term, long_term, sql_cache_short, auth_plugins, repo_cache_long
249 beaker.cache.regions = super_short_term, short_term, long_term, sql_cache_short, auth_plugins, repo_cache_long
248
250
249 beaker.cache.super_short_term.type = memory
251 beaker.cache.super_short_term.type = memory
250 beaker.cache.super_short_term.expire = 10
252 beaker.cache.super_short_term.expire = 10
251 beaker.cache.super_short_term.key_length = 256
253 beaker.cache.super_short_term.key_length = 256
252
254
253 beaker.cache.short_term.type = memory
255 beaker.cache.short_term.type = memory
254 beaker.cache.short_term.expire = 60
256 beaker.cache.short_term.expire = 60
255 beaker.cache.short_term.key_length = 256
257 beaker.cache.short_term.key_length = 256
256
258
257 beaker.cache.long_term.type = memory
259 beaker.cache.long_term.type = memory
258 beaker.cache.long_term.expire = 36000
260 beaker.cache.long_term.expire = 36000
259 beaker.cache.long_term.key_length = 256
261 beaker.cache.long_term.key_length = 256
260
262
261 beaker.cache.sql_cache_short.type = memory
263 beaker.cache.sql_cache_short.type = memory
262 beaker.cache.sql_cache_short.expire = 10
264 beaker.cache.sql_cache_short.expire = 10
263 beaker.cache.sql_cache_short.key_length = 256
265 beaker.cache.sql_cache_short.key_length = 256
264
266
265 # default is memory cache, configure only if required
267 # default is memory cache, configure only if required
266 # using multi-node or multi-worker setup
268 # using multi-node or multi-worker setup
267 #beaker.cache.auth_plugins.type = ext:database
269 #beaker.cache.auth_plugins.type = ext:database
268 #beaker.cache.auth_plugins.lock_dir = %(here)s/data/cache/auth_plugin_lock
270 #beaker.cache.auth_plugins.lock_dir = %(here)s/data/cache/auth_plugin_lock
269 #beaker.cache.auth_plugins.url = postgresql://postgres:secret@localhost/rhodecode
271 #beaker.cache.auth_plugins.url = postgresql://postgres:secret@localhost/rhodecode
270 #beaker.cache.auth_plugins.url = mysql://root:secret@127.0.0.1/rhodecode
272 #beaker.cache.auth_plugins.url = mysql://root:secret@127.0.0.1/rhodecode
271 #beaker.cache.auth_plugins.sa.pool_recycle = 3600
273 #beaker.cache.auth_plugins.sa.pool_recycle = 3600
272 #beaker.cache.auth_plugins.sa.pool_size = 10
274 #beaker.cache.auth_plugins.sa.pool_size = 10
273 #beaker.cache.auth_plugins.sa.max_overflow = 0
275 #beaker.cache.auth_plugins.sa.max_overflow = 0
274
276
275 beaker.cache.repo_cache_long.type = memorylru_base
277 beaker.cache.repo_cache_long.type = memorylru_base
276 beaker.cache.repo_cache_long.max_items = 4096
278 beaker.cache.repo_cache_long.max_items = 4096
277 beaker.cache.repo_cache_long.expire = 2592000
279 beaker.cache.repo_cache_long.expire = 2592000
278
280
279 # default is memorylru_base cache, configure only if required
281 # default is memorylru_base cache, configure only if required
280 # using multi-node or multi-worker setup
282 # using multi-node or multi-worker setup
281 #beaker.cache.repo_cache_long.type = ext:memcached
283 #beaker.cache.repo_cache_long.type = ext:memcached
282 #beaker.cache.repo_cache_long.url = localhost:11211
284 #beaker.cache.repo_cache_long.url = localhost:11211
283 #beaker.cache.repo_cache_long.expire = 1209600
285 #beaker.cache.repo_cache_long.expire = 1209600
284 #beaker.cache.repo_cache_long.key_length = 256
286 #beaker.cache.repo_cache_long.key_length = 256
285
287
286 ####################################
288 ####################################
287 ### BEAKER SESSION ####
289 ### BEAKER SESSION ####
288 ####################################
290 ####################################
289
291
290 ## .session.type is type of storage options for the session, current allowed
292 ## .session.type is type of storage options for the session, current allowed
291 ## types are file(default), ext:memcached, ext:database, and memory.
293 ## types are file, ext:memcached, ext:database, and memory(default).
292 #beaker.session.type = file
294 beaker.session.type = file
295 beaker.session.data_dir = %(here)s/data/sessions/data
293
296
294 ## db based session, fast, and allows easy management over logged in users ##
297 ## db based session, fast, and allows easy management over logged in users ##
295 #beaker.session.type = ext:database
298 #beaker.session.type = ext:database
296 #beaker.session.lock_dir = %(here)s/data/cache/session_db_lock
297 #beaker.session.table_name = db_session
299 #beaker.session.table_name = db_session
298 #beaker.session.sa.url = postgresql://postgres:secret@localhost/rhodecode
300 #beaker.session.sa.url = postgresql://postgres:secret@localhost/rhodecode
299 #beaker.session.sa.url = mysql://root:secret@127.0.0.1/rhodecode
301 #beaker.session.sa.url = mysql://root:secret@127.0.0.1/rhodecode
300 #beaker.session.sa.pool_recycle = 3600
302 #beaker.session.sa.pool_recycle = 3600
301 #beaker.session.sa.echo = false
303 #beaker.session.sa.echo = false
302
304
303 beaker.session.key = rhodecode
305 beaker.session.key = rhodecode
304 beaker.session.secret = production-rc-uytcxaz
306 beaker.session.secret = production-rc-uytcxaz
307 #beaker.session.lock_dir = %(here)s/data/sessions/lock
305
308
306 ## Secure encrypted cookie. Requires AES and AES python libraries
309 ## Secure encrypted cookie. Requires AES and AES python libraries
307 ## you must disable beaker.session.secret to use this
310 ## you must disable beaker.session.secret to use this
308 #beaker.session.encrypt_key = <key_for_encryption>
311 #beaker.session.encrypt_key = <key_for_encryption>
309 #beaker.session.validate_key = <validation_key>
312 #beaker.session.validate_key = <validation_key>
310
313
311 ## sets session as invalid(also logging out user) if it haven not been
314 ## sets session as invalid(also logging out user) if it haven not been
312 ## accessed for given amount of time in seconds
315 ## accessed for given amount of time in seconds
313 beaker.session.timeout = 2592000
316 beaker.session.timeout = 2592000
314 beaker.session.httponly = true
317 beaker.session.httponly = true
315 #beaker.session.cookie_path = /<your-prefix>
318 #beaker.session.cookie_path = /<your-prefix>
316
319
317 ## uncomment for https secure cookie
320 ## uncomment for https secure cookie
318 beaker.session.secure = false
321 beaker.session.secure = false
319
322
320 ## auto save the session to not to use .save()
323 ## auto save the session to not to use .save()
321 beaker.session.auto = false
324 beaker.session.auto = false
322
325
323 ## default cookie expiration time in seconds, set to `true` to set expire
326 ## default cookie expiration time in seconds, set to `true` to set expire
324 ## at browser close
327 ## at browser close
325 #beaker.session.cookie_expires = 3600
328 #beaker.session.cookie_expires = 3600
326
329
327 ###################################
330 ###################################
328 ## SEARCH INDEXING CONFIGURATION ##
331 ## SEARCH INDEXING CONFIGURATION ##
329 ###################################
332 ###################################
330
333
331 search.module = rhodecode.lib.index.whoosh
334 search.module = rhodecode.lib.index.whoosh
332 search.location = %(here)s/data/index
335 search.location = %(here)s/data/index
333
336
334 ###################################
337 ###################################
335 ## ERROR AND LOG HANDLING SYSTEM ##
338 ## ERROR AND LOG HANDLING SYSTEM ##
336 ###################################
339 ###################################
337
340
338 ## Appenlight is tailored to work with RhodeCode, see
341 ## Appenlight is tailored to work with RhodeCode, see
339 ## http://appenlight.com for details how to obtain an account
342 ## http://appenlight.com for details how to obtain an account
340
343
341 ## appenlight integration enabled
344 ## appenlight integration enabled
342 appenlight = false
345 appenlight = false
343
346
344 appenlight.server_url = https://api.appenlight.com
347 appenlight.server_url = https://api.appenlight.com
345 appenlight.api_key = YOUR_API_KEY
348 appenlight.api_key = YOUR_API_KEY
346 ;appenlight.transport_config = https://api.appenlight.com?threaded=1&timeout=5
349 ;appenlight.transport_config = https://api.appenlight.com?threaded=1&timeout=5
347
350
348 # used for JS client
351 # used for JS client
349 appenlight.api_public_key = YOUR_API_PUBLIC_KEY
352 appenlight.api_public_key = YOUR_API_PUBLIC_KEY
350
353
351 ## TWEAK AMOUNT OF INFO SENT HERE
354 ## TWEAK AMOUNT OF INFO SENT HERE
352
355
353 ## enables 404 error logging (default False)
356 ## enables 404 error logging (default False)
354 appenlight.report_404 = false
357 appenlight.report_404 = false
355
358
356 ## time in seconds after request is considered being slow (default 1)
359 ## time in seconds after request is considered being slow (default 1)
357 appenlight.slow_request_time = 1
360 appenlight.slow_request_time = 1
358
361
359 ## record slow requests in application
362 ## record slow requests in application
360 ## (needs to be enabled for slow datastore recording and time tracking)
363 ## (needs to be enabled for slow datastore recording and time tracking)
361 appenlight.slow_requests = true
364 appenlight.slow_requests = true
362
365
363 ## enable hooking to application loggers
366 ## enable hooking to application loggers
364 appenlight.logging = true
367 appenlight.logging = true
365
368
366 ## minimum log level for log capture
369 ## minimum log level for log capture
367 appenlight.logging.level = WARNING
370 appenlight.logging.level = WARNING
368
371
369 ## send logs only from erroneous/slow requests
372 ## send logs only from erroneous/slow requests
370 ## (saves API quota for intensive logging)
373 ## (saves API quota for intensive logging)
371 appenlight.logging_on_error = false
374 appenlight.logging_on_error = false
372
375
373 ## list of additonal keywords that should be grabbed from environ object
376 ## list of additonal keywords that should be grabbed from environ object
374 ## can be string with comma separated list of words in lowercase
377 ## can be string with comma separated list of words in lowercase
375 ## (by default client will always send following info:
378 ## (by default client will always send following info:
376 ## 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
379 ## 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
377 ## start with HTTP* this list be extended with additional keywords here
380 ## start with HTTP* this list be extended with additional keywords here
378 appenlight.environ_keys_whitelist =
381 appenlight.environ_keys_whitelist =
379
382
380 ## list of keywords that should be blanked from request object
383 ## list of keywords that should be blanked from request object
381 ## can be string with comma separated list of words in lowercase
384 ## can be string with comma separated list of words in lowercase
382 ## (by default client will always blank keys that contain following words
385 ## (by default client will always blank keys that contain following words
383 ## 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
386 ## 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
384 ## this list be extended with additional keywords set here
387 ## this list be extended with additional keywords set here
385 appenlight.request_keys_blacklist =
388 appenlight.request_keys_blacklist =
386
389
387 ## list of namespaces that should be ignores when gathering log entries
390 ## list of namespaces that should be ignores when gathering log entries
388 ## can be string with comma separated list of namespaces
391 ## can be string with comma separated list of namespaces
389 ## (by default the client ignores own entries: appenlight_client.client)
392 ## (by default the client ignores own entries: appenlight_client.client)
390 appenlight.log_namespace_blacklist =
393 appenlight.log_namespace_blacklist =
391
394
392
395
393 ################################################################################
396 ################################################################################
394 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
397 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
395 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
398 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
396 ## execute malicious code after an exception is raised. ##
399 ## execute malicious code after an exception is raised. ##
397 ################################################################################
400 ################################################################################
398 set debug = false
401 set debug = false
399
402
400
403
401 ##############
404 ##############
402 ## STYLING ##
405 ## STYLING ##
403 ##############
406 ##############
404 debug_style = false
407 debug_style = false
405
408
406 #########################################################
409 #########################################################
407 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
410 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
408 #########################################################
411 #########################################################
409 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30
412 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30
410 sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode
413 sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode
411 #sqlalchemy.db1.url = mysql://root:qweqwe@localhost/rhodecode
414 #sqlalchemy.db1.url = mysql://root:qweqwe@localhost/rhodecode
412
415
413 # see sqlalchemy docs for other advanced settings
416 # see sqlalchemy docs for other advanced settings
414
417
415 ## print the sql statements to output
418 ## print the sql statements to output
416 sqlalchemy.db1.echo = false
419 sqlalchemy.db1.echo = false
417 ## recycle the connections after this ammount of seconds
420 ## recycle the connections after this ammount of seconds
418 sqlalchemy.db1.pool_recycle = 3600
421 sqlalchemy.db1.pool_recycle = 3600
419 sqlalchemy.db1.convert_unicode = true
422 sqlalchemy.db1.convert_unicode = true
420
423
421 ## the number of connections to keep open inside the connection pool.
424 ## the number of connections to keep open inside the connection pool.
422 ## 0 indicates no limit
425 ## 0 indicates no limit
423 #sqlalchemy.db1.pool_size = 5
426 #sqlalchemy.db1.pool_size = 5
424
427
425 ## the number of connections to allow in connection pool "overflow", that is
428 ## the number of connections to allow in connection pool "overflow", that is
426 ## connections that can be opened above and beyond the pool_size setting,
429 ## connections that can be opened above and beyond the pool_size setting,
427 ## which defaults to five.
430 ## which defaults to five.
428 #sqlalchemy.db1.max_overflow = 10
431 #sqlalchemy.db1.max_overflow = 10
429
432
430
433
431 ##################
434 ##################
432 ### VCS CONFIG ###
435 ### VCS CONFIG ###
433 ##################
436 ##################
434 vcs.server.enable = true
437 vcs.server.enable = true
435 vcs.server = localhost:9900
438 vcs.server = localhost:9900
436 # Available protocols: pyro4, http
439 # Available protocols: pyro4, http
437 vcs.server.protocol = pyro4
440 vcs.server.protocol = pyro4
441
442 # available impl:
443 # vcsserver.scm_app (EE only, for testing),
444 # rhodecode.lib.middleware.utils.scm_app_http
445 # pyro4
446 #vcs.scm_app_implementation = rhodecode.lib.middleware.utils.scm_app_http
447
438 vcs.server.log_level = info
448 vcs.server.log_level = info
439 vcs.start_server = false
449 vcs.start_server = false
440 vcs.backends = hg, git, svn
450 vcs.backends = hg, git, svn
441 vcs.connection_timeout = 3600
451 vcs.connection_timeout = 3600
442 ## Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
452 ## Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
443 ## Available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible
453 ## Available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible
444 #vcs.svn.compatible_version = pre-1.8-compatible
454 #vcs.svn.compatible_version = pre-1.8-compatible
445
455
446 ################################
456 ################################
447 ### LOGGING CONFIGURATION ####
457 ### LOGGING CONFIGURATION ####
448 ################################
458 ################################
449 [loggers]
459 [loggers]
450 keys = root, routes, rhodecode, sqlalchemy, beaker, pyro4, templates, whoosh_indexer
460 keys = root, routes, rhodecode, sqlalchemy, beaker, pyro4, templates, whoosh_indexer
451
461
452 [handlers]
462 [handlers]
453 keys = console, console_sql
463 keys = console, console_sql
454
464
455 [formatters]
465 [formatters]
456 keys = generic, color_formatter, color_formatter_sql
466 keys = generic, color_formatter, color_formatter_sql
457
467
458 #############
468 #############
459 ## LOGGERS ##
469 ## LOGGERS ##
460 #############
470 #############
461 [logger_root]
471 [logger_root]
462 level = NOTSET
472 level = NOTSET
463 handlers = console
473 handlers = console
464
474
465 [logger_routes]
475 [logger_routes]
466 level = DEBUG
476 level = DEBUG
467 handlers =
477 handlers =
468 qualname = routes.middleware
478 qualname = routes.middleware
469 ## "level = DEBUG" logs the route matched and routing variables.
479 ## "level = DEBUG" logs the route matched and routing variables.
470 propagate = 1
480 propagate = 1
471
481
472 [logger_beaker]
482 [logger_beaker]
473 level = DEBUG
483 level = DEBUG
474 handlers =
484 handlers =
475 qualname = beaker.container
485 qualname = beaker.container
476 propagate = 1
486 propagate = 1
477
487
478 [logger_pyro4]
488 [logger_pyro4]
479 level = DEBUG
489 level = DEBUG
480 handlers =
490 handlers =
481 qualname = Pyro4
491 qualname = Pyro4
482 propagate = 1
492 propagate = 1
483
493
484 [logger_templates]
494 [logger_templates]
485 level = INFO
495 level = INFO
486 handlers =
496 handlers =
487 qualname = pylons.templating
497 qualname = pylons.templating
488 propagate = 1
498 propagate = 1
489
499
490 [logger_rhodecode]
500 [logger_rhodecode]
491 level = DEBUG
501 level = DEBUG
492 handlers =
502 handlers =
493 qualname = rhodecode
503 qualname = rhodecode
494 propagate = 1
504 propagate = 1
495
505
496 [logger_sqlalchemy]
506 [logger_sqlalchemy]
497 level = INFO
507 level = INFO
498 handlers = console_sql
508 handlers = console_sql
499 qualname = sqlalchemy.engine
509 qualname = sqlalchemy.engine
500 propagate = 0
510 propagate = 0
501
511
502 [logger_whoosh_indexer]
512 [logger_whoosh_indexer]
503 level = DEBUG
513 level = DEBUG
504 handlers =
514 handlers =
505 qualname = whoosh_indexer
515 qualname = whoosh_indexer
506 propagate = 1
516 propagate = 1
507
517
508 ##############
518 ##############
509 ## HANDLERS ##
519 ## HANDLERS ##
510 ##############
520 ##############
511
521
512 [handler_console]
522 [handler_console]
513 class = StreamHandler
523 class = StreamHandler
514 args = (sys.stderr,)
524 args = (sys.stderr,)
515 level = INFO
525 level = INFO
516 formatter = generic
526 formatter = generic
517
527
518 [handler_console_sql]
528 [handler_console_sql]
519 class = StreamHandler
529 class = StreamHandler
520 args = (sys.stderr,)
530 args = (sys.stderr,)
521 level = WARN
531 level = WARN
522 formatter = generic
532 formatter = generic
523
533
524 ################
534 ################
525 ## FORMATTERS ##
535 ## FORMATTERS ##
526 ################
536 ################
527
537
528 [formatter_generic]
538 [formatter_generic]
529 class = rhodecode.lib.logging_formatter.Pyro4AwareFormatter
539 class = rhodecode.lib.logging_formatter.Pyro4AwareFormatter
530 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
540 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
531 datefmt = %Y-%m-%d %H:%M:%S
541 datefmt = %Y-%m-%d %H:%M:%S
532
542
533 [formatter_color_formatter]
543 [formatter_color_formatter]
534 class = rhodecode.lib.logging_formatter.ColorFormatter
544 class = rhodecode.lib.logging_formatter.ColorFormatter
535 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
545 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
536 datefmt = %Y-%m-%d %H:%M:%S
546 datefmt = %Y-%m-%d %H:%M:%S
537
547
538 [formatter_color_formatter_sql]
548 [formatter_color_formatter_sql]
539 class = rhodecode.lib.logging_formatter.ColorFormatterSql
549 class = rhodecode.lib.logging_formatter.ColorFormatterSql
540 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
550 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
541 datefmt = %Y-%m-%d %H:%M:%S
551 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,75 +1,76 b''
1 .. _rhodecode-release-notes-ref:
1 .. _rhodecode-release-notes-ref:
2
2
3 Release Notes
3 Release Notes
4 =============
4 =============
5
5
6 |RCE| 4.x Versions
6 |RCE| 4.x Versions
7 ------------------
7 ------------------
8
8
9 release-notes-4.0.1.rst
9 release-notes-4.0.0.rst
10 release-notes-4.0.0.rst
10
11
11 |RCE| 3.x Versions
12 |RCE| 3.x Versions
12 ------------------
13 ------------------
13
14
14 .. toctree::
15 .. toctree::
15 :maxdepth: 1
16 :maxdepth: 1
16
17
17 release-notes-3.8.4.rst
18 release-notes-3.8.4.rst
18 release-notes-3.8.3.rst
19 release-notes-3.8.3.rst
19 release-notes-3.8.2.rst
20 release-notes-3.8.2.rst
20 release-notes-3.8.1.rst
21 release-notes-3.8.1.rst
21 release-notes-3.8.0.rst
22 release-notes-3.8.0.rst
22 release-notes-3.7.1.rst
23 release-notes-3.7.1.rst
23 release-notes-3.7.0.rst
24 release-notes-3.7.0.rst
24 release-notes-3.6.1.rst
25 release-notes-3.6.1.rst
25 release-notes-3.6.0.rst
26 release-notes-3.6.0.rst
26 release-notes-3.5.2.rst
27 release-notes-3.5.2.rst
27 release-notes-3.5.1.rst
28 release-notes-3.5.1.rst
28 release-notes-3.5.0.rst
29 release-notes-3.5.0.rst
29 release-notes-3.4.1.rst
30 release-notes-3.4.1.rst
30 release-notes-3.4.0.rst
31 release-notes-3.4.0.rst
31 release-notes-3.3.4.rst
32 release-notes-3.3.4.rst
32 release-notes-3.3.3.rst
33 release-notes-3.3.3.rst
33 release-notes-3.3.2.rst
34 release-notes-3.3.2.rst
34 release-notes-3.3.1.rst
35 release-notes-3.3.1.rst
35 release-notes-3.3.0.rst
36 release-notes-3.3.0.rst
36 release-notes-3.2.3.rst
37 release-notes-3.2.3.rst
37 release-notes-3.2.2.rst
38 release-notes-3.2.2.rst
38 release-notes-3.2.1.rst
39 release-notes-3.2.1.rst
39 release-notes-3.2.0.rst
40 release-notes-3.2.0.rst
40 release-notes-3.1.1.rst
41 release-notes-3.1.1.rst
41 release-notes-3.1.0.rst
42 release-notes-3.1.0.rst
42 release-notes-3.0.2.rst
43 release-notes-3.0.2.rst
43 release-notes-3.0.1.rst
44 release-notes-3.0.1.rst
44 release-notes-3.0.0.rst
45 release-notes-3.0.0.rst
45
46
46 |RCE| 2.x Versions
47 |RCE| 2.x Versions
47 ------------------
48 ------------------
48
49
49 .. toctree::
50 .. toctree::
50 :maxdepth: 1
51 :maxdepth: 1
51
52
52 release-notes-2.2.8.rst
53 release-notes-2.2.8.rst
53 release-notes-2.2.7.rst
54 release-notes-2.2.7.rst
54 release-notes-2.2.6.rst
55 release-notes-2.2.6.rst
55 release-notes-2.2.5.rst
56 release-notes-2.2.5.rst
56 release-notes-2.2.4.rst
57 release-notes-2.2.4.rst
57 release-notes-2.2.3.rst
58 release-notes-2.2.3.rst
58 release-notes-2.2.2.rst
59 release-notes-2.2.2.rst
59 release-notes-2.2.1.rst
60 release-notes-2.2.1.rst
60 release-notes-2.2.0.rst
61 release-notes-2.2.0.rst
61 release-notes-2.1.0.rst
62 release-notes-2.1.0.rst
62 release-notes-2.0.2.rst
63 release-notes-2.0.2.rst
63 release-notes-2.0.1.rst
64 release-notes-2.0.1.rst
64 release-notes-2.0.0.rst
65 release-notes-2.0.0.rst
65
66
66 |RCE| 1.x Versions
67 |RCE| 1.x Versions
67 ------------------
68 ------------------
68
69
69 .. toctree::
70 .. toctree::
70 :maxdepth: 1
71 :maxdepth: 1
71
72
72 release-notes-1.7.2.rst
73 release-notes-1.7.2.rst
73 release-notes-1.7.1.rst
74 release-notes-1.7.1.rst
74 release-notes-1.7.0.rst
75 release-notes-1.7.0.rst
75 release-notes-1.6.0.rst
76 release-notes-1.6.0.rst
@@ -1,1273 +1,1273 b''
1 {
1 {
2 Babel = super.buildPythonPackage {
2 Babel = super.buildPythonPackage {
3 name = "Babel-1.3";
3 name = "Babel-1.3";
4 buildInputs = with self; [];
4 buildInputs = with self; [];
5 doCheck = false;
5 doCheck = false;
6 propagatedBuildInputs = with self; [pytz];
6 propagatedBuildInputs = with self; [pytz];
7 src = fetchurl {
7 src = fetchurl {
8 url = "https://pypi.python.org/packages/33/27/e3978243a03a76398c384c83f7ca879bc6e8f1511233a621fcada135606e/Babel-1.3.tar.gz";
8 url = "https://pypi.python.org/packages/33/27/e3978243a03a76398c384c83f7ca879bc6e8f1511233a621fcada135606e/Babel-1.3.tar.gz";
9 md5 = "5264ceb02717843cbc9ffce8e6e06bdb";
9 md5 = "5264ceb02717843cbc9ffce8e6e06bdb";
10 };
10 };
11 };
11 };
12 Beaker = super.buildPythonPackage {
12 Beaker = super.buildPythonPackage {
13 name = "Beaker-1.7.0";
13 name = "Beaker-1.7.0";
14 buildInputs = with self; [];
14 buildInputs = with self; [];
15 doCheck = false;
15 doCheck = false;
16 propagatedBuildInputs = with self; [];
16 propagatedBuildInputs = with self; [];
17 src = fetchurl {
17 src = fetchurl {
18 url = "https://pypi.python.org/packages/97/8e/409d2e7c009b8aa803dc9e6f239f1db7c3cdf578249087a404e7c27a505d/Beaker-1.7.0.tar.gz";
18 url = "https://pypi.python.org/packages/97/8e/409d2e7c009b8aa803dc9e6f239f1db7c3cdf578249087a404e7c27a505d/Beaker-1.7.0.tar.gz";
19 md5 = "386be3f7fe427358881eee4622b428b3";
19 md5 = "386be3f7fe427358881eee4622b428b3";
20 };
20 };
21 };
21 };
22 CProfileV = super.buildPythonPackage {
22 CProfileV = super.buildPythonPackage {
23 name = "CProfileV-1.0.6";
23 name = "CProfileV-1.0.6";
24 buildInputs = with self; [];
24 buildInputs = with self; [];
25 doCheck = false;
25 doCheck = false;
26 propagatedBuildInputs = with self; [bottle];
26 propagatedBuildInputs = with self; [bottle];
27 src = fetchurl {
27 src = fetchurl {
28 url = "https://pypi.python.org/packages/eb/df/983a0b6cfd3ac94abf023f5011cb04f33613ace196e33f53c86cf91850d5/CProfileV-1.0.6.tar.gz";
28 url = "https://pypi.python.org/packages/eb/df/983a0b6cfd3ac94abf023f5011cb04f33613ace196e33f53c86cf91850d5/CProfileV-1.0.6.tar.gz";
29 md5 = "08c7c242b6e64237bc53c5d13537e03d";
29 md5 = "08c7c242b6e64237bc53c5d13537e03d";
30 };
30 };
31 };
31 };
32 Fabric = super.buildPythonPackage {
32 Fabric = super.buildPythonPackage {
33 name = "Fabric-1.10.0";
33 name = "Fabric-1.10.0";
34 buildInputs = with self; [];
34 buildInputs = with self; [];
35 doCheck = false;
35 doCheck = false;
36 propagatedBuildInputs = with self; [paramiko];
36 propagatedBuildInputs = with self; [paramiko];
37 src = fetchurl {
37 src = fetchurl {
38 url = "https://pypi.python.org/packages/e3/5f/b6ebdb5241d5ec9eab582a5c8a01255c1107da396f849e538801d2fe64a5/Fabric-1.10.0.tar.gz";
38 url = "https://pypi.python.org/packages/e3/5f/b6ebdb5241d5ec9eab582a5c8a01255c1107da396f849e538801d2fe64a5/Fabric-1.10.0.tar.gz";
39 md5 = "2cb96473387f0e7aa035210892352f4a";
39 md5 = "2cb96473387f0e7aa035210892352f4a";
40 };
40 };
41 };
41 };
42 FormEncode = super.buildPythonPackage {
42 FormEncode = super.buildPythonPackage {
43 name = "FormEncode-1.2.4";
43 name = "FormEncode-1.2.4";
44 buildInputs = with self; [];
44 buildInputs = with self; [];
45 doCheck = false;
45 doCheck = false;
46 propagatedBuildInputs = with self; [];
46 propagatedBuildInputs = with self; [];
47 src = fetchurl {
47 src = fetchurl {
48 url = "https://pypi.python.org/packages/8e/59/0174271a6f004512e0201188593e6d319db139d14cb7490e488bbb078015/FormEncode-1.2.4.tar.gz";
48 url = "https://pypi.python.org/packages/8e/59/0174271a6f004512e0201188593e6d319db139d14cb7490e488bbb078015/FormEncode-1.2.4.tar.gz";
49 md5 = "6bc17fb9aed8aea198975e888e2077f4";
49 md5 = "6bc17fb9aed8aea198975e888e2077f4";
50 };
50 };
51 };
51 };
52 Jinja2 = super.buildPythonPackage {
52 Jinja2 = super.buildPythonPackage {
53 name = "Jinja2-2.7.3";
53 name = "Jinja2-2.7.3";
54 buildInputs = with self; [];
54 buildInputs = with self; [];
55 doCheck = false;
55 doCheck = false;
56 propagatedBuildInputs = with self; [MarkupSafe];
56 propagatedBuildInputs = with self; [MarkupSafe];
57 src = fetchurl {
57 src = fetchurl {
58 url = "https://pypi.python.org/packages/b0/73/eab0bca302d6d6a0b5c402f47ad1760dc9cb2dd14bbc1873ad48db258e4d/Jinja2-2.7.3.tar.gz";
58 url = "https://pypi.python.org/packages/b0/73/eab0bca302d6d6a0b5c402f47ad1760dc9cb2dd14bbc1873ad48db258e4d/Jinja2-2.7.3.tar.gz";
59 md5 = "b9dffd2f3b43d673802fe857c8445b1a";
59 md5 = "b9dffd2f3b43d673802fe857c8445b1a";
60 };
60 };
61 };
61 };
62 Mako = super.buildPythonPackage {
62 Mako = super.buildPythonPackage {
63 name = "Mako-1.0.1";
63 name = "Mako-1.0.1";
64 buildInputs = with self; [];
64 buildInputs = with self; [];
65 doCheck = false;
65 doCheck = false;
66 propagatedBuildInputs = with self; [MarkupSafe];
66 propagatedBuildInputs = with self; [MarkupSafe];
67 src = fetchurl {
67 src = fetchurl {
68 url = "https://pypi.python.org/packages/8e/a4/aa56533ecaa5f22ca92428f74e074d0c9337282933c722391902c8f9e0f8/Mako-1.0.1.tar.gz";
68 url = "https://pypi.python.org/packages/8e/a4/aa56533ecaa5f22ca92428f74e074d0c9337282933c722391902c8f9e0f8/Mako-1.0.1.tar.gz";
69 md5 = "9f0aafd177b039ef67b90ea350497a54";
69 md5 = "9f0aafd177b039ef67b90ea350497a54";
70 };
70 };
71 };
71 };
72 Markdown = super.buildPythonPackage {
72 Markdown = super.buildPythonPackage {
73 name = "Markdown-2.6.2";
73 name = "Markdown-2.6.2";
74 buildInputs = with self; [];
74 buildInputs = with self; [];
75 doCheck = false;
75 doCheck = false;
76 propagatedBuildInputs = with self; [];
76 propagatedBuildInputs = with self; [];
77 src = fetchurl {
77 src = fetchurl {
78 url = "https://pypi.python.org/packages/62/8b/83658b5f6c220d5fcde9f9852d46ea54765d734cfbc5a9f4c05bfc36db4d/Markdown-2.6.2.tar.gz";
78 url = "https://pypi.python.org/packages/62/8b/83658b5f6c220d5fcde9f9852d46ea54765d734cfbc5a9f4c05bfc36db4d/Markdown-2.6.2.tar.gz";
79 md5 = "256d19afcc564dc4ce4c229bb762f7ae";
79 md5 = "256d19afcc564dc4ce4c229bb762f7ae";
80 };
80 };
81 };
81 };
82 MarkupSafe = super.buildPythonPackage {
82 MarkupSafe = super.buildPythonPackage {
83 name = "MarkupSafe-0.23";
83 name = "MarkupSafe-0.23";
84 buildInputs = with self; [];
84 buildInputs = with self; [];
85 doCheck = false;
85 doCheck = false;
86 propagatedBuildInputs = with self; [];
86 propagatedBuildInputs = with self; [];
87 src = fetchurl {
87 src = fetchurl {
88 url = "https://pypi.python.org/packages/c0/41/bae1254e0396c0cc8cf1751cb7d9afc90a602353695af5952530482c963f/MarkupSafe-0.23.tar.gz";
88 url = "https://pypi.python.org/packages/c0/41/bae1254e0396c0cc8cf1751cb7d9afc90a602353695af5952530482c963f/MarkupSafe-0.23.tar.gz";
89 md5 = "f5ab3deee4c37cd6a922fb81e730da6e";
89 md5 = "f5ab3deee4c37cd6a922fb81e730da6e";
90 };
90 };
91 };
91 };
92 MySQL-python = super.buildPythonPackage {
92 MySQL-python = super.buildPythonPackage {
93 name = "MySQL-python-1.2.5";
93 name = "MySQL-python-1.2.5";
94 buildInputs = with self; [];
94 buildInputs = with self; [];
95 doCheck = false;
95 doCheck = false;
96 propagatedBuildInputs = with self; [];
96 propagatedBuildInputs = with self; [];
97 src = fetchurl {
97 src = fetchurl {
98 url = "https://pypi.python.org/packages/a5/e9/51b544da85a36a68debe7a7091f068d802fc515a3a202652828c73453cad/MySQL-python-1.2.5.zip";
98 url = "https://pypi.python.org/packages/a5/e9/51b544da85a36a68debe7a7091f068d802fc515a3a202652828c73453cad/MySQL-python-1.2.5.zip";
99 md5 = "654f75b302db6ed8dc5a898c625e030c";
99 md5 = "654f75b302db6ed8dc5a898c625e030c";
100 };
100 };
101 };
101 };
102 Paste = super.buildPythonPackage {
102 Paste = super.buildPythonPackage {
103 name = "Paste-2.0.2";
103 name = "Paste-2.0.2";
104 buildInputs = with self; [];
104 buildInputs = with self; [];
105 doCheck = false;
105 doCheck = false;
106 propagatedBuildInputs = with self; [six];
106 propagatedBuildInputs = with self; [six];
107 src = fetchurl {
107 src = fetchurl {
108 url = "https://pypi.python.org/packages/d5/8d/0f8ac40687b97ff3e07ebd1369be20bdb3f93864d2dc3c2ff542edb4ce50/Paste-2.0.2.tar.gz";
108 url = "https://pypi.python.org/packages/d5/8d/0f8ac40687b97ff3e07ebd1369be20bdb3f93864d2dc3c2ff542edb4ce50/Paste-2.0.2.tar.gz";
109 md5 = "4bfc8a7eaf858f6309d2ac0f40fc951c";
109 md5 = "4bfc8a7eaf858f6309d2ac0f40fc951c";
110 };
110 };
111 };
111 };
112 PasteDeploy = super.buildPythonPackage {
112 PasteDeploy = super.buildPythonPackage {
113 name = "PasteDeploy-1.5.2";
113 name = "PasteDeploy-1.5.2";
114 buildInputs = with self; [];
114 buildInputs = with self; [];
115 doCheck = false;
115 doCheck = false;
116 propagatedBuildInputs = with self; [];
116 propagatedBuildInputs = with self; [];
117 src = fetchurl {
117 src = fetchurl {
118 url = "https://pypi.python.org/packages/0f/90/8e20cdae206c543ea10793cbf4136eb9a8b3f417e04e40a29d72d9922cbd/PasteDeploy-1.5.2.tar.gz";
118 url = "https://pypi.python.org/packages/0f/90/8e20cdae206c543ea10793cbf4136eb9a8b3f417e04e40a29d72d9922cbd/PasteDeploy-1.5.2.tar.gz";
119 md5 = "352b7205c78c8de4987578d19431af3b";
119 md5 = "352b7205c78c8de4987578d19431af3b";
120 };
120 };
121 };
121 };
122 PasteScript = super.buildPythonPackage {
122 PasteScript = super.buildPythonPackage {
123 name = "PasteScript-1.7.5";
123 name = "PasteScript-1.7.5";
124 buildInputs = with self; [];
124 buildInputs = with self; [];
125 doCheck = false;
125 doCheck = false;
126 propagatedBuildInputs = with self; [Paste PasteDeploy];
126 propagatedBuildInputs = with self; [Paste PasteDeploy];
127 src = fetchurl {
127 src = fetchurl {
128 url = "https://pypi.python.org/packages/a5/05/fc60efa7c2f17a1dbaeccb2a903a1e90902d92b9d00eebabe3095829d806/PasteScript-1.7.5.tar.gz";
128 url = "https://pypi.python.org/packages/a5/05/fc60efa7c2f17a1dbaeccb2a903a1e90902d92b9d00eebabe3095829d806/PasteScript-1.7.5.tar.gz";
129 md5 = "4c72d78dcb6bb993f30536842c16af4d";
129 md5 = "4c72d78dcb6bb993f30536842c16af4d";
130 };
130 };
131 };
131 };
132 Pygments = super.buildPythonPackage {
132 Pygments = super.buildPythonPackage {
133 name = "Pygments-2.0.2";
133 name = "Pygments-2.0.2";
134 buildInputs = with self; [];
134 buildInputs = with self; [];
135 doCheck = false;
135 doCheck = false;
136 propagatedBuildInputs = with self; [];
136 propagatedBuildInputs = with self; [];
137 src = fetchurl {
137 src = fetchurl {
138 url = "https://pypi.python.org/packages/f4/c6/bdbc5a8a112256b2b6136af304dbae93d8b1ef8738ff2d12a51018800e46/Pygments-2.0.2.tar.gz";
138 url = "https://pypi.python.org/packages/f4/c6/bdbc5a8a112256b2b6136af304dbae93d8b1ef8738ff2d12a51018800e46/Pygments-2.0.2.tar.gz";
139 md5 = "238587a1370d62405edabd0794b3ec4a";
139 md5 = "238587a1370d62405edabd0794b3ec4a";
140 };
140 };
141 };
141 };
142 Pylons = super.buildPythonPackage {
142 Pylons = super.buildPythonPackage {
143 name = "Pylons-1.0.1";
143 name = "Pylons-1.0.1";
144 buildInputs = with self; [];
144 buildInputs = with self; [];
145 doCheck = false;
145 doCheck = false;
146 propagatedBuildInputs = with self; [Routes WebHelpers Beaker Paste PasteDeploy PasteScript FormEncode simplejson decorator nose Mako WebError WebTest Tempita MarkupSafe WebOb];
146 propagatedBuildInputs = with self; [Routes WebHelpers Beaker Paste PasteDeploy PasteScript FormEncode simplejson decorator nose Mako WebError WebTest Tempita MarkupSafe WebOb];
147 src = fetchurl {
147 src = fetchurl {
148 url = "https://pypi.python.org/packages/a2/69/b835a6bad00acbfeed3f33c6e44fa3f936efc998c795bfb15c61a79ecf62/Pylons-1.0.1.tar.gz";
148 url = "https://pypi.python.org/packages/a2/69/b835a6bad00acbfeed3f33c6e44fa3f936efc998c795bfb15c61a79ecf62/Pylons-1.0.1.tar.gz";
149 md5 = "6cb880d75fa81213192142b07a6e4915";
149 md5 = "6cb880d75fa81213192142b07a6e4915";
150 };
150 };
151 };
151 };
152 Pyro4 = super.buildPythonPackage {
152 Pyro4 = super.buildPythonPackage {
153 name = "Pyro4-4.41";
153 name = "Pyro4-4.41";
154 buildInputs = with self; [];
154 buildInputs = with self; [];
155 doCheck = false;
155 doCheck = false;
156 propagatedBuildInputs = with self; [serpent];
156 propagatedBuildInputs = with self; [serpent];
157 src = fetchurl {
157 src = fetchurl {
158 url = "https://pypi.python.org/packages/56/2b/89b566b4bf3e7f8ba790db2d1223852f8cb454c52cab7693dd41f608ca2a/Pyro4-4.41.tar.gz";
158 url = "https://pypi.python.org/packages/56/2b/89b566b4bf3e7f8ba790db2d1223852f8cb454c52cab7693dd41f608ca2a/Pyro4-4.41.tar.gz";
159 md5 = "ed69e9bfafa9c06c049a87cb0c4c2b6c";
159 md5 = "ed69e9bfafa9c06c049a87cb0c4c2b6c";
160 };
160 };
161 };
161 };
162 Routes = super.buildPythonPackage {
162 Routes = super.buildPythonPackage {
163 name = "Routes-1.13";
163 name = "Routes-1.13";
164 buildInputs = with self; [];
164 buildInputs = with self; [];
165 doCheck = false;
165 doCheck = false;
166 propagatedBuildInputs = with self; [repoze.lru];
166 propagatedBuildInputs = with self; [repoze.lru];
167 src = fetchurl {
167 src = fetchurl {
168 url = "https://pypi.python.org/packages/88/d3/259c3b3cde8837eb9441ab5f574a660e8a4acea8f54a078441d4d2acac1c/Routes-1.13.tar.gz";
168 url = "https://pypi.python.org/packages/88/d3/259c3b3cde8837eb9441ab5f574a660e8a4acea8f54a078441d4d2acac1c/Routes-1.13.tar.gz";
169 md5 = "d527b0ab7dd9172b1275a41f97448783";
169 md5 = "d527b0ab7dd9172b1275a41f97448783";
170 };
170 };
171 };
171 };
172 SQLAlchemy = super.buildPythonPackage {
172 SQLAlchemy = super.buildPythonPackage {
173 name = "SQLAlchemy-0.9.9";
173 name = "SQLAlchemy-0.9.9";
174 buildInputs = with self; [];
174 buildInputs = with self; [];
175 doCheck = false;
175 doCheck = false;
176 propagatedBuildInputs = with self; [];
176 propagatedBuildInputs = with self; [];
177 src = fetchurl {
177 src = fetchurl {
178 url = "https://pypi.python.org/packages/28/f7/1bbfd0d8597e8c358d5e15a166a486ad82fc5579b4e67b6ef7c05b1d182b/SQLAlchemy-0.9.9.tar.gz";
178 url = "https://pypi.python.org/packages/28/f7/1bbfd0d8597e8c358d5e15a166a486ad82fc5579b4e67b6ef7c05b1d182b/SQLAlchemy-0.9.9.tar.gz";
179 md5 = "8a10a9bd13ed3336ef7333ac2cc679ff";
179 md5 = "8a10a9bd13ed3336ef7333ac2cc679ff";
180 };
180 };
181 };
181 };
182 Sphinx = super.buildPythonPackage {
182 Sphinx = super.buildPythonPackage {
183 name = "Sphinx-1.2.2";
183 name = "Sphinx-1.2.2";
184 buildInputs = with self; [];
184 buildInputs = with self; [];
185 doCheck = false;
185 doCheck = false;
186 propagatedBuildInputs = with self; [Pygments docutils Jinja2];
186 propagatedBuildInputs = with self; [Pygments docutils Jinja2];
187 src = fetchurl {
187 src = fetchurl {
188 url = "https://pypi.python.org/packages/0a/50/34017e6efcd372893a416aba14b84a1a149fc7074537b0e9cb6ca7b7abe9/Sphinx-1.2.2.tar.gz";
188 url = "https://pypi.python.org/packages/0a/50/34017e6efcd372893a416aba14b84a1a149fc7074537b0e9cb6ca7b7abe9/Sphinx-1.2.2.tar.gz";
189 md5 = "3dc73ccaa8d0bfb2d62fb671b1f7e8a4";
189 md5 = "3dc73ccaa8d0bfb2d62fb671b1f7e8a4";
190 };
190 };
191 };
191 };
192 Tempita = super.buildPythonPackage {
192 Tempita = super.buildPythonPackage {
193 name = "Tempita-0.5.2";
193 name = "Tempita-0.5.2";
194 buildInputs = with self; [];
194 buildInputs = with self; [];
195 doCheck = false;
195 doCheck = false;
196 propagatedBuildInputs = with self; [];
196 propagatedBuildInputs = with self; [];
197 src = fetchurl {
197 src = fetchurl {
198 url = "https://pypi.python.org/packages/56/c8/8ed6eee83dbddf7b0fc64dd5d4454bc05e6ccaafff47991f73f2894d9ff4/Tempita-0.5.2.tar.gz";
198 url = "https://pypi.python.org/packages/56/c8/8ed6eee83dbddf7b0fc64dd5d4454bc05e6ccaafff47991f73f2894d9ff4/Tempita-0.5.2.tar.gz";
199 md5 = "4c2f17bb9d481821c41b6fbee904cea1";
199 md5 = "4c2f17bb9d481821c41b6fbee904cea1";
200 };
200 };
201 };
201 };
202 URLObject = super.buildPythonPackage {
202 URLObject = super.buildPythonPackage {
203 name = "URLObject-2.4.0";
203 name = "URLObject-2.4.0";
204 buildInputs = with self; [];
204 buildInputs = with self; [];
205 doCheck = false;
205 doCheck = false;
206 propagatedBuildInputs = with self; [];
206 propagatedBuildInputs = with self; [];
207 src = fetchurl {
207 src = fetchurl {
208 url = "https://pypi.python.org/packages/cb/b6/e25e58500f9caef85d664bec71ec67c116897bfebf8622c32cb75d1ca199/URLObject-2.4.0.tar.gz";
208 url = "https://pypi.python.org/packages/cb/b6/e25e58500f9caef85d664bec71ec67c116897bfebf8622c32cb75d1ca199/URLObject-2.4.0.tar.gz";
209 md5 = "2ed819738a9f0a3051f31dc9924e3065";
209 md5 = "2ed819738a9f0a3051f31dc9924e3065";
210 };
210 };
211 };
211 };
212 WebError = super.buildPythonPackage {
212 WebError = super.buildPythonPackage {
213 name = "WebError-0.10.3";
213 name = "WebError-0.10.3";
214 buildInputs = with self; [];
214 buildInputs = with self; [];
215 doCheck = false;
215 doCheck = false;
216 propagatedBuildInputs = with self; [WebOb Tempita Pygments Paste];
216 propagatedBuildInputs = with self; [WebOb Tempita Pygments Paste];
217 src = fetchurl {
217 src = fetchurl {
218 url = "https://pypi.python.org/packages/35/76/e7e5c2ce7e9c7f31b54c1ff295a495886d1279a002557d74dd8957346a79/WebError-0.10.3.tar.gz";
218 url = "https://pypi.python.org/packages/35/76/e7e5c2ce7e9c7f31b54c1ff295a495886d1279a002557d74dd8957346a79/WebError-0.10.3.tar.gz";
219 md5 = "84b9990b0baae6fd440b1e60cdd06f9a";
219 md5 = "84b9990b0baae6fd440b1e60cdd06f9a";
220 };
220 };
221 };
221 };
222 WebHelpers = super.buildPythonPackage {
222 WebHelpers = super.buildPythonPackage {
223 name = "WebHelpers-1.3";
223 name = "WebHelpers-1.3";
224 buildInputs = with self; [];
224 buildInputs = with self; [];
225 doCheck = false;
225 doCheck = false;
226 propagatedBuildInputs = with self; [MarkupSafe];
226 propagatedBuildInputs = with self; [MarkupSafe];
227 src = fetchurl {
227 src = fetchurl {
228 url = "https://pypi.python.org/packages/ee/68/4d07672821d514184357f1552f2dad923324f597e722de3b016ca4f7844f/WebHelpers-1.3.tar.gz";
228 url = "https://pypi.python.org/packages/ee/68/4d07672821d514184357f1552f2dad923324f597e722de3b016ca4f7844f/WebHelpers-1.3.tar.gz";
229 md5 = "32749ffadfc40fea51075a7def32588b";
229 md5 = "32749ffadfc40fea51075a7def32588b";
230 };
230 };
231 };
231 };
232 WebHelpers2 = super.buildPythonPackage {
232 WebHelpers2 = super.buildPythonPackage {
233 name = "WebHelpers2-2.0";
233 name = "WebHelpers2-2.0";
234 buildInputs = with self; [];
234 buildInputs = with self; [];
235 doCheck = false;
235 doCheck = false;
236 propagatedBuildInputs = with self; [MarkupSafe six];
236 propagatedBuildInputs = with self; [MarkupSafe six];
237 src = fetchurl {
237 src = fetchurl {
238 url = "https://pypi.python.org/packages/ff/30/56342c6ea522439e3662427c8d7b5e5b390dff4ff2dc92d8afcb8ab68b75/WebHelpers2-2.0.tar.gz";
238 url = "https://pypi.python.org/packages/ff/30/56342c6ea522439e3662427c8d7b5e5b390dff4ff2dc92d8afcb8ab68b75/WebHelpers2-2.0.tar.gz";
239 md5 = "0f6b68d70c12ee0aed48c00b24da13d3";
239 md5 = "0f6b68d70c12ee0aed48c00b24da13d3";
240 };
240 };
241 };
241 };
242 WebOb = super.buildPythonPackage {
242 WebOb = super.buildPythonPackage {
243 name = "WebOb-1.3.1";
243 name = "WebOb-1.3.1";
244 buildInputs = with self; [];
244 buildInputs = with self; [];
245 doCheck = false;
245 doCheck = false;
246 propagatedBuildInputs = with self; [];
246 propagatedBuildInputs = with self; [];
247 src = fetchurl {
247 src = fetchurl {
248 url = "https://pypi.python.org/packages/16/78/adfc0380b8a0d75b2d543fa7085ba98a573b1ae486d9def88d172b81b9fa/WebOb-1.3.1.tar.gz";
248 url = "https://pypi.python.org/packages/16/78/adfc0380b8a0d75b2d543fa7085ba98a573b1ae486d9def88d172b81b9fa/WebOb-1.3.1.tar.gz";
249 md5 = "20918251c5726956ba8fef22d1556177";
249 md5 = "20918251c5726956ba8fef22d1556177";
250 };
250 };
251 };
251 };
252 WebTest = super.buildPythonPackage {
252 WebTest = super.buildPythonPackage {
253 name = "WebTest-1.4.3";
253 name = "WebTest-1.4.3";
254 buildInputs = with self; [];
254 buildInputs = with self; [];
255 doCheck = false;
255 doCheck = false;
256 propagatedBuildInputs = with self; [WebOb];
256 propagatedBuildInputs = with self; [WebOb];
257 src = fetchurl {
257 src = fetchurl {
258 url = "https://pypi.python.org/packages/51/3d/84fd0f628df10b30c7db87895f56d0158e5411206b721ca903cb51bfd948/WebTest-1.4.3.zip";
258 url = "https://pypi.python.org/packages/51/3d/84fd0f628df10b30c7db87895f56d0158e5411206b721ca903cb51bfd948/WebTest-1.4.3.zip";
259 md5 = "631ce728bed92c681a4020a36adbc353";
259 md5 = "631ce728bed92c681a4020a36adbc353";
260 };
260 };
261 };
261 };
262 Whoosh = super.buildPythonPackage {
262 Whoosh = super.buildPythonPackage {
263 name = "Whoosh-2.7.0";
263 name = "Whoosh-2.7.0";
264 buildInputs = with self; [];
264 buildInputs = with self; [];
265 doCheck = false;
265 doCheck = false;
266 propagatedBuildInputs = with self; [];
266 propagatedBuildInputs = with self; [];
267 src = fetchurl {
267 src = fetchurl {
268 url = "https://pypi.python.org/packages/1c/dc/2f0231ff3875ded36df8c1ab851451e51a237dc0e5a86d3d96036158da94/Whoosh-2.7.0.zip";
268 url = "https://pypi.python.org/packages/1c/dc/2f0231ff3875ded36df8c1ab851451e51a237dc0e5a86d3d96036158da94/Whoosh-2.7.0.zip";
269 md5 = "7abfd970f16fadc7311960f3fa0bc7a9";
269 md5 = "7abfd970f16fadc7311960f3fa0bc7a9";
270 };
270 };
271 };
271 };
272 alembic = super.buildPythonPackage {
272 alembic = super.buildPythonPackage {
273 name = "alembic-0.8.4";
273 name = "alembic-0.8.4";
274 buildInputs = with self; [];
274 buildInputs = with self; [];
275 doCheck = false;
275 doCheck = false;
276 propagatedBuildInputs = with self; [SQLAlchemy Mako python-editor];
276 propagatedBuildInputs = with self; [SQLAlchemy Mako python-editor];
277 src = fetchurl {
277 src = fetchurl {
278 url = "https://pypi.python.org/packages/ca/7e/299b4499b5c75e5a38c5845145ad24755bebfb8eec07a2e1c366b7181eeb/alembic-0.8.4.tar.gz";
278 url = "https://pypi.python.org/packages/ca/7e/299b4499b5c75e5a38c5845145ad24755bebfb8eec07a2e1c366b7181eeb/alembic-0.8.4.tar.gz";
279 md5 = "5f95d8ee62b443f9b37eb5bee76c582d";
279 md5 = "5f95d8ee62b443f9b37eb5bee76c582d";
280 };
280 };
281 };
281 };
282 amqplib = super.buildPythonPackage {
282 amqplib = super.buildPythonPackage {
283 name = "amqplib-1.0.2";
283 name = "amqplib-1.0.2";
284 buildInputs = with self; [];
284 buildInputs = with self; [];
285 doCheck = false;
285 doCheck = false;
286 propagatedBuildInputs = with self; [];
286 propagatedBuildInputs = with self; [];
287 src = fetchurl {
287 src = fetchurl {
288 url = "https://pypi.python.org/packages/75/b7/8c2429bf8d92354a0118614f9a4d15e53bc69ebedce534284111de5a0102/amqplib-1.0.2.tgz";
288 url = "https://pypi.python.org/packages/75/b7/8c2429bf8d92354a0118614f9a4d15e53bc69ebedce534284111de5a0102/amqplib-1.0.2.tgz";
289 md5 = "5c92f17fbedd99b2b4a836d4352d1e2f";
289 md5 = "5c92f17fbedd99b2b4a836d4352d1e2f";
290 };
290 };
291 };
291 };
292 anyjson = super.buildPythonPackage {
292 anyjson = super.buildPythonPackage {
293 name = "anyjson-0.3.3";
293 name = "anyjson-0.3.3";
294 buildInputs = with self; [];
294 buildInputs = with self; [];
295 doCheck = false;
295 doCheck = false;
296 propagatedBuildInputs = with self; [];
296 propagatedBuildInputs = with self; [];
297 src = fetchurl {
297 src = fetchurl {
298 url = "https://pypi.python.org/packages/c3/4d/d4089e1a3dd25b46bebdb55a992b0797cff657b4477bc32ce28038fdecbc/anyjson-0.3.3.tar.gz";
298 url = "https://pypi.python.org/packages/c3/4d/d4089e1a3dd25b46bebdb55a992b0797cff657b4477bc32ce28038fdecbc/anyjson-0.3.3.tar.gz";
299 md5 = "2ea28d6ec311aeeebaf993cb3008b27c";
299 md5 = "2ea28d6ec311aeeebaf993cb3008b27c";
300 };
300 };
301 };
301 };
302 appenlight-client = super.buildPythonPackage {
302 appenlight-client = super.buildPythonPackage {
303 name = "appenlight-client-0.6.14";
303 name = "appenlight-client-0.6.14";
304 buildInputs = with self; [];
304 buildInputs = with self; [];
305 doCheck = false;
305 doCheck = false;
306 propagatedBuildInputs = with self; [WebOb requests];
306 propagatedBuildInputs = with self; [WebOb requests];
307 src = fetchurl {
307 src = fetchurl {
308 url = "https://pypi.python.org/packages/4d/e0/23fee3ebada8143f707e65c06bcb82992040ee64ea8355e044ed55ebf0c1/appenlight_client-0.6.14.tar.gz";
308 url = "https://pypi.python.org/packages/4d/e0/23fee3ebada8143f707e65c06bcb82992040ee64ea8355e044ed55ebf0c1/appenlight_client-0.6.14.tar.gz";
309 md5 = "578c69b09f4356d898fff1199b98a95c";
309 md5 = "578c69b09f4356d898fff1199b98a95c";
310 };
310 };
311 };
311 };
312 authomatic = super.buildPythonPackage {
312 authomatic = super.buildPythonPackage {
313 name = "authomatic-0.1.0.post1";
313 name = "authomatic-0.1.0.post1";
314 buildInputs = with self; [];
314 buildInputs = with self; [];
315 doCheck = false;
315 doCheck = false;
316 propagatedBuildInputs = with self; [];
316 propagatedBuildInputs = with self; [];
317 src = fetchurl {
317 src = fetchurl {
318 url = "https://pypi.python.org/packages/08/1a/8a930461e604c2d5a7a871e1ac59fa82ccf994c32e807230c8d2fb07815a/Authomatic-0.1.0.post1.tar.gz";
318 url = "https://pypi.python.org/packages/08/1a/8a930461e604c2d5a7a871e1ac59fa82ccf994c32e807230c8d2fb07815a/Authomatic-0.1.0.post1.tar.gz";
319 md5 = "be3f3ce08747d776aae6d6cc8dcb49a9";
319 md5 = "be3f3ce08747d776aae6d6cc8dcb49a9";
320 };
320 };
321 };
321 };
322 backport-ipaddress = super.buildPythonPackage {
322 backport-ipaddress = super.buildPythonPackage {
323 name = "backport-ipaddress-0.1";
323 name = "backport-ipaddress-0.1";
324 buildInputs = with self; [];
324 buildInputs = with self; [];
325 doCheck = false;
325 doCheck = false;
326 propagatedBuildInputs = with self; [];
326 propagatedBuildInputs = with self; [];
327 src = fetchurl {
327 src = fetchurl {
328 url = "https://pypi.python.org/packages/d3/30/54c6dab05a4dec44db25ff309f1fbb6b7a8bde3f2bade38bb9da67bbab8f/backport_ipaddress-0.1.tar.gz";
328 url = "https://pypi.python.org/packages/d3/30/54c6dab05a4dec44db25ff309f1fbb6b7a8bde3f2bade38bb9da67bbab8f/backport_ipaddress-0.1.tar.gz";
329 md5 = "9c1f45f4361f71b124d7293a60006c05";
329 md5 = "9c1f45f4361f71b124d7293a60006c05";
330 };
330 };
331 };
331 };
332 bottle = super.buildPythonPackage {
332 bottle = super.buildPythonPackage {
333 name = "bottle-0.12.8";
333 name = "bottle-0.12.8";
334 buildInputs = with self; [];
334 buildInputs = with self; [];
335 doCheck = false;
335 doCheck = false;
336 propagatedBuildInputs = with self; [];
336 propagatedBuildInputs = with self; [];
337 src = fetchurl {
337 src = fetchurl {
338 url = "https://pypi.python.org/packages/52/df/e4a408f3a7af396d186d4ecd3b389dd764f0f943b4fa8d257bfe7b49d343/bottle-0.12.8.tar.gz";
338 url = "https://pypi.python.org/packages/52/df/e4a408f3a7af396d186d4ecd3b389dd764f0f943b4fa8d257bfe7b49d343/bottle-0.12.8.tar.gz";
339 md5 = "13132c0a8f607bf860810a6ee9064c5b";
339 md5 = "13132c0a8f607bf860810a6ee9064c5b";
340 };
340 };
341 };
341 };
342 bumpversion = super.buildPythonPackage {
342 bumpversion = super.buildPythonPackage {
343 name = "bumpversion-0.5.3";
343 name = "bumpversion-0.5.3";
344 buildInputs = with self; [];
344 buildInputs = with self; [];
345 doCheck = false;
345 doCheck = false;
346 propagatedBuildInputs = with self; [];
346 propagatedBuildInputs = with self; [];
347 src = fetchurl {
347 src = fetchurl {
348 url = "https://pypi.python.org/packages/14/41/8c9da3549f8e00c84f0432c3a8cf8ed6898374714676aab91501d48760db/bumpversion-0.5.3.tar.gz";
348 url = "https://pypi.python.org/packages/14/41/8c9da3549f8e00c84f0432c3a8cf8ed6898374714676aab91501d48760db/bumpversion-0.5.3.tar.gz";
349 md5 = "c66a3492eafcf5ad4b024be9fca29820";
349 md5 = "c66a3492eafcf5ad4b024be9fca29820";
350 };
350 };
351 };
351 };
352 celery = super.buildPythonPackage {
352 celery = super.buildPythonPackage {
353 name = "celery-2.2.10";
353 name = "celery-2.2.10";
354 buildInputs = with self; [];
354 buildInputs = with self; [];
355 doCheck = false;
355 doCheck = false;
356 propagatedBuildInputs = with self; [python-dateutil anyjson kombu pyparsing];
356 propagatedBuildInputs = with self; [python-dateutil anyjson kombu pyparsing];
357 src = fetchurl {
357 src = fetchurl {
358 url = "https://pypi.python.org/packages/b1/64/860fd50e45844c83442e7953effcddeff66b2851d90b2d784f7201c111b8/celery-2.2.10.tar.gz";
358 url = "https://pypi.python.org/packages/b1/64/860fd50e45844c83442e7953effcddeff66b2851d90b2d784f7201c111b8/celery-2.2.10.tar.gz";
359 md5 = "898bc87e54f278055b561316ba73e222";
359 md5 = "898bc87e54f278055b561316ba73e222";
360 };
360 };
361 };
361 };
362 certifi = super.buildPythonPackage {
362 certifi = super.buildPythonPackage {
363 name = "certifi-2016.02.28";
363 name = "certifi-2016.2.28";
364 buildInputs = with self; [];
364 buildInputs = with self; [];
365 doCheck = false;
365 doCheck = false;
366 propagatedBuildInputs = with self; [];
366 propagatedBuildInputs = with self; [];
367 src = fetchurl {
367 src = fetchurl {
368 url = "https://pypi.python.org/packages/5c/f8/f6c54727c74579c6bbe5926f5deb9677c5810a33e11da58d1a4e2d09d041/certifi-2016.2.28.tar.gz";
368 url = "https://pypi.python.org/packages/5c/f8/f6c54727c74579c6bbe5926f5deb9677c5810a33e11da58d1a4e2d09d041/certifi-2016.2.28.tar.gz";
369 md5 = "5d672aa766e1f773c75cfeccd02d3650";
369 md5 = "5d672aa766e1f773c75cfeccd02d3650";
370 };
370 };
371 };
371 };
372 click = super.buildPythonPackage {
372 click = super.buildPythonPackage {
373 name = "click-5.1";
373 name = "click-5.1";
374 buildInputs = with self; [];
374 buildInputs = with self; [];
375 doCheck = false;
375 doCheck = false;
376 propagatedBuildInputs = with self; [];
376 propagatedBuildInputs = with self; [];
377 src = fetchurl {
377 src = fetchurl {
378 url = "https://pypi.python.org/packages/b7/34/a496632c4fb6c1ee76efedf77bb8d28b29363d839953d95095b12defe791/click-5.1.tar.gz";
378 url = "https://pypi.python.org/packages/b7/34/a496632c4fb6c1ee76efedf77bb8d28b29363d839953d95095b12defe791/click-5.1.tar.gz";
379 md5 = "9c5323008cccfe232a8b161fc8196d41";
379 md5 = "9c5323008cccfe232a8b161fc8196d41";
380 };
380 };
381 };
381 };
382 colander = super.buildPythonPackage {
382 colander = super.buildPythonPackage {
383 name = "colander-1.2";
383 name = "colander-1.2";
384 buildInputs = with self; [];
384 buildInputs = with self; [];
385 doCheck = false;
385 doCheck = false;
386 propagatedBuildInputs = with self; [translationstring iso8601];
386 propagatedBuildInputs = with self; [translationstring iso8601];
387 src = fetchurl {
387 src = fetchurl {
388 url = "https://pypi.python.org/packages/14/23/c9ceba07a6a1dc0eefbb215fc0dc64aabc2b22ee756bc0f0c13278fa0887/colander-1.2.tar.gz";
388 url = "https://pypi.python.org/packages/14/23/c9ceba07a6a1dc0eefbb215fc0dc64aabc2b22ee756bc0f0c13278fa0887/colander-1.2.tar.gz";
389 md5 = "83db21b07936a0726e588dae1914b9ed";
389 md5 = "83db21b07936a0726e588dae1914b9ed";
390 };
390 };
391 };
391 };
392 configobj = super.buildPythonPackage {
392 configobj = super.buildPythonPackage {
393 name = "configobj-5.0.6";
393 name = "configobj-5.0.6";
394 buildInputs = with self; [];
394 buildInputs = with self; [];
395 doCheck = false;
395 doCheck = false;
396 propagatedBuildInputs = with self; [six];
396 propagatedBuildInputs = with self; [six];
397 src = fetchurl {
397 src = fetchurl {
398 url = "https://pypi.python.org/packages/64/61/079eb60459c44929e684fa7d9e2fdca403f67d64dd9dbac27296be2e0fab/configobj-5.0.6.tar.gz";
398 url = "https://pypi.python.org/packages/64/61/079eb60459c44929e684fa7d9e2fdca403f67d64dd9dbac27296be2e0fab/configobj-5.0.6.tar.gz";
399 md5 = "e472a3a1c2a67bb0ec9b5d54c13a47d6";
399 md5 = "e472a3a1c2a67bb0ec9b5d54c13a47d6";
400 };
400 };
401 };
401 };
402 cov-core = super.buildPythonPackage {
402 cov-core = super.buildPythonPackage {
403 name = "cov-core-1.15.0";
403 name = "cov-core-1.15.0";
404 buildInputs = with self; [];
404 buildInputs = with self; [];
405 doCheck = false;
405 doCheck = false;
406 propagatedBuildInputs = with self; [coverage];
406 propagatedBuildInputs = with self; [coverage];
407 src = fetchurl {
407 src = fetchurl {
408 url = "https://pypi.python.org/packages/4b/87/13e75a47b4ba1be06f29f6d807ca99638bedc6b57fa491cd3de891ca2923/cov-core-1.15.0.tar.gz";
408 url = "https://pypi.python.org/packages/4b/87/13e75a47b4ba1be06f29f6d807ca99638bedc6b57fa491cd3de891ca2923/cov-core-1.15.0.tar.gz";
409 md5 = "f519d4cb4c4e52856afb14af52919fe6";
409 md5 = "f519d4cb4c4e52856afb14af52919fe6";
410 };
410 };
411 };
411 };
412 coverage = super.buildPythonPackage {
412 coverage = super.buildPythonPackage {
413 name = "coverage-3.7.1";
413 name = "coverage-3.7.1";
414 buildInputs = with self; [];
414 buildInputs = with self; [];
415 doCheck = false;
415 doCheck = false;
416 propagatedBuildInputs = with self; [];
416 propagatedBuildInputs = with self; [];
417 src = fetchurl {
417 src = fetchurl {
418 url = "https://pypi.python.org/packages/09/4f/89b06c7fdc09687bca507dc411c342556ef9c5a3b26756137a4878ff19bf/coverage-3.7.1.tar.gz";
418 url = "https://pypi.python.org/packages/09/4f/89b06c7fdc09687bca507dc411c342556ef9c5a3b26756137a4878ff19bf/coverage-3.7.1.tar.gz";
419 md5 = "c47b36ceb17eaff3ecfab3bcd347d0df";
419 md5 = "c47b36ceb17eaff3ecfab3bcd347d0df";
420 };
420 };
421 };
421 };
422 cssselect = super.buildPythonPackage {
422 cssselect = super.buildPythonPackage {
423 name = "cssselect-0.9.1";
423 name = "cssselect-0.9.1";
424 buildInputs = with self; [];
424 buildInputs = with self; [];
425 doCheck = false;
425 doCheck = false;
426 propagatedBuildInputs = with self; [];
426 propagatedBuildInputs = with self; [];
427 src = fetchurl {
427 src = fetchurl {
428 url = "https://pypi.python.org/packages/aa/e5/9ee1460d485b94a6d55732eb7ad5b6c084caf73dd6f9cb0bb7d2a78fafe8/cssselect-0.9.1.tar.gz";
428 url = "https://pypi.python.org/packages/aa/e5/9ee1460d485b94a6d55732eb7ad5b6c084caf73dd6f9cb0bb7d2a78fafe8/cssselect-0.9.1.tar.gz";
429 md5 = "c74f45966277dc7a0f768b9b0f3522ac";
429 md5 = "c74f45966277dc7a0f768b9b0f3522ac";
430 };
430 };
431 };
431 };
432 decorator = super.buildPythonPackage {
432 decorator = super.buildPythonPackage {
433 name = "decorator-3.4.2";
433 name = "decorator-3.4.2";
434 buildInputs = with self; [];
434 buildInputs = with self; [];
435 doCheck = false;
435 doCheck = false;
436 propagatedBuildInputs = with self; [];
436 propagatedBuildInputs = with self; [];
437 src = fetchurl {
437 src = fetchurl {
438 url = "https://pypi.python.org/packages/35/3a/42566eb7a2cbac774399871af04e11d7ae3fc2579e7dae85213b8d1d1c57/decorator-3.4.2.tar.gz";
438 url = "https://pypi.python.org/packages/35/3a/42566eb7a2cbac774399871af04e11d7ae3fc2579e7dae85213b8d1d1c57/decorator-3.4.2.tar.gz";
439 md5 = "9e0536870d2b83ae27d58dbf22582f4d";
439 md5 = "9e0536870d2b83ae27d58dbf22582f4d";
440 };
440 };
441 };
441 };
442 docutils = super.buildPythonPackage {
442 docutils = super.buildPythonPackage {
443 name = "docutils-0.12";
443 name = "docutils-0.12";
444 buildInputs = with self; [];
444 buildInputs = with self; [];
445 doCheck = false;
445 doCheck = false;
446 propagatedBuildInputs = with self; [];
446 propagatedBuildInputs = with self; [];
447 src = fetchurl {
447 src = fetchurl {
448 url = "https://pypi.python.org/packages/37/38/ceda70135b9144d84884ae2fc5886c6baac4edea39550f28bcd144c1234d/docutils-0.12.tar.gz";
448 url = "https://pypi.python.org/packages/37/38/ceda70135b9144d84884ae2fc5886c6baac4edea39550f28bcd144c1234d/docutils-0.12.tar.gz";
449 md5 = "4622263b62c5c771c03502afa3157768";
449 md5 = "4622263b62c5c771c03502afa3157768";
450 };
450 };
451 };
451 };
452 dogpile.cache = super.buildPythonPackage {
452 dogpile.cache = super.buildPythonPackage {
453 name = "dogpile.cache-0.5.7";
453 name = "dogpile.cache-0.5.7";
454 buildInputs = with self; [];
454 buildInputs = with self; [];
455 doCheck = false;
455 doCheck = false;
456 propagatedBuildInputs = with self; [dogpile.core];
456 propagatedBuildInputs = with self; [dogpile.core];
457 src = fetchurl {
457 src = fetchurl {
458 url = "https://pypi.python.org/packages/07/74/2a83bedf758156d9c95d112691bbad870d3b77ccbcfb781b4ef836ea7d96/dogpile.cache-0.5.7.tar.gz";
458 url = "https://pypi.python.org/packages/07/74/2a83bedf758156d9c95d112691bbad870d3b77ccbcfb781b4ef836ea7d96/dogpile.cache-0.5.7.tar.gz";
459 md5 = "3e58ce41af574aab41d78e9c4190f194";
459 md5 = "3e58ce41af574aab41d78e9c4190f194";
460 };
460 };
461 };
461 };
462 dogpile.core = super.buildPythonPackage {
462 dogpile.core = super.buildPythonPackage {
463 name = "dogpile.core-0.4.1";
463 name = "dogpile.core-0.4.1";
464 buildInputs = with self; [];
464 buildInputs = with self; [];
465 doCheck = false;
465 doCheck = false;
466 propagatedBuildInputs = with self; [];
466 propagatedBuildInputs = with self; [];
467 src = fetchurl {
467 src = fetchurl {
468 url = "https://pypi.python.org/packages/0e/77/e72abc04c22aedf874301861e5c1e761231c288b5de369c18be8f4b5c9bb/dogpile.core-0.4.1.tar.gz";
468 url = "https://pypi.python.org/packages/0e/77/e72abc04c22aedf874301861e5c1e761231c288b5de369c18be8f4b5c9bb/dogpile.core-0.4.1.tar.gz";
469 md5 = "01cb19f52bba3e95c9b560f39341f045";
469 md5 = "01cb19f52bba3e95c9b560f39341f045";
470 };
470 };
471 };
471 };
472 dulwich = super.buildPythonPackage {
472 dulwich = super.buildPythonPackage {
473 name = "dulwich-0.12.0";
473 name = "dulwich-0.12.0";
474 buildInputs = with self; [];
474 buildInputs = with self; [];
475 doCheck = false;
475 doCheck = false;
476 propagatedBuildInputs = with self; [];
476 propagatedBuildInputs = with self; [];
477 src = fetchurl {
477 src = fetchurl {
478 url = "https://pypi.python.org/packages/6f/04/fbe561b6d45c0ec758330d5b7f5ba4b6cb4f1ca1ab49859d2fc16320da75/dulwich-0.12.0.tar.gz";
478 url = "https://pypi.python.org/packages/6f/04/fbe561b6d45c0ec758330d5b7f5ba4b6cb4f1ca1ab49859d2fc16320da75/dulwich-0.12.0.tar.gz";
479 md5 = "f3a8a12bd9f9dd8c233e18f3d49436fa";
479 md5 = "f3a8a12bd9f9dd8c233e18f3d49436fa";
480 };
480 };
481 };
481 };
482 ecdsa = super.buildPythonPackage {
482 ecdsa = super.buildPythonPackage {
483 name = "ecdsa-0.11";
483 name = "ecdsa-0.11";
484 buildInputs = with self; [];
484 buildInputs = with self; [];
485 doCheck = false;
485 doCheck = false;
486 propagatedBuildInputs = with self; [];
486 propagatedBuildInputs = with self; [];
487 src = fetchurl {
487 src = fetchurl {
488 url = "https://pypi.python.org/packages/6c/3f/92fe5dcdcaa7bd117be21e5520c9a54375112b66ec000d209e9e9519fad1/ecdsa-0.11.tar.gz";
488 url = "https://pypi.python.org/packages/6c/3f/92fe5dcdcaa7bd117be21e5520c9a54375112b66ec000d209e9e9519fad1/ecdsa-0.11.tar.gz";
489 md5 = "8ef586fe4dbb156697d756900cb41d7c";
489 md5 = "8ef586fe4dbb156697d756900cb41d7c";
490 };
490 };
491 };
491 };
492 elasticsearch = super.buildPythonPackage {
492 elasticsearch = super.buildPythonPackage {
493 name = "elasticsearch-1.9.0";
493 name = "elasticsearch-1.9.0";
494 buildInputs = with self; [];
494 buildInputs = with self; [];
495 doCheck = false;
495 doCheck = false;
496 propagatedBuildInputs = with self; [urllib3];
496 propagatedBuildInputs = with self; [urllib3];
497 src = fetchurl {
497 src = fetchurl {
498 url = "https://pypi.python.org/packages/13/9b/540e311b31a10c2a904acfb08030c656047e5c7ba479d35df2799e5dccfe/elasticsearch-1.9.0.tar.gz";
498 url = "https://pypi.python.org/packages/13/9b/540e311b31a10c2a904acfb08030c656047e5c7ba479d35df2799e5dccfe/elasticsearch-1.9.0.tar.gz";
499 md5 = "3550390baea1639479f79758d66ab032";
499 md5 = "3550390baea1639479f79758d66ab032";
500 };
500 };
501 };
501 };
502 flake8 = super.buildPythonPackage {
502 flake8 = super.buildPythonPackage {
503 name = "flake8-2.4.1";
503 name = "flake8-2.4.1";
504 buildInputs = with self; [];
504 buildInputs = with self; [];
505 doCheck = false;
505 doCheck = false;
506 propagatedBuildInputs = with self; [pyflakes pep8 mccabe];
506 propagatedBuildInputs = with self; [pyflakes pep8 mccabe];
507 src = fetchurl {
507 src = fetchurl {
508 url = "https://pypi.python.org/packages/8f/b5/9a73c66c7dba273bac8758398f060c008a25f3e84531063b42503b5d0a95/flake8-2.4.1.tar.gz";
508 url = "https://pypi.python.org/packages/8f/b5/9a73c66c7dba273bac8758398f060c008a25f3e84531063b42503b5d0a95/flake8-2.4.1.tar.gz";
509 md5 = "ed45d3db81a3b7c88bd63c6e37ca1d65";
509 md5 = "ed45d3db81a3b7c88bd63c6e37ca1d65";
510 };
510 };
511 };
511 };
512 future = super.buildPythonPackage {
512 future = super.buildPythonPackage {
513 name = "future-0.14.3";
513 name = "future-0.14.3";
514 buildInputs = with self; [];
514 buildInputs = with self; [];
515 doCheck = false;
515 doCheck = false;
516 propagatedBuildInputs = with self; [];
516 propagatedBuildInputs = with self; [];
517 src = fetchurl {
517 src = fetchurl {
518 url = "https://pypi.python.org/packages/83/80/8ef3a11a15f8eaafafa0937b20c1b3f73527e69ab6b3fa1cf94a5a96aabb/future-0.14.3.tar.gz";
518 url = "https://pypi.python.org/packages/83/80/8ef3a11a15f8eaafafa0937b20c1b3f73527e69ab6b3fa1cf94a5a96aabb/future-0.14.3.tar.gz";
519 md5 = "e94079b0bd1fc054929e8769fc0f6083";
519 md5 = "e94079b0bd1fc054929e8769fc0f6083";
520 };
520 };
521 };
521 };
522 futures = super.buildPythonPackage {
522 futures = super.buildPythonPackage {
523 name = "futures-3.0.2";
523 name = "futures-3.0.2";
524 buildInputs = with self; [];
524 buildInputs = with self; [];
525 doCheck = false;
525 doCheck = false;
526 propagatedBuildInputs = with self; [];
526 propagatedBuildInputs = with self; [];
527 src = fetchurl {
527 src = fetchurl {
528 url = "https://pypi.python.org/packages/f8/e7/fc0fcbeb9193ba2d4de00b065e7fd5aecd0679e93ce95a07322b2b1434f4/futures-3.0.2.tar.gz";
528 url = "https://pypi.python.org/packages/f8/e7/fc0fcbeb9193ba2d4de00b065e7fd5aecd0679e93ce95a07322b2b1434f4/futures-3.0.2.tar.gz";
529 md5 = "42aaf1e4de48d6e871d77dc1f9d96d5a";
529 md5 = "42aaf1e4de48d6e871d77dc1f9d96d5a";
530 };
530 };
531 };
531 };
532 gnureadline = super.buildPythonPackage {
532 gnureadline = super.buildPythonPackage {
533 name = "gnureadline-6.3.3";
533 name = "gnureadline-6.3.3";
534 buildInputs = with self; [];
534 buildInputs = with self; [];
535 doCheck = false;
535 doCheck = false;
536 propagatedBuildInputs = with self; [];
536 propagatedBuildInputs = with self; [];
537 src = fetchurl {
537 src = fetchurl {
538 url = "https://pypi.python.org/packages/3a/ee/2c3f568b0a74974791ac590ec742ef6133e2fbd287a074ba72a53fa5e97c/gnureadline-6.3.3.tar.gz";
538 url = "https://pypi.python.org/packages/3a/ee/2c3f568b0a74974791ac590ec742ef6133e2fbd287a074ba72a53fa5e97c/gnureadline-6.3.3.tar.gz";
539 md5 = "c4af83c9a3fbeac8f2da9b5a7c60e51c";
539 md5 = "c4af83c9a3fbeac8f2da9b5a7c60e51c";
540 };
540 };
541 };
541 };
542 gprof2dot = super.buildPythonPackage {
542 gprof2dot = super.buildPythonPackage {
543 name = "gprof2dot-2015.12.1";
543 name = "gprof2dot-2015.12.1";
544 buildInputs = with self; [];
544 buildInputs = with self; [];
545 doCheck = false;
545 doCheck = false;
546 propagatedBuildInputs = with self; [];
546 propagatedBuildInputs = with self; [];
547 src = fetchurl {
547 src = fetchurl {
548 url = "https://pypi.python.org/packages/b9/34/7bf93c1952d40fa5c95ad963f4d8344b61ef58558632402eca18e6c14127/gprof2dot-2015.12.1.tar.gz";
548 url = "https://pypi.python.org/packages/b9/34/7bf93c1952d40fa5c95ad963f4d8344b61ef58558632402eca18e6c14127/gprof2dot-2015.12.1.tar.gz";
549 md5 = "e23bf4e2f94db032750c193384b4165b";
549 md5 = "e23bf4e2f94db032750c193384b4165b";
550 };
550 };
551 };
551 };
552 greenlet = super.buildPythonPackage {
552 greenlet = super.buildPythonPackage {
553 name = "greenlet-0.4.7";
553 name = "greenlet-0.4.7";
554 buildInputs = with self; [];
554 buildInputs = with self; [];
555 doCheck = false;
555 doCheck = false;
556 propagatedBuildInputs = with self; [];
556 propagatedBuildInputs = with self; [];
557 src = fetchurl {
557 src = fetchurl {
558 url = "https://pypi.python.org/packages/7a/9f/a1a0d9bdf3203ae1502c5a8434fe89d323599d78a106985bc327351a69d4/greenlet-0.4.7.zip";
558 url = "https://pypi.python.org/packages/7a/9f/a1a0d9bdf3203ae1502c5a8434fe89d323599d78a106985bc327351a69d4/greenlet-0.4.7.zip";
559 md5 = "c2333a8ff30fa75c5d5ec0e67b461086";
559 md5 = "c2333a8ff30fa75c5d5ec0e67b461086";
560 };
560 };
561 };
561 };
562 gunicorn = super.buildPythonPackage {
562 gunicorn = super.buildPythonPackage {
563 name = "gunicorn-19.6.0";
563 name = "gunicorn-19.6.0";
564 buildInputs = with self; [];
564 buildInputs = with self; [];
565 doCheck = false;
565 doCheck = false;
566 propagatedBuildInputs = with self; [];
566 propagatedBuildInputs = with self; [];
567 src = fetchurl {
567 src = fetchurl {
568 url = "https://pypi.python.org/packages/84/ce/7ea5396efad1cef682bbc4068e72a0276341d9d9d0f501da609fab9fcb80/gunicorn-19.6.0.tar.gz";
568 url = "https://pypi.python.org/packages/84/ce/7ea5396efad1cef682bbc4068e72a0276341d9d9d0f501da609fab9fcb80/gunicorn-19.6.0.tar.gz";
569 md5 = "338e5e8a83ea0f0625f768dba4597530";
569 md5 = "338e5e8a83ea0f0625f768dba4597530";
570 };
570 };
571 };
571 };
572 infrae.cache = super.buildPythonPackage {
572 infrae.cache = super.buildPythonPackage {
573 name = "infrae.cache-1.0.1";
573 name = "infrae.cache-1.0.1";
574 buildInputs = with self; [];
574 buildInputs = with self; [];
575 doCheck = false;
575 doCheck = false;
576 propagatedBuildInputs = with self; [Beaker repoze.lru];
576 propagatedBuildInputs = with self; [Beaker repoze.lru];
577 src = fetchurl {
577 src = fetchurl {
578 url = "https://pypi.python.org/packages/bb/f0/e7d5e984cf6592fd2807dc7bc44a93f9d18e04e6a61f87fdfb2622422d74/infrae.cache-1.0.1.tar.gz";
578 url = "https://pypi.python.org/packages/bb/f0/e7d5e984cf6592fd2807dc7bc44a93f9d18e04e6a61f87fdfb2622422d74/infrae.cache-1.0.1.tar.gz";
579 md5 = "b09076a766747e6ed2a755cc62088e32";
579 md5 = "b09076a766747e6ed2a755cc62088e32";
580 };
580 };
581 };
581 };
582 invoke = super.buildPythonPackage {
582 invoke = super.buildPythonPackage {
583 name = "invoke-0.11.1";
583 name = "invoke-0.11.1";
584 buildInputs = with self; [];
584 buildInputs = with self; [];
585 doCheck = false;
585 doCheck = false;
586 propagatedBuildInputs = with self; [];
586 propagatedBuildInputs = with self; [];
587 src = fetchurl {
587 src = fetchurl {
588 url = "https://pypi.python.org/packages/d3/bb/36a5558ea19882073def7b0edeef4a0e6282056fed96506dd10b1d532bd4/invoke-0.11.1.tar.gz";
588 url = "https://pypi.python.org/packages/d3/bb/36a5558ea19882073def7b0edeef4a0e6282056fed96506dd10b1d532bd4/invoke-0.11.1.tar.gz";
589 md5 = "3d4ecbe26779ceef1046ecf702c9c4a8";
589 md5 = "3d4ecbe26779ceef1046ecf702c9c4a8";
590 };
590 };
591 };
591 };
592 ipdb = super.buildPythonPackage {
592 ipdb = super.buildPythonPackage {
593 name = "ipdb-0.8";
593 name = "ipdb-0.8";
594 buildInputs = with self; [];
594 buildInputs = with self; [];
595 doCheck = false;
595 doCheck = false;
596 propagatedBuildInputs = with self; [ipython];
596 propagatedBuildInputs = with self; [ipython];
597 src = fetchurl {
597 src = fetchurl {
598 url = "https://pypi.python.org/packages/f0/25/d7dd430ced6cd8dc242a933c8682b5dbf32eb4011d82f87e34209e5ec845/ipdb-0.8.zip";
598 url = "https://pypi.python.org/packages/f0/25/d7dd430ced6cd8dc242a933c8682b5dbf32eb4011d82f87e34209e5ec845/ipdb-0.8.zip";
599 md5 = "96dca0712efa01aa5eaf6b22071dd3ed";
599 md5 = "96dca0712efa01aa5eaf6b22071dd3ed";
600 };
600 };
601 };
601 };
602 ipython = super.buildPythonPackage {
602 ipython = super.buildPythonPackage {
603 name = "ipython-3.1.0";
603 name = "ipython-3.1.0";
604 buildInputs = with self; [];
604 buildInputs = with self; [];
605 doCheck = false;
605 doCheck = false;
606 propagatedBuildInputs = with self; [gnureadline];
606 propagatedBuildInputs = with self; [gnureadline];
607 src = fetchurl {
607 src = fetchurl {
608 url = "https://pypi.python.org/packages/06/91/120c0835254c120af89f066afaabf81289bc2726c1fc3ca0555df6882f58/ipython-3.1.0.tar.gz";
608 url = "https://pypi.python.org/packages/06/91/120c0835254c120af89f066afaabf81289bc2726c1fc3ca0555df6882f58/ipython-3.1.0.tar.gz";
609 md5 = "a749d90c16068687b0ec45a27e72ef8f";
609 md5 = "a749d90c16068687b0ec45a27e72ef8f";
610 };
610 };
611 };
611 };
612 iso8601 = super.buildPythonPackage {
612 iso8601 = super.buildPythonPackage {
613 name = "iso8601-0.1.11";
613 name = "iso8601-0.1.11";
614 buildInputs = with self; [];
614 buildInputs = with self; [];
615 doCheck = false;
615 doCheck = false;
616 propagatedBuildInputs = with self; [];
616 propagatedBuildInputs = with self; [];
617 src = fetchurl {
617 src = fetchurl {
618 url = "https://pypi.python.org/packages/c0/75/c9209ee4d1b5975eb8c2cba4428bde6b61bd55664a98290dd015cdb18e98/iso8601-0.1.11.tar.gz";
618 url = "https://pypi.python.org/packages/c0/75/c9209ee4d1b5975eb8c2cba4428bde6b61bd55664a98290dd015cdb18e98/iso8601-0.1.11.tar.gz";
619 md5 = "b06d11cd14a64096f907086044f0fe38";
619 md5 = "b06d11cd14a64096f907086044f0fe38";
620 };
620 };
621 };
621 };
622 itsdangerous = super.buildPythonPackage {
622 itsdangerous = super.buildPythonPackage {
623 name = "itsdangerous-0.24";
623 name = "itsdangerous-0.24";
624 buildInputs = with self; [];
624 buildInputs = with self; [];
625 doCheck = false;
625 doCheck = false;
626 propagatedBuildInputs = with self; [];
626 propagatedBuildInputs = with self; [];
627 src = fetchurl {
627 src = fetchurl {
628 url = "https://pypi.python.org/packages/dc/b4/a60bcdba945c00f6d608d8975131ab3f25b22f2bcfe1dab221165194b2d4/itsdangerous-0.24.tar.gz";
628 url = "https://pypi.python.org/packages/dc/b4/a60bcdba945c00f6d608d8975131ab3f25b22f2bcfe1dab221165194b2d4/itsdangerous-0.24.tar.gz";
629 md5 = "a3d55aa79369aef5345c036a8a26307f";
629 md5 = "a3d55aa79369aef5345c036a8a26307f";
630 };
630 };
631 };
631 };
632 kombu = super.buildPythonPackage {
632 kombu = super.buildPythonPackage {
633 name = "kombu-1.5.1";
633 name = "kombu-1.5.1";
634 buildInputs = with self; [];
634 buildInputs = with self; [];
635 doCheck = false;
635 doCheck = false;
636 propagatedBuildInputs = with self; [anyjson amqplib];
636 propagatedBuildInputs = with self; [anyjson amqplib];
637 src = fetchurl {
637 src = fetchurl {
638 url = "https://pypi.python.org/packages/19/53/74bf2a624644b45f0850a638752514fc10a8e1cbd738f10804951a6df3f5/kombu-1.5.1.tar.gz";
638 url = "https://pypi.python.org/packages/19/53/74bf2a624644b45f0850a638752514fc10a8e1cbd738f10804951a6df3f5/kombu-1.5.1.tar.gz";
639 md5 = "50662f3c7e9395b3d0721fb75d100b63";
639 md5 = "50662f3c7e9395b3d0721fb75d100b63";
640 };
640 };
641 };
641 };
642 lxml = super.buildPythonPackage {
642 lxml = super.buildPythonPackage {
643 name = "lxml-3.4.4";
643 name = "lxml-3.4.4";
644 buildInputs = with self; [];
644 buildInputs = with self; [];
645 doCheck = false;
645 doCheck = false;
646 propagatedBuildInputs = with self; [];
646 propagatedBuildInputs = with self; [];
647 src = fetchurl {
647 src = fetchurl {
648 url = "https://pypi.python.org/packages/63/c7/4f2a2a4ad6c6fa99b14be6b3c1cece9142e2d915aa7c43c908677afc8fa4/lxml-3.4.4.tar.gz";
648 url = "https://pypi.python.org/packages/63/c7/4f2a2a4ad6c6fa99b14be6b3c1cece9142e2d915aa7c43c908677afc8fa4/lxml-3.4.4.tar.gz";
649 md5 = "a9a65972afc173ec7a39c585f4eea69c";
649 md5 = "a9a65972afc173ec7a39c585f4eea69c";
650 };
650 };
651 };
651 };
652 mccabe = super.buildPythonPackage {
652 mccabe = super.buildPythonPackage {
653 name = "mccabe-0.3";
653 name = "mccabe-0.3";
654 buildInputs = with self; [];
654 buildInputs = with self; [];
655 doCheck = false;
655 doCheck = false;
656 propagatedBuildInputs = with self; [];
656 propagatedBuildInputs = with self; [];
657 src = fetchurl {
657 src = fetchurl {
658 url = "https://pypi.python.org/packages/c9/2e/75231479e11a906b64ac43bad9d0bb534d00080b18bdca8db9da46e1faf7/mccabe-0.3.tar.gz";
658 url = "https://pypi.python.org/packages/c9/2e/75231479e11a906b64ac43bad9d0bb534d00080b18bdca8db9da46e1faf7/mccabe-0.3.tar.gz";
659 md5 = "81640948ff226f8c12b3277059489157";
659 md5 = "81640948ff226f8c12b3277059489157";
660 };
660 };
661 };
661 };
662 meld3 = super.buildPythonPackage {
662 meld3 = super.buildPythonPackage {
663 name = "meld3-1.0.2";
663 name = "meld3-1.0.2";
664 buildInputs = with self; [];
664 buildInputs = with self; [];
665 doCheck = false;
665 doCheck = false;
666 propagatedBuildInputs = with self; [];
666 propagatedBuildInputs = with self; [];
667 src = fetchurl {
667 src = fetchurl {
668 url = "https://pypi.python.org/packages/45/a0/317c6422b26c12fe0161e936fc35f36552069ba8e6f7ecbd99bbffe32a5f/meld3-1.0.2.tar.gz";
668 url = "https://pypi.python.org/packages/45/a0/317c6422b26c12fe0161e936fc35f36552069ba8e6f7ecbd99bbffe32a5f/meld3-1.0.2.tar.gz";
669 md5 = "3ccc78cd79cffd63a751ad7684c02c91";
669 md5 = "3ccc78cd79cffd63a751ad7684c02c91";
670 };
670 };
671 };
671 };
672 mock = super.buildPythonPackage {
672 mock = super.buildPythonPackage {
673 name = "mock-1.0.1";
673 name = "mock-1.0.1";
674 buildInputs = with self; [];
674 buildInputs = with self; [];
675 doCheck = false;
675 doCheck = false;
676 propagatedBuildInputs = with self; [];
676 propagatedBuildInputs = with self; [];
677 src = fetchurl {
677 src = fetchurl {
678 url = "https://pypi.python.org/packages/15/45/30273ee91feb60dabb8fbb2da7868520525f02cf910279b3047182feed80/mock-1.0.1.zip";
678 url = "https://pypi.python.org/packages/15/45/30273ee91feb60dabb8fbb2da7868520525f02cf910279b3047182feed80/mock-1.0.1.zip";
679 md5 = "869f08d003c289a97c1a6610faf5e913";
679 md5 = "869f08d003c289a97c1a6610faf5e913";
680 };
680 };
681 };
681 };
682 msgpack-python = super.buildPythonPackage {
682 msgpack-python = super.buildPythonPackage {
683 name = "msgpack-python-0.4.6";
683 name = "msgpack-python-0.4.6";
684 buildInputs = with self; [];
684 buildInputs = with self; [];
685 doCheck = false;
685 doCheck = false;
686 propagatedBuildInputs = with self; [];
686 propagatedBuildInputs = with self; [];
687 src = fetchurl {
687 src = fetchurl {
688 url = "https://pypi.python.org/packages/15/ce/ff2840885789ef8035f66cd506ea05bdb228340307d5e71a7b1e3f82224c/msgpack-python-0.4.6.tar.gz";
688 url = "https://pypi.python.org/packages/15/ce/ff2840885789ef8035f66cd506ea05bdb228340307d5e71a7b1e3f82224c/msgpack-python-0.4.6.tar.gz";
689 md5 = "8b317669314cf1bc881716cccdaccb30";
689 md5 = "8b317669314cf1bc881716cccdaccb30";
690 };
690 };
691 };
691 };
692 nose = super.buildPythonPackage {
692 nose = super.buildPythonPackage {
693 name = "nose-1.3.6";
693 name = "nose-1.3.6";
694 buildInputs = with self; [];
694 buildInputs = with self; [];
695 doCheck = false;
695 doCheck = false;
696 propagatedBuildInputs = with self; [];
696 propagatedBuildInputs = with self; [];
697 src = fetchurl {
697 src = fetchurl {
698 url = "https://pypi.python.org/packages/70/c7/469e68148d17a0d3db5ed49150242fd70a74a8147b8f3f8b87776e028d99/nose-1.3.6.tar.gz";
698 url = "https://pypi.python.org/packages/70/c7/469e68148d17a0d3db5ed49150242fd70a74a8147b8f3f8b87776e028d99/nose-1.3.6.tar.gz";
699 md5 = "0ca546d81ca8309080fc80cb389e7a16";
699 md5 = "0ca546d81ca8309080fc80cb389e7a16";
700 };
700 };
701 };
701 };
702 objgraph = super.buildPythonPackage {
702 objgraph = super.buildPythonPackage {
703 name = "objgraph-2.0.0";
703 name = "objgraph-2.0.0";
704 buildInputs = with self; [];
704 buildInputs = with self; [];
705 doCheck = false;
705 doCheck = false;
706 propagatedBuildInputs = with self; [];
706 propagatedBuildInputs = with self; [];
707 src = fetchurl {
707 src = fetchurl {
708 url = "https://pypi.python.org/packages/d7/33/ace750b59247496ed769b170586c5def7202683f3d98e737b75b767ff29e/objgraph-2.0.0.tar.gz";
708 url = "https://pypi.python.org/packages/d7/33/ace750b59247496ed769b170586c5def7202683f3d98e737b75b767ff29e/objgraph-2.0.0.tar.gz";
709 md5 = "25b0d5e5adc74aa63ead15699614159c";
709 md5 = "25b0d5e5adc74aa63ead15699614159c";
710 };
710 };
711 };
711 };
712 packaging = super.buildPythonPackage {
712 packaging = super.buildPythonPackage {
713 name = "packaging-15.2";
713 name = "packaging-15.2";
714 buildInputs = with self; [];
714 buildInputs = with self; [];
715 doCheck = false;
715 doCheck = false;
716 propagatedBuildInputs = with self; [];
716 propagatedBuildInputs = with self; [];
717 src = fetchurl {
717 src = fetchurl {
718 url = "https://pypi.python.org/packages/24/c4/185da1304f07047dc9e0c46c31db75c0351bd73458ac3efad7da3dbcfbe1/packaging-15.2.tar.gz";
718 url = "https://pypi.python.org/packages/24/c4/185da1304f07047dc9e0c46c31db75c0351bd73458ac3efad7da3dbcfbe1/packaging-15.2.tar.gz";
719 md5 = "c16093476f6ced42128bf610e5db3784";
719 md5 = "c16093476f6ced42128bf610e5db3784";
720 };
720 };
721 };
721 };
722 paramiko = super.buildPythonPackage {
722 paramiko = super.buildPythonPackage {
723 name = "paramiko-1.15.1";
723 name = "paramiko-1.15.1";
724 buildInputs = with self; [];
724 buildInputs = with self; [];
725 doCheck = false;
725 doCheck = false;
726 propagatedBuildInputs = with self; [pycrypto ecdsa];
726 propagatedBuildInputs = with self; [pycrypto ecdsa];
727 src = fetchurl {
727 src = fetchurl {
728 url = "https://pypi.python.org/packages/04/2b/a22d2a560c1951abbbf95a0628e245945565f70dc082d9e784666887222c/paramiko-1.15.1.tar.gz";
728 url = "https://pypi.python.org/packages/04/2b/a22d2a560c1951abbbf95a0628e245945565f70dc082d9e784666887222c/paramiko-1.15.1.tar.gz";
729 md5 = "48c274c3f9b1282932567b21f6acf3b5";
729 md5 = "48c274c3f9b1282932567b21f6acf3b5";
730 };
730 };
731 };
731 };
732 pep8 = super.buildPythonPackage {
732 pep8 = super.buildPythonPackage {
733 name = "pep8-1.5.7";
733 name = "pep8-1.5.7";
734 buildInputs = with self; [];
734 buildInputs = with self; [];
735 doCheck = false;
735 doCheck = false;
736 propagatedBuildInputs = with self; [];
736 propagatedBuildInputs = with self; [];
737 src = fetchurl {
737 src = fetchurl {
738 url = "https://pypi.python.org/packages/8b/de/259f5e735897ada1683489dd514b2a1c91aaa74e5e6b68f80acf128a6368/pep8-1.5.7.tar.gz";
738 url = "https://pypi.python.org/packages/8b/de/259f5e735897ada1683489dd514b2a1c91aaa74e5e6b68f80acf128a6368/pep8-1.5.7.tar.gz";
739 md5 = "f6adbdd69365ecca20513c709f9b7c93";
739 md5 = "f6adbdd69365ecca20513c709f9b7c93";
740 };
740 };
741 };
741 };
742 psutil = super.buildPythonPackage {
742 psutil = super.buildPythonPackage {
743 name = "psutil-2.2.1";
743 name = "psutil-2.2.1";
744 buildInputs = with self; [];
744 buildInputs = with self; [];
745 doCheck = false;
745 doCheck = false;
746 propagatedBuildInputs = with self; [];
746 propagatedBuildInputs = with self; [];
747 src = fetchurl {
747 src = fetchurl {
748 url = "https://pypi.python.org/packages/df/47/ee54ef14dd40f8ce831a7581001a5096494dc99fe71586260ca6b531fe86/psutil-2.2.1.tar.gz";
748 url = "https://pypi.python.org/packages/df/47/ee54ef14dd40f8ce831a7581001a5096494dc99fe71586260ca6b531fe86/psutil-2.2.1.tar.gz";
749 md5 = "1a2b58cd9e3a53528bb6148f0c4d5244";
749 md5 = "1a2b58cd9e3a53528bb6148f0c4d5244";
750 };
750 };
751 };
751 };
752 psycopg2 = super.buildPythonPackage {
752 psycopg2 = super.buildPythonPackage {
753 name = "psycopg2-2.6";
753 name = "psycopg2-2.6";
754 buildInputs = with self; [];
754 buildInputs = with self; [];
755 doCheck = false;
755 doCheck = false;
756 propagatedBuildInputs = with self; [];
756 propagatedBuildInputs = with self; [];
757 src = fetchurl {
757 src = fetchurl {
758 url = "https://pypi.python.org/packages/dd/c7/9016ff8ff69da269b1848276eebfb264af5badf6b38caad805426771f04d/psycopg2-2.6.tar.gz";
758 url = "https://pypi.python.org/packages/dd/c7/9016ff8ff69da269b1848276eebfb264af5badf6b38caad805426771f04d/psycopg2-2.6.tar.gz";
759 md5 = "fbbb039a8765d561a1c04969bbae7c74";
759 md5 = "fbbb039a8765d561a1c04969bbae7c74";
760 };
760 };
761 };
761 };
762 py = super.buildPythonPackage {
762 py = super.buildPythonPackage {
763 name = "py-1.4.29";
763 name = "py-1.4.29";
764 buildInputs = with self; [];
764 buildInputs = with self; [];
765 doCheck = false;
765 doCheck = false;
766 propagatedBuildInputs = with self; [];
766 propagatedBuildInputs = with self; [];
767 src = fetchurl {
767 src = fetchurl {
768 url = "https://pypi.python.org/packages/2a/bc/a1a4a332ac10069b8e5e25136a35e08a03f01fd6ab03d819889d79a1fd65/py-1.4.29.tar.gz";
768 url = "https://pypi.python.org/packages/2a/bc/a1a4a332ac10069b8e5e25136a35e08a03f01fd6ab03d819889d79a1fd65/py-1.4.29.tar.gz";
769 md5 = "c28e0accba523a29b35a48bb703fb96c";
769 md5 = "c28e0accba523a29b35a48bb703fb96c";
770 };
770 };
771 };
771 };
772 py-bcrypt = super.buildPythonPackage {
772 py-bcrypt = super.buildPythonPackage {
773 name = "py-bcrypt-0.4";
773 name = "py-bcrypt-0.4";
774 buildInputs = with self; [];
774 buildInputs = with self; [];
775 doCheck = false;
775 doCheck = false;
776 propagatedBuildInputs = with self; [];
776 propagatedBuildInputs = with self; [];
777 src = fetchurl {
777 src = fetchurl {
778 url = "https://pypi.python.org/packages/68/b1/1c3068c5c4d2e35c48b38dcc865301ebfdf45f54507086ac65ced1fd3b3d/py-bcrypt-0.4.tar.gz";
778 url = "https://pypi.python.org/packages/68/b1/1c3068c5c4d2e35c48b38dcc865301ebfdf45f54507086ac65ced1fd3b3d/py-bcrypt-0.4.tar.gz";
779 md5 = "dd8b367d6b716a2ea2e72392525f4e36";
779 md5 = "dd8b367d6b716a2ea2e72392525f4e36";
780 };
780 };
781 };
781 };
782 pycrypto = super.buildPythonPackage {
782 pycrypto = super.buildPythonPackage {
783 name = "pycrypto-2.6.1";
783 name = "pycrypto-2.6.1";
784 buildInputs = with self; [];
784 buildInputs = with self; [];
785 doCheck = false;
785 doCheck = false;
786 propagatedBuildInputs = with self; [];
786 propagatedBuildInputs = with self; [];
787 src = fetchurl {
787 src = fetchurl {
788 url = "https://pypi.python.org/packages/60/db/645aa9af249f059cc3a368b118de33889219e0362141e75d4eaf6f80f163/pycrypto-2.6.1.tar.gz";
788 url = "https://pypi.python.org/packages/60/db/645aa9af249f059cc3a368b118de33889219e0362141e75d4eaf6f80f163/pycrypto-2.6.1.tar.gz";
789 md5 = "55a61a054aa66812daf5161a0d5d7eda";
789 md5 = "55a61a054aa66812daf5161a0d5d7eda";
790 };
790 };
791 };
791 };
792 pycurl = super.buildPythonPackage {
792 pycurl = super.buildPythonPackage {
793 name = "pycurl-7.19.5";
793 name = "pycurl-7.19.5";
794 buildInputs = with self; [];
794 buildInputs = with self; [];
795 doCheck = false;
795 doCheck = false;
796 propagatedBuildInputs = with self; [];
796 propagatedBuildInputs = with self; [];
797 src = fetchurl {
797 src = fetchurl {
798 url = "https://pypi.python.org/packages/6c/48/13bad289ef6f4869b1d8fc11ae54de8cfb3cc4a2eb9f7419c506f763be46/pycurl-7.19.5.tar.gz";
798 url = "https://pypi.python.org/packages/6c/48/13bad289ef6f4869b1d8fc11ae54de8cfb3cc4a2eb9f7419c506f763be46/pycurl-7.19.5.tar.gz";
799 md5 = "47b4eac84118e2606658122104e62072";
799 md5 = "47b4eac84118e2606658122104e62072";
800 };
800 };
801 };
801 };
802 pyelasticsearch = super.buildPythonPackage {
802 pyelasticsearch = super.buildPythonPackage {
803 name = "pyelasticsearch-1.4";
803 name = "pyelasticsearch-1.4";
804 buildInputs = with self; [];
804 buildInputs = with self; [];
805 doCheck = false;
805 doCheck = false;
806 propagatedBuildInputs = with self; [certifi elasticsearch urllib3 simplejson six];
806 propagatedBuildInputs = with self; [certifi elasticsearch urllib3 simplejson six];
807 src = fetchurl {
807 src = fetchurl {
808 url = "https://pypi.python.org/packages/2f/3a/7643cfcfc4cbdbb20ada800bbd54ac9705d0c047d7b8f8d5eeeb3047b4eb/pyelasticsearch-1.4.tar.gz";
808 url = "https://pypi.python.org/packages/2f/3a/7643cfcfc4cbdbb20ada800bbd54ac9705d0c047d7b8f8d5eeeb3047b4eb/pyelasticsearch-1.4.tar.gz";
809 md5 = "ed61ebb7b253364e55b4923d11e17049";
809 md5 = "ed61ebb7b253364e55b4923d11e17049";
810 };
810 };
811 };
811 };
812 pyflakes = super.buildPythonPackage {
812 pyflakes = super.buildPythonPackage {
813 name = "pyflakes-0.8.1";
813 name = "pyflakes-0.8.1";
814 buildInputs = with self; [];
814 buildInputs = with self; [];
815 doCheck = false;
815 doCheck = false;
816 propagatedBuildInputs = with self; [];
816 propagatedBuildInputs = with self; [];
817 src = fetchurl {
817 src = fetchurl {
818 url = "https://pypi.python.org/packages/75/22/a90ec0252f4f87f3ffb6336504de71fe16a49d69c4538dae2f12b9360a38/pyflakes-0.8.1.tar.gz";
818 url = "https://pypi.python.org/packages/75/22/a90ec0252f4f87f3ffb6336504de71fe16a49d69c4538dae2f12b9360a38/pyflakes-0.8.1.tar.gz";
819 md5 = "905fe91ad14b912807e8fdc2ac2e2c23";
819 md5 = "905fe91ad14b912807e8fdc2ac2e2c23";
820 };
820 };
821 };
821 };
822 pyparsing = super.buildPythonPackage {
822 pyparsing = super.buildPythonPackage {
823 name = "pyparsing-1.5.7";
823 name = "pyparsing-1.5.7";
824 buildInputs = with self; [];
824 buildInputs = with self; [];
825 doCheck = false;
825 doCheck = false;
826 propagatedBuildInputs = with self; [];
826 propagatedBuildInputs = with self; [];
827 src = fetchurl {
827 src = fetchurl {
828 url = "https://pypi.python.org/packages/2e/26/e8fb5b4256a5f5036be7ce115ef8db8d06bc537becfbdc46c6af008314ee/pyparsing-1.5.7.zip";
828 url = "https://pypi.python.org/packages/2e/26/e8fb5b4256a5f5036be7ce115ef8db8d06bc537becfbdc46c6af008314ee/pyparsing-1.5.7.zip";
829 md5 = "b86854857a368d6ccb4d5b6e76d0637f";
829 md5 = "b86854857a368d6ccb4d5b6e76d0637f";
830 };
830 };
831 };
831 };
832 pyramid = super.buildPythonPackage {
832 pyramid = super.buildPythonPackage {
833 name = "pyramid-1.6.1";
833 name = "pyramid-1.6.1";
834 buildInputs = with self; [];
834 buildInputs = with self; [];
835 doCheck = false;
835 doCheck = false;
836 propagatedBuildInputs = with self; [setuptools WebOb repoze.lru zope.interface zope.deprecation venusian translationstring PasteDeploy];
836 propagatedBuildInputs = with self; [setuptools WebOb repoze.lru zope.interface zope.deprecation venusian translationstring PasteDeploy];
837 src = fetchurl {
837 src = fetchurl {
838 url = "https://pypi.python.org/packages/30/b3/fcc4a2a4800cbf21989e00454b5828cf1f7fe35c63e0810b350e56d4c475/pyramid-1.6.1.tar.gz";
838 url = "https://pypi.python.org/packages/30/b3/fcc4a2a4800cbf21989e00454b5828cf1f7fe35c63e0810b350e56d4c475/pyramid-1.6.1.tar.gz";
839 md5 = "b18688ff3cc33efdbb098a35b45dd122";
839 md5 = "b18688ff3cc33efdbb098a35b45dd122";
840 };
840 };
841 };
841 };
842 pyramid-beaker = super.buildPythonPackage {
842 pyramid-beaker = super.buildPythonPackage {
843 name = "pyramid-beaker-0.8";
843 name = "pyramid-beaker-0.8";
844 buildInputs = with self; [];
844 buildInputs = with self; [];
845 doCheck = false;
845 doCheck = false;
846 propagatedBuildInputs = with self; [pyramid Beaker];
846 propagatedBuildInputs = with self; [pyramid Beaker];
847 src = fetchurl {
847 src = fetchurl {
848 url = "https://pypi.python.org/packages/d9/6e/b85426e00fd3d57f4545f74e1c3828552d8700f13ededeef9233f7bca8be/pyramid_beaker-0.8.tar.gz";
848 url = "https://pypi.python.org/packages/d9/6e/b85426e00fd3d57f4545f74e1c3828552d8700f13ededeef9233f7bca8be/pyramid_beaker-0.8.tar.gz";
849 md5 = "22f14be31b06549f80890e2c63a93834";
849 md5 = "22f14be31b06549f80890e2c63a93834";
850 };
850 };
851 };
851 };
852 pyramid-debugtoolbar = super.buildPythonPackage {
852 pyramid-debugtoolbar = super.buildPythonPackage {
853 name = "pyramid-debugtoolbar-2.4.2";
853 name = "pyramid-debugtoolbar-2.4.2";
854 buildInputs = with self; [];
854 buildInputs = with self; [];
855 doCheck = false;
855 doCheck = false;
856 propagatedBuildInputs = with self; [pyramid pyramid-mako repoze.lru Pygments];
856 propagatedBuildInputs = with self; [pyramid pyramid-mako repoze.lru Pygments];
857 src = fetchurl {
857 src = fetchurl {
858 url = "https://pypi.python.org/packages/89/00/ed5426ee41ed747ba3ffd30e8230841a6878286ea67d480b1444d24f06a2/pyramid_debugtoolbar-2.4.2.tar.gz";
858 url = "https://pypi.python.org/packages/89/00/ed5426ee41ed747ba3ffd30e8230841a6878286ea67d480b1444d24f06a2/pyramid_debugtoolbar-2.4.2.tar.gz";
859 md5 = "073ea67086cc4bd5decc3a000853642d";
859 md5 = "073ea67086cc4bd5decc3a000853642d";
860 };
860 };
861 };
861 };
862 pyramid-jinja2 = super.buildPythonPackage {
862 pyramid-jinja2 = super.buildPythonPackage {
863 name = "pyramid-jinja2-2.5";
863 name = "pyramid-jinja2-2.5";
864 buildInputs = with self; [];
864 buildInputs = with self; [];
865 doCheck = false;
865 doCheck = false;
866 propagatedBuildInputs = with self; [pyramid zope.deprecation Jinja2 MarkupSafe];
866 propagatedBuildInputs = with self; [pyramid zope.deprecation Jinja2 MarkupSafe];
867 src = fetchurl {
867 src = fetchurl {
868 url = "https://pypi.python.org/packages/a1/80/595e26ffab7deba7208676b6936b7e5a721875710f982e59899013cae1ed/pyramid_jinja2-2.5.tar.gz";
868 url = "https://pypi.python.org/packages/a1/80/595e26ffab7deba7208676b6936b7e5a721875710f982e59899013cae1ed/pyramid_jinja2-2.5.tar.gz";
869 md5 = "07cb6547204ac5e6f0b22a954ccee928";
869 md5 = "07cb6547204ac5e6f0b22a954ccee928";
870 };
870 };
871 };
871 };
872 pyramid-mako = super.buildPythonPackage {
872 pyramid-mako = super.buildPythonPackage {
873 name = "pyramid-mako-1.0.2";
873 name = "pyramid-mako-1.0.2";
874 buildInputs = with self; [];
874 buildInputs = with self; [];
875 doCheck = false;
875 doCheck = false;
876 propagatedBuildInputs = with self; [pyramid Mako];
876 propagatedBuildInputs = with self; [pyramid Mako];
877 src = fetchurl {
877 src = fetchurl {
878 url = "https://pypi.python.org/packages/f1/92/7e69bcf09676d286a71cb3bbb887b16595b96f9ba7adbdc239ffdd4b1eb9/pyramid_mako-1.0.2.tar.gz";
878 url = "https://pypi.python.org/packages/f1/92/7e69bcf09676d286a71cb3bbb887b16595b96f9ba7adbdc239ffdd4b1eb9/pyramid_mako-1.0.2.tar.gz";
879 md5 = "ee25343a97eb76bd90abdc2a774eb48a";
879 md5 = "ee25343a97eb76bd90abdc2a774eb48a";
880 };
880 };
881 };
881 };
882 pysqlite = super.buildPythonPackage {
882 pysqlite = super.buildPythonPackage {
883 name = "pysqlite-2.6.3";
883 name = "pysqlite-2.6.3";
884 buildInputs = with self; [];
884 buildInputs = with self; [];
885 doCheck = false;
885 doCheck = false;
886 propagatedBuildInputs = with self; [];
886 propagatedBuildInputs = with self; [];
887 src = fetchurl {
887 src = fetchurl {
888 url = "https://pypi.python.org/packages/5c/a6/1c429cd4c8069cf4bfbd0eb4d592b3f4042155a8202df83d7e9b93aa3dc2/pysqlite-2.6.3.tar.gz";
888 url = "https://pypi.python.org/packages/5c/a6/1c429cd4c8069cf4bfbd0eb4d592b3f4042155a8202df83d7e9b93aa3dc2/pysqlite-2.6.3.tar.gz";
889 md5 = "7ff1cedee74646b50117acff87aa1cfa";
889 md5 = "7ff1cedee74646b50117acff87aa1cfa";
890 };
890 };
891 };
891 };
892 pytest = super.buildPythonPackage {
892 pytest = super.buildPythonPackage {
893 name = "pytest-2.8.5";
893 name = "pytest-2.8.5";
894 buildInputs = with self; [];
894 buildInputs = with self; [];
895 doCheck = false;
895 doCheck = false;
896 propagatedBuildInputs = with self; [py];
896 propagatedBuildInputs = with self; [py];
897 src = fetchurl {
897 src = fetchurl {
898 url = "https://pypi.python.org/packages/b1/3d/d7ea9b0c51e0cacded856e49859f0a13452747491e842c236bbab3714afe/pytest-2.8.5.zip";
898 url = "https://pypi.python.org/packages/b1/3d/d7ea9b0c51e0cacded856e49859f0a13452747491e842c236bbab3714afe/pytest-2.8.5.zip";
899 md5 = "8493b06f700862f1294298d6c1b715a9";
899 md5 = "8493b06f700862f1294298d6c1b715a9";
900 };
900 };
901 };
901 };
902 pytest-catchlog = super.buildPythonPackage {
902 pytest-catchlog = super.buildPythonPackage {
903 name = "pytest-catchlog-1.2.2";
903 name = "pytest-catchlog-1.2.2";
904 buildInputs = with self; [];
904 buildInputs = with self; [];
905 doCheck = false;
905 doCheck = false;
906 propagatedBuildInputs = with self; [py pytest];
906 propagatedBuildInputs = with self; [py pytest];
907 src = fetchurl {
907 src = fetchurl {
908 url = "https://pypi.python.org/packages/f2/2b/2faccdb1a978fab9dd0bf31cca9f6847fbe9184a0bdcc3011ac41dd44191/pytest-catchlog-1.2.2.zip";
908 url = "https://pypi.python.org/packages/f2/2b/2faccdb1a978fab9dd0bf31cca9f6847fbe9184a0bdcc3011ac41dd44191/pytest-catchlog-1.2.2.zip";
909 md5 = "09d890c54c7456c818102b7ff8c182c8";
909 md5 = "09d890c54c7456c818102b7ff8c182c8";
910 };
910 };
911 };
911 };
912 pytest-cov = super.buildPythonPackage {
912 pytest-cov = super.buildPythonPackage {
913 name = "pytest-cov-1.8.1";
913 name = "pytest-cov-1.8.1";
914 buildInputs = with self; [];
914 buildInputs = with self; [];
915 doCheck = false;
915 doCheck = false;
916 propagatedBuildInputs = with self; [py pytest coverage cov-core];
916 propagatedBuildInputs = with self; [py pytest coverage cov-core];
917 src = fetchurl {
917 src = fetchurl {
918 url = "https://pypi.python.org/packages/11/4b/b04646e97f1721878eb21e9f779102d84dd044d324382263b1770a3e4838/pytest-cov-1.8.1.tar.gz";
918 url = "https://pypi.python.org/packages/11/4b/b04646e97f1721878eb21e9f779102d84dd044d324382263b1770a3e4838/pytest-cov-1.8.1.tar.gz";
919 md5 = "76c778afa2494088270348be42d759fc";
919 md5 = "76c778afa2494088270348be42d759fc";
920 };
920 };
921 };
921 };
922 pytest-profiling = super.buildPythonPackage {
922 pytest-profiling = super.buildPythonPackage {
923 name = "pytest-profiling-1.0.1";
923 name = "pytest-profiling-1.0.1";
924 buildInputs = with self; [];
924 buildInputs = with self; [];
925 doCheck = false;
925 doCheck = false;
926 propagatedBuildInputs = with self; [six pytest gprof2dot];
926 propagatedBuildInputs = with self; [six pytest gprof2dot];
927 src = fetchurl {
927 src = fetchurl {
928 url = "https://pypi.python.org/packages/d8/67/8ffab73406e22870e07fa4dc8dce1d7689b26dba8efd00161c9b6fc01ec0/pytest-profiling-1.0.1.tar.gz";
928 url = "https://pypi.python.org/packages/d8/67/8ffab73406e22870e07fa4dc8dce1d7689b26dba8efd00161c9b6fc01ec0/pytest-profiling-1.0.1.tar.gz";
929 md5 = "354404eb5b3fd4dc5eb7fffbb3d9b68b";
929 md5 = "354404eb5b3fd4dc5eb7fffbb3d9b68b";
930 };
930 };
931 };
931 };
932 pytest-runner = super.buildPythonPackage {
932 pytest-runner = super.buildPythonPackage {
933 name = "pytest-runner-2.7.1";
933 name = "pytest-runner-2.7.1";
934 buildInputs = with self; [];
934 buildInputs = with self; [];
935 doCheck = false;
935 doCheck = false;
936 propagatedBuildInputs = with self; [];
936 propagatedBuildInputs = with self; [];
937 src = fetchurl {
937 src = fetchurl {
938 url = "https://pypi.python.org/packages/99/6b/c4ff4418d3424d4475b7af60724fd4a5cdd91ed8e489dc9443281f0052bc/pytest-runner-2.7.1.tar.gz";
938 url = "https://pypi.python.org/packages/99/6b/c4ff4418d3424d4475b7af60724fd4a5cdd91ed8e489dc9443281f0052bc/pytest-runner-2.7.1.tar.gz";
939 md5 = "e56f0bc8d79a6bd91772b44ef4215c7e";
939 md5 = "e56f0bc8d79a6bd91772b44ef4215c7e";
940 };
940 };
941 };
941 };
942 pytest-timeout = super.buildPythonPackage {
942 pytest-timeout = super.buildPythonPackage {
943 name = "pytest-timeout-0.4";
943 name = "pytest-timeout-0.4";
944 buildInputs = with self; [];
944 buildInputs = with self; [];
945 doCheck = false;
945 doCheck = false;
946 propagatedBuildInputs = with self; [pytest];
946 propagatedBuildInputs = with self; [pytest];
947 src = fetchurl {
947 src = fetchurl {
948 url = "https://pypi.python.org/packages/24/48/5f6bd4b8026a26e1dd427243d560a29a0f1b24a5c7cffca4bf049a7bb65b/pytest-timeout-0.4.tar.gz";
948 url = "https://pypi.python.org/packages/24/48/5f6bd4b8026a26e1dd427243d560a29a0f1b24a5c7cffca4bf049a7bb65b/pytest-timeout-0.4.tar.gz";
949 md5 = "03b28aff69cbbfb959ed35ade5fde262";
949 md5 = "03b28aff69cbbfb959ed35ade5fde262";
950 };
950 };
951 };
951 };
952 python-dateutil = super.buildPythonPackage {
952 python-dateutil = super.buildPythonPackage {
953 name = "python-dateutil-1.5";
953 name = "python-dateutil-1.5";
954 buildInputs = with self; [];
954 buildInputs = with self; [];
955 doCheck = false;
955 doCheck = false;
956 propagatedBuildInputs = with self; [];
956 propagatedBuildInputs = with self; [];
957 src = fetchurl {
957 src = fetchurl {
958 url = "https://pypi.python.org/packages/b4/7c/df59c89a753eb33c7c44e1dd42de0e9bc2ccdd5a4d576e0bfad97cc280cb/python-dateutil-1.5.tar.gz";
958 url = "https://pypi.python.org/packages/b4/7c/df59c89a753eb33c7c44e1dd42de0e9bc2ccdd5a4d576e0bfad97cc280cb/python-dateutil-1.5.tar.gz";
959 md5 = "0dcb1de5e5cad69490a3b6ab63f0cfa5";
959 md5 = "0dcb1de5e5cad69490a3b6ab63f0cfa5";
960 };
960 };
961 };
961 };
962 python-editor = super.buildPythonPackage {
962 python-editor = super.buildPythonPackage {
963 name = "python-editor-1.0";
963 name = "python-editor-1.0";
964 buildInputs = with self; [];
964 buildInputs = with self; [];
965 doCheck = false;
965 doCheck = false;
966 propagatedBuildInputs = with self; [];
966 propagatedBuildInputs = with self; [];
967 src = fetchurl {
967 src = fetchurl {
968 url = "https://pypi.python.org/packages/f5/d9/01eb441489c8bd2adb33ee4f3aea299a3db531a584cb39c57a0ecf516d9c/python-editor-1.0.tar.gz";
968 url = "https://pypi.python.org/packages/f5/d9/01eb441489c8bd2adb33ee4f3aea299a3db531a584cb39c57a0ecf516d9c/python-editor-1.0.tar.gz";
969 md5 = "a5ead611360b17b52507297d8590b4e8";
969 md5 = "a5ead611360b17b52507297d8590b4e8";
970 };
970 };
971 };
971 };
972 python-ldap = super.buildPythonPackage {
972 python-ldap = super.buildPythonPackage {
973 name = "python-ldap-2.4.19";
973 name = "python-ldap-2.4.19";
974 buildInputs = with self; [];
974 buildInputs = with self; [];
975 doCheck = false;
975 doCheck = false;
976 propagatedBuildInputs = with self; [setuptools];
976 propagatedBuildInputs = with self; [setuptools];
977 src = fetchurl {
977 src = fetchurl {
978 url = "https://pypi.python.org/packages/42/81/1b64838c82e64f14d4e246ff00b52e650a35c012551b891ada2b85d40737/python-ldap-2.4.19.tar.gz";
978 url = "https://pypi.python.org/packages/42/81/1b64838c82e64f14d4e246ff00b52e650a35c012551b891ada2b85d40737/python-ldap-2.4.19.tar.gz";
979 md5 = "b941bf31d09739492aa19ef679e94ae3";
979 md5 = "b941bf31d09739492aa19ef679e94ae3";
980 };
980 };
981 };
981 };
982 python-memcached = super.buildPythonPackage {
982 python-memcached = super.buildPythonPackage {
983 name = "python-memcached-1.57";
983 name = "python-memcached-1.57";
984 buildInputs = with self; [];
984 buildInputs = with self; [];
985 doCheck = false;
985 doCheck = false;
986 propagatedBuildInputs = with self; [six];
986 propagatedBuildInputs = with self; [six];
987 src = fetchurl {
987 src = fetchurl {
988 url = "https://pypi.python.org/packages/52/9d/eebc0dcbc5c7c66840ad207dfc1baa376dadb74912484bff73819cce01e6/python-memcached-1.57.tar.gz";
988 url = "https://pypi.python.org/packages/52/9d/eebc0dcbc5c7c66840ad207dfc1baa376dadb74912484bff73819cce01e6/python-memcached-1.57.tar.gz";
989 md5 = "de21f64b42b2d961f3d4ad7beb5468a1";
989 md5 = "de21f64b42b2d961f3d4ad7beb5468a1";
990 };
990 };
991 };
991 };
992 python-pam = super.buildPythonPackage {
992 python-pam = super.buildPythonPackage {
993 name = "python-pam-1.8.2";
993 name = "python-pam-1.8.2";
994 buildInputs = with self; [];
994 buildInputs = with self; [];
995 doCheck = false;
995 doCheck = false;
996 propagatedBuildInputs = with self; [];
996 propagatedBuildInputs = with self; [];
997 src = fetchurl {
997 src = fetchurl {
998 url = "https://pypi.python.org/packages/de/8c/f8f5d38b4f26893af267ea0b39023d4951705ab0413a39e0cf7cf4900505/python-pam-1.8.2.tar.gz";
998 url = "https://pypi.python.org/packages/de/8c/f8f5d38b4f26893af267ea0b39023d4951705ab0413a39e0cf7cf4900505/python-pam-1.8.2.tar.gz";
999 md5 = "db71b6b999246fb05d78ecfbe166629d";
999 md5 = "db71b6b999246fb05d78ecfbe166629d";
1000 };
1000 };
1001 };
1001 };
1002 pytz = super.buildPythonPackage {
1002 pytz = super.buildPythonPackage {
1003 name = "pytz-2015.4";
1003 name = "pytz-2015.4";
1004 buildInputs = with self; [];
1004 buildInputs = with self; [];
1005 doCheck = false;
1005 doCheck = false;
1006 propagatedBuildInputs = with self; [];
1006 propagatedBuildInputs = with self; [];
1007 src = fetchurl {
1007 src = fetchurl {
1008 url = "https://pypi.python.org/packages/7e/1a/f43b5c92df7b156822030fed151327ea096bcf417e45acc23bd1df43472f/pytz-2015.4.zip";
1008 url = "https://pypi.python.org/packages/7e/1a/f43b5c92df7b156822030fed151327ea096bcf417e45acc23bd1df43472f/pytz-2015.4.zip";
1009 md5 = "233f2a2b370d03f9b5911700cc9ebf3c";
1009 md5 = "233f2a2b370d03f9b5911700cc9ebf3c";
1010 };
1010 };
1011 };
1011 };
1012 pyzmq = super.buildPythonPackage {
1012 pyzmq = super.buildPythonPackage {
1013 name = "pyzmq-14.6.0";
1013 name = "pyzmq-14.6.0";
1014 buildInputs = with self; [];
1014 buildInputs = with self; [];
1015 doCheck = false;
1015 doCheck = false;
1016 propagatedBuildInputs = with self; [];
1016 propagatedBuildInputs = with self; [];
1017 src = fetchurl {
1017 src = fetchurl {
1018 url = "https://pypi.python.org/packages/8a/3b/5463d5a9d712cd8bbdac335daece0d69f6a6792da4e3dd89956c0db4e4e6/pyzmq-14.6.0.tar.gz";
1018 url = "https://pypi.python.org/packages/8a/3b/5463d5a9d712cd8bbdac335daece0d69f6a6792da4e3dd89956c0db4e4e6/pyzmq-14.6.0.tar.gz";
1019 md5 = "395b5de95a931afa5b14c9349a5b8024";
1019 md5 = "395b5de95a931afa5b14c9349a5b8024";
1020 };
1020 };
1021 };
1021 };
1022 recaptcha-client = super.buildPythonPackage {
1022 recaptcha-client = super.buildPythonPackage {
1023 name = "recaptcha-client-1.0.6";
1023 name = "recaptcha-client-1.0.6";
1024 buildInputs = with self; [];
1024 buildInputs = with self; [];
1025 doCheck = false;
1025 doCheck = false;
1026 propagatedBuildInputs = with self; [];
1026 propagatedBuildInputs = with self; [];
1027 src = fetchurl {
1027 src = fetchurl {
1028 url = "https://pypi.python.org/packages/0a/ea/5f2fbbfd894bdac1c68ef8d92019066cfcf9fbff5fe3d728d2b5c25c8db4/recaptcha-client-1.0.6.tar.gz";
1028 url = "https://pypi.python.org/packages/0a/ea/5f2fbbfd894bdac1c68ef8d92019066cfcf9fbff5fe3d728d2b5c25c8db4/recaptcha-client-1.0.6.tar.gz";
1029 md5 = "74228180f7e1fb76c4d7089160b0d919";
1029 md5 = "74228180f7e1fb76c4d7089160b0d919";
1030 };
1030 };
1031 };
1031 };
1032 repoze.lru = super.buildPythonPackage {
1032 repoze.lru = super.buildPythonPackage {
1033 name = "repoze.lru-0.6";
1033 name = "repoze.lru-0.6";
1034 buildInputs = with self; [];
1034 buildInputs = with self; [];
1035 doCheck = false;
1035 doCheck = false;
1036 propagatedBuildInputs = with self; [];
1036 propagatedBuildInputs = with self; [];
1037 src = fetchurl {
1037 src = fetchurl {
1038 url = "https://pypi.python.org/packages/6e/1e/aa15cc90217e086dc8769872c8778b409812ff036bf021b15795638939e4/repoze.lru-0.6.tar.gz";
1038 url = "https://pypi.python.org/packages/6e/1e/aa15cc90217e086dc8769872c8778b409812ff036bf021b15795638939e4/repoze.lru-0.6.tar.gz";
1039 md5 = "2c3b64b17a8e18b405f55d46173e14dd";
1039 md5 = "2c3b64b17a8e18b405f55d46173e14dd";
1040 };
1040 };
1041 };
1041 };
1042 requests = super.buildPythonPackage {
1042 requests = super.buildPythonPackage {
1043 name = "requests-2.9.1";
1043 name = "requests-2.9.1";
1044 buildInputs = with self; [];
1044 buildInputs = with self; [];
1045 doCheck = false;
1045 doCheck = false;
1046 propagatedBuildInputs = with self; [];
1046 propagatedBuildInputs = with self; [];
1047 src = fetchurl {
1047 src = fetchurl {
1048 url = "https://pypi.python.org/packages/f9/6d/07c44fb1ebe04d069459a189e7dab9e4abfe9432adcd4477367c25332748/requests-2.9.1.tar.gz";
1048 url = "https://pypi.python.org/packages/f9/6d/07c44fb1ebe04d069459a189e7dab9e4abfe9432adcd4477367c25332748/requests-2.9.1.tar.gz";
1049 md5 = "0b7f480d19012ec52bab78292efd976d";
1049 md5 = "0b7f480d19012ec52bab78292efd976d";
1050 };
1050 };
1051 };
1051 };
1052 rhodecode-enterprise-ce = super.buildPythonPackage {
1052 rhodecode-enterprise-ce = super.buildPythonPackage {
1053 name = "rhodecode-enterprise-ce-4.0.0";
1053 name = "rhodecode-enterprise-ce-4.0.1";
1054 buildInputs = with self; [WebTest configobj cssselect flake8 lxml mock pytest pytest-runner pytest-cov];
1054 buildInputs = with self; [WebTest configobj cssselect flake8 lxml mock pytest pytest-cov pytest-runner];
1055 doCheck = true;
1055 doCheck = true;
1056 propagatedBuildInputs = with self; [Babel Beaker FormEncode Mako Markdown MarkupSafe MySQL-python Paste PasteDeploy PasteScript Pygments Pylons Pyro4 Routes SQLAlchemy Tempita URLObject WebError WebHelpers WebHelpers2 WebOb WebTest Whoosh alembic amqplib anyjson appenlight-client authomatic backport-ipaddress celery colander decorator docutils infrae.cache ipython iso8601 kombu msgpack-python packaging psycopg2 pycrypto pycurl pyparsing pyramid pyramid-debugtoolbar pyramid-mako pyramid-beaker pysqlite python-dateutil python-ldap python-memcached python-pam recaptcha-client repoze.lru requests simplejson waitress zope.cachedescriptors psutil py-bcrypt];
1056 propagatedBuildInputs = with self; [Babel Beaker FormEncode Mako Markdown MarkupSafe MySQL-python Paste PasteDeploy PasteScript Pygments Pylons Pyro4 Routes SQLAlchemy Tempita URLObject WebError WebHelpers WebHelpers2 WebOb WebTest Whoosh alembic amqplib anyjson appenlight-client authomatic backport-ipaddress celery colander decorator docutils gunicorn infrae.cache ipython iso8601 kombu msgpack-python packaging psycopg2 pycrypto pycurl pyparsing pyramid pyramid-debugtoolbar pyramid-mako pyramid-beaker pysqlite python-dateutil python-ldap python-memcached python-pam recaptcha-client repoze.lru requests simplejson waitress zope.cachedescriptors psutil py-bcrypt];
1057 src = ./.;
1057 src = ./.;
1058 };
1058 };
1059 rhodecode-tools = super.buildPythonPackage {
1059 rhodecode-tools = super.buildPythonPackage {
1060 name = "rhodecode-tools-0.7.1";
1060 name = "rhodecode-tools-0.7.1";
1061 buildInputs = with self; [];
1061 buildInputs = with self; [];
1062 doCheck = false;
1062 doCheck = false;
1063 propagatedBuildInputs = with self; [click future six Mako MarkupSafe requests Whoosh pyelasticsearch];
1063 propagatedBuildInputs = with self; [click future six Mako MarkupSafe requests Whoosh pyelasticsearch];
1064 src = fetchurl {
1064 src = fetchurl {
1065 url = "https://code.rhodecode.com/rhodecode-tools-ce/archive/v0.7.1.zip";
1065 url = "https://code.rhodecode.com/rhodecode-tools-ce/archive/v0.7.1.zip";
1066 md5 = "91daea803aaa264ce7a8213bc2220d4c";
1066 md5 = "91daea803aaa264ce7a8213bc2220d4c";
1067 };
1067 };
1068 };
1068 };
1069 serpent = super.buildPythonPackage {
1069 serpent = super.buildPythonPackage {
1070 name = "serpent-1.12";
1070 name = "serpent-1.12";
1071 buildInputs = with self; [];
1071 buildInputs = with self; [];
1072 doCheck = false;
1072 doCheck = false;
1073 propagatedBuildInputs = with self; [];
1073 propagatedBuildInputs = with self; [];
1074 src = fetchurl {
1074 src = fetchurl {
1075 url = "https://pypi.python.org/packages/3b/19/1e0e83b47c09edaef8398655088036e7e67386b5c48770218ebb339fbbd5/serpent-1.12.tar.gz";
1075 url = "https://pypi.python.org/packages/3b/19/1e0e83b47c09edaef8398655088036e7e67386b5c48770218ebb339fbbd5/serpent-1.12.tar.gz";
1076 md5 = "05869ac7b062828b34f8f927f0457b65";
1076 md5 = "05869ac7b062828b34f8f927f0457b65";
1077 };
1077 };
1078 };
1078 };
1079 setproctitle = super.buildPythonPackage {
1079 setproctitle = super.buildPythonPackage {
1080 name = "setproctitle-1.1.8";
1080 name = "setproctitle-1.1.8";
1081 buildInputs = with self; [];
1081 buildInputs = with self; [];
1082 doCheck = false;
1082 doCheck = false;
1083 propagatedBuildInputs = with self; [];
1083 propagatedBuildInputs = with self; [];
1084 src = fetchurl {
1084 src = fetchurl {
1085 url = "https://pypi.python.org/packages/33/c3/ad367a4f4f1ca90468863ae727ac62f6edb558fc09a003d344a02cfc6ea6/setproctitle-1.1.8.tar.gz";
1085 url = "https://pypi.python.org/packages/33/c3/ad367a4f4f1ca90468863ae727ac62f6edb558fc09a003d344a02cfc6ea6/setproctitle-1.1.8.tar.gz";
1086 md5 = "728f4c8c6031bbe56083a48594027edd";
1086 md5 = "728f4c8c6031bbe56083a48594027edd";
1087 };
1087 };
1088 };
1088 };
1089 setuptools = super.buildPythonPackage {
1089 setuptools = super.buildPythonPackage {
1090 name = "setuptools-20.8.1";
1090 name = "setuptools-20.8.1";
1091 buildInputs = with self; [];
1091 buildInputs = with self; [];
1092 doCheck = false;
1092 doCheck = false;
1093 propagatedBuildInputs = with self; [];
1093 propagatedBuildInputs = with self; [];
1094 src = fetchurl {
1094 src = fetchurl {
1095 url = "https://pypi.python.org/packages/c4/19/c1bdc88b53da654df43770f941079dbab4e4788c2dcb5658fb86259894c7/setuptools-20.8.1.zip";
1095 url = "https://pypi.python.org/packages/c4/19/c1bdc88b53da654df43770f941079dbab4e4788c2dcb5658fb86259894c7/setuptools-20.8.1.zip";
1096 md5 = "fe58a5cac0df20bb83942b252a4b0543";
1096 md5 = "fe58a5cac0df20bb83942b252a4b0543";
1097 };
1097 };
1098 };
1098 };
1099 setuptools-scm = super.buildPythonPackage {
1099 setuptools-scm = super.buildPythonPackage {
1100 name = "setuptools-scm-1.11.0";
1100 name = "setuptools-scm-1.11.0";
1101 buildInputs = with self; [];
1101 buildInputs = with self; [];
1102 doCheck = false;
1102 doCheck = false;
1103 propagatedBuildInputs = with self; [];
1103 propagatedBuildInputs = with self; [];
1104 src = fetchurl {
1104 src = fetchurl {
1105 url = "https://pypi.python.org/packages/cd/5f/e3a038292358058d83d764a47d09114aa5a8003ed4529518f9e580f1a94f/setuptools_scm-1.11.0.tar.gz";
1105 url = "https://pypi.python.org/packages/cd/5f/e3a038292358058d83d764a47d09114aa5a8003ed4529518f9e580f1a94f/setuptools_scm-1.11.0.tar.gz";
1106 md5 = "4c5c896ba52e134bbc3507bac6400087";
1106 md5 = "4c5c896ba52e134bbc3507bac6400087";
1107 };
1107 };
1108 };
1108 };
1109 simplejson = super.buildPythonPackage {
1109 simplejson = super.buildPythonPackage {
1110 name = "simplejson-3.7.2";
1110 name = "simplejson-3.7.2";
1111 buildInputs = with self; [];
1111 buildInputs = with self; [];
1112 doCheck = false;
1112 doCheck = false;
1113 propagatedBuildInputs = with self; [];
1113 propagatedBuildInputs = with self; [];
1114 src = fetchurl {
1114 src = fetchurl {
1115 url = "https://pypi.python.org/packages/6d/89/7f13f099344eea9d6722779a1f165087cb559598107844b1ac5dbd831fb1/simplejson-3.7.2.tar.gz";
1115 url = "https://pypi.python.org/packages/6d/89/7f13f099344eea9d6722779a1f165087cb559598107844b1ac5dbd831fb1/simplejson-3.7.2.tar.gz";
1116 md5 = "a5fc7d05d4cb38492285553def5d4b46";
1116 md5 = "a5fc7d05d4cb38492285553def5d4b46";
1117 };
1117 };
1118 };
1118 };
1119 six = super.buildPythonPackage {
1119 six = super.buildPythonPackage {
1120 name = "six-1.9.0";
1120 name = "six-1.9.0";
1121 buildInputs = with self; [];
1121 buildInputs = with self; [];
1122 doCheck = false;
1122 doCheck = false;
1123 propagatedBuildInputs = with self; [];
1123 propagatedBuildInputs = with self; [];
1124 src = fetchurl {
1124 src = fetchurl {
1125 url = "https://pypi.python.org/packages/16/64/1dc5e5976b17466fd7d712e59cbe9fb1e18bec153109e5ba3ed6c9102f1a/six-1.9.0.tar.gz";
1125 url = "https://pypi.python.org/packages/16/64/1dc5e5976b17466fd7d712e59cbe9fb1e18bec153109e5ba3ed6c9102f1a/six-1.9.0.tar.gz";
1126 md5 = "476881ef4012262dfc8adc645ee786c4";
1126 md5 = "476881ef4012262dfc8adc645ee786c4";
1127 };
1127 };
1128 };
1128 };
1129 subprocess32 = super.buildPythonPackage {
1129 subprocess32 = super.buildPythonPackage {
1130 name = "subprocess32-3.2.6";
1130 name = "subprocess32-3.2.6";
1131 buildInputs = with self; [];
1131 buildInputs = with self; [];
1132 doCheck = false;
1132 doCheck = false;
1133 propagatedBuildInputs = with self; [];
1133 propagatedBuildInputs = with self; [];
1134 src = fetchurl {
1134 src = fetchurl {
1135 url = "https://pypi.python.org/packages/28/8d/33ccbff51053f59ae6c357310cac0e79246bbed1d345ecc6188b176d72c3/subprocess32-3.2.6.tar.gz";
1135 url = "https://pypi.python.org/packages/28/8d/33ccbff51053f59ae6c357310cac0e79246bbed1d345ecc6188b176d72c3/subprocess32-3.2.6.tar.gz";
1136 md5 = "754c5ab9f533e764f931136974b618f1";
1136 md5 = "754c5ab9f533e764f931136974b618f1";
1137 };
1137 };
1138 };
1138 };
1139 supervisor = super.buildPythonPackage {
1139 supervisor = super.buildPythonPackage {
1140 name = "supervisor-3.1.3";
1140 name = "supervisor-3.1.3";
1141 buildInputs = with self; [];
1141 buildInputs = with self; [];
1142 doCheck = false;
1142 doCheck = false;
1143 propagatedBuildInputs = with self; [meld3];
1143 propagatedBuildInputs = with self; [meld3];
1144 src = fetchurl {
1144 src = fetchurl {
1145 url = "https://pypi.python.org/packages/a6/41/65ad5bd66230b173eb4d0b8810230f3a9c59ef52ae066e540b6b99895db7/supervisor-3.1.3.tar.gz";
1145 url = "https://pypi.python.org/packages/a6/41/65ad5bd66230b173eb4d0b8810230f3a9c59ef52ae066e540b6b99895db7/supervisor-3.1.3.tar.gz";
1146 md5 = "aad263c4fbc070de63dd354864d5e552";
1146 md5 = "aad263c4fbc070de63dd354864d5e552";
1147 };
1147 };
1148 };
1148 };
1149 transifex-client = super.buildPythonPackage {
1149 transifex-client = super.buildPythonPackage {
1150 name = "transifex-client-0.10";
1150 name = "transifex-client-0.10";
1151 buildInputs = with self; [];
1151 buildInputs = with self; [];
1152 doCheck = false;
1152 doCheck = false;
1153 propagatedBuildInputs = with self; [];
1153 propagatedBuildInputs = with self; [];
1154 src = fetchurl {
1154 src = fetchurl {
1155 url = "https://pypi.python.org/packages/f3/4e/7b925192aee656fb3e04fa6381c8b3dc40198047c3b4a356f6cfd642c809/transifex-client-0.10.tar.gz";
1155 url = "https://pypi.python.org/packages/f3/4e/7b925192aee656fb3e04fa6381c8b3dc40198047c3b4a356f6cfd642c809/transifex-client-0.10.tar.gz";
1156 md5 = "5549538d84b8eede6b254cd81ae024fa";
1156 md5 = "5549538d84b8eede6b254cd81ae024fa";
1157 };
1157 };
1158 };
1158 };
1159 translationstring = super.buildPythonPackage {
1159 translationstring = super.buildPythonPackage {
1160 name = "translationstring-1.3";
1160 name = "translationstring-1.3";
1161 buildInputs = with self; [];
1161 buildInputs = with self; [];
1162 doCheck = false;
1162 doCheck = false;
1163 propagatedBuildInputs = with self; [];
1163 propagatedBuildInputs = with self; [];
1164 src = fetchurl {
1164 src = fetchurl {
1165 url = "https://pypi.python.org/packages/5e/eb/bee578cc150b44c653b63f5ebe258b5d0d812ddac12497e5f80fcad5d0b4/translationstring-1.3.tar.gz";
1165 url = "https://pypi.python.org/packages/5e/eb/bee578cc150b44c653b63f5ebe258b5d0d812ddac12497e5f80fcad5d0b4/translationstring-1.3.tar.gz";
1166 md5 = "a4b62e0f3c189c783a1685b3027f7c90";
1166 md5 = "a4b62e0f3c189c783a1685b3027f7c90";
1167 };
1167 };
1168 };
1168 };
1169 trollius = super.buildPythonPackage {
1169 trollius = super.buildPythonPackage {
1170 name = "trollius-1.0.4";
1170 name = "trollius-1.0.4";
1171 buildInputs = with self; [];
1171 buildInputs = with self; [];
1172 doCheck = false;
1172 doCheck = false;
1173 propagatedBuildInputs = with self; [futures];
1173 propagatedBuildInputs = with self; [futures];
1174 src = fetchurl {
1174 src = fetchurl {
1175 url = "https://pypi.python.org/packages/aa/e6/4141db437f55e6ee7a3fb69663239e3fde7841a811b4bef293145ad6c836/trollius-1.0.4.tar.gz";
1175 url = "https://pypi.python.org/packages/aa/e6/4141db437f55e6ee7a3fb69663239e3fde7841a811b4bef293145ad6c836/trollius-1.0.4.tar.gz";
1176 md5 = "3631a464d49d0cbfd30ab2918ef2b783";
1176 md5 = "3631a464d49d0cbfd30ab2918ef2b783";
1177 };
1177 };
1178 };
1178 };
1179 uWSGI = super.buildPythonPackage {
1179 uWSGI = super.buildPythonPackage {
1180 name = "uWSGI-2.0.11.2";
1180 name = "uWSGI-2.0.11.2";
1181 buildInputs = with self; [];
1181 buildInputs = with self; [];
1182 doCheck = false;
1182 doCheck = false;
1183 propagatedBuildInputs = with self; [];
1183 propagatedBuildInputs = with self; [];
1184 src = fetchurl {
1184 src = fetchurl {
1185 url = "https://pypi.python.org/packages/9b/78/918db0cfab0546afa580c1e565209c49aaf1476bbfe491314eadbe47c556/uwsgi-2.0.11.2.tar.gz";
1185 url = "https://pypi.python.org/packages/9b/78/918db0cfab0546afa580c1e565209c49aaf1476bbfe491314eadbe47c556/uwsgi-2.0.11.2.tar.gz";
1186 md5 = "1f02dcbee7f6f61de4b1fd68350cf16f";
1186 md5 = "1f02dcbee7f6f61de4b1fd68350cf16f";
1187 };
1187 };
1188 };
1188 };
1189 urllib3 = super.buildPythonPackage {
1189 urllib3 = super.buildPythonPackage {
1190 name = "urllib3-1.15.1";
1190 name = "urllib3-1.15.1";
1191 buildInputs = with self; [];
1191 buildInputs = with self; [];
1192 doCheck = false;
1192 doCheck = false;
1193 propagatedBuildInputs = with self; [];
1193 propagatedBuildInputs = with self; [];
1194 src = fetchurl {
1194 src = fetchurl {
1195 url = "https://pypi.python.org/packages/49/26/a7d12ea00cb4b9fa1e13b5980e5a04a1fe7c477eb8f657ce0b757a7a497d/urllib3-1.15.1.tar.gz";
1195 url = "https://pypi.python.org/packages/49/26/a7d12ea00cb4b9fa1e13b5980e5a04a1fe7c477eb8f657ce0b757a7a497d/urllib3-1.15.1.tar.gz";
1196 md5 = "5be254b0dbb55d1307ede99e1895c8dd";
1196 md5 = "5be254b0dbb55d1307ede99e1895c8dd";
1197 };
1197 };
1198 };
1198 };
1199 venusian = super.buildPythonPackage {
1199 venusian = super.buildPythonPackage {
1200 name = "venusian-1.0";
1200 name = "venusian-1.0";
1201 buildInputs = with self; [];
1201 buildInputs = with self; [];
1202 doCheck = false;
1202 doCheck = false;
1203 propagatedBuildInputs = with self; [];
1203 propagatedBuildInputs = with self; [];
1204 src = fetchurl {
1204 src = fetchurl {
1205 url = "https://pypi.python.org/packages/86/20/1948e0dfc4930ddde3da8c33612f6a5717c0b4bc28f591a5c5cf014dd390/venusian-1.0.tar.gz";
1205 url = "https://pypi.python.org/packages/86/20/1948e0dfc4930ddde3da8c33612f6a5717c0b4bc28f591a5c5cf014dd390/venusian-1.0.tar.gz";
1206 md5 = "dccf2eafb7113759d60c86faf5538756";
1206 md5 = "dccf2eafb7113759d60c86faf5538756";
1207 };
1207 };
1208 };
1208 };
1209 waitress = super.buildPythonPackage {
1209 waitress = super.buildPythonPackage {
1210 name = "waitress-0.8.9";
1210 name = "waitress-0.8.9";
1211 buildInputs = with self; [];
1211 buildInputs = with self; [];
1212 doCheck = false;
1212 doCheck = false;
1213 propagatedBuildInputs = with self; [setuptools];
1213 propagatedBuildInputs = with self; [setuptools];
1214 src = fetchurl {
1214 src = fetchurl {
1215 url = "https://pypi.python.org/packages/ee/65/fc9dee74a909a1187ca51e4f15ad9c4d35476e4ab5813f73421505c48053/waitress-0.8.9.tar.gz";
1215 url = "https://pypi.python.org/packages/ee/65/fc9dee74a909a1187ca51e4f15ad9c4d35476e4ab5813f73421505c48053/waitress-0.8.9.tar.gz";
1216 md5 = "da3f2e62b3676be5dd630703a68e2a04";
1216 md5 = "da3f2e62b3676be5dd630703a68e2a04";
1217 };
1217 };
1218 };
1218 };
1219 wsgiref = super.buildPythonPackage {
1219 wsgiref = super.buildPythonPackage {
1220 name = "wsgiref-0.1.2";
1220 name = "wsgiref-0.1.2";
1221 buildInputs = with self; [];
1221 buildInputs = with self; [];
1222 doCheck = false;
1222 doCheck = false;
1223 propagatedBuildInputs = with self; [];
1223 propagatedBuildInputs = with self; [];
1224 src = fetchurl {
1224 src = fetchurl {
1225 url = "https://pypi.python.org/packages/41/9e/309259ce8dff8c596e8c26df86dbc4e848b9249fd36797fd60be456f03fc/wsgiref-0.1.2.zip";
1225 url = "https://pypi.python.org/packages/41/9e/309259ce8dff8c596e8c26df86dbc4e848b9249fd36797fd60be456f03fc/wsgiref-0.1.2.zip";
1226 md5 = "29b146e6ebd0f9fb119fe321f7bcf6cb";
1226 md5 = "29b146e6ebd0f9fb119fe321f7bcf6cb";
1227 };
1227 };
1228 };
1228 };
1229 zope.cachedescriptors = super.buildPythonPackage {
1229 zope.cachedescriptors = super.buildPythonPackage {
1230 name = "zope.cachedescriptors-4.0.0";
1230 name = "zope.cachedescriptors-4.0.0";
1231 buildInputs = with self; [];
1231 buildInputs = with self; [];
1232 doCheck = false;
1232 doCheck = false;
1233 propagatedBuildInputs = with self; [setuptools];
1233 propagatedBuildInputs = with self; [setuptools];
1234 src = fetchurl {
1234 src = fetchurl {
1235 url = "https://pypi.python.org/packages/40/33/694b6644c37f28553f4b9f20b3c3a20fb709a22574dff20b5bdffb09ecd5/zope.cachedescriptors-4.0.0.tar.gz";
1235 url = "https://pypi.python.org/packages/40/33/694b6644c37f28553f4b9f20b3c3a20fb709a22574dff20b5bdffb09ecd5/zope.cachedescriptors-4.0.0.tar.gz";
1236 md5 = "8d308de8c936792c8e758058fcb7d0f0";
1236 md5 = "8d308de8c936792c8e758058fcb7d0f0";
1237 };
1237 };
1238 };
1238 };
1239 zope.deprecation = super.buildPythonPackage {
1239 zope.deprecation = super.buildPythonPackage {
1240 name = "zope.deprecation-4.1.2";
1240 name = "zope.deprecation-4.1.2";
1241 buildInputs = with self; [];
1241 buildInputs = with self; [];
1242 doCheck = false;
1242 doCheck = false;
1243 propagatedBuildInputs = with self; [setuptools];
1243 propagatedBuildInputs = with self; [setuptools];
1244 src = fetchurl {
1244 src = fetchurl {
1245 url = "https://pypi.python.org/packages/c1/d3/3919492d5e57d8dd01b36f30b34fc8404a30577392b1eb817c303499ad20/zope.deprecation-4.1.2.tar.gz";
1245 url = "https://pypi.python.org/packages/c1/d3/3919492d5e57d8dd01b36f30b34fc8404a30577392b1eb817c303499ad20/zope.deprecation-4.1.2.tar.gz";
1246 md5 = "e9a663ded58f4f9f7881beb56cae2782";
1246 md5 = "e9a663ded58f4f9f7881beb56cae2782";
1247 };
1247 };
1248 };
1248 };
1249 zope.event = super.buildPythonPackage {
1249 zope.event = super.buildPythonPackage {
1250 name = "zope.event-4.0.3";
1250 name = "zope.event-4.0.3";
1251 buildInputs = with self; [];
1251 buildInputs = with self; [];
1252 doCheck = false;
1252 doCheck = false;
1253 propagatedBuildInputs = with self; [setuptools];
1253 propagatedBuildInputs = with self; [setuptools];
1254 src = fetchurl {
1254 src = fetchurl {
1255 url = "https://pypi.python.org/packages/c1/29/91ba884d7d6d96691df592e9e9c2bfa57a47040ec1ff47eff18c85137152/zope.event-4.0.3.tar.gz";
1255 url = "https://pypi.python.org/packages/c1/29/91ba884d7d6d96691df592e9e9c2bfa57a47040ec1ff47eff18c85137152/zope.event-4.0.3.tar.gz";
1256 md5 = "9a3780916332b18b8b85f522bcc3e249";
1256 md5 = "9a3780916332b18b8b85f522bcc3e249";
1257 };
1257 };
1258 };
1258 };
1259 zope.interface = super.buildPythonPackage {
1259 zope.interface = super.buildPythonPackage {
1260 name = "zope.interface-4.1.3";
1260 name = "zope.interface-4.1.3";
1261 buildInputs = with self; [];
1261 buildInputs = with self; [];
1262 doCheck = false;
1262 doCheck = false;
1263 propagatedBuildInputs = with self; [setuptools];
1263 propagatedBuildInputs = with self; [setuptools];
1264 src = fetchurl {
1264 src = fetchurl {
1265 url = "https://pypi.python.org/packages/9d/81/2509ca3c6f59080123c1a8a97125eb48414022618cec0e64eb1313727bfe/zope.interface-4.1.3.tar.gz";
1265 url = "https://pypi.python.org/packages/9d/81/2509ca3c6f59080123c1a8a97125eb48414022618cec0e64eb1313727bfe/zope.interface-4.1.3.tar.gz";
1266 md5 = "9ae3d24c0c7415deb249dd1a132f0f79";
1266 md5 = "9ae3d24c0c7415deb249dd1a132f0f79";
1267 };
1267 };
1268 };
1268 };
1269
1269
1270 ### Test requirements
1270 ### Test requirements
1271
1271
1272
1272
1273 }
1273 }
@@ -1,1 +1,1 b''
1 4.0.0 No newline at end of file
1 4.0.1 No newline at end of file
@@ -1,166 +1,163 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2016 RhodeCode GmbH
3 # Copyright (C) 2012-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 RhodeCode authentication plugin for Jasig CAS
22 RhodeCode authentication plugin for Jasig CAS
23 http://www.jasig.org/cas
23 http://www.jasig.org/cas
24 """
24 """
25
25
26
26
27 import colander
27 import colander
28 import logging
28 import logging
29 import rhodecode
29 import rhodecode
30 import urllib
30 import urllib
31 import urllib2
31 import urllib2
32
32
33 from pylons.i18n.translation import lazy_ugettext as _
33 from pylons.i18n.translation import lazy_ugettext as _
34 from sqlalchemy.ext.hybrid import hybrid_property
34 from sqlalchemy.ext.hybrid import hybrid_property
35
35
36 from rhodecode.authentication.base import RhodeCodeExternalAuthPlugin
36 from rhodecode.authentication.base import RhodeCodeExternalAuthPlugin
37 from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
37 from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
38 from rhodecode.authentication.routes import AuthnPluginResourceBase
38 from rhodecode.authentication.routes import AuthnPluginResourceBase
39 from rhodecode.lib.ext_json import formatted_json
40 from rhodecode.lib.utils2 import safe_unicode
39 from rhodecode.lib.utils2 import safe_unicode
41 from rhodecode.model.db import User
40 from rhodecode.model.db import User
42
41
43 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
44
43
45
44
46 def plugin_factory(plugin_id, *args, **kwds):
45 def plugin_factory(plugin_id, *args, **kwds):
47 """
46 """
48 Factory function that is called during plugin discovery.
47 Factory function that is called during plugin discovery.
49 It returns the plugin instance.
48 It returns the plugin instance.
50 """
49 """
51 plugin = RhodeCodeAuthPlugin(plugin_id)
50 plugin = RhodeCodeAuthPlugin(plugin_id)
52 return plugin
51 return plugin
53
52
54
53
55 class JasigCasAuthnResource(AuthnPluginResourceBase):
54 class JasigCasAuthnResource(AuthnPluginResourceBase):
56 pass
55 pass
57
56
58
57
59 class JasigCasSettingsSchema(AuthnPluginSettingsSchemaBase):
58 class JasigCasSettingsSchema(AuthnPluginSettingsSchemaBase):
60 service_url = colander.SchemaNode(
59 service_url = colander.SchemaNode(
61 colander.String(),
60 colander.String(),
62 default='https://domain.com/cas/v1/tickets',
61 default='https://domain.com/cas/v1/tickets',
63 description=_('The url of the Jasig CAS REST service'),
62 description=_('The url of the Jasig CAS REST service'),
64 title=_('URL'),
63 title=_('URL'),
65 widget='string')
64 widget='string')
66
65
67
66
68 class RhodeCodeAuthPlugin(RhodeCodeExternalAuthPlugin):
67 class RhodeCodeAuthPlugin(RhodeCodeExternalAuthPlugin):
69
68
70 def includeme(self, config):
69 def includeme(self, config):
71 config.add_authn_plugin(self)
70 config.add_authn_plugin(self)
72 config.add_authn_resource(self.get_id(), JasigCasAuthnResource(self))
71 config.add_authn_resource(self.get_id(), JasigCasAuthnResource(self))
73 config.add_view(
72 config.add_view(
74 'rhodecode.authentication.views.AuthnPluginViewBase',
73 'rhodecode.authentication.views.AuthnPluginViewBase',
75 attr='settings_get',
74 attr='settings_get',
76 request_method='GET',
75 request_method='GET',
77 route_name='auth_home',
76 route_name='auth_home',
78 context=JasigCasAuthnResource)
77 context=JasigCasAuthnResource)
79 config.add_view(
78 config.add_view(
80 'rhodecode.authentication.views.AuthnPluginViewBase',
79 'rhodecode.authentication.views.AuthnPluginViewBase',
81 attr='settings_post',
80 attr='settings_post',
82 request_method='POST',
81 request_method='POST',
83 route_name='auth_home',
82 route_name='auth_home',
84 context=JasigCasAuthnResource)
83 context=JasigCasAuthnResource)
85
84
86 def get_settings_schema(self):
85 def get_settings_schema(self):
87 return JasigCasSettingsSchema()
86 return JasigCasSettingsSchema()
88
87
89 def get_display_name(self):
88 def get_display_name(self):
90 return _('Jasig-CAS')
89 return _('Jasig-CAS')
91
90
92 @hybrid_property
91 @hybrid_property
93 def name(self):
92 def name(self):
94 return "jasig-cas"
93 return "jasig-cas"
95
94
96 @hybrid_property
95 @hybrid_property
97 def is_container_auth(self):
96 def is_container_auth(self):
98 return True
97 return True
99
98
100 def use_fake_password(self):
99 def use_fake_password(self):
101 return True
100 return True
102
101
103 def user_activation_state(self):
102 def user_activation_state(self):
104 def_user_perms = User.get_default_user().AuthUser.permissions['global']
103 def_user_perms = User.get_default_user().AuthUser.permissions['global']
105 return 'hg.extern_activate.auto' in def_user_perms
104 return 'hg.extern_activate.auto' in def_user_perms
106
105
107 def auth(self, userobj, username, password, settings, **kwargs):
106 def auth(self, userobj, username, password, settings, **kwargs):
108 """
107 """
109 Given a user object (which may be null), username, a plaintext password,
108 Given a user object (which may be null), username, a plaintext password,
110 and a settings object (containing all the keys needed as listed in settings()),
109 and a settings object (containing all the keys needed as listed in settings()),
111 authenticate this user's login attempt.
110 authenticate this user's login attempt.
112
111
113 Return None on failure. On success, return a dictionary of the form:
112 Return None on failure. On success, return a dictionary of the form:
114
113
115 see: RhodeCodeAuthPluginBase.auth_func_attrs
114 see: RhodeCodeAuthPluginBase.auth_func_attrs
116 This is later validated for correctness
115 This is later validated for correctness
117 """
116 """
118 if not username or not password:
117 if not username or not password:
119 log.debug('Empty username or password skipping...')
118 log.debug('Empty username or password skipping...')
120 return None
119 return None
121
120
122 log.debug("Jasig CAS settings: \n%s" % (formatted_json(settings)))
121 log.debug("Jasig CAS settings: %s", settings)
123 params = urllib.urlencode({'username': username, 'password': password})
122 params = urllib.urlencode({'username': username, 'password': password})
124 headers = {"Content-type": "application/x-www-form-urlencoded",
123 headers = {"Content-type": "application/x-www-form-urlencoded",
125 "Accept": "text/plain",
124 "Accept": "text/plain",
126 "User-Agent": "RhodeCode-auth-%s" % rhodecode.__version__}
125 "User-Agent": "RhodeCode-auth-%s" % rhodecode.__version__}
127 url = settings["service_url"]
126 url = settings["service_url"]
128
127
129 log.debug("Sent Jasig CAS: \n%s"
128 log.debug("Sent Jasig CAS: \n%s",
130 % (formatted_json({"url": url,
129 {"url": url, "body": params, "headers": headers})
131 "body": params,
132 "headers": headers})))
133 request = urllib2.Request(url, params, headers)
130 request = urllib2.Request(url, params, headers)
134 try:
131 try:
135 response = urllib2.urlopen(request)
132 response = urllib2.urlopen(request)
136 except urllib2.HTTPError as e:
133 except urllib2.HTTPError as e:
137 log.debug("HTTPError when requesting Jasig CAS (status code: %d)" % e.code)
134 log.debug("HTTPError when requesting Jasig CAS (status code: %d)" % e.code)
138 return None
135 return None
139 except urllib2.URLError as e:
136 except urllib2.URLError as e:
140 log.debug("URLError when requesting Jasig CAS url: %s " % url)
137 log.debug("URLError when requesting Jasig CAS url: %s " % url)
141 return None
138 return None
142
139
143 # old attrs fetched from RhodeCode database
140 # old attrs fetched from RhodeCode database
144 admin = getattr(userobj, 'admin', False)
141 admin = getattr(userobj, 'admin', False)
145 active = getattr(userobj, 'active', True)
142 active = getattr(userobj, 'active', True)
146 email = getattr(userobj, 'email', '')
143 email = getattr(userobj, 'email', '')
147 username = getattr(userobj, 'username', username)
144 username = getattr(userobj, 'username', username)
148 firstname = getattr(userobj, 'firstname', '')
145 firstname = getattr(userobj, 'firstname', '')
149 lastname = getattr(userobj, 'lastname', '')
146 lastname = getattr(userobj, 'lastname', '')
150 extern_type = getattr(userobj, 'extern_type', '')
147 extern_type = getattr(userobj, 'extern_type', '')
151
148
152 user_attrs = {
149 user_attrs = {
153 'username': username,
150 'username': username,
154 'firstname': safe_unicode(firstname or username),
151 'firstname': safe_unicode(firstname or username),
155 'lastname': safe_unicode(lastname or ''),
152 'lastname': safe_unicode(lastname or ''),
156 'groups': [],
153 'groups': [],
157 'email': email or '',
154 'email': email or '',
158 'admin': admin or False,
155 'admin': admin or False,
159 'active': active,
156 'active': active,
160 'active_from_extern': True,
157 'active_from_extern': True,
161 'extern_name': username,
158 'extern_name': username,
162 'extern_type': extern_type,
159 'extern_type': extern_type,
163 }
160 }
164
161
165 log.info('user %s authenticated correctly' % user_attrs['username'])
162 log.info('user %s authenticated correctly' % user_attrs['username'])
166 return user_attrs
163 return user_attrs
@@ -1,448 +1,447 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 RhodeCode authentication plugin for LDAP
22 RhodeCode authentication plugin for LDAP
23 """
23 """
24
24
25
25
26 import colander
26 import colander
27 import logging
27 import logging
28 import traceback
28 import traceback
29
29
30 from pylons.i18n.translation import lazy_ugettext as _
30 from pylons.i18n.translation import lazy_ugettext as _
31 from sqlalchemy.ext.hybrid import hybrid_property
31 from sqlalchemy.ext.hybrid import hybrid_property
32
32
33 from rhodecode.authentication.base import RhodeCodeExternalAuthPlugin
33 from rhodecode.authentication.base import RhodeCodeExternalAuthPlugin
34 from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
34 from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
35 from rhodecode.authentication.routes import AuthnPluginResourceBase
35 from rhodecode.authentication.routes import AuthnPluginResourceBase
36 from rhodecode.lib.exceptions import (
36 from rhodecode.lib.exceptions import (
37 LdapConnectionError, LdapUsernameError, LdapPasswordError, LdapImportError
37 LdapConnectionError, LdapUsernameError, LdapPasswordError, LdapImportError
38 )
38 )
39 from rhodecode.lib.ext_json import formatted_json
40 from rhodecode.lib.utils2 import safe_unicode, safe_str
39 from rhodecode.lib.utils2 import safe_unicode, safe_str
41 from rhodecode.model.db import User
40 from rhodecode.model.db import User
42 from rhodecode.model.validators import Missing
41 from rhodecode.model.validators import Missing
43
42
44 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
45
44
46 try:
45 try:
47 import ldap
46 import ldap
48 except ImportError:
47 except ImportError:
49 # means that python-ldap is not installed
48 # means that python-ldap is not installed
50 ldap = Missing()
49 ldap = Missing()
51
50
52
51
53 def plugin_factory(plugin_id, *args, **kwds):
52 def plugin_factory(plugin_id, *args, **kwds):
54 """
53 """
55 Factory function that is called during plugin discovery.
54 Factory function that is called during plugin discovery.
56 It returns the plugin instance.
55 It returns the plugin instance.
57 """
56 """
58 plugin = RhodeCodeAuthPlugin(plugin_id)
57 plugin = RhodeCodeAuthPlugin(plugin_id)
59 return plugin
58 return plugin
60
59
61
60
62 class LdapAuthnResource(AuthnPluginResourceBase):
61 class LdapAuthnResource(AuthnPluginResourceBase):
63 pass
62 pass
64
63
65
64
66 class LdapSettingsSchema(AuthnPluginSettingsSchemaBase):
65 class LdapSettingsSchema(AuthnPluginSettingsSchemaBase):
67 tls_kind_choices = ['PLAIN', 'LDAPS', 'START_TLS']
66 tls_kind_choices = ['PLAIN', 'LDAPS', 'START_TLS']
68 tls_reqcert_choices = ['NEVER', 'ALLOW', 'TRY', 'DEMAND', 'HARD']
67 tls_reqcert_choices = ['NEVER', 'ALLOW', 'TRY', 'DEMAND', 'HARD']
69 search_scope_choices = ['BASE', 'ONELEVEL', 'SUBTREE']
68 search_scope_choices = ['BASE', 'ONELEVEL', 'SUBTREE']
70
69
71 host = colander.SchemaNode(
70 host = colander.SchemaNode(
72 colander.String(),
71 colander.String(),
73 default='',
72 default='',
74 description=_('Host of the LDAP Server'),
73 description=_('Host of the LDAP Server'),
75 title=_('LDAP Host'),
74 title=_('LDAP Host'),
76 widget='string')
75 widget='string')
77 port = colander.SchemaNode(
76 port = colander.SchemaNode(
78 colander.Int(),
77 colander.Int(),
79 default=389,
78 default=389,
80 description=_('Port that the LDAP server is listening on'),
79 description=_('Port that the LDAP server is listening on'),
81 title=_('Port'),
80 title=_('Port'),
82 validator=colander.Range(min=0, max=65536),
81 validator=colander.Range(min=0, max=65536),
83 widget='int')
82 widget='int')
84 dn_user = colander.SchemaNode(
83 dn_user = colander.SchemaNode(
85 colander.String(),
84 colander.String(),
86 default='',
85 default='',
87 description=_('User to connect to LDAP'),
86 description=_('User to connect to LDAP'),
88 missing='',
87 missing='',
89 title=_('Account'),
88 title=_('Account'),
90 widget='string')
89 widget='string')
91 dn_pass = colander.SchemaNode(
90 dn_pass = colander.SchemaNode(
92 colander.String(),
91 colander.String(),
93 default='',
92 default='',
94 description=_('Password to connect to LDAP'),
93 description=_('Password to connect to LDAP'),
95 missing='',
94 missing='',
96 title=_('Password'),
95 title=_('Password'),
97 widget='password')
96 widget='password')
98 tls_kind = colander.SchemaNode(
97 tls_kind = colander.SchemaNode(
99 colander.String(),
98 colander.String(),
100 default=tls_kind_choices[0],
99 default=tls_kind_choices[0],
101 description=_('TLS Type'),
100 description=_('TLS Type'),
102 title=_('Connection Security'),
101 title=_('Connection Security'),
103 validator=colander.OneOf(tls_kind_choices),
102 validator=colander.OneOf(tls_kind_choices),
104 widget='select')
103 widget='select')
105 tls_reqcert = colander.SchemaNode(
104 tls_reqcert = colander.SchemaNode(
106 colander.String(),
105 colander.String(),
107 default=tls_reqcert_choices[0],
106 default=tls_reqcert_choices[0],
108 description=_('Require Cert over TLS?'),
107 description=_('Require Cert over TLS?'),
109 title=_('Certificate Checks'),
108 title=_('Certificate Checks'),
110 validator=colander.OneOf(tls_reqcert_choices),
109 validator=colander.OneOf(tls_reqcert_choices),
111 widget='select')
110 widget='select')
112 base_dn = colander.SchemaNode(
111 base_dn = colander.SchemaNode(
113 colander.String(),
112 colander.String(),
114 default='',
113 default='',
115 description=_('Base DN to search (e.g., dc=mydomain,dc=com)'),
114 description=_('Base DN to search (e.g., dc=mydomain,dc=com)'),
116 missing='',
115 missing='',
117 title=_('Base DN'),
116 title=_('Base DN'),
118 widget='string')
117 widget='string')
119 filter = colander.SchemaNode(
118 filter = colander.SchemaNode(
120 colander.String(),
119 colander.String(),
121 default='',
120 default='',
122 description=_('Filter to narrow results (e.g., ou=Users, etc)'),
121 description=_('Filter to narrow results (e.g., ou=Users, etc)'),
123 missing='',
122 missing='',
124 title=_('LDAP Search Filter'),
123 title=_('LDAP Search Filter'),
125 widget='string')
124 widget='string')
126 search_scope = colander.SchemaNode(
125 search_scope = colander.SchemaNode(
127 colander.String(),
126 colander.String(),
128 default=search_scope_choices[0],
127 default=search_scope_choices[0],
129 description=_('How deep to search LDAP'),
128 description=_('How deep to search LDAP'),
130 title=_('LDAP Search Scope'),
129 title=_('LDAP Search Scope'),
131 validator=colander.OneOf(search_scope_choices),
130 validator=colander.OneOf(search_scope_choices),
132 widget='select')
131 widget='select')
133 attr_login = colander.SchemaNode(
132 attr_login = colander.SchemaNode(
134 colander.String(),
133 colander.String(),
135 default='',
134 default='',
136 description=_('LDAP Attribute to map to user name'),
135 description=_('LDAP Attribute to map to user name'),
137 title=_('Login Attribute'),
136 title=_('Login Attribute'),
138 missing_msg=_('The LDAP Login attribute of the CN must be specified'),
137 missing_msg=_('The LDAP Login attribute of the CN must be specified'),
139 widget='string')
138 widget='string')
140 attr_firstname = colander.SchemaNode(
139 attr_firstname = colander.SchemaNode(
141 colander.String(),
140 colander.String(),
142 default='',
141 default='',
143 description=_('LDAP Attribute to map to first name'),
142 description=_('LDAP Attribute to map to first name'),
144 missing='',
143 missing='',
145 title=_('First Name Attribute'),
144 title=_('First Name Attribute'),
146 widget='string')
145 widget='string')
147 attr_lastname = colander.SchemaNode(
146 attr_lastname = colander.SchemaNode(
148 colander.String(),
147 colander.String(),
149 default='',
148 default='',
150 description=_('LDAP Attribute to map to last name'),
149 description=_('LDAP Attribute to map to last name'),
151 missing='',
150 missing='',
152 title=_('Last Name Attribute'),
151 title=_('Last Name Attribute'),
153 widget='string')
152 widget='string')
154 attr_email = colander.SchemaNode(
153 attr_email = colander.SchemaNode(
155 colander.String(),
154 colander.String(),
156 default='',
155 default='',
157 description=_('LDAP Attribute to map to email address'),
156 description=_('LDAP Attribute to map to email address'),
158 missing='',
157 missing='',
159 title=_('Email Attribute'),
158 title=_('Email Attribute'),
160 widget='string')
159 widget='string')
161
160
162
161
163 class AuthLdap(object):
162 class AuthLdap(object):
164
163
165 def _build_servers(self):
164 def _build_servers(self):
166 return ', '.join(
165 return ', '.join(
167 ["{}://{}:{}".format(
166 ["{}://{}:{}".format(
168 self.ldap_server_type, host.strip(), self.LDAP_SERVER_PORT)
167 self.ldap_server_type, host.strip(), self.LDAP_SERVER_PORT)
169 for host in self.SERVER_ADDRESSES])
168 for host in self.SERVER_ADDRESSES])
170
169
171 def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
170 def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
172 tls_kind='PLAIN', tls_reqcert='DEMAND', ldap_version=3,
171 tls_kind='PLAIN', tls_reqcert='DEMAND', ldap_version=3,
173 search_scope='SUBTREE', attr_login='uid',
172 search_scope='SUBTREE', attr_login='uid',
174 ldap_filter='(&(objectClass=user)(!(objectClass=computer)))'):
173 ldap_filter='(&(objectClass=user)(!(objectClass=computer)))'):
175 if isinstance(ldap, Missing):
174 if isinstance(ldap, Missing):
176 raise LdapImportError("Missing or incompatible ldap library")
175 raise LdapImportError("Missing or incompatible ldap library")
177
176
178 self.ldap_version = ldap_version
177 self.ldap_version = ldap_version
179 self.ldap_server_type = 'ldap'
178 self.ldap_server_type = 'ldap'
180
179
181 self.TLS_KIND = tls_kind
180 self.TLS_KIND = tls_kind
182
181
183 if self.TLS_KIND == 'LDAPS':
182 if self.TLS_KIND == 'LDAPS':
184 port = port or 689
183 port = port or 689
185 self.ldap_server_type += 's'
184 self.ldap_server_type += 's'
186
185
187 OPT_X_TLS_DEMAND = 2
186 OPT_X_TLS_DEMAND = 2
188 self.TLS_REQCERT = getattr(ldap, 'OPT_X_TLS_%s' % tls_reqcert,
187 self.TLS_REQCERT = getattr(ldap, 'OPT_X_TLS_%s' % tls_reqcert,
189 OPT_X_TLS_DEMAND)
188 OPT_X_TLS_DEMAND)
190 # split server into list
189 # split server into list
191 self.SERVER_ADDRESSES = server.split(',')
190 self.SERVER_ADDRESSES = server.split(',')
192 self.LDAP_SERVER_PORT = port
191 self.LDAP_SERVER_PORT = port
193
192
194 # USE FOR READ ONLY BIND TO LDAP SERVER
193 # USE FOR READ ONLY BIND TO LDAP SERVER
195 self.attr_login = attr_login
194 self.attr_login = attr_login
196
195
197 self.LDAP_BIND_DN = safe_str(bind_dn)
196 self.LDAP_BIND_DN = safe_str(bind_dn)
198 self.LDAP_BIND_PASS = safe_str(bind_pass)
197 self.LDAP_BIND_PASS = safe_str(bind_pass)
199 self.LDAP_SERVER = self._build_servers()
198 self.LDAP_SERVER = self._build_servers()
200 self.SEARCH_SCOPE = getattr(ldap, 'SCOPE_%s' % search_scope)
199 self.SEARCH_SCOPE = getattr(ldap, 'SCOPE_%s' % search_scope)
201 self.BASE_DN = safe_str(base_dn)
200 self.BASE_DN = safe_str(base_dn)
202 self.LDAP_FILTER = safe_str(ldap_filter)
201 self.LDAP_FILTER = safe_str(ldap_filter)
203
202
204 def _get_ldap_server(self):
203 def _get_ldap_server(self):
205 if hasattr(ldap, 'OPT_X_TLS_CACERTDIR'):
204 if hasattr(ldap, 'OPT_X_TLS_CACERTDIR'):
206 ldap.set_option(ldap.OPT_X_TLS_CACERTDIR,
205 ldap.set_option(ldap.OPT_X_TLS_CACERTDIR,
207 '/etc/openldap/cacerts')
206 '/etc/openldap/cacerts')
208 ldap.set_option(ldap.OPT_REFERRALS, ldap.OPT_OFF)
207 ldap.set_option(ldap.OPT_REFERRALS, ldap.OPT_OFF)
209 ldap.set_option(ldap.OPT_RESTART, ldap.OPT_ON)
208 ldap.set_option(ldap.OPT_RESTART, ldap.OPT_ON)
210 ldap.set_option(ldap.OPT_TIMEOUT, 20)
209 ldap.set_option(ldap.OPT_TIMEOUT, 20)
211 ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 10)
210 ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 10)
212 ldap.set_option(ldap.OPT_TIMELIMIT, 15)
211 ldap.set_option(ldap.OPT_TIMELIMIT, 15)
213 if self.TLS_KIND != 'PLAIN':
212 if self.TLS_KIND != 'PLAIN':
214 ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, self.TLS_REQCERT)
213 ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, self.TLS_REQCERT)
215 server = ldap.initialize(self.LDAP_SERVER)
214 server = ldap.initialize(self.LDAP_SERVER)
216 if self.ldap_version == 2:
215 if self.ldap_version == 2:
217 server.protocol = ldap.VERSION2
216 server.protocol = ldap.VERSION2
218 else:
217 else:
219 server.protocol = ldap.VERSION3
218 server.protocol = ldap.VERSION3
220
219
221 if self.TLS_KIND == 'START_TLS':
220 if self.TLS_KIND == 'START_TLS':
222 server.start_tls_s()
221 server.start_tls_s()
223
222
224 if self.LDAP_BIND_DN and self.LDAP_BIND_PASS:
223 if self.LDAP_BIND_DN and self.LDAP_BIND_PASS:
225 log.debug('Trying simple_bind with password and given DN: %s',
224 log.debug('Trying simple_bind with password and given DN: %s',
226 self.LDAP_BIND_DN)
225 self.LDAP_BIND_DN)
227 server.simple_bind_s(self.LDAP_BIND_DN, self.LDAP_BIND_PASS)
226 server.simple_bind_s(self.LDAP_BIND_DN, self.LDAP_BIND_PASS)
228
227
229 return server
228 return server
230
229
231 def get_uid(self, username):
230 def get_uid(self, username):
232 from rhodecode.lib.helpers import chop_at
231 from rhodecode.lib.helpers import chop_at
233 uid = username
232 uid = username
234 for server_addr in self.SERVER_ADDRESSES:
233 for server_addr in self.SERVER_ADDRESSES:
235 uid = chop_at(username, "@%s" % server_addr)
234 uid = chop_at(username, "@%s" % server_addr)
236 return uid
235 return uid
237
236
238 def fetch_attrs_from_simple_bind(self, server, dn, username, password):
237 def fetch_attrs_from_simple_bind(self, server, dn, username, password):
239 try:
238 try:
240 log.debug('Trying simple bind with %s', dn)
239 log.debug('Trying simple bind with %s', dn)
241 server.simple_bind_s(dn, safe_str(password))
240 server.simple_bind_s(dn, safe_str(password))
242 user = server.search_ext_s(
241 user = server.search_ext_s(
243 dn, ldap.SCOPE_BASE, '(objectClass=*)', )[0]
242 dn, ldap.SCOPE_BASE, '(objectClass=*)', )[0]
244 _, attrs = user
243 _, attrs = user
245 return attrs
244 return attrs
246
245
247 except ldap.INVALID_CREDENTIALS:
246 except ldap.INVALID_CREDENTIALS:
248 log.debug(
247 log.debug(
249 "LDAP rejected password for user '%s': %s, org_exc:",
248 "LDAP rejected password for user '%s': %s, org_exc:",
250 username, dn, exc_info=True)
249 username, dn, exc_info=True)
251
250
252 def authenticate_ldap(self, username, password):
251 def authenticate_ldap(self, username, password):
253 """
252 """
254 Authenticate a user via LDAP and return his/her LDAP properties.
253 Authenticate a user via LDAP and return his/her LDAP properties.
255
254
256 Raises AuthenticationError if the credentials are rejected, or
255 Raises AuthenticationError if the credentials are rejected, or
257 EnvironmentError if the LDAP server can't be reached.
256 EnvironmentError if the LDAP server can't be reached.
258
257
259 :param username: username
258 :param username: username
260 :param password: password
259 :param password: password
261 """
260 """
262
261
263 uid = self.get_uid(username)
262 uid = self.get_uid(username)
264
263
265 if not password:
264 if not password:
266 msg = "Authenticating user %s with blank password not allowed"
265 msg = "Authenticating user %s with blank password not allowed"
267 log.warning(msg, username)
266 log.warning(msg, username)
268 raise LdapPasswordError(msg)
267 raise LdapPasswordError(msg)
269 if "," in username:
268 if "," in username:
270 raise LdapUsernameError("invalid character in username: ,")
269 raise LdapUsernameError("invalid character in username: ,")
271 try:
270 try:
272 server = self._get_ldap_server()
271 server = self._get_ldap_server()
273 filter_ = '(&%s(%s=%s))' % (
272 filter_ = '(&%s(%s=%s))' % (
274 self.LDAP_FILTER, self.attr_login, username)
273 self.LDAP_FILTER, self.attr_login, username)
275 log.debug("Authenticating %r filter %s at %s", self.BASE_DN,
274 log.debug("Authenticating %r filter %s at %s", self.BASE_DN,
276 filter_, self.LDAP_SERVER)
275 filter_, self.LDAP_SERVER)
277 lobjects = server.search_ext_s(
276 lobjects = server.search_ext_s(
278 self.BASE_DN, self.SEARCH_SCOPE, filter_)
277 self.BASE_DN, self.SEARCH_SCOPE, filter_)
279
278
280 if not lobjects:
279 if not lobjects:
281 raise ldap.NO_SUCH_OBJECT()
280 raise ldap.NO_SUCH_OBJECT()
282
281
283 for (dn, _attrs) in lobjects:
282 for (dn, _attrs) in lobjects:
284 if dn is None:
283 if dn is None:
285 continue
284 continue
286
285
287 user_attrs = self.fetch_attrs_from_simple_bind(
286 user_attrs = self.fetch_attrs_from_simple_bind(
288 server, dn, username, password)
287 server, dn, username, password)
289 if user_attrs:
288 if user_attrs:
290 break
289 break
291
290
292 else:
291 else:
293 log.debug("No matching LDAP objects for authentication "
292 log.debug("No matching LDAP objects for authentication "
294 "of '%s' (%s)", uid, username)
293 "of '%s' (%s)", uid, username)
295 raise LdapPasswordError('Failed to authenticate user '
294 raise LdapPasswordError('Failed to authenticate user '
296 'with given password')
295 'with given password')
297
296
298 except ldap.NO_SUCH_OBJECT:
297 except ldap.NO_SUCH_OBJECT:
299 log.debug("LDAP says no such user '%s' (%s), org_exc:",
298 log.debug("LDAP says no such user '%s' (%s), org_exc:",
300 uid, username, exc_info=True)
299 uid, username, exc_info=True)
301 raise LdapUsernameError()
300 raise LdapUsernameError()
302 except ldap.SERVER_DOWN:
301 except ldap.SERVER_DOWN:
303 org_exc = traceback.format_exc()
302 org_exc = traceback.format_exc()
304 raise LdapConnectionError(
303 raise LdapConnectionError(
305 "LDAP can't access authentication "
304 "LDAP can't access authentication "
306 "server, org_exc:%s" % org_exc)
305 "server, org_exc:%s" % org_exc)
307
306
308 return dn, user_attrs
307 return dn, user_attrs
309
308
310
309
311 class RhodeCodeAuthPlugin(RhodeCodeExternalAuthPlugin):
310 class RhodeCodeAuthPlugin(RhodeCodeExternalAuthPlugin):
312 # used to define dynamic binding in the
311 # used to define dynamic binding in the
313 DYNAMIC_BIND_VAR = '$login'
312 DYNAMIC_BIND_VAR = '$login'
314
313
315 def includeme(self, config):
314 def includeme(self, config):
316 config.add_authn_plugin(self)
315 config.add_authn_plugin(self)
317 config.add_authn_resource(self.get_id(), LdapAuthnResource(self))
316 config.add_authn_resource(self.get_id(), LdapAuthnResource(self))
318 config.add_view(
317 config.add_view(
319 'rhodecode.authentication.views.AuthnPluginViewBase',
318 'rhodecode.authentication.views.AuthnPluginViewBase',
320 attr='settings_get',
319 attr='settings_get',
321 request_method='GET',
320 request_method='GET',
322 route_name='auth_home',
321 route_name='auth_home',
323 context=LdapAuthnResource)
322 context=LdapAuthnResource)
324 config.add_view(
323 config.add_view(
325 'rhodecode.authentication.views.AuthnPluginViewBase',
324 'rhodecode.authentication.views.AuthnPluginViewBase',
326 attr='settings_post',
325 attr='settings_post',
327 request_method='POST',
326 request_method='POST',
328 route_name='auth_home',
327 route_name='auth_home',
329 context=LdapAuthnResource)
328 context=LdapAuthnResource)
330
329
331 def get_settings_schema(self):
330 def get_settings_schema(self):
332 return LdapSettingsSchema()
331 return LdapSettingsSchema()
333
332
334 def get_display_name(self):
333 def get_display_name(self):
335 return _('LDAP')
334 return _('LDAP')
336
335
337 @hybrid_property
336 @hybrid_property
338 def name(self):
337 def name(self):
339 return "ldap"
338 return "ldap"
340
339
341 def use_fake_password(self):
340 def use_fake_password(self):
342 return True
341 return True
343
342
344 def user_activation_state(self):
343 def user_activation_state(self):
345 def_user_perms = User.get_default_user().AuthUser.permissions['global']
344 def_user_perms = User.get_default_user().AuthUser.permissions['global']
346 return 'hg.extern_activate.auto' in def_user_perms
345 return 'hg.extern_activate.auto' in def_user_perms
347
346
348 def try_dynamic_binding(self, username, password, current_args):
347 def try_dynamic_binding(self, username, password, current_args):
349 """
348 """
350 Detects marker inside our original bind, and uses dynamic auth if
349 Detects marker inside our original bind, and uses dynamic auth if
351 present
350 present
352 """
351 """
353
352
354 org_bind = current_args['bind_dn']
353 org_bind = current_args['bind_dn']
355 passwd = current_args['bind_pass']
354 passwd = current_args['bind_pass']
356
355
357 def has_bind_marker(username):
356 def has_bind_marker(username):
358 if self.DYNAMIC_BIND_VAR in username:
357 if self.DYNAMIC_BIND_VAR in username:
359 return True
358 return True
360
359
361 # we only passed in user with "special" variable
360 # we only passed in user with "special" variable
362 if org_bind and has_bind_marker(org_bind) and not passwd:
361 if org_bind and has_bind_marker(org_bind) and not passwd:
363 log.debug('Using dynamic user/password binding for ldap '
362 log.debug('Using dynamic user/password binding for ldap '
364 'authentication. Replacing `%s` with username',
363 'authentication. Replacing `%s` with username',
365 self.DYNAMIC_BIND_VAR)
364 self.DYNAMIC_BIND_VAR)
366 current_args['bind_dn'] = org_bind.replace(
365 current_args['bind_dn'] = org_bind.replace(
367 self.DYNAMIC_BIND_VAR, username)
366 self.DYNAMIC_BIND_VAR, username)
368 current_args['bind_pass'] = password
367 current_args['bind_pass'] = password
369
368
370 return current_args
369 return current_args
371
370
372 def auth(self, userobj, username, password, settings, **kwargs):
371 def auth(self, userobj, username, password, settings, **kwargs):
373 """
372 """
374 Given a user object (which may be null), username, a plaintext password,
373 Given a user object (which may be null), username, a plaintext password,
375 and a settings object (containing all the keys needed as listed in
374 and a settings object (containing all the keys needed as listed in
376 settings()), authenticate this user's login attempt.
375 settings()), authenticate this user's login attempt.
377
376
378 Return None on failure. On success, return a dictionary of the form:
377 Return None on failure. On success, return a dictionary of the form:
379
378
380 see: RhodeCodeAuthPluginBase.auth_func_attrs
379 see: RhodeCodeAuthPluginBase.auth_func_attrs
381 This is later validated for correctness
380 This is later validated for correctness
382 """
381 """
383
382
384 if not username or not password:
383 if not username or not password:
385 log.debug('Empty username or password skipping...')
384 log.debug('Empty username or password skipping...')
386 return None
385 return None
387
386
388 ldap_args = {
387 ldap_args = {
389 'server': settings.get('host', ''),
388 'server': settings.get('host', ''),
390 'base_dn': settings.get('base_dn', ''),
389 'base_dn': settings.get('base_dn', ''),
391 'port': settings.get('port'),
390 'port': settings.get('port'),
392 'bind_dn': settings.get('dn_user'),
391 'bind_dn': settings.get('dn_user'),
393 'bind_pass': settings.get('dn_pass'),
392 'bind_pass': settings.get('dn_pass'),
394 'tls_kind': settings.get('tls_kind'),
393 'tls_kind': settings.get('tls_kind'),
395 'tls_reqcert': settings.get('tls_reqcert'),
394 'tls_reqcert': settings.get('tls_reqcert'),
396 'search_scope': settings.get('search_scope'),
395 'search_scope': settings.get('search_scope'),
397 'attr_login': settings.get('attr_login'),
396 'attr_login': settings.get('attr_login'),
398 'ldap_version': 3,
397 'ldap_version': 3,
399 'ldap_filter': settings.get('filter'),
398 'ldap_filter': settings.get('filter'),
400 }
399 }
401
400
402 ldap_attrs = self.try_dynamic_binding(username, password, ldap_args)
401 ldap_attrs = self.try_dynamic_binding(username, password, ldap_args)
403
402
404 log.debug('Checking for ldap authentication.')
403 log.debug('Checking for ldap authentication.')
405
404
406 try:
405 try:
407 aldap = AuthLdap(**ldap_args)
406 aldap = AuthLdap(**ldap_args)
408 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username, password)
407 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username, password)
409 log.debug('Got ldap DN response %s', user_dn)
408 log.debug('Got ldap DN response %s', user_dn)
410
409
411 def get_ldap_attr(k):
410 def get_ldap_attr(k):
412 return ldap_attrs.get(settings.get(k), [''])[0]
411 return ldap_attrs.get(settings.get(k), [''])[0]
413
412
414 # old attrs fetched from RhodeCode database
413 # old attrs fetched from RhodeCode database
415 admin = getattr(userobj, 'admin', False)
414 admin = getattr(userobj, 'admin', False)
416 active = getattr(userobj, 'active', True)
415 active = getattr(userobj, 'active', True)
417 email = getattr(userobj, 'email', '')
416 email = getattr(userobj, 'email', '')
418 username = getattr(userobj, 'username', username)
417 username = getattr(userobj, 'username', username)
419 firstname = getattr(userobj, 'firstname', '')
418 firstname = getattr(userobj, 'firstname', '')
420 lastname = getattr(userobj, 'lastname', '')
419 lastname = getattr(userobj, 'lastname', '')
421 extern_type = getattr(userobj, 'extern_type', '')
420 extern_type = getattr(userobj, 'extern_type', '')
422
421
423 groups = []
422 groups = []
424 user_attrs = {
423 user_attrs = {
425 'username': username,
424 'username': username,
426 'firstname': safe_unicode(
425 'firstname': safe_unicode(
427 get_ldap_attr('attr_firstname') or firstname),
426 get_ldap_attr('attr_firstname') or firstname),
428 'lastname': safe_unicode(
427 'lastname': safe_unicode(
429 get_ldap_attr('attr_lastname') or lastname),
428 get_ldap_attr('attr_lastname') or lastname),
430 'groups': groups,
429 'groups': groups,
431 'email': get_ldap_attr('attr_email' or email),
430 'email': get_ldap_attr('attr_email' or email),
432 'admin': admin,
431 'admin': admin,
433 'active': active,
432 'active': active,
434 "active_from_extern": None,
433 "active_from_extern": None,
435 'extern_name': user_dn,
434 'extern_name': user_dn,
436 'extern_type': extern_type,
435 'extern_type': extern_type,
437 }
436 }
438 log.debug('ldap user: \n%s', formatted_json(user_attrs))
437 log.debug('ldap user: %s', user_attrs)
439 log.info('user %s authenticated correctly', user_attrs['username'])
438 log.info('user %s authenticated correctly', user_attrs['username'])
440
439
441 return user_attrs
440 return user_attrs
442
441
443 except (LdapUsernameError, LdapPasswordError, LdapImportError):
442 except (LdapUsernameError, LdapPasswordError, LdapImportError):
444 log.exception("LDAP related exception")
443 log.exception("LDAP related exception")
445 return None
444 return None
446 except (Exception,):
445 except (Exception,):
447 log.exception("Other exception")
446 log.exception("Other exception")
448 return None
447 return None
@@ -1,156 +1,155 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2016 RhodeCode GmbH
3 # Copyright (C) 2012-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 """
20 """
21 RhodeCode authentication library for PAM
21 RhodeCode authentication library for PAM
22 """
22 """
23
23
24 import colander
24 import colander
25 import grp
25 import grp
26 import logging
26 import logging
27 import pam
27 import pam
28 import pwd
28 import pwd
29 import re
29 import re
30 import socket
30 import socket
31
31
32 from pylons.i18n.translation import lazy_ugettext as _
32 from pylons.i18n.translation import lazy_ugettext as _
33 from sqlalchemy.ext.hybrid import hybrid_property
33 from sqlalchemy.ext.hybrid import hybrid_property
34
34
35 from rhodecode.authentication.base import RhodeCodeExternalAuthPlugin
35 from rhodecode.authentication.base import RhodeCodeExternalAuthPlugin
36 from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
36 from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
37 from rhodecode.authentication.routes import AuthnPluginResourceBase
37 from rhodecode.authentication.routes import AuthnPluginResourceBase
38 from rhodecode.lib.ext_json import formatted_json
39
38
40 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
41
40
42
41
43 def plugin_factory(plugin_id, *args, **kwds):
42 def plugin_factory(plugin_id, *args, **kwds):
44 """
43 """
45 Factory function that is called during plugin discovery.
44 Factory function that is called during plugin discovery.
46 It returns the plugin instance.
45 It returns the plugin instance.
47 """
46 """
48 plugin = RhodeCodeAuthPlugin(plugin_id)
47 plugin = RhodeCodeAuthPlugin(plugin_id)
49 return plugin
48 return plugin
50
49
51
50
52 class PamAuthnResource(AuthnPluginResourceBase):
51 class PamAuthnResource(AuthnPluginResourceBase):
53 pass
52 pass
54
53
55
54
56 class PamSettingsSchema(AuthnPluginSettingsSchemaBase):
55 class PamSettingsSchema(AuthnPluginSettingsSchemaBase):
57 service = colander.SchemaNode(
56 service = colander.SchemaNode(
58 colander.String(),
57 colander.String(),
59 default='login',
58 default='login',
60 description=_('PAM service name to use for authentication.'),
59 description=_('PAM service name to use for authentication.'),
61 title=_('PAM service name'),
60 title=_('PAM service name'),
62 widget='string')
61 widget='string')
63 gecos = colander.SchemaNode(
62 gecos = colander.SchemaNode(
64 colander.String(),
63 colander.String(),
65 default='(?P<last_name>.+),\s*(?P<first_name>\w+)',
64 default='(?P<last_name>.+),\s*(?P<first_name>\w+)',
66 description=_('Regular expression for extracting user name/email etc. '
65 description=_('Regular expression for extracting user name/email etc. '
67 'from Unix userinfo.'),
66 'from Unix userinfo.'),
68 title=_('Gecos Regex'),
67 title=_('Gecos Regex'),
69 widget='string')
68 widget='string')
70
69
71
70
72 class RhodeCodeAuthPlugin(RhodeCodeExternalAuthPlugin):
71 class RhodeCodeAuthPlugin(RhodeCodeExternalAuthPlugin):
73 # PAM authentication can be slow. Repository operations involve a lot of
72 # PAM authentication can be slow. Repository operations involve a lot of
74 # auth calls. Little caching helps speedup push/pull operations significantly
73 # auth calls. Little caching helps speedup push/pull operations significantly
75 AUTH_CACHE_TTL = 4
74 AUTH_CACHE_TTL = 4
76
75
77 def includeme(self, config):
76 def includeme(self, config):
78 config.add_authn_plugin(self)
77 config.add_authn_plugin(self)
79 config.add_authn_resource(self.get_id(), PamAuthnResource(self))
78 config.add_authn_resource(self.get_id(), PamAuthnResource(self))
80 config.add_view(
79 config.add_view(
81 'rhodecode.authentication.views.AuthnPluginViewBase',
80 'rhodecode.authentication.views.AuthnPluginViewBase',
82 attr='settings_get',
81 attr='settings_get',
83 request_method='GET',
82 request_method='GET',
84 route_name='auth_home',
83 route_name='auth_home',
85 context=PamAuthnResource)
84 context=PamAuthnResource)
86 config.add_view(
85 config.add_view(
87 'rhodecode.authentication.views.AuthnPluginViewBase',
86 'rhodecode.authentication.views.AuthnPluginViewBase',
88 attr='settings_post',
87 attr='settings_post',
89 request_method='POST',
88 request_method='POST',
90 route_name='auth_home',
89 route_name='auth_home',
91 context=PamAuthnResource)
90 context=PamAuthnResource)
92
91
93 def get_display_name(self):
92 def get_display_name(self):
94 return _('PAM')
93 return _('PAM')
95
94
96 @hybrid_property
95 @hybrid_property
97 def name(self):
96 def name(self):
98 return "pam"
97 return "pam"
99
98
100 def get_settings_schema(self):
99 def get_settings_schema(self):
101 return PamSettingsSchema()
100 return PamSettingsSchema()
102
101
103 def use_fake_password(self):
102 def use_fake_password(self):
104 return True
103 return True
105
104
106 def auth(self, userobj, username, password, settings, **kwargs):
105 def auth(self, userobj, username, password, settings, **kwargs):
107 if not username or not password:
106 if not username or not password:
108 log.debug('Empty username or password skipping...')
107 log.debug('Empty username or password skipping...')
109 return None
108 return None
110
109
111 auth_result = pam.authenticate(username, password, settings["service"])
110 auth_result = pam.authenticate(username, password, settings["service"])
112
111
113 if not auth_result:
112 if not auth_result:
114 log.error("PAM was unable to authenticate user: %s" % (username, ))
113 log.error("PAM was unable to authenticate user: %s" % (username, ))
115 return None
114 return None
116
115
117 log.debug('Got PAM response %s' % (auth_result, ))
116 log.debug('Got PAM response %s' % (auth_result, ))
118
117
119 # old attrs fetched from RhodeCode database
118 # old attrs fetched from RhodeCode database
120 default_email = "%s@%s" % (username, socket.gethostname())
119 default_email = "%s@%s" % (username, socket.gethostname())
121 admin = getattr(userobj, 'admin', False)
120 admin = getattr(userobj, 'admin', False)
122 active = getattr(userobj, 'active', True)
121 active = getattr(userobj, 'active', True)
123 email = getattr(userobj, 'email', '') or default_email
122 email = getattr(userobj, 'email', '') or default_email
124 username = getattr(userobj, 'username', username)
123 username = getattr(userobj, 'username', username)
125 firstname = getattr(userobj, 'firstname', '')
124 firstname = getattr(userobj, 'firstname', '')
126 lastname = getattr(userobj, 'lastname', '')
125 lastname = getattr(userobj, 'lastname', '')
127 extern_type = getattr(userobj, 'extern_type', '')
126 extern_type = getattr(userobj, 'extern_type', '')
128
127
129 user_attrs = {
128 user_attrs = {
130 'username': username,
129 'username': username,
131 'firstname': firstname,
130 'firstname': firstname,
132 'lastname': lastname,
131 'lastname': lastname,
133 'groups': [g.gr_name for g in grp.getgrall()
132 'groups': [g.gr_name for g in grp.getgrall()
134 if username in g.gr_mem],
133 if username in g.gr_mem],
135 'email': email,
134 'email': email,
136 'admin': admin,
135 'admin': admin,
137 'active': active,
136 'active': active,
138 'active_from_extern': None,
137 'active_from_extern': None,
139 'extern_name': username,
138 'extern_name': username,
140 'extern_type': extern_type,
139 'extern_type': extern_type,
141 }
140 }
142
141
143 try:
142 try:
144 user_data = pwd.getpwnam(username)
143 user_data = pwd.getpwnam(username)
145 regex = settings["gecos"]
144 regex = settings["gecos"]
146 match = re.search(regex, user_data.pw_gecos)
145 match = re.search(regex, user_data.pw_gecos)
147 if match:
146 if match:
148 user_attrs["firstname"] = match.group('first_name')
147 user_attrs["firstname"] = match.group('first_name')
149 user_attrs["lastname"] = match.group('last_name')
148 user_attrs["lastname"] = match.group('last_name')
150 except Exception:
149 except Exception:
151 log.warning("Cannot extract additional info for PAM user")
150 log.warning("Cannot extract additional info for PAM user")
152 pass
151 pass
153
152
154 log.debug("pamuser: \n%s" % formatted_json(user_attrs))
153 log.debug("pamuser: %s", user_attrs)
155 log.info('user %s authenticated correctly' % user_attrs['username'])
154 log.info('user %s authenticated correctly' % user_attrs['username'])
156 return user_attrs
155 return user_attrs
@@ -1,440 +1,441 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2016 RhodeCode GmbH
3 # Copyright (C) 2014-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 SimpleVCS middleware for handling protocol request (push/clone etc.)
22 SimpleVCS middleware for handling protocol request (push/clone etc.)
23 It's implemented with basic auth function
23 It's implemented with basic auth function
24 """
24 """
25
25
26 import os
26 import os
27 import logging
27 import logging
28 import importlib
28 import importlib
29 from functools import wraps
29 from functools import wraps
30
30
31 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
31 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
32 from webob.exc import (
32 from webob.exc import (
33 HTTPNotFound, HTTPForbidden, HTTPNotAcceptable, HTTPInternalServerError)
33 HTTPNotFound, HTTPForbidden, HTTPNotAcceptable, HTTPInternalServerError)
34
34
35 import rhodecode
35 import rhodecode
36 from rhodecode.authentication.base import authenticate, VCS_TYPE
36 from rhodecode.authentication.base import authenticate, VCS_TYPE
37 from rhodecode.lib.auth import AuthUser, HasPermissionAnyMiddleware
37 from rhodecode.lib.auth import AuthUser, HasPermissionAnyMiddleware
38 from rhodecode.lib.base import BasicAuth, get_ip_addr, vcs_operation_context
38 from rhodecode.lib.base import BasicAuth, get_ip_addr, vcs_operation_context
39 from rhodecode.lib.exceptions import (
39 from rhodecode.lib.exceptions import (
40 HTTPLockedRC, HTTPRequirementError, UserCreationError,
40 HTTPLockedRC, HTTPRequirementError, UserCreationError,
41 NotAllowedToCreateUserError)
41 NotAllowedToCreateUserError)
42 from rhodecode.lib.hooks_daemon import prepare_callback_daemon
42 from rhodecode.lib.hooks_daemon import prepare_callback_daemon
43 from rhodecode.lib.middleware import appenlight
43 from rhodecode.lib.middleware import appenlight
44 from rhodecode.lib.middleware.utils import scm_app
44 from rhodecode.lib.middleware.utils import scm_app
45 from rhodecode.lib.utils import is_valid_repo
45 from rhodecode.lib.utils import is_valid_repo
46 from rhodecode.lib.utils2 import safe_str, fix_PATH, str2bool
46 from rhodecode.lib.utils2 import safe_str, fix_PATH, str2bool
47 from rhodecode.model import meta
47 from rhodecode.model import meta
48 from rhodecode.model.db import User, Repository
48 from rhodecode.model.db import User, Repository
49 from rhodecode.model.scm import ScmModel
49 from rhodecode.model.scm import ScmModel
50 from rhodecode.model.settings import SettingsModel
50 from rhodecode.model.settings import SettingsModel
51
51
52 log = logging.getLogger(__name__)
52 log = logging.getLogger(__name__)
53
53
54
54
55 def initialize_generator(factory):
55 def initialize_generator(factory):
56 """
56 """
57 Initializes the returned generator by draining its first element.
57 Initializes the returned generator by draining its first element.
58
58
59 This can be used to give a generator an initializer, which is the code
59 This can be used to give a generator an initializer, which is the code
60 up to the first yield statement. This decorator enforces that the first
60 up to the first yield statement. This decorator enforces that the first
61 produced element has the value ``"__init__"`` to make its special
61 produced element has the value ``"__init__"`` to make its special
62 purpose very explicit in the using code.
62 purpose very explicit in the using code.
63 """
63 """
64
64
65 @wraps(factory)
65 @wraps(factory)
66 def wrapper(*args, **kwargs):
66 def wrapper(*args, **kwargs):
67 gen = factory(*args, **kwargs)
67 gen = factory(*args, **kwargs)
68 try:
68 try:
69 init = gen.next()
69 init = gen.next()
70 except StopIteration:
70 except StopIteration:
71 raise ValueError('Generator must yield at least one element.')
71 raise ValueError('Generator must yield at least one element.')
72 if init != "__init__":
72 if init != "__init__":
73 raise ValueError('First yielded element must be "__init__".')
73 raise ValueError('First yielded element must be "__init__".')
74 return gen
74 return gen
75 return wrapper
75 return wrapper
76
76
77
77
78 class SimpleVCS(object):
78 class SimpleVCS(object):
79 """Common functionality for SCM HTTP handlers."""
79 """Common functionality for SCM HTTP handlers."""
80
80
81 SCM = 'unknown'
81 SCM = 'unknown'
82
82
83 def __init__(self, application, config):
83 def __init__(self, application, config):
84 self.application = application
84 self.application = application
85 self.config = config
85 self.config = config
86 # base path of repo locations
86 # base path of repo locations
87 self.basepath = self.config['base_path']
87 self.basepath = self.config['base_path']
88 # authenticate this VCS request using authfunc
88 # authenticate this VCS request using authfunc
89 auth_ret_code_detection = \
89 auth_ret_code_detection = \
90 str2bool(self.config.get('auth_ret_code_detection', False))
90 str2bool(self.config.get('auth_ret_code_detection', False))
91 self.authenticate = BasicAuth('', authenticate,
91 self.authenticate = BasicAuth('', authenticate,
92 config.get('auth_ret_code'),
92 config.get('auth_ret_code'),
93 auth_ret_code_detection)
93 auth_ret_code_detection)
94 self.ip_addr = '0.0.0.0'
94 self.ip_addr = '0.0.0.0'
95
95
96 @property
96 @property
97 def scm_app(self):
97 def scm_app(self):
98 custom_implementation = self.config.get('vcs.scm_app_implementation')
98 custom_implementation = self.config.get('vcs.scm_app_implementation')
99 if custom_implementation:
99 if custom_implementation:
100 log.info(
100 log.info(
101 "Using custom implementation of scm_app: %s",
101 "Using custom implementation of scm_app: %s",
102 custom_implementation)
102 custom_implementation)
103 scm_app_impl = importlib.import_module(custom_implementation)
103 scm_app_impl = importlib.import_module(custom_implementation)
104 else:
104 else:
105 scm_app_impl = scm_app
105 scm_app_impl = scm_app
106 return scm_app_impl
106 return scm_app_impl
107
107
108 def _get_by_id(self, repo_name):
108 def _get_by_id(self, repo_name):
109 """
109 """
110 Gets a special pattern _<ID> from clone url and tries to replace it
110 Gets a special pattern _<ID> from clone url and tries to replace it
111 with a repository_name for support of _<ID> non changable urls
111 with a repository_name for support of _<ID> non changable urls
112
112
113 :param repo_name:
113 :param repo_name:
114 """
114 """
115
115
116 data = repo_name.split('/')
116 data = repo_name.split('/')
117 if len(data) >= 2:
117 if len(data) >= 2:
118 from rhodecode.model.repo import RepoModel
118 from rhodecode.model.repo import RepoModel
119 by_id_match = RepoModel().get_repo_by_id(repo_name)
119 by_id_match = RepoModel().get_repo_by_id(repo_name)
120 if by_id_match:
120 if by_id_match:
121 data[1] = by_id_match.repo_name
121 data[1] = by_id_match.repo_name
122
122
123 return safe_str('/'.join(data))
123 return safe_str('/'.join(data))
124
124
125 def _invalidate_cache(self, repo_name):
125 def _invalidate_cache(self, repo_name):
126 """
126 """
127 Set's cache for this repository for invalidation on next access
127 Set's cache for this repository for invalidation on next access
128
128
129 :param repo_name: full repo name, also a cache key
129 :param repo_name: full repo name, also a cache key
130 """
130 """
131 ScmModel().mark_for_invalidation(repo_name)
131 ScmModel().mark_for_invalidation(repo_name)
132
132
133 def is_valid_and_existing_repo(self, repo_name, base_path, scm_type):
133 def is_valid_and_existing_repo(self, repo_name, base_path, scm_type):
134 db_repo = Repository.get_by_repo_name(repo_name)
134 db_repo = Repository.get_by_repo_name(repo_name)
135 if not db_repo:
135 if not db_repo:
136 log.debug('Repository `%s` not found inside the database.')
136 log.debug('Repository `%s` not found inside the database.',
137 repo_name)
137 return False
138 return False
138
139
139 if db_repo.repo_type != scm_type:
140 if db_repo.repo_type != scm_type:
140 log.warning(
141 log.warning(
141 'Repository `%s` have incorrect scm_type, expected %s got %s',
142 'Repository `%s` have incorrect scm_type, expected %s got %s',
142 repo_name, db_repo.repo_type, scm_type)
143 repo_name, db_repo.repo_type, scm_type)
143 return False
144 return False
144
145
145 return is_valid_repo(repo_name, base_path, expect_scm=scm_type)
146 return is_valid_repo(repo_name, base_path, expect_scm=scm_type)
146
147
147 def valid_and_active_user(self, user):
148 def valid_and_active_user(self, user):
148 """
149 """
149 Checks if that user is not empty, and if it's actually object it checks
150 Checks if that user is not empty, and if it's actually object it checks
150 if he's active.
151 if he's active.
151
152
152 :param user: user object or None
153 :param user: user object or None
153 :return: boolean
154 :return: boolean
154 """
155 """
155 if user is None:
156 if user is None:
156 return False
157 return False
157
158
158 elif user.active:
159 elif user.active:
159 return True
160 return True
160
161
161 return False
162 return False
162
163
163 def _check_permission(self, action, user, repo_name, ip_addr=None):
164 def _check_permission(self, action, user, repo_name, ip_addr=None):
164 """
165 """
165 Checks permissions using action (push/pull) user and repository
166 Checks permissions using action (push/pull) user and repository
166 name
167 name
167
168
168 :param action: push or pull action
169 :param action: push or pull action
169 :param user: user instance
170 :param user: user instance
170 :param repo_name: repository name
171 :param repo_name: repository name
171 """
172 """
172 # check IP
173 # check IP
173 inherit = user.inherit_default_permissions
174 inherit = user.inherit_default_permissions
174 ip_allowed = AuthUser.check_ip_allowed(user.user_id, ip_addr,
175 ip_allowed = AuthUser.check_ip_allowed(user.user_id, ip_addr,
175 inherit_from_default=inherit)
176 inherit_from_default=inherit)
176 if ip_allowed:
177 if ip_allowed:
177 log.info('Access for IP:%s allowed', ip_addr)
178 log.info('Access for IP:%s allowed', ip_addr)
178 else:
179 else:
179 return False
180 return False
180
181
181 if action == 'push':
182 if action == 'push':
182 if not HasPermissionAnyMiddleware('repository.write',
183 if not HasPermissionAnyMiddleware('repository.write',
183 'repository.admin')(user,
184 'repository.admin')(user,
184 repo_name):
185 repo_name):
185 return False
186 return False
186
187
187 else:
188 else:
188 # any other action need at least read permission
189 # any other action need at least read permission
189 if not HasPermissionAnyMiddleware('repository.read',
190 if not HasPermissionAnyMiddleware('repository.read',
190 'repository.write',
191 'repository.write',
191 'repository.admin')(user,
192 'repository.admin')(user,
192 repo_name):
193 repo_name):
193 return False
194 return False
194
195
195 return True
196 return True
196
197
197 def _check_ssl(self, environ, start_response):
198 def _check_ssl(self, environ, start_response):
198 """
199 """
199 Checks the SSL check flag and returns False if SSL is not present
200 Checks the SSL check flag and returns False if SSL is not present
200 and required True otherwise
201 and required True otherwise
201 """
202 """
202 org_proto = environ['wsgi._org_proto']
203 org_proto = environ['wsgi._org_proto']
203 # check if we have SSL required ! if not it's a bad request !
204 # check if we have SSL required ! if not it's a bad request !
204 require_ssl = str2bool(
205 require_ssl = str2bool(
205 SettingsModel().get_ui_by_key('push_ssl').ui_value)
206 SettingsModel().get_ui_by_key('push_ssl').ui_value)
206 if require_ssl and org_proto == 'http':
207 if require_ssl and org_proto == 'http':
207 log.debug('proto is %s and SSL is required BAD REQUEST !',
208 log.debug('proto is %s and SSL is required BAD REQUEST !',
208 org_proto)
209 org_proto)
209 return False
210 return False
210 return True
211 return True
211
212
212 def __call__(self, environ, start_response):
213 def __call__(self, environ, start_response):
213 try:
214 try:
214 return self._handle_request(environ, start_response)
215 return self._handle_request(environ, start_response)
215 except Exception:
216 except Exception:
216 log.exception("Exception while handling request")
217 log.exception("Exception while handling request")
217 appenlight.track_exception(environ)
218 appenlight.track_exception(environ)
218 return HTTPInternalServerError()(environ, start_response)
219 return HTTPInternalServerError()(environ, start_response)
219 finally:
220 finally:
220 meta.Session.remove()
221 meta.Session.remove()
221
222
222 def _handle_request(self, environ, start_response):
223 def _handle_request(self, environ, start_response):
223
224
224 if not self._check_ssl(environ, start_response):
225 if not self._check_ssl(environ, start_response):
225 reason = ('SSL required, while RhodeCode was unable '
226 reason = ('SSL required, while RhodeCode was unable '
226 'to detect this as SSL request')
227 'to detect this as SSL request')
227 log.debug('User not allowed to proceed, %s', reason)
228 log.debug('User not allowed to proceed, %s', reason)
228 return HTTPNotAcceptable(reason)(environ, start_response)
229 return HTTPNotAcceptable(reason)(environ, start_response)
229
230
230 ip_addr = get_ip_addr(environ)
231 ip_addr = get_ip_addr(environ)
231 username = None
232 username = None
232
233
233 # skip passing error to error controller
234 # skip passing error to error controller
234 environ['pylons.status_code_redirect'] = True
235 environ['pylons.status_code_redirect'] = True
235
236
236 # ======================================================================
237 # ======================================================================
237 # EXTRACT REPOSITORY NAME FROM ENV
238 # EXTRACT REPOSITORY NAME FROM ENV
238 # ======================================================================
239 # ======================================================================
239 environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
240 environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
240 repo_name = self._get_repository_name(environ)
241 repo_name = self._get_repository_name(environ)
241 environ['REPO_NAME'] = repo_name
242 environ['REPO_NAME'] = repo_name
242 log.debug('Extracted repo name is %s', repo_name)
243 log.debug('Extracted repo name is %s', repo_name)
243
244
244 # check for type, presence in database and on filesystem
245 # check for type, presence in database and on filesystem
245 if not self.is_valid_and_existing_repo(
246 if not self.is_valid_and_existing_repo(
246 repo_name, self.basepath, self.SCM):
247 repo_name, self.basepath, self.SCM):
247 return HTTPNotFound()(environ, start_response)
248 return HTTPNotFound()(environ, start_response)
248
249
249 # ======================================================================
250 # ======================================================================
250 # GET ACTION PULL or PUSH
251 # GET ACTION PULL or PUSH
251 # ======================================================================
252 # ======================================================================
252 action = self._get_action(environ)
253 action = self._get_action(environ)
253
254
254 # ======================================================================
255 # ======================================================================
255 # CHECK ANONYMOUS PERMISSION
256 # CHECK ANONYMOUS PERMISSION
256 # ======================================================================
257 # ======================================================================
257 if action in ['pull', 'push']:
258 if action in ['pull', 'push']:
258 anonymous_user = User.get_default_user()
259 anonymous_user = User.get_default_user()
259 username = anonymous_user.username
260 username = anonymous_user.username
260 if anonymous_user.active:
261 if anonymous_user.active:
261 # ONLY check permissions if the user is activated
262 # ONLY check permissions if the user is activated
262 anonymous_perm = self._check_permission(
263 anonymous_perm = self._check_permission(
263 action, anonymous_user, repo_name, ip_addr)
264 action, anonymous_user, repo_name, ip_addr)
264 else:
265 else:
265 anonymous_perm = False
266 anonymous_perm = False
266
267
267 if not anonymous_user.active or not anonymous_perm:
268 if not anonymous_user.active or not anonymous_perm:
268 if not anonymous_user.active:
269 if not anonymous_user.active:
269 log.debug('Anonymous access is disabled, running '
270 log.debug('Anonymous access is disabled, running '
270 'authentication')
271 'authentication')
271
272
272 if not anonymous_perm:
273 if not anonymous_perm:
273 log.debug('Not enough credentials to access this '
274 log.debug('Not enough credentials to access this '
274 'repository as anonymous user')
275 'repository as anonymous user')
275
276
276 username = None
277 username = None
277 # ==============================================================
278 # ==============================================================
278 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
279 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
279 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
280 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
280 # ==============================================================
281 # ==============================================================
281
282
282 # try to auth based on environ, container auth methods
283 # try to auth based on environ, container auth methods
283 log.debug('Running PRE-AUTH for container based authentication')
284 log.debug('Running PRE-AUTH for container based authentication')
284 pre_auth = authenticate('', '', environ,VCS_TYPE)
285 pre_auth = authenticate('', '', environ,VCS_TYPE)
285 if pre_auth and pre_auth.get('username'):
286 if pre_auth and pre_auth.get('username'):
286 username = pre_auth['username']
287 username = pre_auth['username']
287 log.debug('PRE-AUTH got %s as username', username)
288 log.debug('PRE-AUTH got %s as username', username)
288
289
289 # If not authenticated by the container, running basic auth
290 # If not authenticated by the container, running basic auth
290 if not username:
291 if not username:
291 self.authenticate.realm = \
292 self.authenticate.realm = \
292 safe_str(self.config['rhodecode_realm'])
293 safe_str(self.config['rhodecode_realm'])
293
294
294 try:
295 try:
295 result = self.authenticate(environ)
296 result = self.authenticate(environ)
296 except (UserCreationError, NotAllowedToCreateUserError) as e:
297 except (UserCreationError, NotAllowedToCreateUserError) as e:
297 log.error(e)
298 log.error(e)
298 reason = safe_str(e)
299 reason = safe_str(e)
299 return HTTPNotAcceptable(reason)(environ, start_response)
300 return HTTPNotAcceptable(reason)(environ, start_response)
300
301
301 if isinstance(result, str):
302 if isinstance(result, str):
302 AUTH_TYPE.update(environ, 'basic')
303 AUTH_TYPE.update(environ, 'basic')
303 REMOTE_USER.update(environ, result)
304 REMOTE_USER.update(environ, result)
304 username = result
305 username = result
305 else:
306 else:
306 return result.wsgi_application(environ, start_response)
307 return result.wsgi_application(environ, start_response)
307
308
308 # ==============================================================
309 # ==============================================================
309 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
310 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
310 # ==============================================================
311 # ==============================================================
311 user = User.get_by_username(username)
312 user = User.get_by_username(username)
312 if not self.valid_and_active_user(user):
313 if not self.valid_and_active_user(user):
313 return HTTPForbidden()(environ, start_response)
314 return HTTPForbidden()(environ, start_response)
314 username = user.username
315 username = user.username
315 user.update_lastactivity()
316 user.update_lastactivity()
316 meta.Session().commit()
317 meta.Session().commit()
317
318
318 # check user attributes for password change flag
319 # check user attributes for password change flag
319 user_obj = user
320 user_obj = user
320 if user_obj and user_obj.username != User.DEFAULT_USER and \
321 if user_obj and user_obj.username != User.DEFAULT_USER and \
321 user_obj.user_data.get('force_password_change'):
322 user_obj.user_data.get('force_password_change'):
322 reason = 'password change required'
323 reason = 'password change required'
323 log.debug('User not allowed to authenticate, %s', reason)
324 log.debug('User not allowed to authenticate, %s', reason)
324 return HTTPNotAcceptable(reason)(environ, start_response)
325 return HTTPNotAcceptable(reason)(environ, start_response)
325
326
326 # check permissions for this repository
327 # check permissions for this repository
327 perm = self._check_permission(action, user, repo_name, ip_addr)
328 perm = self._check_permission(action, user, repo_name, ip_addr)
328 if not perm:
329 if not perm:
329 return HTTPForbidden()(environ, start_response)
330 return HTTPForbidden()(environ, start_response)
330
331
331 # extras are injected into UI object and later available
332 # extras are injected into UI object and later available
332 # in hooks executed by rhodecode
333 # in hooks executed by rhodecode
333 check_locking = _should_check_locking(environ.get('QUERY_STRING'))
334 check_locking = _should_check_locking(environ.get('QUERY_STRING'))
334 extras = vcs_operation_context(
335 extras = vcs_operation_context(
335 environ, repo_name=repo_name, username=username,
336 environ, repo_name=repo_name, username=username,
336 action=action, scm=self.SCM,
337 action=action, scm=self.SCM,
337 check_locking=check_locking)
338 check_locking=check_locking)
338
339
339 # ======================================================================
340 # ======================================================================
340 # REQUEST HANDLING
341 # REQUEST HANDLING
341 # ======================================================================
342 # ======================================================================
342 str_repo_name = safe_str(repo_name)
343 str_repo_name = safe_str(repo_name)
343 repo_path = os.path.join(safe_str(self.basepath), str_repo_name)
344 repo_path = os.path.join(safe_str(self.basepath), str_repo_name)
344 log.debug('Repository path is %s', repo_path)
345 log.debug('Repository path is %s', repo_path)
345
346
346 fix_PATH()
347 fix_PATH()
347
348
348 log.info(
349 log.info(
349 '%s action on %s repo "%s" by "%s" from %s',
350 '%s action on %s repo "%s" by "%s" from %s',
350 action, self.SCM, str_repo_name, safe_str(username), ip_addr)
351 action, self.SCM, str_repo_name, safe_str(username), ip_addr)
351 return self._generate_vcs_response(
352 return self._generate_vcs_response(
352 environ, start_response, repo_path, repo_name, extras, action)
353 environ, start_response, repo_path, repo_name, extras, action)
353
354
354 @initialize_generator
355 @initialize_generator
355 def _generate_vcs_response(
356 def _generate_vcs_response(
356 self, environ, start_response, repo_path, repo_name, extras,
357 self, environ, start_response, repo_path, repo_name, extras,
357 action):
358 action):
358 """
359 """
359 Returns a generator for the response content.
360 Returns a generator for the response content.
360
361
361 This method is implemented as a generator, so that it can trigger
362 This method is implemented as a generator, so that it can trigger
362 the cache validation after all content sent back to the client. It
363 the cache validation after all content sent back to the client. It
363 also handles the locking exceptions which will be triggered when
364 also handles the locking exceptions which will be triggered when
364 the first chunk is produced by the underlying WSGI application.
365 the first chunk is produced by the underlying WSGI application.
365 """
366 """
366 callback_daemon, extras = self._prepare_callback_daemon(extras)
367 callback_daemon, extras = self._prepare_callback_daemon(extras)
367 config = self._create_config(extras, repo_name)
368 config = self._create_config(extras, repo_name)
368 log.debug('HOOKS extras is %s', extras)
369 log.debug('HOOKS extras is %s', extras)
369 app = self._create_wsgi_app(repo_path, repo_name, config)
370 app = self._create_wsgi_app(repo_path, repo_name, config)
370
371
371 try:
372 try:
372 with callback_daemon:
373 with callback_daemon:
373 try:
374 try:
374 response = app(environ, start_response)
375 response = app(environ, start_response)
375 finally:
376 finally:
376 # This statement works together with the decorator
377 # This statement works together with the decorator
377 # "initialize_generator" above. The decorator ensures that
378 # "initialize_generator" above. The decorator ensures that
378 # we hit the first yield statement before the generator is
379 # we hit the first yield statement before the generator is
379 # returned back to the WSGI server. This is needed to
380 # returned back to the WSGI server. This is needed to
380 # ensure that the call to "app" above triggers the
381 # ensure that the call to "app" above triggers the
381 # needed callback to "start_response" before the
382 # needed callback to "start_response" before the
382 # generator is actually used.
383 # generator is actually used.
383 yield "__init__"
384 yield "__init__"
384
385
385 for chunk in response:
386 for chunk in response:
386 yield chunk
387 yield chunk
387 except Exception as exc:
388 except Exception as exc:
388 # TODO: johbo: Improve "translating" back the exception.
389 # TODO: johbo: Improve "translating" back the exception.
389 if getattr(exc, '_vcs_kind', None) == 'repo_locked':
390 if getattr(exc, '_vcs_kind', None) == 'repo_locked':
390 exc = HTTPLockedRC(*exc.args)
391 exc = HTTPLockedRC(*exc.args)
391 _code = rhodecode.CONFIG.get('lock_ret_code')
392 _code = rhodecode.CONFIG.get('lock_ret_code')
392 log.debug('Repository LOCKED ret code %s!', (_code,))
393 log.debug('Repository LOCKED ret code %s!', (_code,))
393 elif getattr(exc, '_vcs_kind', None) == 'requirement':
394 elif getattr(exc, '_vcs_kind', None) == 'requirement':
394 log.debug(
395 log.debug(
395 'Repository requires features unknown to this Mercurial')
396 'Repository requires features unknown to this Mercurial')
396 exc = HTTPRequirementError(*exc.args)
397 exc = HTTPRequirementError(*exc.args)
397 else:
398 else:
398 raise
399 raise
399
400
400 for chunk in exc(environ, start_response):
401 for chunk in exc(environ, start_response):
401 yield chunk
402 yield chunk
402 finally:
403 finally:
403 # invalidate cache on push
404 # invalidate cache on push
404 if action == 'push':
405 if action == 'push':
405 self._invalidate_cache(repo_name)
406 self._invalidate_cache(repo_name)
406
407
407 def _get_repository_name(self, environ):
408 def _get_repository_name(self, environ):
408 """Get repository name out of the environmnent
409 """Get repository name out of the environmnent
409
410
410 :param environ: WSGI environment
411 :param environ: WSGI environment
411 """
412 """
412 raise NotImplementedError()
413 raise NotImplementedError()
413
414
414 def _get_action(self, environ):
415 def _get_action(self, environ):
415 """Map request commands into a pull or push command.
416 """Map request commands into a pull or push command.
416
417
417 :param environ: WSGI environment
418 :param environ: WSGI environment
418 """
419 """
419 raise NotImplementedError()
420 raise NotImplementedError()
420
421
421 def _create_wsgi_app(self, repo_path, repo_name, config):
422 def _create_wsgi_app(self, repo_path, repo_name, config):
422 """Return the WSGI app that will finally handle the request."""
423 """Return the WSGI app that will finally handle the request."""
423 raise NotImplementedError()
424 raise NotImplementedError()
424
425
425 def _create_config(self, extras, repo_name):
426 def _create_config(self, extras, repo_name):
426 """Create a Pyro safe config representation."""
427 """Create a Pyro safe config representation."""
427 raise NotImplementedError()
428 raise NotImplementedError()
428
429
429 def _prepare_callback_daemon(self, extras):
430 def _prepare_callback_daemon(self, extras):
430 return prepare_callback_daemon(
431 return prepare_callback_daemon(
431 extras, protocol=self.config.get('vcs.hooks.protocol'),
432 extras, protocol=self.config.get('vcs.hooks.protocol'),
432 use_direct_calls=self.config.get('vcs.hooks.direct_calls'))
433 use_direct_calls=self.config.get('vcs.hooks.direct_calls'))
433
434
434
435
435 def _should_check_locking(query_string):
436 def _should_check_locking(query_string):
436 # this is kind of hacky, but due to how mercurial handles client-server
437 # this is kind of hacky, but due to how mercurial handles client-server
437 # server see all operation on commit; bookmarks, phases and
438 # server see all operation on commit; bookmarks, phases and
438 # obsolescence marker in different transaction, we don't want to check
439 # obsolescence marker in different transaction, we don't want to check
439 # locking on those
440 # locking on those
440 return query_string not in ['cmd=listkeys']
441 return query_string not in ['cmd=listkeys']
@@ -1,1128 +1,1130 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2016 RhodeCode GmbH
3 # Copyright (C) 2012-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 """
22 """
23 pull request model for RhodeCode
23 pull request model for RhodeCode
24 """
24 """
25
25
26 from collections import namedtuple
26 from collections import namedtuple
27 import json
27 import json
28 import logging
28 import logging
29 import datetime
29 import datetime
30
30
31 from pylons.i18n.translation import _
31 from pylons.i18n.translation import _
32 from pylons.i18n.translation import lazy_ugettext
32 from pylons.i18n.translation import lazy_ugettext
33
33
34 import rhodecode
34 import rhodecode
35 from rhodecode.lib import helpers as h, hooks_utils, diffs
35 from rhodecode.lib import helpers as h, hooks_utils, diffs
36 from rhodecode.lib.compat import OrderedDict
36 from rhodecode.lib.compat import OrderedDict
37 from rhodecode.lib.hooks_daemon import prepare_callback_daemon
37 from rhodecode.lib.hooks_daemon import prepare_callback_daemon
38 from rhodecode.lib.markup_renderer import (
38 from rhodecode.lib.markup_renderer import (
39 DEFAULT_COMMENTS_RENDERER, RstTemplateRenderer)
39 DEFAULT_COMMENTS_RENDERER, RstTemplateRenderer)
40 from rhodecode.lib.utils import action_logger
40 from rhodecode.lib.utils import action_logger
41 from rhodecode.lib.utils2 import safe_unicode, safe_str, md5_safe
41 from rhodecode.lib.utils2 import safe_unicode, safe_str, md5_safe
42 from rhodecode.lib.vcs.backends.base import (
42 from rhodecode.lib.vcs.backends.base import (
43 Reference, MergeResponse, MergeFailureReason)
43 Reference, MergeResponse, MergeFailureReason)
44 from rhodecode.lib.vcs.exceptions import (
44 from rhodecode.lib.vcs.exceptions import (
45 CommitDoesNotExistError, EmptyRepositoryError)
45 CommitDoesNotExistError, EmptyRepositoryError)
46 from rhodecode.model import BaseModel
46 from rhodecode.model import BaseModel
47 from rhodecode.model.changeset_status import ChangesetStatusModel
47 from rhodecode.model.changeset_status import ChangesetStatusModel
48 from rhodecode.model.comment import ChangesetCommentsModel
48 from rhodecode.model.comment import ChangesetCommentsModel
49 from rhodecode.model.db import (
49 from rhodecode.model.db import (
50 PullRequest, PullRequestReviewers, Notification, ChangesetStatus,
50 PullRequest, PullRequestReviewers, Notification, ChangesetStatus,
51 PullRequestVersion, ChangesetComment)
51 PullRequestVersion, ChangesetComment)
52 from rhodecode.model.meta import Session
52 from rhodecode.model.meta import Session
53 from rhodecode.model.notification import NotificationModel, \
53 from rhodecode.model.notification import NotificationModel, \
54 EmailNotificationModel
54 EmailNotificationModel
55 from rhodecode.model.scm import ScmModel
55 from rhodecode.model.scm import ScmModel
56 from rhodecode.model.settings import VcsSettingsModel
56 from rhodecode.model.settings import VcsSettingsModel
57
57
58
58
59 log = logging.getLogger(__name__)
59 log = logging.getLogger(__name__)
60
60
61
61
62 class PullRequestModel(BaseModel):
62 class PullRequestModel(BaseModel):
63
63
64 cls = PullRequest
64 cls = PullRequest
65
65
66 DIFF_CONTEXT = 3
66 DIFF_CONTEXT = 3
67
67
68 MERGE_STATUS_MESSAGES = {
68 MERGE_STATUS_MESSAGES = {
69 MergeFailureReason.NONE: lazy_ugettext(
69 MergeFailureReason.NONE: lazy_ugettext(
70 'This pull request can be automatically merged.'),
70 'This pull request can be automatically merged.'),
71 MergeFailureReason.UNKNOWN: lazy_ugettext(
71 MergeFailureReason.UNKNOWN: lazy_ugettext(
72 'This pull request cannot be merged because of an unhandled'
72 'This pull request cannot be merged because of an unhandled'
73 ' exception.'),
73 ' exception.'),
74 MergeFailureReason.MERGE_FAILED: lazy_ugettext(
74 MergeFailureReason.MERGE_FAILED: lazy_ugettext(
75 'This pull request cannot be merged because of conflicts.'),
75 'This pull request cannot be merged because of conflicts.'),
76 MergeFailureReason.PUSH_FAILED: lazy_ugettext(
76 MergeFailureReason.PUSH_FAILED: lazy_ugettext(
77 'This pull request could not be merged because push to target'
77 'This pull request could not be merged because push to target'
78 ' failed.'),
78 ' failed.'),
79 MergeFailureReason.TARGET_IS_NOT_HEAD: lazy_ugettext(
79 MergeFailureReason.TARGET_IS_NOT_HEAD: lazy_ugettext(
80 'This pull request cannot be merged because the target is not a'
80 'This pull request cannot be merged because the target is not a'
81 ' head.'),
81 ' head.'),
82 MergeFailureReason.HG_SOURCE_HAS_MORE_BRANCHES: lazy_ugettext(
82 MergeFailureReason.HG_SOURCE_HAS_MORE_BRANCHES: lazy_ugettext(
83 'This pull request cannot be merged because the source contains'
83 'This pull request cannot be merged because the source contains'
84 ' more branches than the target.'),
84 ' more branches than the target.'),
85 MergeFailureReason.HG_TARGET_HAS_MULTIPLE_HEADS: lazy_ugettext(
85 MergeFailureReason.HG_TARGET_HAS_MULTIPLE_HEADS: lazy_ugettext(
86 'This pull request cannot be merged because the target has'
86 'This pull request cannot be merged because the target has'
87 ' multiple heads.'),
87 ' multiple heads.'),
88 MergeFailureReason.TARGET_IS_LOCKED: lazy_ugettext(
88 MergeFailureReason.TARGET_IS_LOCKED: lazy_ugettext(
89 'This pull request cannot be merged because the target repository'
89 'This pull request cannot be merged because the target repository'
90 ' is locked.'),
90 ' is locked.'),
91 MergeFailureReason.MISSING_COMMIT: lazy_ugettext(
91 MergeFailureReason.MISSING_COMMIT: lazy_ugettext(
92 'This pull request cannot be merged because the target or the '
92 'This pull request cannot be merged because the target or the '
93 'source reference is missing.'),
93 'source reference is missing.'),
94 }
94 }
95
95
96 def __get_pull_request(self, pull_request):
96 def __get_pull_request(self, pull_request):
97 return self._get_instance(PullRequest, pull_request)
97 return self._get_instance(PullRequest, pull_request)
98
98
99 def _check_perms(self, perms, pull_request, user, api=False):
99 def _check_perms(self, perms, pull_request, user, api=False):
100 if not api:
100 if not api:
101 return h.HasRepoPermissionAny(*perms)(
101 return h.HasRepoPermissionAny(*perms)(
102 user=user, repo_name=pull_request.target_repo.repo_name)
102 user=user, repo_name=pull_request.target_repo.repo_name)
103 else:
103 else:
104 return h.HasRepoPermissionAnyApi(*perms)(
104 return h.HasRepoPermissionAnyApi(*perms)(
105 user=user, repo_name=pull_request.target_repo.repo_name)
105 user=user, repo_name=pull_request.target_repo.repo_name)
106
106
107 def check_user_read(self, pull_request, user, api=False):
107 def check_user_read(self, pull_request, user, api=False):
108 _perms = ('repository.admin', 'repository.write', 'repository.read',)
108 _perms = ('repository.admin', 'repository.write', 'repository.read',)
109 return self._check_perms(_perms, pull_request, user, api)
109 return self._check_perms(_perms, pull_request, user, api)
110
110
111 def check_user_merge(self, pull_request, user, api=False):
111 def check_user_merge(self, pull_request, user, api=False):
112 _perms = ('repository.admin', 'repository.write', 'hg.admin',)
112 _perms = ('repository.admin', 'repository.write', 'hg.admin',)
113 return self._check_perms(_perms, pull_request, user, api)
113 return self._check_perms(_perms, pull_request, user, api)
114
114
115 def check_user_update(self, pull_request, user, api=False):
115 def check_user_update(self, pull_request, user, api=False):
116 owner = user.user_id == pull_request.user_id
116 owner = user.user_id == pull_request.user_id
117 return self.check_user_merge(pull_request, user, api) or owner
117 return self.check_user_merge(pull_request, user, api) or owner
118
118
119 def check_user_change_status(self, pull_request, user, api=False):
119 def check_user_change_status(self, pull_request, user, api=False):
120 reviewer = user.user_id in [x.user_id for x in
120 reviewer = user.user_id in [x.user_id for x in
121 pull_request.reviewers]
121 pull_request.reviewers]
122 return self.check_user_update(pull_request, user, api) or reviewer
122 return self.check_user_update(pull_request, user, api) or reviewer
123
123
124 def get(self, pull_request):
124 def get(self, pull_request):
125 return self.__get_pull_request(pull_request)
125 return self.__get_pull_request(pull_request)
126
126
127 def _prepare_get_all_query(self, repo_name, source=False, statuses=None,
127 def _prepare_get_all_query(self, repo_name, source=False, statuses=None,
128 opened_by=None, order_by=None,
128 opened_by=None, order_by=None,
129 order_dir='desc'):
129 order_dir='desc'):
130 repo = self._get_repo(repo_name)
130 repo = self._get_repo(repo_name)
131 q = PullRequest.query()
131 q = PullRequest.query()
132 # source or target
132 # source or target
133 if source:
133 if source:
134 q = q.filter(PullRequest.source_repo == repo)
134 q = q.filter(PullRequest.source_repo == repo)
135 else:
135 else:
136 q = q.filter(PullRequest.target_repo == repo)
136 q = q.filter(PullRequest.target_repo == repo)
137
137
138 # closed,opened
138 # closed,opened
139 if statuses:
139 if statuses:
140 q = q.filter(PullRequest.status.in_(statuses))
140 q = q.filter(PullRequest.status.in_(statuses))
141
141
142 # opened by filter
142 # opened by filter
143 if opened_by:
143 if opened_by:
144 q = q.filter(PullRequest.user_id.in_(opened_by))
144 q = q.filter(PullRequest.user_id.in_(opened_by))
145
145
146 if order_by:
146 if order_by:
147 order_map = {
147 order_map = {
148 'name_raw': PullRequest.pull_request_id,
148 'name_raw': PullRequest.pull_request_id,
149 'title': PullRequest.title,
149 'title': PullRequest.title,
150 'updated_on_raw': PullRequest.updated_on
150 'updated_on_raw': PullRequest.updated_on
151 }
151 }
152 if order_dir == 'asc':
152 if order_dir == 'asc':
153 q = q.order_by(order_map[order_by].asc())
153 q = q.order_by(order_map[order_by].asc())
154 else:
154 else:
155 q = q.order_by(order_map[order_by].desc())
155 q = q.order_by(order_map[order_by].desc())
156
156
157 return q
157 return q
158
158
159 def count_all(self, repo_name, source=False, statuses=None,
159 def count_all(self, repo_name, source=False, statuses=None,
160 opened_by=None):
160 opened_by=None):
161 """
161 """
162 Count the number of pull requests for a specific repository.
162 Count the number of pull requests for a specific repository.
163
163
164 :param repo_name: target or source repo
164 :param repo_name: target or source repo
165 :param source: boolean flag to specify if repo_name refers to source
165 :param source: boolean flag to specify if repo_name refers to source
166 :param statuses: list of pull request statuses
166 :param statuses: list of pull request statuses
167 :param opened_by: author user of the pull request
167 :param opened_by: author user of the pull request
168 :returns: int number of pull requests
168 :returns: int number of pull requests
169 """
169 """
170 q = self._prepare_get_all_query(
170 q = self._prepare_get_all_query(
171 repo_name, source=source, statuses=statuses, opened_by=opened_by)
171 repo_name, source=source, statuses=statuses, opened_by=opened_by)
172
172
173 return q.count()
173 return q.count()
174
174
175 def get_all(self, repo_name, source=False, statuses=None, opened_by=None,
175 def get_all(self, repo_name, source=False, statuses=None, opened_by=None,
176 offset=0, length=None, order_by=None, order_dir='desc'):
176 offset=0, length=None, order_by=None, order_dir='desc'):
177 """
177 """
178 Get all pull requests for a specific repository.
178 Get all pull requests for a specific repository.
179
179
180 :param repo_name: target or source repo
180 :param repo_name: target or source repo
181 :param source: boolean flag to specify if repo_name refers to source
181 :param source: boolean flag to specify if repo_name refers to source
182 :param statuses: list of pull request statuses
182 :param statuses: list of pull request statuses
183 :param opened_by: author user of the pull request
183 :param opened_by: author user of the pull request
184 :param offset: pagination offset
184 :param offset: pagination offset
185 :param length: length of returned list
185 :param length: length of returned list
186 :param order_by: order of the returned list
186 :param order_by: order of the returned list
187 :param order_dir: 'asc' or 'desc' ordering direction
187 :param order_dir: 'asc' or 'desc' ordering direction
188 :returns: list of pull requests
188 :returns: list of pull requests
189 """
189 """
190 q = self._prepare_get_all_query(
190 q = self._prepare_get_all_query(
191 repo_name, source=source, statuses=statuses, opened_by=opened_by,
191 repo_name, source=source, statuses=statuses, opened_by=opened_by,
192 order_by=order_by, order_dir=order_dir)
192 order_by=order_by, order_dir=order_dir)
193
193
194 if length:
194 if length:
195 pull_requests = q.limit(length).offset(offset).all()
195 pull_requests = q.limit(length).offset(offset).all()
196 else:
196 else:
197 pull_requests = q.all()
197 pull_requests = q.all()
198
198
199 return pull_requests
199 return pull_requests
200
200
201 def count_awaiting_review(self, repo_name, source=False, statuses=None,
201 def count_awaiting_review(self, repo_name, source=False, statuses=None,
202 opened_by=None):
202 opened_by=None):
203 """
203 """
204 Count the number of pull requests for a specific repository that are
204 Count the number of pull requests for a specific repository that are
205 awaiting review.
205 awaiting review.
206
206
207 :param repo_name: target or source repo
207 :param repo_name: target or source repo
208 :param source: boolean flag to specify if repo_name refers to source
208 :param source: boolean flag to specify if repo_name refers to source
209 :param statuses: list of pull request statuses
209 :param statuses: list of pull request statuses
210 :param opened_by: author user of the pull request
210 :param opened_by: author user of the pull request
211 :returns: int number of pull requests
211 :returns: int number of pull requests
212 """
212 """
213 pull_requests = self.get_awaiting_review(
213 pull_requests = self.get_awaiting_review(
214 repo_name, source=source, statuses=statuses, opened_by=opened_by)
214 repo_name, source=source, statuses=statuses, opened_by=opened_by)
215
215
216 return len(pull_requests)
216 return len(pull_requests)
217
217
218 def get_awaiting_review(self, repo_name, source=False, statuses=None,
218 def get_awaiting_review(self, repo_name, source=False, statuses=None,
219 opened_by=None, offset=0, length=None,
219 opened_by=None, offset=0, length=None,
220 order_by=None, order_dir='desc'):
220 order_by=None, order_dir='desc'):
221 """
221 """
222 Get all pull requests for a specific repository that are awaiting
222 Get all pull requests for a specific repository that are awaiting
223 review.
223 review.
224
224
225 :param repo_name: target or source repo
225 :param repo_name: target or source repo
226 :param source: boolean flag to specify if repo_name refers to source
226 :param source: boolean flag to specify if repo_name refers to source
227 :param statuses: list of pull request statuses
227 :param statuses: list of pull request statuses
228 :param opened_by: author user of the pull request
228 :param opened_by: author user of the pull request
229 :param offset: pagination offset
229 :param offset: pagination offset
230 :param length: length of returned list
230 :param length: length of returned list
231 :param order_by: order of the returned list
231 :param order_by: order of the returned list
232 :param order_dir: 'asc' or 'desc' ordering direction
232 :param order_dir: 'asc' or 'desc' ordering direction
233 :returns: list of pull requests
233 :returns: list of pull requests
234 """
234 """
235 pull_requests = self.get_all(
235 pull_requests = self.get_all(
236 repo_name, source=source, statuses=statuses, opened_by=opened_by,
236 repo_name, source=source, statuses=statuses, opened_by=opened_by,
237 order_by=order_by, order_dir=order_dir)
237 order_by=order_by, order_dir=order_dir)
238
238
239 _filtered_pull_requests = []
239 _filtered_pull_requests = []
240 for pr in pull_requests:
240 for pr in pull_requests:
241 status = pr.calculated_review_status()
241 status = pr.calculated_review_status()
242 if status in [ChangesetStatus.STATUS_NOT_REVIEWED,
242 if status in [ChangesetStatus.STATUS_NOT_REVIEWED,
243 ChangesetStatus.STATUS_UNDER_REVIEW]:
243 ChangesetStatus.STATUS_UNDER_REVIEW]:
244 _filtered_pull_requests.append(pr)
244 _filtered_pull_requests.append(pr)
245 if length:
245 if length:
246 return _filtered_pull_requests[offset:offset+length]
246 return _filtered_pull_requests[offset:offset+length]
247 else:
247 else:
248 return _filtered_pull_requests
248 return _filtered_pull_requests
249
249
250 def count_awaiting_my_review(self, repo_name, source=False, statuses=None,
250 def count_awaiting_my_review(self, repo_name, source=False, statuses=None,
251 opened_by=None, user_id=None):
251 opened_by=None, user_id=None):
252 """
252 """
253 Count the number of pull requests for a specific repository that are
253 Count the number of pull requests for a specific repository that are
254 awaiting review from a specific user.
254 awaiting review from a specific user.
255
255
256 :param repo_name: target or source repo
256 :param repo_name: target or source repo
257 :param source: boolean flag to specify if repo_name refers to source
257 :param source: boolean flag to specify if repo_name refers to source
258 :param statuses: list of pull request statuses
258 :param statuses: list of pull request statuses
259 :param opened_by: author user of the pull request
259 :param opened_by: author user of the pull request
260 :param user_id: reviewer user of the pull request
260 :param user_id: reviewer user of the pull request
261 :returns: int number of pull requests
261 :returns: int number of pull requests
262 """
262 """
263 pull_requests = self.get_awaiting_my_review(
263 pull_requests = self.get_awaiting_my_review(
264 repo_name, source=source, statuses=statuses, opened_by=opened_by,
264 repo_name, source=source, statuses=statuses, opened_by=opened_by,
265 user_id=user_id)
265 user_id=user_id)
266
266
267 return len(pull_requests)
267 return len(pull_requests)
268
268
269 def get_awaiting_my_review(self, repo_name, source=False, statuses=None,
269 def get_awaiting_my_review(self, repo_name, source=False, statuses=None,
270 opened_by=None, user_id=None, offset=0,
270 opened_by=None, user_id=None, offset=0,
271 length=None, order_by=None, order_dir='desc'):
271 length=None, order_by=None, order_dir='desc'):
272 """
272 """
273 Get all pull requests for a specific repository that are awaiting
273 Get all pull requests for a specific repository that are awaiting
274 review from a specific user.
274 review from a specific user.
275
275
276 :param repo_name: target or source repo
276 :param repo_name: target or source repo
277 :param source: boolean flag to specify if repo_name refers to source
277 :param source: boolean flag to specify if repo_name refers to source
278 :param statuses: list of pull request statuses
278 :param statuses: list of pull request statuses
279 :param opened_by: author user of the pull request
279 :param opened_by: author user of the pull request
280 :param user_id: reviewer user of the pull request
280 :param user_id: reviewer user of the pull request
281 :param offset: pagination offset
281 :param offset: pagination offset
282 :param length: length of returned list
282 :param length: length of returned list
283 :param order_by: order of the returned list
283 :param order_by: order of the returned list
284 :param order_dir: 'asc' or 'desc' ordering direction
284 :param order_dir: 'asc' or 'desc' ordering direction
285 :returns: list of pull requests
285 :returns: list of pull requests
286 """
286 """
287 pull_requests = self.get_all(
287 pull_requests = self.get_all(
288 repo_name, source=source, statuses=statuses, opened_by=opened_by,
288 repo_name, source=source, statuses=statuses, opened_by=opened_by,
289 order_by=order_by, order_dir=order_dir)
289 order_by=order_by, order_dir=order_dir)
290
290
291 _my = PullRequestModel().get_not_reviewed(user_id)
291 _my = PullRequestModel().get_not_reviewed(user_id)
292 my_participation = []
292 my_participation = []
293 for pr in pull_requests:
293 for pr in pull_requests:
294 if pr in _my:
294 if pr in _my:
295 my_participation.append(pr)
295 my_participation.append(pr)
296 _filtered_pull_requests = my_participation
296 _filtered_pull_requests = my_participation
297 if length:
297 if length:
298 return _filtered_pull_requests[offset:offset+length]
298 return _filtered_pull_requests[offset:offset+length]
299 else:
299 else:
300 return _filtered_pull_requests
300 return _filtered_pull_requests
301
301
302 def get_not_reviewed(self, user_id):
302 def get_not_reviewed(self, user_id):
303 return [
303 return [
304 x.pull_request for x in PullRequestReviewers.query().filter(
304 x.pull_request for x in PullRequestReviewers.query().filter(
305 PullRequestReviewers.user_id == user_id).all()
305 PullRequestReviewers.user_id == user_id).all()
306 ]
306 ]
307
307
308 def get_versions(self, pull_request):
308 def get_versions(self, pull_request):
309 """
309 """
310 returns version of pull request sorted by ID descending
310 returns version of pull request sorted by ID descending
311 """
311 """
312 return PullRequestVersion.query()\
312 return PullRequestVersion.query()\
313 .filter(PullRequestVersion.pull_request == pull_request)\
313 .filter(PullRequestVersion.pull_request == pull_request)\
314 .order_by(PullRequestVersion.pull_request_version_id.asc())\
314 .order_by(PullRequestVersion.pull_request_version_id.asc())\
315 .all()
315 .all()
316
316
317 def create(self, created_by, source_repo, source_ref, target_repo,
317 def create(self, created_by, source_repo, source_ref, target_repo,
318 target_ref, revisions, reviewers, title, description=None):
318 target_ref, revisions, reviewers, title, description=None):
319 created_by_user = self._get_user(created_by)
319 created_by_user = self._get_user(created_by)
320 source_repo = self._get_repo(source_repo)
320 source_repo = self._get_repo(source_repo)
321 target_repo = self._get_repo(target_repo)
321 target_repo = self._get_repo(target_repo)
322
322
323 pull_request = PullRequest()
323 pull_request = PullRequest()
324 pull_request.source_repo = source_repo
324 pull_request.source_repo = source_repo
325 pull_request.source_ref = source_ref
325 pull_request.source_ref = source_ref
326 pull_request.target_repo = target_repo
326 pull_request.target_repo = target_repo
327 pull_request.target_ref = target_ref
327 pull_request.target_ref = target_ref
328 pull_request.revisions = revisions
328 pull_request.revisions = revisions
329 pull_request.title = title
329 pull_request.title = title
330 pull_request.description = description
330 pull_request.description = description
331 pull_request.author = created_by_user
331 pull_request.author = created_by_user
332
332
333 Session().add(pull_request)
333 Session().add(pull_request)
334 Session().flush()
334 Session().flush()
335
335
336 # members / reviewers
336 # members / reviewers
337 for user_id in set(reviewers):
337 for user_id in set(reviewers):
338 user = self._get_user(user_id)
338 user = self._get_user(user_id)
339 reviewer = PullRequestReviewers(user, pull_request)
339 reviewer = PullRequestReviewers(user, pull_request)
340 Session().add(reviewer)
340 Session().add(reviewer)
341
341
342 # Set approval status to "Under Review" for all commits which are
342 # Set approval status to "Under Review" for all commits which are
343 # part of this pull request.
343 # part of this pull request.
344 ChangesetStatusModel().set_status(
344 ChangesetStatusModel().set_status(
345 repo=target_repo,
345 repo=target_repo,
346 status=ChangesetStatus.STATUS_UNDER_REVIEW,
346 status=ChangesetStatus.STATUS_UNDER_REVIEW,
347 user=created_by_user,
347 user=created_by_user,
348 pull_request=pull_request
348 pull_request=pull_request
349 )
349 )
350
350
351 self.notify_reviewers(pull_request, reviewers)
351 self.notify_reviewers(pull_request, reviewers)
352 self._trigger_pull_request_hook(
352 self._trigger_pull_request_hook(
353 pull_request, created_by_user, 'create')
353 pull_request, created_by_user, 'create')
354
354
355 return pull_request
355 return pull_request
356
356
357 def _trigger_pull_request_hook(self, pull_request, user, action):
357 def _trigger_pull_request_hook(self, pull_request, user, action):
358 pull_request = self.__get_pull_request(pull_request)
358 pull_request = self.__get_pull_request(pull_request)
359 target_scm = pull_request.target_repo.scm_instance()
359 target_scm = pull_request.target_repo.scm_instance()
360 if action == 'create':
360 if action == 'create':
361 trigger_hook = hooks_utils.trigger_log_create_pull_request_hook
361 trigger_hook = hooks_utils.trigger_log_create_pull_request_hook
362 elif action == 'merge':
362 elif action == 'merge':
363 trigger_hook = hooks_utils.trigger_log_merge_pull_request_hook
363 trigger_hook = hooks_utils.trigger_log_merge_pull_request_hook
364 elif action == 'close':
364 elif action == 'close':
365 trigger_hook = hooks_utils.trigger_log_close_pull_request_hook
365 trigger_hook = hooks_utils.trigger_log_close_pull_request_hook
366 elif action == 'review_status_change':
366 elif action == 'review_status_change':
367 trigger_hook = hooks_utils.trigger_log_review_pull_request_hook
367 trigger_hook = hooks_utils.trigger_log_review_pull_request_hook
368 elif action == 'update':
368 elif action == 'update':
369 trigger_hook = hooks_utils.trigger_log_update_pull_request_hook
369 trigger_hook = hooks_utils.trigger_log_update_pull_request_hook
370 else:
370 else:
371 return
371 return
372
372
373 trigger_hook(
373 trigger_hook(
374 username=user.username,
374 username=user.username,
375 repo_name=pull_request.target_repo.repo_name,
375 repo_name=pull_request.target_repo.repo_name,
376 repo_alias=target_scm.alias,
376 repo_alias=target_scm.alias,
377 pull_request=pull_request)
377 pull_request=pull_request)
378
378
379 def _get_commit_ids(self, pull_request):
379 def _get_commit_ids(self, pull_request):
380 """
380 """
381 Return the commit ids of the merged pull request.
381 Return the commit ids of the merged pull request.
382
382
383 This method is not dealing correctly yet with the lack of autoupdates
383 This method is not dealing correctly yet with the lack of autoupdates
384 nor with the implicit target updates.
384 nor with the implicit target updates.
385 For example: if a commit in the source repo is already in the target it
385 For example: if a commit in the source repo is already in the target it
386 will be reported anyways.
386 will be reported anyways.
387 """
387 """
388 merge_rev = pull_request.merge_rev
388 merge_rev = pull_request.merge_rev
389 if merge_rev is None:
389 if merge_rev is None:
390 raise ValueError('This pull request was not merged yet')
390 raise ValueError('This pull request was not merged yet')
391
391
392 commit_ids = list(pull_request.revisions)
392 commit_ids = list(pull_request.revisions)
393 if merge_rev not in commit_ids:
393 if merge_rev not in commit_ids:
394 commit_ids.append(merge_rev)
394 commit_ids.append(merge_rev)
395
395
396 return commit_ids
396 return commit_ids
397
397
398 def merge(self, pull_request, user, extras):
398 def merge(self, pull_request, user, extras):
399 merge_state = self._merge_pull_request(pull_request, user, extras)
399 merge_state = self._merge_pull_request(pull_request, user, extras)
400 if merge_state.executed:
400 if merge_state.executed:
401 self._comment_and_close_pr(pull_request, user, merge_state)
401 self._comment_and_close_pr(pull_request, user, merge_state)
402 self._log_action('user_merged_pull_request', user, pull_request)
402 self._log_action('user_merged_pull_request', user, pull_request)
403 return merge_state
403 return merge_state
404
404
405 def _merge_pull_request(self, pull_request, user, extras):
405 def _merge_pull_request(self, pull_request, user, extras):
406 target_vcs = pull_request.target_repo.scm_instance()
406 target_vcs = pull_request.target_repo.scm_instance()
407 source_vcs = pull_request.source_repo.scm_instance()
407 source_vcs = pull_request.source_repo.scm_instance()
408 target_ref = self._refresh_reference(
408 target_ref = self._refresh_reference(
409 pull_request.target_ref_parts, target_vcs)
409 pull_request.target_ref_parts, target_vcs)
410
410
411 message = _(
411 message = _(
412 'Merge pull request #%(pr_id)s from '
412 'Merge pull request #%(pr_id)s from '
413 '%(source_repo)s %(source_ref_name)s\n\n %(pr_title)s') % {
413 '%(source_repo)s %(source_ref_name)s\n\n %(pr_title)s') % {
414 'pr_id': pull_request.pull_request_id,
414 'pr_id': pull_request.pull_request_id,
415 'source_repo': source_vcs.name,
415 'source_repo': source_vcs.name,
416 'source_ref_name': pull_request.source_ref_parts.name,
416 'source_ref_name': pull_request.source_ref_parts.name,
417 'pr_title': pull_request.title
417 'pr_title': pull_request.title
418 }
418 }
419
419
420 workspace_id = self._workspace_id(pull_request)
420 workspace_id = self._workspace_id(pull_request)
421 protocol = rhodecode.CONFIG.get('vcs.hooks.protocol')
421 protocol = rhodecode.CONFIG.get('vcs.hooks.protocol')
422 use_direct_calls = rhodecode.CONFIG.get('vcs.hooks.direct_calls')
422 use_direct_calls = rhodecode.CONFIG.get('vcs.hooks.direct_calls')
423
423
424 callback_daemon, extras = prepare_callback_daemon(
424 callback_daemon, extras = prepare_callback_daemon(
425 extras, protocol=protocol, use_direct_calls=use_direct_calls)
425 extras, protocol=protocol, use_direct_calls=use_direct_calls)
426
426
427 with callback_daemon:
427 with callback_daemon:
428 # TODO: johbo: Implement a clean way to run a config_override
428 # TODO: johbo: Implement a clean way to run a config_override
429 # for a single call.
429 # for a single call.
430 target_vcs.config.set(
430 target_vcs.config.set(
431 'rhodecode', 'RC_SCM_DATA', json.dumps(extras))
431 'rhodecode', 'RC_SCM_DATA', json.dumps(extras))
432 merge_state = target_vcs.merge(
432 merge_state = target_vcs.merge(
433 target_ref, source_vcs, pull_request.source_ref_parts,
433 target_ref, source_vcs, pull_request.source_ref_parts,
434 workspace_id, user_name=user.username,
434 workspace_id, user_name=user.username,
435 user_email=user.email, message=message)
435 user_email=user.email, message=message)
436 return merge_state
436 return merge_state
437
437
438 def _comment_and_close_pr(self, pull_request, user, merge_state):
438 def _comment_and_close_pr(self, pull_request, user, merge_state):
439 pull_request.merge_rev = merge_state.merge_commit_id
439 pull_request.merge_rev = merge_state.merge_commit_id
440 pull_request.updated_on = datetime.datetime.now()
440 pull_request.updated_on = datetime.datetime.now()
441
441
442 ChangesetCommentsModel().create(
442 ChangesetCommentsModel().create(
443 text=unicode(_('Pull request merged and closed')),
443 text=unicode(_('Pull request merged and closed')),
444 repo=pull_request.target_repo.repo_id,
444 repo=pull_request.target_repo.repo_id,
445 user=user.user_id,
445 user=user.user_id,
446 pull_request=pull_request.pull_request_id,
446 pull_request=pull_request.pull_request_id,
447 f_path=None,
447 f_path=None,
448 line_no=None,
448 line_no=None,
449 closing_pr=True
449 closing_pr=True
450 )
450 )
451
451
452 Session().add(pull_request)
452 Session().add(pull_request)
453 Session().flush()
453 Session().flush()
454 # TODO: paris: replace invalidation with less radical solution
454 # TODO: paris: replace invalidation with less radical solution
455 ScmModel().mark_for_invalidation(
455 ScmModel().mark_for_invalidation(
456 pull_request.target_repo.repo_name)
456 pull_request.target_repo.repo_name)
457 self._trigger_pull_request_hook(pull_request, user, 'merge')
457 self._trigger_pull_request_hook(pull_request, user, 'merge')
458
458
459 def has_valid_update_type(self, pull_request):
459 def has_valid_update_type(self, pull_request):
460 source_ref_type = pull_request.source_ref_parts.type
460 source_ref_type = pull_request.source_ref_parts.type
461 return source_ref_type in ['book', 'branch', 'tag']
461 return source_ref_type in ['book', 'branch', 'tag']
462
462
463 def update_commits(self, pull_request):
463 def update_commits(self, pull_request):
464 """
464 """
465 Get the updated list of commits for the pull request
465 Get the updated list of commits for the pull request
466 and return the new pull request version and the list
466 and return the new pull request version and the list
467 of commits processed by this update action
467 of commits processed by this update action
468 """
468 """
469
469
470 pull_request = self.__get_pull_request(pull_request)
470 pull_request = self.__get_pull_request(pull_request)
471 source_ref_type = pull_request.source_ref_parts.type
471 source_ref_type = pull_request.source_ref_parts.type
472 source_ref_name = pull_request.source_ref_parts.name
472 source_ref_name = pull_request.source_ref_parts.name
473 source_ref_id = pull_request.source_ref_parts.commit_id
473 source_ref_id = pull_request.source_ref_parts.commit_id
474
474
475 if not self.has_valid_update_type(pull_request):
475 if not self.has_valid_update_type(pull_request):
476 log.debug(
476 log.debug(
477 "Skipping update of pull request %s due to ref type: %s",
477 "Skipping update of pull request %s due to ref type: %s",
478 pull_request, source_ref_type)
478 pull_request, source_ref_type)
479 return (None, None)
479 return (None, None)
480
480
481 source_repo = pull_request.source_repo.scm_instance()
481 source_repo = pull_request.source_repo.scm_instance()
482 source_commit = source_repo.get_commit(commit_id=source_ref_name)
482 source_commit = source_repo.get_commit(commit_id=source_ref_name)
483 if source_ref_id == source_commit.raw_id:
483 if source_ref_id == source_commit.raw_id:
484 log.debug("Nothing changed in pull request %s", pull_request)
484 log.debug("Nothing changed in pull request %s", pull_request)
485 return (None, None)
485 return (None, None)
486
486
487 # Finally there is a need for an update
487 # Finally there is a need for an update
488 pull_request_version = self._create_version_from_snapshot(pull_request)
488 pull_request_version = self._create_version_from_snapshot(pull_request)
489 self._link_comments_to_version(pull_request_version)
489 self._link_comments_to_version(pull_request_version)
490
490
491 target_ref_type = pull_request.target_ref_parts.type
491 target_ref_type = pull_request.target_ref_parts.type
492 target_ref_name = pull_request.target_ref_parts.name
492 target_ref_name = pull_request.target_ref_parts.name
493 target_ref_id = pull_request.target_ref_parts.commit_id
493 target_ref_id = pull_request.target_ref_parts.commit_id
494 target_repo = pull_request.target_repo.scm_instance()
494 target_repo = pull_request.target_repo.scm_instance()
495
495
496 if target_ref_type in ('tag', 'branch', 'book'):
496 if target_ref_type in ('tag', 'branch', 'book'):
497 target_commit = target_repo.get_commit(target_ref_name)
497 target_commit = target_repo.get_commit(target_ref_name)
498 else:
498 else:
499 target_commit = target_repo.get_commit(target_ref_id)
499 target_commit = target_repo.get_commit(target_ref_id)
500
500
501 # re-compute commit ids
501 # re-compute commit ids
502 old_commit_ids = set(pull_request.revisions)
502 old_commit_ids = set(pull_request.revisions)
503 pre_load = ["author", "branch", "date", "message"]
503 pre_load = ["author", "branch", "date", "message"]
504 commit_ranges = target_repo.compare(
504 commit_ranges = target_repo.compare(
505 target_commit.raw_id, source_commit.raw_id, source_repo, merge=True,
505 target_commit.raw_id, source_commit.raw_id, source_repo, merge=True,
506 pre_load=pre_load)
506 pre_load=pre_load)
507
507
508 ancestor = target_repo.get_common_ancestor(
508 ancestor = target_repo.get_common_ancestor(
509 target_commit.raw_id, source_commit.raw_id, source_repo)
509 target_commit.raw_id, source_commit.raw_id, source_repo)
510
510
511 pull_request.source_ref = '%s:%s:%s' % (
511 pull_request.source_ref = '%s:%s:%s' % (
512 source_ref_type, source_ref_name, source_commit.raw_id)
512 source_ref_type, source_ref_name, source_commit.raw_id)
513 pull_request.target_ref = '%s:%s:%s' % (
513 pull_request.target_ref = '%s:%s:%s' % (
514 target_ref_type, target_ref_name, ancestor)
514 target_ref_type, target_ref_name, ancestor)
515 pull_request.revisions = [
515 pull_request.revisions = [
516 commit.raw_id for commit in reversed(commit_ranges)]
516 commit.raw_id for commit in reversed(commit_ranges)]
517 pull_request.updated_on = datetime.datetime.now()
517 pull_request.updated_on = datetime.datetime.now()
518 Session().add(pull_request)
518 Session().add(pull_request)
519 new_commit_ids = set(pull_request.revisions)
519 new_commit_ids = set(pull_request.revisions)
520
520
521 changes = self._calculate_commit_id_changes(
521 changes = self._calculate_commit_id_changes(
522 old_commit_ids, new_commit_ids)
522 old_commit_ids, new_commit_ids)
523
523
524 old_diff_data, new_diff_data = self._generate_update_diffs(
524 old_diff_data, new_diff_data = self._generate_update_diffs(
525 pull_request, pull_request_version)
525 pull_request, pull_request_version)
526
526
527 ChangesetCommentsModel().outdate_comments(
527 ChangesetCommentsModel().outdate_comments(
528 pull_request, old_diff_data=old_diff_data,
528 pull_request, old_diff_data=old_diff_data,
529 new_diff_data=new_diff_data)
529 new_diff_data=new_diff_data)
530
530
531 file_changes = self._calculate_file_changes(
531 file_changes = self._calculate_file_changes(
532 old_diff_data, new_diff_data)
532 old_diff_data, new_diff_data)
533
533
534 # Add an automatic comment to the pull request
534 # Add an automatic comment to the pull request
535 update_comment = ChangesetCommentsModel().create(
535 update_comment = ChangesetCommentsModel().create(
536 text=self._render_update_message(changes, file_changes),
536 text=self._render_update_message(changes, file_changes),
537 repo=pull_request.target_repo,
537 repo=pull_request.target_repo,
538 user=pull_request.author,
538 user=pull_request.author,
539 pull_request=pull_request,
539 pull_request=pull_request,
540 send_email=False, renderer=DEFAULT_COMMENTS_RENDERER)
540 send_email=False, renderer=DEFAULT_COMMENTS_RENDERER)
541
541
542 # Update status to "Under Review" for added commits
542 # Update status to "Under Review" for added commits
543 for commit_id in changes.added:
543 for commit_id in changes.added:
544 ChangesetStatusModel().set_status(
544 ChangesetStatusModel().set_status(
545 repo=pull_request.source_repo,
545 repo=pull_request.source_repo,
546 status=ChangesetStatus.STATUS_UNDER_REVIEW,
546 status=ChangesetStatus.STATUS_UNDER_REVIEW,
547 comment=update_comment,
547 comment=update_comment,
548 user=pull_request.author,
548 user=pull_request.author,
549 pull_request=pull_request,
549 pull_request=pull_request,
550 revision=commit_id)
550 revision=commit_id)
551
551
552 log.debug(
552 log.debug(
553 'Updated pull request %s, added_ids: %s, common_ids: %s, '
553 'Updated pull request %s, added_ids: %s, common_ids: %s, '
554 'removed_ids: %s', pull_request.pull_request_id,
554 'removed_ids: %s', pull_request.pull_request_id,
555 changes.added, changes.common, changes.removed)
555 changes.added, changes.common, changes.removed)
556 log.debug('Updated pull request with the following file changes: %s',
556 log.debug('Updated pull request with the following file changes: %s',
557 file_changes)
557 file_changes)
558
558
559 log.info(
559 log.info(
560 "Updated pull request %s from commit %s to commit %s, "
560 "Updated pull request %s from commit %s to commit %s, "
561 "stored new version %s of this pull request.",
561 "stored new version %s of this pull request.",
562 pull_request.pull_request_id, source_ref_id,
562 pull_request.pull_request_id, source_ref_id,
563 pull_request.source_ref_parts.commit_id,
563 pull_request.source_ref_parts.commit_id,
564 pull_request_version.pull_request_version_id)
564 pull_request_version.pull_request_version_id)
565 Session().commit()
565 Session().commit()
566 self._trigger_pull_request_hook(pull_request, pull_request.author,
566 self._trigger_pull_request_hook(pull_request, pull_request.author,
567 'update')
567 'update')
568 return (pull_request_version, changes)
568 return (pull_request_version, changes)
569
569
570 def _create_version_from_snapshot(self, pull_request):
570 def _create_version_from_snapshot(self, pull_request):
571 version = PullRequestVersion()
571 version = PullRequestVersion()
572 version.title = pull_request.title
572 version.title = pull_request.title
573 version.description = pull_request.description
573 version.description = pull_request.description
574 version.status = pull_request.status
574 version.status = pull_request.status
575 version.created_on = pull_request.created_on
575 version.created_on = pull_request.created_on
576 version.updated_on = pull_request.updated_on
576 version.updated_on = pull_request.updated_on
577 version.user_id = pull_request.user_id
577 version.user_id = pull_request.user_id
578 version.source_repo = pull_request.source_repo
578 version.source_repo = pull_request.source_repo
579 version.source_ref = pull_request.source_ref
579 version.source_ref = pull_request.source_ref
580 version.target_repo = pull_request.target_repo
580 version.target_repo = pull_request.target_repo
581 version.target_ref = pull_request.target_ref
581 version.target_ref = pull_request.target_ref
582
582
583 version._last_merge_source_rev = pull_request._last_merge_source_rev
583 version._last_merge_source_rev = pull_request._last_merge_source_rev
584 version._last_merge_target_rev = pull_request._last_merge_target_rev
584 version._last_merge_target_rev = pull_request._last_merge_target_rev
585 version._last_merge_status = pull_request._last_merge_status
585 version._last_merge_status = pull_request._last_merge_status
586 version.merge_rev = pull_request.merge_rev
586 version.merge_rev = pull_request.merge_rev
587
587
588 version.revisions = pull_request.revisions
588 version.revisions = pull_request.revisions
589 version.pull_request = pull_request
589 version.pull_request = pull_request
590 Session().add(version)
590 Session().add(version)
591 Session().flush()
591 Session().flush()
592
592
593 return version
593 return version
594
594
595 def _generate_update_diffs(self, pull_request, pull_request_version):
595 def _generate_update_diffs(self, pull_request, pull_request_version):
596 diff_context = (
596 diff_context = (
597 self.DIFF_CONTEXT +
597 self.DIFF_CONTEXT +
598 ChangesetCommentsModel.needed_extra_diff_context())
598 ChangesetCommentsModel.needed_extra_diff_context())
599 old_diff = self._get_diff_from_pr_or_version(
599 old_diff = self._get_diff_from_pr_or_version(
600 pull_request_version, context=diff_context)
600 pull_request_version, context=diff_context)
601 new_diff = self._get_diff_from_pr_or_version(
601 new_diff = self._get_diff_from_pr_or_version(
602 pull_request, context=diff_context)
602 pull_request, context=diff_context)
603
603
604 old_diff_data = diffs.DiffProcessor(old_diff)
604 old_diff_data = diffs.DiffProcessor(old_diff)
605 old_diff_data.prepare()
605 old_diff_data.prepare()
606 new_diff_data = diffs.DiffProcessor(new_diff)
606 new_diff_data = diffs.DiffProcessor(new_diff)
607 new_diff_data.prepare()
607 new_diff_data.prepare()
608
608
609 return old_diff_data, new_diff_data
609 return old_diff_data, new_diff_data
610
610
611 def _link_comments_to_version(self, pull_request_version):
611 def _link_comments_to_version(self, pull_request_version):
612 """
612 """
613 Link all unlinked comments of this pull request to the given version.
613 Link all unlinked comments of this pull request to the given version.
614
614
615 :param pull_request_version: The `PullRequestVersion` to which
615 :param pull_request_version: The `PullRequestVersion` to which
616 the comments shall be linked.
616 the comments shall be linked.
617
617
618 """
618 """
619 pull_request = pull_request_version.pull_request
619 pull_request = pull_request_version.pull_request
620 comments = ChangesetComment.query().filter(
620 comments = ChangesetComment.query().filter(
621 # TODO: johbo: Should we query for the repo at all here?
621 # TODO: johbo: Should we query for the repo at all here?
622 # Pending decision on how comments of PRs are to be related
622 # Pending decision on how comments of PRs are to be related
623 # to either the source repo, the target repo or no repo at all.
623 # to either the source repo, the target repo or no repo at all.
624 ChangesetComment.repo_id == pull_request.target_repo.repo_id,
624 ChangesetComment.repo_id == pull_request.target_repo.repo_id,
625 ChangesetComment.pull_request == pull_request,
625 ChangesetComment.pull_request == pull_request,
626 ChangesetComment.pull_request_version == None)
626 ChangesetComment.pull_request_version == None)
627
627
628 # TODO: johbo: Find out why this breaks if it is done in a bulk
628 # TODO: johbo: Find out why this breaks if it is done in a bulk
629 # operation.
629 # operation.
630 for comment in comments:
630 for comment in comments:
631 comment.pull_request_version_id = (
631 comment.pull_request_version_id = (
632 pull_request_version.pull_request_version_id)
632 pull_request_version.pull_request_version_id)
633 Session().add(comment)
633 Session().add(comment)
634
634
635 def _calculate_commit_id_changes(self, old_ids, new_ids):
635 def _calculate_commit_id_changes(self, old_ids, new_ids):
636 added = new_ids.difference(old_ids)
636 added = new_ids.difference(old_ids)
637 common = old_ids.intersection(new_ids)
637 common = old_ids.intersection(new_ids)
638 removed = old_ids.difference(new_ids)
638 removed = old_ids.difference(new_ids)
639 return ChangeTuple(added, common, removed)
639 return ChangeTuple(added, common, removed)
640
640
641 def _calculate_file_changes(self, old_diff_data, new_diff_data):
641 def _calculate_file_changes(self, old_diff_data, new_diff_data):
642
642
643 old_files = OrderedDict()
643 old_files = OrderedDict()
644 for diff_data in old_diff_data.parsed_diff:
644 for diff_data in old_diff_data.parsed_diff:
645 old_files[diff_data['filename']] = md5_safe(diff_data['raw_diff'])
645 old_files[diff_data['filename']] = md5_safe(diff_data['raw_diff'])
646
646
647 added_files = []
647 added_files = []
648 modified_files = []
648 modified_files = []
649 removed_files = []
649 removed_files = []
650 for diff_data in new_diff_data.parsed_diff:
650 for diff_data in new_diff_data.parsed_diff:
651 new_filename = diff_data['filename']
651 new_filename = diff_data['filename']
652 new_hash = md5_safe(diff_data['raw_diff'])
652 new_hash = md5_safe(diff_data['raw_diff'])
653
653
654 old_hash = old_files.get(new_filename)
654 old_hash = old_files.get(new_filename)
655 if not old_hash:
655 if not old_hash:
656 # file is not present in old diff, means it's added
656 # file is not present in old diff, means it's added
657 added_files.append(new_filename)
657 added_files.append(new_filename)
658 else:
658 else:
659 if new_hash != old_hash:
659 if new_hash != old_hash:
660 modified_files.append(new_filename)
660 modified_files.append(new_filename)
661 # now remove a file from old, since we have seen it already
661 # now remove a file from old, since we have seen it already
662 del old_files[new_filename]
662 del old_files[new_filename]
663
663
664 # removed files is when there are present in old, but not in NEW,
664 # removed files is when there are present in old, but not in NEW,
665 # since we remove old files that are present in new diff, left-overs
665 # since we remove old files that are present in new diff, left-overs
666 # if any should be the removed files
666 # if any should be the removed files
667 removed_files.extend(old_files.keys())
667 removed_files.extend(old_files.keys())
668
668
669 return FileChangeTuple(added_files, modified_files, removed_files)
669 return FileChangeTuple(added_files, modified_files, removed_files)
670
670
671 def _render_update_message(self, changes, file_changes):
671 def _render_update_message(self, changes, file_changes):
672 """
672 """
673 render the message using DEFAULT_COMMENTS_RENDERER (RST renderer),
673 render the message using DEFAULT_COMMENTS_RENDERER (RST renderer),
674 so it's always looking the same disregarding on which default
674 so it's always looking the same disregarding on which default
675 renderer system is using.
675 renderer system is using.
676
676
677 :param changes: changes named tuple
677 :param changes: changes named tuple
678 :param file_changes: file changes named tuple
678 :param file_changes: file changes named tuple
679
679
680 """
680 """
681 new_status = ChangesetStatus.get_status_lbl(
681 new_status = ChangesetStatus.get_status_lbl(
682 ChangesetStatus.STATUS_UNDER_REVIEW)
682 ChangesetStatus.STATUS_UNDER_REVIEW)
683
683
684 changed_files = (
684 changed_files = (
685 file_changes.added + file_changes.modified + file_changes.removed)
685 file_changes.added + file_changes.modified + file_changes.removed)
686
686
687 params = {
687 params = {
688 'under_review_label': new_status,
688 'under_review_label': new_status,
689 'added_commits': changes.added,
689 'added_commits': changes.added,
690 'removed_commits': changes.removed,
690 'removed_commits': changes.removed,
691 'changed_files': changed_files,
691 'changed_files': changed_files,
692 'added_files': file_changes.added,
692 'added_files': file_changes.added,
693 'modified_files': file_changes.modified,
693 'modified_files': file_changes.modified,
694 'removed_files': file_changes.removed,
694 'removed_files': file_changes.removed,
695 }
695 }
696 renderer = RstTemplateRenderer()
696 renderer = RstTemplateRenderer()
697 return renderer.render('pull_request_update.mako', **params)
697 return renderer.render('pull_request_update.mako', **params)
698
698
699 def edit(self, pull_request, title, description):
699 def edit(self, pull_request, title, description):
700 pull_request = self.__get_pull_request(pull_request)
700 pull_request = self.__get_pull_request(pull_request)
701 if pull_request.is_closed():
701 if pull_request.is_closed():
702 raise ValueError('This pull request is closed')
702 raise ValueError('This pull request is closed')
703 if title:
703 if title:
704 pull_request.title = title
704 pull_request.title = title
705 pull_request.description = description
705 pull_request.description = description
706 pull_request.updated_on = datetime.datetime.now()
706 pull_request.updated_on = datetime.datetime.now()
707 Session().add(pull_request)
707 Session().add(pull_request)
708
708
709 def update_reviewers(self, pull_request, reviewers_ids):
709 def update_reviewers(self, pull_request, reviewers_ids):
710 reviewers_ids = set(reviewers_ids)
710 reviewers_ids = set(reviewers_ids)
711 pull_request = self.__get_pull_request(pull_request)
711 pull_request = self.__get_pull_request(pull_request)
712 current_reviewers = PullRequestReviewers.query()\
712 current_reviewers = PullRequestReviewers.query()\
713 .filter(PullRequestReviewers.pull_request ==
713 .filter(PullRequestReviewers.pull_request ==
714 pull_request).all()
714 pull_request).all()
715 current_reviewers_ids = set([x.user.user_id for x in current_reviewers])
715 current_reviewers_ids = set([x.user.user_id for x in current_reviewers])
716
716
717 ids_to_add = reviewers_ids.difference(current_reviewers_ids)
717 ids_to_add = reviewers_ids.difference(current_reviewers_ids)
718 ids_to_remove = current_reviewers_ids.difference(reviewers_ids)
718 ids_to_remove = current_reviewers_ids.difference(reviewers_ids)
719
719
720 log.debug("Adding %s reviewers", ids_to_add)
720 log.debug("Adding %s reviewers", ids_to_add)
721 log.debug("Removing %s reviewers", ids_to_remove)
721 log.debug("Removing %s reviewers", ids_to_remove)
722 changed = False
722 changed = False
723 for uid in ids_to_add:
723 for uid in ids_to_add:
724 changed = True
724 changed = True
725 _usr = self._get_user(uid)
725 _usr = self._get_user(uid)
726 reviewer = PullRequestReviewers(_usr, pull_request)
726 reviewer = PullRequestReviewers(_usr, pull_request)
727 Session().add(reviewer)
727 Session().add(reviewer)
728
728
729 self.notify_reviewers(pull_request, ids_to_add)
729 self.notify_reviewers(pull_request, ids_to_add)
730
730
731 for uid in ids_to_remove:
731 for uid in ids_to_remove:
732 changed = True
732 changed = True
733 reviewer = PullRequestReviewers.query()\
733 reviewer = PullRequestReviewers.query()\
734 .filter(PullRequestReviewers.user_id == uid,
734 .filter(PullRequestReviewers.user_id == uid,
735 PullRequestReviewers.pull_request == pull_request)\
735 PullRequestReviewers.pull_request == pull_request)\
736 .scalar()
736 .scalar()
737 if reviewer:
737 if reviewer:
738 Session().delete(reviewer)
738 Session().delete(reviewer)
739 if changed:
739 if changed:
740 pull_request.updated_on = datetime.datetime.now()
740 pull_request.updated_on = datetime.datetime.now()
741 Session().add(pull_request)
741 Session().add(pull_request)
742
742
743 return ids_to_add, ids_to_remove
743 return ids_to_add, ids_to_remove
744
744
745 def notify_reviewers(self, pull_request, reviewers_ids):
745 def notify_reviewers(self, pull_request, reviewers_ids):
746 # notification to reviewers
746 # notification to reviewers
747 if not reviewers_ids:
747 if not reviewers_ids:
748 return
748 return
749
749
750 pull_request_obj = pull_request
750 pull_request_obj = pull_request
751 # get the current participants of this pull request
751 # get the current participants of this pull request
752 recipients = reviewers_ids
752 recipients = reviewers_ids
753 notification_type = EmailNotificationModel.TYPE_PULL_REQUEST
753 notification_type = EmailNotificationModel.TYPE_PULL_REQUEST
754
754
755 pr_source_repo = pull_request_obj.source_repo
755 pr_source_repo = pull_request_obj.source_repo
756 pr_target_repo = pull_request_obj.target_repo
756 pr_target_repo = pull_request_obj.target_repo
757
757
758 pr_url = h.url(
758 pr_url = h.url(
759 'pullrequest_show',
759 'pullrequest_show',
760 repo_name=pr_target_repo.repo_name,
760 repo_name=pr_target_repo.repo_name,
761 pull_request_id=pull_request_obj.pull_request_id,
761 pull_request_id=pull_request_obj.pull_request_id,
762 qualified=True,)
762 qualified=True,)
763
763
764 # set some variables for email notification
764 # set some variables for email notification
765 pr_target_repo_url = h.url(
765 pr_target_repo_url = h.url(
766 'summary_home',
766 'summary_home',
767 repo_name=pr_target_repo.repo_name,
767 repo_name=pr_target_repo.repo_name,
768 qualified=True)
768 qualified=True)
769
769
770 pr_source_repo_url = h.url(
770 pr_source_repo_url = h.url(
771 'summary_home',
771 'summary_home',
772 repo_name=pr_source_repo.repo_name,
772 repo_name=pr_source_repo.repo_name,
773 qualified=True)
773 qualified=True)
774
774
775 # pull request specifics
775 # pull request specifics
776 pull_request_commits = [
776 pull_request_commits = [
777 (x.raw_id, x.message)
777 (x.raw_id, x.message)
778 for x in map(pr_source_repo.get_commit, pull_request.revisions)]
778 for x in map(pr_source_repo.get_commit, pull_request.revisions)]
779
779
780 kwargs = {
780 kwargs = {
781 'user': pull_request.author,
781 'user': pull_request.author,
782 'pull_request': pull_request_obj,
782 'pull_request': pull_request_obj,
783 'pull_request_commits': pull_request_commits,
783 'pull_request_commits': pull_request_commits,
784
784
785 'pull_request_target_repo': pr_target_repo,
785 'pull_request_target_repo': pr_target_repo,
786 'pull_request_target_repo_url': pr_target_repo_url,
786 'pull_request_target_repo_url': pr_target_repo_url,
787
787
788 'pull_request_source_repo': pr_source_repo,
788 'pull_request_source_repo': pr_source_repo,
789 'pull_request_source_repo_url': pr_source_repo_url,
789 'pull_request_source_repo_url': pr_source_repo_url,
790
790
791 'pull_request_url': pr_url,
791 'pull_request_url': pr_url,
792 }
792 }
793
793
794 # pre-generate the subject for notification itself
794 # pre-generate the subject for notification itself
795 (subject,
795 (subject,
796 _h, _e, # we don't care about those
796 _h, _e, # we don't care about those
797 body_plaintext) = EmailNotificationModel().render_email(
797 body_plaintext) = EmailNotificationModel().render_email(
798 notification_type, **kwargs)
798 notification_type, **kwargs)
799
799
800 # create notification objects, and emails
800 # create notification objects, and emails
801 NotificationModel().create(
801 NotificationModel().create(
802 created_by=pull_request.author,
802 created_by=pull_request.author,
803 notification_subject=subject,
803 notification_subject=subject,
804 notification_body=body_plaintext,
804 notification_body=body_plaintext,
805 notification_type=notification_type,
805 notification_type=notification_type,
806 recipients=recipients,
806 recipients=recipients,
807 email_kwargs=kwargs,
807 email_kwargs=kwargs,
808 )
808 )
809
809
810 def delete(self, pull_request):
810 def delete(self, pull_request):
811 pull_request = self.__get_pull_request(pull_request)
811 pull_request = self.__get_pull_request(pull_request)
812 self._cleanup_merge_workspace(pull_request)
812 self._cleanup_merge_workspace(pull_request)
813 Session().delete(pull_request)
813 Session().delete(pull_request)
814
814
815 def close_pull_request(self, pull_request, user):
815 def close_pull_request(self, pull_request, user):
816 pull_request = self.__get_pull_request(pull_request)
816 pull_request = self.__get_pull_request(pull_request)
817 self._cleanup_merge_workspace(pull_request)
817 self._cleanup_merge_workspace(pull_request)
818 pull_request.status = PullRequest.STATUS_CLOSED
818 pull_request.status = PullRequest.STATUS_CLOSED
819 pull_request.updated_on = datetime.datetime.now()
819 pull_request.updated_on = datetime.datetime.now()
820 Session().add(pull_request)
820 Session().add(pull_request)
821 self._trigger_pull_request_hook(
821 self._trigger_pull_request_hook(
822 pull_request, pull_request.author, 'close')
822 pull_request, pull_request.author, 'close')
823 self._log_action('user_closed_pull_request', user, pull_request)
823 self._log_action('user_closed_pull_request', user, pull_request)
824
824
825 def close_pull_request_with_comment(self, pull_request, user, repo,
825 def close_pull_request_with_comment(self, pull_request, user, repo,
826 message=None):
826 message=None):
827 status = ChangesetStatus.STATUS_REJECTED
827 status = ChangesetStatus.STATUS_REJECTED
828
828
829 if not message:
829 if not message:
830 message = (
830 message = (
831 _('Status change %(transition_icon)s %(status)s') % {
831 _('Status change %(transition_icon)s %(status)s') % {
832 'transition_icon': '>',
832 'transition_icon': '>',
833 'status': ChangesetStatus.get_status_lbl(status)})
833 'status': ChangesetStatus.get_status_lbl(status)})
834
834
835 internal_message = _('Closing with') + ' ' + message
835 internal_message = _('Closing with') + ' ' + message
836
836
837 comm = ChangesetCommentsModel().create(
837 comm = ChangesetCommentsModel().create(
838 text=internal_message,
838 text=internal_message,
839 repo=repo.repo_id,
839 repo=repo.repo_id,
840 user=user.user_id,
840 user=user.user_id,
841 pull_request=pull_request.pull_request_id,
841 pull_request=pull_request.pull_request_id,
842 f_path=None,
842 f_path=None,
843 line_no=None,
843 line_no=None,
844 status_change=ChangesetStatus.get_status_lbl(status),
844 status_change=ChangesetStatus.get_status_lbl(status),
845 closing_pr=True
845 closing_pr=True
846 )
846 )
847
847
848 ChangesetStatusModel().set_status(
848 ChangesetStatusModel().set_status(
849 repo.repo_id,
849 repo.repo_id,
850 status,
850 status,
851 user.user_id,
851 user.user_id,
852 comm,
852 comm,
853 pull_request=pull_request.pull_request_id
853 pull_request=pull_request.pull_request_id
854 )
854 )
855 Session().flush()
855 Session().flush()
856
856
857 PullRequestModel().close_pull_request(
857 PullRequestModel().close_pull_request(
858 pull_request.pull_request_id, user)
858 pull_request.pull_request_id, user)
859
859
860 def merge_status(self, pull_request):
860 def merge_status(self, pull_request):
861 if not self._is_merge_enabled(pull_request):
861 if not self._is_merge_enabled(pull_request):
862 return False, _('Server-side pull request merging is disabled.')
862 return False, _('Server-side pull request merging is disabled.')
863 if pull_request.is_closed():
863 if pull_request.is_closed():
864 return False, _('This pull request is closed.')
864 return False, _('This pull request is closed.')
865 merge_possible, msg = self._check_repo_requirements(
865 merge_possible, msg = self._check_repo_requirements(
866 target=pull_request.target_repo, source=pull_request.source_repo)
866 target=pull_request.target_repo, source=pull_request.source_repo)
867 if not merge_possible:
867 if not merge_possible:
868 return merge_possible, msg
868 return merge_possible, msg
869
869
870 try:
870 try:
871 resp = self._try_merge(pull_request)
871 resp = self._try_merge(pull_request)
872 status = resp.possible, self.merge_status_message(
872 status = resp.possible, self.merge_status_message(
873 resp.failure_reason)
873 resp.failure_reason)
874 except NotImplementedError:
874 except NotImplementedError:
875 status = False, _('Pull request merging is not supported.')
875 status = False, _('Pull request merging is not supported.')
876
876
877 return status
877 return status
878
878
879 def _check_repo_requirements(self, target, source):
879 def _check_repo_requirements(self, target, source):
880 """
880 """
881 Check if `target` and `source` have compatible requirements.
881 Check if `target` and `source` have compatible requirements.
882
882
883 Currently this is just checking for largefiles.
883 Currently this is just checking for largefiles.
884 """
884 """
885 target_has_largefiles = self._has_largefiles(target)
885 target_has_largefiles = self._has_largefiles(target)
886 source_has_largefiles = self._has_largefiles(source)
886 source_has_largefiles = self._has_largefiles(source)
887 merge_possible = True
887 merge_possible = True
888 message = u''
888 message = u''
889
889
890 if target_has_largefiles != source_has_largefiles:
890 if target_has_largefiles != source_has_largefiles:
891 merge_possible = False
891 merge_possible = False
892 if source_has_largefiles:
892 if source_has_largefiles:
893 message = _(
893 message = _(
894 'Target repository large files support is disabled.')
894 'Target repository large files support is disabled.')
895 else:
895 else:
896 message = _(
896 message = _(
897 'Source repository large files support is disabled.')
897 'Source repository large files support is disabled.')
898
898
899 return merge_possible, message
899 return merge_possible, message
900
900
901 def _has_largefiles(self, repo):
901 def _has_largefiles(self, repo):
902 largefiles_ui = VcsSettingsModel(repo=repo).get_ui_settings(
902 largefiles_ui = VcsSettingsModel(repo=repo).get_ui_settings(
903 'extensions', 'largefiles')
903 'extensions', 'largefiles')
904 return largefiles_ui and largefiles_ui[0].active
904 return largefiles_ui and largefiles_ui[0].active
905
905
906 def _try_merge(self, pull_request):
906 def _try_merge(self, pull_request):
907 """
907 """
908 Try to merge the pull request and return the merge status.
908 Try to merge the pull request and return the merge status.
909 """
909 """
910 target_vcs = pull_request.target_repo.scm_instance()
910 target_vcs = pull_request.target_repo.scm_instance()
911 target_ref = self._refresh_reference(
911 target_ref = self._refresh_reference(
912 pull_request.target_ref_parts, target_vcs)
912 pull_request.target_ref_parts, target_vcs)
913
913
914 target_locked = pull_request.target_repo.locked
914 target_locked = pull_request.target_repo.locked
915 if target_locked and target_locked[0]:
915 if target_locked and target_locked[0]:
916 merge_state = MergeResponse(
916 merge_state = MergeResponse(
917 False, False, None, MergeFailureReason.TARGET_IS_LOCKED)
917 False, False, None, MergeFailureReason.TARGET_IS_LOCKED)
918 elif self._needs_merge_state_refresh(pull_request, target_ref):
918 elif self._needs_merge_state_refresh(pull_request, target_ref):
919 merge_state = self._refresh_merge_state(
919 merge_state = self._refresh_merge_state(
920 pull_request, target_vcs, target_ref)
920 pull_request, target_vcs, target_ref)
921 else:
921 else:
922 possible = pull_request.\
922 possible = pull_request.\
923 _last_merge_status == MergeFailureReason.NONE
923 _last_merge_status == MergeFailureReason.NONE
924 merge_state = MergeResponse(
924 merge_state = MergeResponse(
925 possible, False, None, pull_request._last_merge_status)
925 possible, False, None, pull_request._last_merge_status)
926 return merge_state
926 return merge_state
927
927
928 def _refresh_reference(self, reference, vcs_repository):
928 def _refresh_reference(self, reference, vcs_repository):
929 if reference.type in ('branch', 'book'):
929 if reference.type in ('branch', 'book'):
930 name_or_id = reference.name
930 name_or_id = reference.name
931 else:
931 else:
932 name_or_id = reference.commit_id
932 name_or_id = reference.commit_id
933 refreshed_commit = vcs_repository.get_commit(name_or_id)
933 refreshed_commit = vcs_repository.get_commit(name_or_id)
934 refreshed_reference = Reference(
934 refreshed_reference = Reference(
935 reference.type, reference.name, refreshed_commit.raw_id)
935 reference.type, reference.name, refreshed_commit.raw_id)
936 return refreshed_reference
936 return refreshed_reference
937
937
938 def _needs_merge_state_refresh(self, pull_request, target_reference):
938 def _needs_merge_state_refresh(self, pull_request, target_reference):
939 return not(
939 return not(
940 pull_request.revisions and
940 pull_request.revisions and
941 pull_request.revisions[0] == pull_request._last_merge_source_rev and
941 pull_request.revisions[0] == pull_request._last_merge_source_rev and
942 target_reference.commit_id == pull_request._last_merge_target_rev)
942 target_reference.commit_id == pull_request._last_merge_target_rev)
943
943
944 def _refresh_merge_state(self, pull_request, target_vcs, target_reference):
944 def _refresh_merge_state(self, pull_request, target_vcs, target_reference):
945 workspace_id = self._workspace_id(pull_request)
945 workspace_id = self._workspace_id(pull_request)
946 source_vcs = pull_request.source_repo.scm_instance()
946 source_vcs = pull_request.source_repo.scm_instance()
947 merge_state = target_vcs.merge(
947 merge_state = target_vcs.merge(
948 target_reference, source_vcs, pull_request.source_ref_parts,
948 target_reference, source_vcs, pull_request.source_ref_parts,
949 workspace_id, dry_run=True)
949 workspace_id, dry_run=True)
950
950
951 # Do not store the response if there was an unknown error.
951 # Do not store the response if there was an unknown error.
952 if merge_state.failure_reason != MergeFailureReason.UNKNOWN:
952 if merge_state.failure_reason != MergeFailureReason.UNKNOWN:
953 pull_request._last_merge_source_rev = pull_request.\
953 pull_request._last_merge_source_rev = pull_request.\
954 source_ref_parts.commit_id
954 source_ref_parts.commit_id
955 pull_request._last_merge_target_rev = target_reference.commit_id
955 pull_request._last_merge_target_rev = target_reference.commit_id
956 pull_request._last_merge_status = (
956 pull_request._last_merge_status = (
957 merge_state.failure_reason)
957 merge_state.failure_reason)
958 Session().add(pull_request)
958 Session().add(pull_request)
959 Session().flush()
959 Session().flush()
960
960
961 return merge_state
961 return merge_state
962
962
963 def _workspace_id(self, pull_request):
963 def _workspace_id(self, pull_request):
964 workspace_id = 'pr-%s' % pull_request.pull_request_id
964 workspace_id = 'pr-%s' % pull_request.pull_request_id
965 return workspace_id
965 return workspace_id
966
966
967 def merge_status_message(self, status_code):
967 def merge_status_message(self, status_code):
968 """
968 """
969 Return a human friendly error message for the given merge status code.
969 Return a human friendly error message for the given merge status code.
970 """
970 """
971 return self.MERGE_STATUS_MESSAGES[status_code]
971 return self.MERGE_STATUS_MESSAGES[status_code]
972
972
973 def generate_repo_data(self, repo, commit_id=None, branch=None,
973 def generate_repo_data(self, repo, commit_id=None, branch=None,
974 bookmark=None):
974 bookmark=None):
975 all_refs, selected_ref = \
975 all_refs, selected_ref = \
976 self._get_repo_pullrequest_sources(
976 self._get_repo_pullrequest_sources(
977 repo.scm_instance(), commit_id=commit_id,
977 repo.scm_instance(), commit_id=commit_id,
978 branch=branch, bookmark=bookmark)
978 branch=branch, bookmark=bookmark)
979
979
980 refs_select2 = []
980 refs_select2 = []
981 for element in all_refs:
981 for element in all_refs:
982 children = [{'id': x[0], 'text': x[1]} for x in element[0]]
982 children = [{'id': x[0], 'text': x[1]} for x in element[0]]
983 refs_select2.append({'text': element[1], 'children': children})
983 refs_select2.append({'text': element[1], 'children': children})
984
984
985 return {
985 return {
986 'user': {
986 'user': {
987 'user_id': repo.user.user_id,
987 'user_id': repo.user.user_id,
988 'username': repo.user.username,
988 'username': repo.user.username,
989 'firstname': repo.user.firstname,
989 'firstname': repo.user.firstname,
990 'lastname': repo.user.lastname,
990 'lastname': repo.user.lastname,
991 'gravatar_link': h.gravatar_url(repo.user.email, 14),
991 'gravatar_link': h.gravatar_url(repo.user.email, 14),
992 },
992 },
993 'description': h.chop_at_smart(repo.description, '\n'),
993 'description': h.chop_at_smart(repo.description, '\n'),
994 'refs': {
994 'refs': {
995 'all_refs': all_refs,
995 'all_refs': all_refs,
996 'selected_ref': selected_ref,
996 'selected_ref': selected_ref,
997 'select2_refs': refs_select2
997 'select2_refs': refs_select2
998 }
998 }
999 }
999 }
1000
1000
1001 def generate_pullrequest_title(self, source, source_ref, target):
1001 def generate_pullrequest_title(self, source, source_ref, target):
1002 return '{source}#{at_ref} to {target}'.format(
1002 return '{source}#{at_ref} to {target}'.format(
1003 source=source,
1003 source=source,
1004 at_ref=source_ref,
1004 at_ref=source_ref,
1005 target=target,
1005 target=target,
1006 )
1006 )
1007
1007
1008 def _cleanup_merge_workspace(self, pull_request):
1008 def _cleanup_merge_workspace(self, pull_request):
1009 # Merging related cleanup
1009 # Merging related cleanup
1010 target_scm = pull_request.target_repo.scm_instance()
1010 target_scm = pull_request.target_repo.scm_instance()
1011 workspace_id = 'pr-%s' % pull_request.pull_request_id
1011 workspace_id = 'pr-%s' % pull_request.pull_request_id
1012
1012
1013 try:
1013 try:
1014 target_scm.cleanup_merge_workspace(workspace_id)
1014 target_scm.cleanup_merge_workspace(workspace_id)
1015 except NotImplementedError:
1015 except NotImplementedError:
1016 pass
1016 pass
1017
1017
1018 def _get_repo_pullrequest_sources(
1018 def _get_repo_pullrequest_sources(
1019 self, repo, commit_id=None, branch=None, bookmark=None):
1019 self, repo, commit_id=None, branch=None, bookmark=None):
1020 """
1020 """
1021 Return a structure with repo's interesting commits, suitable for
1021 Return a structure with repo's interesting commits, suitable for
1022 the selectors in pullrequest controller
1022 the selectors in pullrequest controller
1023
1023
1024 :param commit_id: a commit that must be in the list somehow
1024 :param commit_id: a commit that must be in the list somehow
1025 and selected by default
1025 and selected by default
1026 :param branch: a branch that must be in the list and selected
1026 :param branch: a branch that must be in the list and selected
1027 by default - even if closed
1027 by default - even if closed
1028 :param bookmark: a bookmark that must be in the list and selected
1028 :param bookmark: a bookmark that must be in the list and selected
1029 """
1029 """
1030
1030
1031 commit_id = safe_str(commit_id) if commit_id else None
1031 commit_id = safe_str(commit_id) if commit_id else None
1032 branch = safe_str(branch) if branch else None
1032 branch = safe_str(branch) if branch else None
1033 bookmark = safe_str(bookmark) if bookmark else None
1033 bookmark = safe_str(bookmark) if bookmark else None
1034
1034
1035 selected = None
1035 selected = None
1036
1036
1037 # order matters: first source that has commit_id in it will be selected
1037 # order matters: first source that has commit_id in it will be selected
1038 sources = []
1038 sources = []
1039 sources.append(('book', repo.bookmarks.items(), _('Bookmarks'), bookmark))
1039 sources.append(('book', repo.bookmarks.items(), _('Bookmarks'), bookmark))
1040 sources.append(('branch', repo.branches.items(), _('Branches'), branch))
1040 sources.append(('branch', repo.branches.items(), _('Branches'), branch))
1041
1041
1042 if commit_id:
1042 if commit_id:
1043 ref_commit = (h.short_id(commit_id), commit_id)
1043 ref_commit = (h.short_id(commit_id), commit_id)
1044 sources.append(('rev', [ref_commit], _('Commit IDs'), commit_id))
1044 sources.append(('rev', [ref_commit], _('Commit IDs'), commit_id))
1045
1045
1046 sources.append(
1046 sources.append(
1047 ('branch', repo.branches_closed.items(), _('Closed Branches'), branch),
1047 ('branch', repo.branches_closed.items(), _('Closed Branches'), branch),
1048 )
1048 )
1049
1049
1050 groups = []
1050 groups = []
1051 for group_key, ref_list, group_name, match in sources:
1051 for group_key, ref_list, group_name, match in sources:
1052 group_refs = []
1052 group_refs = []
1053 for ref_name, ref_id in ref_list:
1053 for ref_name, ref_id in ref_list:
1054 ref_key = '%s:%s:%s' % (group_key, ref_name, ref_id)
1054 ref_key = '%s:%s:%s' % (group_key, ref_name, ref_id)
1055 group_refs.append((ref_key, ref_name))
1055 group_refs.append((ref_key, ref_name))
1056
1056
1057 if not selected and match in (ref_id, ref_name):
1057 if not selected:
1058 selected = ref_key
1058 if set([commit_id, match]) & set([ref_id, ref_name]):
1059 selected = ref_key
1060
1059 if group_refs:
1061 if group_refs:
1060 groups.append((group_refs, group_name))
1062 groups.append((group_refs, group_name))
1061
1063
1062 if not selected:
1064 if not selected:
1063 ref = commit_id or branch or bookmark
1065 ref = commit_id or branch or bookmark
1064 if ref:
1066 if ref:
1065 raise CommitDoesNotExistError(
1067 raise CommitDoesNotExistError(
1066 'No commit refs could be found matching: %s' % ref)
1068 'No commit refs could be found matching: %s' % ref)
1067 elif repo.DEFAULT_BRANCH_NAME in repo.branches:
1069 elif repo.DEFAULT_BRANCH_NAME in repo.branches:
1068 selected = 'branch:%s:%s' % (
1070 selected = 'branch:%s:%s' % (
1069 repo.DEFAULT_BRANCH_NAME,
1071 repo.DEFAULT_BRANCH_NAME,
1070 repo.branches[repo.DEFAULT_BRANCH_NAME]
1072 repo.branches[repo.DEFAULT_BRANCH_NAME]
1071 )
1073 )
1072 elif repo.commit_ids:
1074 elif repo.commit_ids:
1073 rev = repo.commit_ids[0]
1075 rev = repo.commit_ids[0]
1074 selected = 'rev:%s:%s' % (rev, rev)
1076 selected = 'rev:%s:%s' % (rev, rev)
1075 else:
1077 else:
1076 raise EmptyRepositoryError()
1078 raise EmptyRepositoryError()
1077 return groups, selected
1079 return groups, selected
1078
1080
1079 def get_diff(self, pull_request, context=DIFF_CONTEXT):
1081 def get_diff(self, pull_request, context=DIFF_CONTEXT):
1080 pull_request = self.__get_pull_request(pull_request)
1082 pull_request = self.__get_pull_request(pull_request)
1081 return self._get_diff_from_pr_or_version(pull_request, context=context)
1083 return self._get_diff_from_pr_or_version(pull_request, context=context)
1082
1084
1083 def _get_diff_from_pr_or_version(self, pr_or_version, context):
1085 def _get_diff_from_pr_or_version(self, pr_or_version, context):
1084 source_repo = pr_or_version.source_repo
1086 source_repo = pr_or_version.source_repo
1085
1087
1086 # we swap org/other ref since we run a simple diff on one repo
1088 # we swap org/other ref since we run a simple diff on one repo
1087 target_ref_id = pr_or_version.target_ref_parts.commit_id
1089 target_ref_id = pr_or_version.target_ref_parts.commit_id
1088 source_ref_id = pr_or_version.source_ref_parts.commit_id
1090 source_ref_id = pr_or_version.source_ref_parts.commit_id
1089 target_commit = source_repo.get_commit(
1091 target_commit = source_repo.get_commit(
1090 commit_id=safe_str(target_ref_id))
1092 commit_id=safe_str(target_ref_id))
1091 source_commit = source_repo.get_commit(commit_id=safe_str(source_ref_id))
1093 source_commit = source_repo.get_commit(commit_id=safe_str(source_ref_id))
1092 vcs_repo = source_repo.scm_instance()
1094 vcs_repo = source_repo.scm_instance()
1093
1095
1094 # TODO: johbo: In the context of an update, we cannot reach
1096 # TODO: johbo: In the context of an update, we cannot reach
1095 # the old commit anymore with our normal mechanisms. It needs
1097 # the old commit anymore with our normal mechanisms. It needs
1096 # some sort of special support in the vcs layer to avoid this
1098 # some sort of special support in the vcs layer to avoid this
1097 # workaround.
1099 # workaround.
1098 if (source_commit.raw_id == vcs_repo.EMPTY_COMMIT_ID and
1100 if (source_commit.raw_id == vcs_repo.EMPTY_COMMIT_ID and
1099 vcs_repo.alias == 'git'):
1101 vcs_repo.alias == 'git'):
1100 source_commit.raw_id = safe_str(source_ref_id)
1102 source_commit.raw_id = safe_str(source_ref_id)
1101
1103
1102 log.debug('calculating diff between '
1104 log.debug('calculating diff between '
1103 'source_ref:%s and target_ref:%s for repo `%s`',
1105 'source_ref:%s and target_ref:%s for repo `%s`',
1104 target_ref_id, source_ref_id,
1106 target_ref_id, source_ref_id,
1105 safe_unicode(vcs_repo.path))
1107 safe_unicode(vcs_repo.path))
1106
1108
1107 vcs_diff = vcs_repo.get_diff(
1109 vcs_diff = vcs_repo.get_diff(
1108 commit1=target_commit, commit2=source_commit, context=context)
1110 commit1=target_commit, commit2=source_commit, context=context)
1109 return vcs_diff
1111 return vcs_diff
1110
1112
1111 def _is_merge_enabled(self, pull_request):
1113 def _is_merge_enabled(self, pull_request):
1112 settings_model = VcsSettingsModel(repo=pull_request.target_repo)
1114 settings_model = VcsSettingsModel(repo=pull_request.target_repo)
1113 settings = settings_model.get_general_settings()
1115 settings = settings_model.get_general_settings()
1114 return settings.get('rhodecode_pr_merge_enabled', False)
1116 return settings.get('rhodecode_pr_merge_enabled', False)
1115
1117
1116 def _log_action(self, action, user, pull_request):
1118 def _log_action(self, action, user, pull_request):
1117 action_logger(
1119 action_logger(
1118 user,
1120 user,
1119 '{action}:{pr_id}'.format(
1121 '{action}:{pr_id}'.format(
1120 action=action, pr_id=pull_request.pull_request_id),
1122 action=action, pr_id=pull_request.pull_request_id),
1121 pull_request.target_repo)
1123 pull_request.target_repo)
1122
1124
1123
1125
1124 ChangeTuple = namedtuple('ChangeTuple',
1126 ChangeTuple = namedtuple('ChangeTuple',
1125 ['added', 'common', 'removed'])
1127 ['added', 'common', 'removed'])
1126
1128
1127 FileChangeTuple = namedtuple('FileChangeTuple',
1129 FileChangeTuple = namedtuple('FileChangeTuple',
1128 ['added', 'modified', 'removed'])
1130 ['added', 'modified', 'removed'])
@@ -1,600 +1,605 b''
1 // Default styles
1 // Default styles
2
2
3 .diff-collapse {
3 .diff-collapse {
4 margin: @padding 0;
4 margin: @padding 0;
5 text-align: right;
5 text-align: right;
6 }
6 }
7
7
8 .diff-container {
8 .diff-container {
9 margin-bottom: @space;
9 margin-bottom: @space;
10
10
11 .diffblock {
11 .diffblock {
12 margin-bottom: @space;
12 margin-bottom: @space;
13 }
13 }
14
14
15 &.hidden {
15 &.hidden {
16 display: none;
16 display: none;
17 overflow: hidden;
17 overflow: hidden;
18 }
18 }
19 }
19 }
20
20
21 .compare_view_files {
21 .compare_view_files {
22
22
23 .diff-container {
23 .diff-container {
24
24
25 .diffblock {
25 .diffblock {
26 margin-bottom: 0;
26 margin-bottom: 0;
27 }
27 }
28 }
28 }
29 }
29 }
30
30
31 div.diffblock .sidebyside {
31 div.diffblock .sidebyside {
32 background: #ffffff;
32 background: #ffffff;
33 }
33 }
34
34
35 div.diffblock {
35 div.diffblock {
36 overflow-x: auto;
36 overflow-x: auto;
37 overflow-y: hidden;
37 overflow-y: hidden;
38 clear: both;
38 clear: both;
39 padding: 0px;
39 padding: 0px;
40 background: @grey6;
40 background: @grey6;
41 border: @border-thickness solid @grey5;
41 border: @border-thickness solid @grey5;
42 -webkit-border-radius: @border-radius @border-radius 0px 0px;
42 -webkit-border-radius: @border-radius @border-radius 0px 0px;
43 border-radius: @border-radius @border-radius 0px 0px;
43 border-radius: @border-radius @border-radius 0px 0px;
44
44
45
45
46 .comments-number {
46 .comments-number {
47 float: right;
47 float: right;
48 }
48 }
49
49
50 // BEGIN CODE-HEADER STYLES
50 // BEGIN CODE-HEADER STYLES
51
51
52 .code-header {
52 .code-header {
53 background: @grey6;
53 background: @grey6;
54 padding: 10px 0 10px 0;
54 padding: 10px 0 10px 0;
55 height: auto;
55 height: auto;
56 width: 100%;
56 width: 100%;
57
57
58 .hash {
58 .hash {
59 float: left;
59 float: left;
60 padding: 2px 0 0 2px;
60 padding: 2px 0 0 2px;
61 }
61 }
62
62
63 .date {
63 .date {
64 float: left;
64 float: left;
65 text-transform: uppercase;
65 text-transform: uppercase;
66 padding: 4px 0px 0px 2px;
66 padding: 4px 0px 0px 2px;
67 }
67 }
68
68
69 div {
69 div {
70 margin-left: 4px;
70 margin-left: 4px;
71 }
71 }
72
72
73 div.compare_header {
73 div.compare_header {
74 min-height: 40px;
74 min-height: 40px;
75 margin: 0;
75 margin: 0;
76 padding: 0 @padding;
76 padding: 0 @padding;
77
77
78 .drop-menu {
78 .drop-menu {
79 float:left;
79 float:left;
80 display: block;
80 display: block;
81 margin:0 0 @padding 0;
81 margin:0 0 @padding 0;
82 }
82 }
83
83
84 .compare-label {
84 .compare-label {
85 float: left;
85 float: left;
86 clear: both;
86 clear: both;
87 display: inline-block;
87 display: inline-block;
88 min-width: 5em;
88 min-width: 5em;
89 margin: 0;
89 margin: 0;
90 padding: @button-padding @button-padding @button-padding 0;
90 padding: @button-padding @button-padding @button-padding 0;
91 font-family: @text-semibold;
91 font-family: @text-semibold;
92 }
92 }
93
93
94 .compare-buttons {
94 .compare-buttons {
95 float: left;
95 float: left;
96 margin: 0;
96 margin: 0;
97 padding: 0 0 @padding;
97 padding: 0 0 @padding;
98
98
99 .btn {
99 .btn {
100 margin: 0 @padding 0 0;
100 margin: 0 @padding 0 0;
101 }
101 }
102 }
102 }
103 }
103 }
104
104
105 }
105 }
106
106
107 .parents {
107 .parents {
108 float: left;
108 float: left;
109 width: 100px;
109 width: 100px;
110 font-weight: 400;
110 font-weight: 400;
111 vertical-align: middle;
111 vertical-align: middle;
112 padding: 0px 2px 0px 2px;
112 padding: 0px 2px 0px 2px;
113 background-color: @grey6;
113 background-color: @grey6;
114
114
115 #parent_link {
115 #parent_link {
116 margin: 00px 2px;
116 margin: 00px 2px;
117
117
118 &.double {
118 &.double {
119 margin: 0px 2px;
119 margin: 0px 2px;
120 }
120 }
121
121
122 &.disabled{
122 &.disabled{
123 margin-right: @padding;
123 margin-right: @padding;
124 }
124 }
125 }
125 }
126 }
126 }
127
127
128 .children {
128 .children {
129 float: right;
129 float: right;
130 width: 100px;
130 width: 100px;
131 font-weight: 400;
131 font-weight: 400;
132 vertical-align: middle;
132 vertical-align: middle;
133 text-align: right;
133 text-align: right;
134 padding: 0px 2px 0px 2px;
134 padding: 0px 2px 0px 2px;
135 background-color: @grey6;
135 background-color: @grey6;
136
136
137 #child_link {
137 #child_link {
138 margin: 0px 2px;
138 margin: 0px 2px;
139
139
140 &.double {
140 &.double {
141 margin: 0px 2px;
141 margin: 0px 2px;
142 }
142 }
143
143
144 &.disabled{
144 &.disabled{
145 margin-right: @padding;
145 margin-right: @padding;
146 }
146 }
147 }
147 }
148 }
148 }
149
149
150 .changeset_header {
150 .changeset_header {
151 height: 16px;
151 height: 16px;
152
152
153 & > div{
153 & > div{
154 margin-right: @padding;
154 margin-right: @padding;
155 }
155 }
156 }
156 }
157
157
158 .changeset_file {
158 .changeset_file {
159 text-align: left;
159 text-align: left;
160 float: left;
160 float: left;
161 padding: 0;
161 padding: 0;
162
162
163 a{
163 a{
164 display: inline-block;
164 display: inline-block;
165 margin-right: 0.5em;
165 margin-right: 0.5em;
166 }
166 }
167
167
168 #selected_mode{
168 #selected_mode{
169 margin-left: 0;
169 margin-left: 0;
170 }
170 }
171 }
171 }
172
172
173 .diff-menu-wrapper {
173 .diff-menu-wrapper {
174 float: left;
174 float: left;
175 }
175 }
176
176
177 .diff-menu {
177 .diff-menu {
178 position: absolute;
178 position: absolute;
179 background: none repeat scroll 0 0 #FFFFFF;
179 background: none repeat scroll 0 0 #FFFFFF;
180 border-color: #003367 @grey3 @grey3;
180 border-color: #003367 @grey3 @grey3;
181 border-right: 1px solid @grey3;
181 border-right: 1px solid @grey3;
182 border-style: solid solid solid;
182 border-style: solid solid solid;
183 border-width: @border-thickness;
183 border-width: @border-thickness;
184 box-shadow: 2px 8px 4px rgba(0, 0, 0, 0.2);
184 box-shadow: 2px 8px 4px rgba(0, 0, 0, 0.2);
185 margin-top: 5px;
185 margin-top: 5px;
186 margin-left: 1px;
186 margin-left: 1px;
187 }
187 }
188
188
189 .diff-actions, .editor-actions {
189 .diff-actions, .editor-actions {
190 float: left;
190 float: left;
191
191
192 input{
192 input{
193 margin: 0 0.5em 0 0;
193 margin: 0 0.5em 0 0;
194 }
194 }
195 }
195 }
196
196
197 // END CODE-HEADER STYLES
197 // END CODE-HEADER STYLES
198
198
199 // BEGIN CODE-BODY STYLES
199 // BEGIN CODE-BODY STYLES
200
200
201 .code-body {
201 .code-body {
202 background: white;
202 background: white;
203 padding: 0;
203 padding: 0;
204 background-color: #ffffff;
204 background-color: #ffffff;
205 position: relative;
205 position: relative;
206 max-width: none;
206 max-width: none;
207 box-sizing: border-box;
207 box-sizing: border-box;
208 // TODO: johbo: Parent has overflow: auto, this forces the child here
208 // TODO: johbo: Parent has overflow: auto, this forces the child here
209 // to have the intended size and to scroll. Should be simplified.
209 // to have the intended size and to scroll. Should be simplified.
210 width: 100%;
210 width: 100%;
211 overflow-x: auto;
211 overflow-x: auto;
212 }
212 }
213
213
214 pre.raw {
214 pre.raw {
215 background: white;
215 background: white;
216 color: @grey1;
216 color: @grey1;
217 }
217 }
218 // END CODE-BODY STYLES
218 // END CODE-BODY STYLES
219
219
220 }
220 }
221
221
222
222
223 table.code-difftable {
223 table.code-difftable {
224 border-collapse: collapse;
224 border-collapse: collapse;
225 width: 99%;
225 width: 99%;
226 border-radius: 0px !important;
226 border-radius: 0px !important;
227
227
228 td {
228 td {
229 padding: 0 !important;
229 padding: 0 !important;
230 background: none !important;
230 background: none !important;
231 border: 0 !important;
231 border: 0 !important;
232 }
232 }
233
233
234 .context {
234 .context {
235 background: none repeat scroll 0 0 #DDE7EF;
235 background: none repeat scroll 0 0 #DDE7EF;
236 }
236 }
237
237
238 .add {
238 .add {
239 background: none repeat scroll 0 0 #DDFFDD;
239 background: none repeat scroll 0 0 #DDFFDD;
240
240
241 ins {
241 ins {
242 background: none repeat scroll 0 0 #AAFFAA;
242 background: none repeat scroll 0 0 #AAFFAA;
243 text-decoration: none;
243 text-decoration: none;
244 }
244 }
245 }
245 }
246
246
247 .del {
247 .del {
248 background: none repeat scroll 0 0 #FFDDDD;
248 background: none repeat scroll 0 0 #FFDDDD;
249
249
250 del {
250 del {
251 background: none repeat scroll 0 0 #FFAAAA;
251 background: none repeat scroll 0 0 #FFAAAA;
252 text-decoration: none;
252 text-decoration: none;
253 }
253 }
254 }
254 }
255
255
256 /** LINE NUMBERS **/
256 /** LINE NUMBERS **/
257 .lineno {
257 .lineno {
258 padding-left: 2px;
258 padding-left: 2px;
259 padding-right: 2px;
259 padding-right: 2px;
260 text-align: right;
260 text-align: right;
261 width: 32px;
261 width: 32px;
262 -moz-user-select: none;
262 -moz-user-select: none;
263 -webkit-user-select: none;
263 -webkit-user-select: none;
264 border-right: @border-thickness solid @grey5 !important;
264 border-right: @border-thickness solid @grey5 !important;
265 border-left: 0px solid #CCC !important;
265 border-left: 0px solid #CCC !important;
266 border-top: 0px solid #CCC !important;
266 border-top: 0px solid #CCC !important;
267 border-bottom: none !important;
267 border-bottom: none !important;
268
268
269 a {
269 a {
270 &:extend(pre);
270 &:extend(pre);
271 text-align: right;
271 text-align: right;
272 padding-right: 2px;
272 padding-right: 2px;
273 cursor: pointer;
273 cursor: pointer;
274 display: block;
274 display: block;
275 width: 32px;
275 width: 32px;
276 }
276 }
277 }
277 }
278
278
279 .context {
279 .context {
280 cursor: auto;
280 cursor: auto;
281 &:extend(pre);
281 &:extend(pre);
282 }
282 }
283
283
284 .lineno-inline {
284 .lineno-inline {
285 background: none repeat scroll 0 0 #FFF !important;
285 background: none repeat scroll 0 0 #FFF !important;
286 padding-left: 2px;
286 padding-left: 2px;
287 padding-right: 2px;
287 padding-right: 2px;
288 text-align: right;
288 text-align: right;
289 width: 30px;
289 width: 30px;
290 -moz-user-select: none;
290 -moz-user-select: none;
291 -webkit-user-select: none;
291 -webkit-user-select: none;
292 }
292 }
293
293
294 /** CODE **/
294 /** CODE **/
295 .code {
295 .code {
296 display: block;
296 display: block;
297 width: 100%;
297 width: 100%;
298
298
299 td {
299 td {
300 margin: 0;
300 margin: 0;
301 padding: 0;
301 padding: 0;
302 }
302 }
303
303
304 pre {
304 pre {
305 margin: 0;
305 margin: 0;
306 padding: 0;
306 padding: 0;
307 margin-left: .5em;
307 margin-left: .5em;
308 }
308 }
309 }
309 }
310 }
310 }
311
311
312
312
313 // Comments
313 // Comments
314
314
315 div.comment:target {
315 div.comment:target {
316 border-left: 6px solid @comment-highlight-color;
316 border-left: 6px solid @comment-highlight-color;
317 padding-left: 3px;
317 padding-left: 3px;
318 margin-left: -9px;
318 margin-left: -9px;
319 }
319 }
320
320
321 //TODO: anderson: can't get an absolute number out of anything, so had to put the
321 //TODO: anderson: can't get an absolute number out of anything, so had to put the
322 //current values that might change. But to make it clear I put as a calculation
322 //current values that might change. But to make it clear I put as a calculation
323 @comment-max-width: 1065px;
323 @comment-max-width: 1065px;
324 @pr-extra-margin: 34px;
324 @pr-extra-margin: 34px;
325 @pr-border-spacing: 4px;
325 @pr-border-spacing: 4px;
326 @pr-comment-width: @comment-max-width - @pr-extra-margin - @pr-border-spacing;
326 @pr-comment-width: @comment-max-width - @pr-extra-margin - @pr-border-spacing;
327
327
328 // Pull Request
328 // Pull Request
329 .cs_files .code-difftable {
329 .cs_files .code-difftable {
330 border: @border-thickness solid @grey5; //borders only on PRs
330 border: @border-thickness solid @grey5; //borders only on PRs
331
331
332 .comment-inline-form,
332 .comment-inline-form,
333 div.comment {
333 div.comment {
334 width: @pr-comment-width;
334 width: @pr-comment-width;
335 }
335 }
336 }
336 }
337
337
338 // Changeset
338 // Changeset
339 .code-difftable {
339 .code-difftable {
340 .comment-inline-form,
340 .comment-inline-form,
341 div.comment {
341 div.comment {
342 width: @comment-max-width;
342 width: @comment-max-width;
343 }
343 }
344 }
344 }
345
345
346 //Style page
346 //Style page
347 @style-extra-margin: @sidebar-width + (@sidebarpadding * 3) + @padding;
347 @style-extra-margin: @sidebar-width + (@sidebarpadding * 3) + @padding;
348 #style-page .code-difftable{
348 #style-page .code-difftable{
349 .comment-inline-form,
349 .comment-inline-form,
350 div.comment {
350 div.comment {
351 width: @comment-max-width - @style-extra-margin;
351 width: @comment-max-width - @style-extra-margin;
352 }
352 }
353 }
353 }
354
354
355 #context-bar > h2 {
355 #context-bar > h2 {
356 font-size: 20px;
356 font-size: 20px;
357 }
357 }
358
358
359 #context-bar > h2> a {
359 #context-bar > h2> a {
360 font-size: 20px;
360 font-size: 20px;
361 }
361 }
362 // end of defaults
362 // end of defaults
363
363
364 .file_diff_buttons {
364 .file_diff_buttons {
365 padding: 0 0 @padding;
365 padding: 0 0 @padding;
366
366
367 .drop-menu {
367 .drop-menu {
368 float: left;
368 float: left;
369 margin: 0 @padding 0 0;
369 margin: 0 @padding 0 0;
370 }
370 }
371 .btn {
371 .btn {
372 margin: 0 @padding 0 0;
372 margin: 0 @padding 0 0;
373 }
373 }
374 }
374 }
375
375
376 .code-body.textarea.editor {
376 .code-body.textarea.editor {
377 max-width: none;
377 max-width: none;
378 padding: 15px;
378 padding: 15px;
379 }
379 }
380
380
381 td.injected_diff{
381 td.injected_diff{
382 max-width: 1178px;
382 max-width: 1178px;
383 overflow-x: auto;
383 overflow-x: auto;
384 overflow-y: hidden;
384 overflow-y: hidden;
385
385
386 div.diff-container,
386 div.diff-container,
387 div.diffblock{
387 div.diffblock{
388 max-width: 100%;
388 max-width: 100%;
389 }
389 }
390
390
391 div.code-body {
391 div.code-body {
392 max-width: 1124px;
392 max-width: 1124px;
393 overflow-x: auto;
393 overflow-x: auto;
394 padding: 0;
394 padding: 0;
395 }
395 }
396 div.diffblock {
396 div.diffblock {
397 border: none;
397 border: none;
398 }
398 }
399
399
400 &.inline-form {
400 &.inline-form {
401 width: 99%
401 width: 99%
402 }
402 }
403 }
403 }
404
404
405
405
406 table.code-difftable {
406 table.code-difftable {
407 width: 100%;
407 width: 100%;
408 }
408 }
409
409
410 /** PYGMENTS COLORING **/
410 /** PYGMENTS COLORING **/
411 div.codeblock {
411 div.codeblock {
412
412
413 // TODO: johbo: Added interim to get rid of the margin around
413 // TODO: johbo: Added interim to get rid of the margin around
414 // Select2 widgets. This needs further cleanup.
414 // Select2 widgets. This needs further cleanup.
415 margin-top: @padding;
415 margin-top: @padding;
416
416
417 overflow: auto;
417 overflow: auto;
418 padding: 0px;
418 padding: 0px;
419 border: @border-thickness solid @grey5;
419 border: @border-thickness solid @grey5;
420 background: @grey6;
420 background: @grey6;
421 .border-radius(@border-radius);
421 .border-radius(@border-radius);
422
422
423 #remove_gist {
423 #remove_gist {
424 float: right;
424 float: right;
425 }
425 }
426
426
427 .author {
427 .author {
428 clear: both;
428 clear: both;
429 vertical-align: middle;
429 vertical-align: middle;
430 font-family: @text-bold;
430 font-family: @text-bold;
431 }
431 }
432
432
433 .btn-mini {
433 .btn-mini {
434 float: left;
434 float: left;
435 margin: 0 5px 0 0;
435 margin: 0 5px 0 0;
436 }
436 }
437
437
438 .code-header {
438 .code-header {
439 padding: @padding;
439 padding: @padding;
440 border-bottom: @border-thickness solid @grey5;
440 border-bottom: @border-thickness solid @grey5;
441
441
442 .rc-user {
443 min-width: 0;
444 margin-right: .5em;
445 }
446
442 .stats {
447 .stats {
443 clear: both;
448 clear: both;
444 margin: 0 0 @padding 0;
449 margin: 0 0 @padding 0;
445 padding: 0;
450 padding: 0;
446 .left {
451 .left {
447 float: left;
452 float: left;
448 clear: left;
453 clear: left;
449 max-width: 75%;
454 max-width: 75%;
450 margin: 0 0 @padding 0;
455 margin: 0 0 @padding 0;
451
456
452 &.item {
457 &.item {
453 margin-right: @padding;
458 margin-right: @padding;
454 &.last { border-right: none; }
459 &.last { border-right: none; }
455 }
460 }
456 }
461 }
457 .buttons { float: right; }
462 .buttons { float: right; }
458 .author {
463 .author {
459 height: 25px; margin-left: 15px; font-weight: bold;
464 height: 25px; margin-left: 15px; font-weight: bold;
460 }
465 }
461 }
466 }
462
467
463 .commit {
468 .commit {
464 margin: 5px 0 0 26px;
469 margin: 5px 0 0 26px;
465 font-weight: normal;
470 font-weight: normal;
466 white-space: pre-wrap;
471 white-space: pre-wrap;
467 }
472 }
468 }
473 }
469
474
470 .message {
475 .message {
471 position: relative;
476 position: relative;
472 margin: @padding;
477 margin: @padding;
473
478
474 .codeblock-label {
479 .codeblock-label {
475 margin: 0 0 1em 0;
480 margin: 0 0 1em 0;
476 }
481 }
477 }
482 }
478
483
479 .code-body {
484 .code-body {
480 padding: @padding;
485 padding: @padding;
481 background-color: #ffffff;
486 background-color: #ffffff;
482 min-width: 100%;
487 min-width: 100%;
483 box-sizing: border-box;
488 box-sizing: border-box;
484 // TODO: johbo: Parent has overflow: auto, this forces the child here
489 // TODO: johbo: Parent has overflow: auto, this forces the child here
485 // to have the intended size and to scroll. Should be simplified.
490 // to have the intended size and to scroll. Should be simplified.
486 width: 100%;
491 width: 100%;
487 overflow-x: auto;
492 overflow-x: auto;
488 }
493 }
489 }
494 }
490
495
491 .code-highlighttable,
496 .code-highlighttable,
492 div.codeblock .code-body table {
497 div.codeblock .code-body table {
493 width: 0 !important;
498 width: 0 !important;
494 border: 0px !important;
499 border: 0px !important;
495 margin: 0;
500 margin: 0;
496 letter-spacing: normal;
501 letter-spacing: normal;
497
502
498
503
499 td {
504 td {
500 border: 0px !important;
505 border: 0px !important;
501 vertical-align: top;
506 vertical-align: top;
502 }
507 }
503 }
508 }
504
509
505 div.codeblock .code-header .search-path { padding: 0 0 0 10px; }
510 div.codeblock .code-header .search-path { padding: 0 0 0 10px; }
506 div.search-code-body {
511 div.search-code-body {
507 background-color: #ffffff; padding: 5px 0 5px 10px;
512 background-color: #ffffff; padding: 5px 0 5px 10px;
508 pre {
513 pre {
509 .match { background-color: #faffa6;}
514 .match { background-color: #faffa6;}
510 .break { display: block; width: 100%; background-color: #DDE7EF; color: #747474; }
515 .break { display: block; width: 100%; background-color: #DDE7EF; color: #747474; }
511 }
516 }
512 }
517 }
513
518
514 div.annotatediv { margin-left: 2px; margin-right: 4px; }
519 div.annotatediv { margin-left: 2px; margin-right: 4px; }
515 .code-highlight {
520 .code-highlight {
516 margin: 0; padding: 0; border-left: @border-thickness solid @grey5;
521 margin: 0; padding: 0; border-left: @border-thickness solid @grey5;
517 pre, .linenodiv pre { padding: 0 5px; margin: 0; }
522 pre, .linenodiv pre { padding: 0 5px; margin: 0; }
518 pre div:target {background-color: @comment-highlight-color !important;}
523 pre div:target {background-color: @comment-highlight-color !important;}
519 }
524 }
520
525
521 .linenos a { text-decoration: none; }
526 .linenos a { text-decoration: none; }
522
527
523 .CodeMirror-selected { background: @rchighlightblue; }
528 .CodeMirror-selected { background: @rchighlightblue; }
524 .CodeMirror-focused .CodeMirror-selected { background: @rchighlightblue; }
529 .CodeMirror-focused .CodeMirror-selected { background: @rchighlightblue; }
525 .CodeMirror ::selection { background: @rchighlightblue; }
530 .CodeMirror ::selection { background: @rchighlightblue; }
526 .CodeMirror ::-moz-selection { background: @rchighlightblue; }
531 .CodeMirror ::-moz-selection { background: @rchighlightblue; }
527
532
528 .code { display: block; border:0px !important; }
533 .code { display: block; border:0px !important; }
529 .code-highlight,
534 .code-highlight,
530 .codehilite {
535 .codehilite {
531 .hll { background-color: #ffffcc }
536 .hll { background-color: #ffffcc }
532 .c { color: #408080; font-style: italic } /* Comment */
537 .c { color: #408080; font-style: italic } /* Comment */
533 .err, .codehilite .err { border: @border-thickness solid #FF0000 } /* Error */
538 .err, .codehilite .err { border: @border-thickness solid #FF0000 } /* Error */
534 .k { color: #008000; font-weight: bold } /* Keyword */
539 .k { color: #008000; font-weight: bold } /* Keyword */
535 .o { color: #666666 } /* Operator */
540 .o { color: #666666 } /* Operator */
536 .cm { color: #408080; font-style: italic } /* Comment.Multiline */
541 .cm { color: #408080; font-style: italic } /* Comment.Multiline */
537 .cp { color: #BC7A00 } /* Comment.Preproc */
542 .cp { color: #BC7A00 } /* Comment.Preproc */
538 .c1 { color: #408080; font-style: italic } /* Comment.Single */
543 .c1 { color: #408080; font-style: italic } /* Comment.Single */
539 .cs { color: #408080; font-style: italic } /* Comment.Special */
544 .cs { color: #408080; font-style: italic } /* Comment.Special */
540 .gd { color: #A00000 } /* Generic.Deleted */
545 .gd { color: #A00000 } /* Generic.Deleted */
541 .ge { font-style: italic } /* Generic.Emph */
546 .ge { font-style: italic } /* Generic.Emph */
542 .gr { color: #FF0000 } /* Generic.Error */
547 .gr { color: #FF0000 } /* Generic.Error */
543 .gh { color: #000080; font-weight: bold } /* Generic.Heading */
548 .gh { color: #000080; font-weight: bold } /* Generic.Heading */
544 .gi { color: #00A000 } /* Generic.Inserted */
549 .gi { color: #00A000 } /* Generic.Inserted */
545 .go { color: #808080 } /* Generic.Output */
550 .go { color: #808080 } /* Generic.Output */
546 .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
551 .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
547 .gs { font-weight: bold } /* Generic.Strong */
552 .gs { font-weight: bold } /* Generic.Strong */
548 .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
553 .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
549 .gt { color: #0040D0 } /* Generic.Traceback */
554 .gt { color: #0040D0 } /* Generic.Traceback */
550 .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
555 .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
551 .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
556 .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
552 .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
557 .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
553 .kp { color: #008000 } /* Keyword.Pseudo */
558 .kp { color: #008000 } /* Keyword.Pseudo */
554 .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
559 .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
555 .kt { color: #B00040 } /* Keyword.Type */
560 .kt { color: #B00040 } /* Keyword.Type */
556 .m { color: #666666 } /* Literal.Number */
561 .m { color: #666666 } /* Literal.Number */
557 .s { color: #BA2121 } /* Literal.String */
562 .s { color: #BA2121 } /* Literal.String */
558 .na { color: #7D9029 } /* Name.Attribute */
563 .na { color: #7D9029 } /* Name.Attribute */
559 .nb { color: #008000 } /* Name.Builtin */
564 .nb { color: #008000 } /* Name.Builtin */
560 .nc { color: #0000FF; font-weight: bold } /* Name.Class */
565 .nc { color: #0000FF; font-weight: bold } /* Name.Class */
561 .no { color: #880000 } /* Name.Constant */
566 .no { color: #880000 } /* Name.Constant */
562 .nd { color: #AA22FF } /* Name.Decorator */
567 .nd { color: #AA22FF } /* Name.Decorator */
563 .ni { color: #999999; font-weight: bold } /* Name.Entity */
568 .ni { color: #999999; font-weight: bold } /* Name.Entity */
564 .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
569 .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
565 .nf { color: #0000FF } /* Name.Function */
570 .nf { color: #0000FF } /* Name.Function */
566 .nl { color: #A0A000 } /* Name.Label */
571 .nl { color: #A0A000 } /* Name.Label */
567 .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
572 .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
568 .nt { color: #008000; font-weight: bold } /* Name.Tag */
573 .nt { color: #008000; font-weight: bold } /* Name.Tag */
569 .nv { color: #19177C } /* Name.Variable */
574 .nv { color: #19177C } /* Name.Variable */
570 .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
575 .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
571 .w { color: #bbbbbb } /* Text.Whitespace */
576 .w { color: #bbbbbb } /* Text.Whitespace */
572 .mf { color: #666666 } /* Literal.Number.Float */
577 .mf { color: #666666 } /* Literal.Number.Float */
573 .mh { color: #666666 } /* Literal.Number.Hex */
578 .mh { color: #666666 } /* Literal.Number.Hex */
574 .mi { color: #666666 } /* Literal.Number.Integer */
579 .mi { color: #666666 } /* Literal.Number.Integer */
575 .mo { color: #666666 } /* Literal.Number.Oct */
580 .mo { color: #666666 } /* Literal.Number.Oct */
576 .sb { color: #BA2121 } /* Literal.String.Backtick */
581 .sb { color: #BA2121 } /* Literal.String.Backtick */
577 .sc { color: #BA2121 } /* Literal.String.Char */
582 .sc { color: #BA2121 } /* Literal.String.Char */
578 .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
583 .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
579 .s2 { color: #BA2121 } /* Literal.String.Double */
584 .s2 { color: #BA2121 } /* Literal.String.Double */
580 .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
585 .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
581 .sh { color: #BA2121 } /* Literal.String.Heredoc */
586 .sh { color: #BA2121 } /* Literal.String.Heredoc */
582 .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
587 .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
583 .sx { color: #008000 } /* Literal.String.Other */
588 .sx { color: #008000 } /* Literal.String.Other */
584 .sr { color: #BB6688 } /* Literal.String.Regex */
589 .sr { color: #BB6688 } /* Literal.String.Regex */
585 .s1 { color: #BA2121 } /* Literal.String.Single */
590 .s1 { color: #BA2121 } /* Literal.String.Single */
586 .ss { color: #19177C } /* Literal.String.Symbol */
591 .ss { color: #19177C } /* Literal.String.Symbol */
587 .bp { color: #008000 } /* Name.Builtin.Pseudo */
592 .bp { color: #008000 } /* Name.Builtin.Pseudo */
588 .vc { color: #19177C } /* Name.Variable.Class */
593 .vc { color: #19177C } /* Name.Variable.Class */
589 .vg { color: #19177C } /* Name.Variable.Global */
594 .vg { color: #19177C } /* Name.Variable.Global */
590 .vi { color: #19177C } /* Name.Variable.Instance */
595 .vi { color: #19177C } /* Name.Variable.Instance */
591 .il { color: #666666 } /* Literal.Number.Integer.Long */
596 .il { color: #666666 } /* Literal.Number.Integer.Long */
592 }
597 }
593
598
594 /* customized pre blocks for markdown/rst */
599 /* customized pre blocks for markdown/rst */
595 pre.literal-block, .codehilite pre{
600 pre.literal-block, .codehilite pre{
596 padding: @padding;
601 padding: @padding;
597 border: 1px solid @grey6;
602 border: 1px solid @grey6;
598 .border-radius(@border-radius);
603 .border-radius(@border-radius);
599 background-color: @grey7;
604 background-color: @grey7;
600 }
605 }
@@ -1,356 +1,361 b''
1 // comments.less
1 // comments.less
2 // For use in RhodeCode applications;
2 // For use in RhodeCode applications;
3 // see style guide documentation for guidelines.
3 // see style guide documentation for guidelines.
4
4
5
5
6 // Comments
6 // Comments
7 .comments {
7 .comments {
8 width: 100%;
8 width: 100%;
9 }
9 }
10
10
11 tr.inline-comments div {
11 tr.inline-comments div {
12 max-width: 100%;
12 max-width: 100%;
13
13
14 p {
14 p {
15 white-space: normal;
15 white-space: normal;
16 }
16 }
17
17
18 code, pre, .code, dd {
18 code, pre, .code, dd {
19 overflow-x: auto;
19 overflow-x: auto;
20 width: 1062px;
20 width: 1062px;
21 }
21 }
22
22
23 dd {
23 dd {
24 width: auto;
24 width: auto;
25 }
25 }
26 }
26 }
27
27
28 #injected_page_comments {
28 #injected_page_comments {
29 .comment-previous-link,
29 .comment-previous-link,
30 .comment-next-link,
30 .comment-next-link,
31 .comment-links-divider {
31 .comment-links-divider {
32 display: none;
32 display: none;
33 }
33 }
34 }
34 }
35
35
36 .add-comment {
36 .add-comment {
37 margin-bottom: 10px;
37 margin-bottom: 10px;
38 }
38 }
39 .hide-comment-button .add-comment {
39 .hide-comment-button .add-comment {
40 display: none;
40 display: none;
41 }
41 }
42
42
43 .comment-bubble {
43 .comment-bubble {
44 color: @grey4;
44 color: @grey4;
45 margin-top: 4px;
45 margin-top: 4px;
46 margin-right: 30px;
46 margin-right: 30px;
47 visibility: hidden;
47 visibility: hidden;
48 }
48 }
49
49
50 .comment {
50 .comment {
51 margin: @padding 0;
51 margin: @padding 0;
52 padding: 4px 0 0 0;
52 padding: 4px 0 0 0;
53 line-height: 1em;
53 line-height: 1em;
54
54
55 .rc-user {
56 min-width: 0;
57 margin: -2px .5em 0 0;
58 }
59
55 .meta {
60 .meta {
56 position: relative;
61 position: relative;
57 width: 100%;
62 width: 100%;
58 margin: 0 0 .5em 0;
63 margin: 0 0 .5em 0;
59
64
60 &:hover .permalink {
65 &:hover .permalink {
61 visibility: visible;
66 visibility: visible;
62 color: @rcblue;
67 color: @rcblue;
63 }
68 }
64 }
69 }
65
70
66 .author,
71 .author,
67 .date {
72 .date {
68 display: inline;
73 display: inline;
69 margin: 0 .5 0 0;
74 margin: 0 .5 0 0;
70 padding: 0 .5 0 0;
75 padding: 0 .5 0 0;
71
76
72 &:after {
77 &:after {
73 content: ' | ';
78 content: ' | ';
74 color: @grey5;
79 color: @grey5;
75 }
80 }
76 }
81 }
77
82
78 .status-change,
83 .status-change,
79 .permalink,
84 .permalink,
80 .changeset-status-lbl {
85 .changeset-status-lbl {
81 display: inline;
86 display: inline;
82 }
87 }
83
88
84 .permalink {
89 .permalink {
85 visibility: hidden;
90 visibility: hidden;
86 }
91 }
87
92
88 .comment-links-divider {
93 .comment-links-divider {
89 display: inline;
94 display: inline;
90 }
95 }
91
96
92 .comment-links-block {
97 .comment-links-block {
93 float:right;
98 float:right;
94 text-align: right;
99 text-align: right;
95 min-width: 85px;
100 min-width: 85px;
96
101
97 [class^="icon-"]:before,
102 [class^="icon-"]:before,
98 [class*=" icon-"]:before {
103 [class*=" icon-"]:before {
99 margin-left: 0;
104 margin-left: 0;
100 margin-right: 0;
105 margin-right: 0;
101 }
106 }
102 }
107 }
103
108
104 .comment-previous-link {
109 .comment-previous-link {
105 display: inline-block;
110 display: inline-block;
106
111
107 .arrow_comment_link{
112 .arrow_comment_link{
108 cursor: pointer;
113 cursor: pointer;
109 i {
114 i {
110 font-size:10px;
115 font-size:10px;
111 }
116 }
112 }
117 }
113 .arrow_comment_link.disabled {
118 .arrow_comment_link.disabled {
114 cursor: default;
119 cursor: default;
115 color: @grey5;
120 color: @grey5;
116 }
121 }
117 }
122 }
118
123
119 .comment-next-link {
124 .comment-next-link {
120 display: inline-block;
125 display: inline-block;
121
126
122 .arrow_comment_link{
127 .arrow_comment_link{
123 cursor: pointer;
128 cursor: pointer;
124 i {
129 i {
125 font-size:10px;
130 font-size:10px;
126 }
131 }
127 }
132 }
128 .arrow_comment_link.disabled {
133 .arrow_comment_link.disabled {
129 cursor: default;
134 cursor: default;
130 color: @grey5;
135 color: @grey5;
131 }
136 }
132 }
137 }
133
138
134 .flag_status {
139 .flag_status {
135 display: inline-block;
140 display: inline-block;
136 margin: -2px .5em 0 .25em
141 margin: -2px .5em 0 .25em
137 }
142 }
138
143
139 .delete-comment {
144 .delete-comment {
140 display: inline-block;
145 display: inline-block;
141 color: @rcblue;
146 color: @rcblue;
142
147
143 &:hover {
148 &:hover {
144 cursor: pointer;
149 cursor: pointer;
145 }
150 }
146 }
151 }
147
152
148
153
149 .text {
154 .text {
150 clear: both;
155 clear: both;
151 border: @border-thickness solid @grey5;
156 border: @border-thickness solid @grey5;
152 .border-radius(@border-radius);
157 .border-radius(@border-radius);
153 .box-sizing(border-box);
158 .box-sizing(border-box);
154
159
155 .markdown-block p,
160 .markdown-block p,
156 .rst-block p {
161 .rst-block p {
157 margin: .5em 0 !important;
162 margin: .5em 0 !important;
158 // TODO: lisa: This is needed because of other rst !important rules :[
163 // TODO: lisa: This is needed because of other rst !important rules :[
159 }
164 }
160 }
165 }
161 }
166 }
162
167
163 .show-outdated-comments {
168 .show-outdated-comments {
164 display: inline;
169 display: inline;
165 color: @rcblue;
170 color: @rcblue;
166 }
171 }
167
172
168 .outdated {
173 .outdated {
169 display: none;
174 display: none;
170 opacity: 0.6;
175 opacity: 0.6;
171
176
172 .comment {
177 .comment {
173 margin: 0 0 @padding;
178 margin: 0 0 @padding;
174
179
175 .date:after {
180 .date:after {
176 content: none;
181 content: none;
177 }
182 }
178 }
183 }
179 .outdated_comment_block {
184 .outdated_comment_block {
180 padding: 0 0 @space 0;
185 padding: 0 0 @space 0;
181 }
186 }
182 }
187 }
183
188
184 // Comment Form
189 // Comment Form
185 div.comment-form {
190 div.comment-form {
186 margin-top: 20px;
191 margin-top: 20px;
187 }
192 }
188
193
189 .comment-form strong {
194 .comment-form strong {
190 display: block;
195 display: block;
191 margin-bottom: 15px;
196 margin-bottom: 15px;
192 }
197 }
193
198
194 .comment-form textarea {
199 .comment-form textarea {
195 width: 100%;
200 width: 100%;
196 height: 100px;
201 height: 100px;
197 font-family: 'Monaco', 'Courier', 'Courier New', monospace;
202 font-family: 'Monaco', 'Courier', 'Courier New', monospace;
198 }
203 }
199
204
200 form.comment-form {
205 form.comment-form {
201 margin-top: 10px;
206 margin-top: 10px;
202 margin-left: 10px;
207 margin-left: 10px;
203 }
208 }
204
209
205 .comment-inline-form .comment-block-ta,
210 .comment-inline-form .comment-block-ta,
206 .comment-form .comment-block-ta,
211 .comment-form .comment-block-ta,
207 .comment-form .preview-box {
212 .comment-form .preview-box {
208 border: @border-thickness solid @grey5;
213 border: @border-thickness solid @grey5;
209 .border-radius(@border-radius);
214 .border-radius(@border-radius);
210 .box-sizing(border-box);
215 .box-sizing(border-box);
211 background-color: white;
216 background-color: white;
212 }
217 }
213
218
214 .comment-form-submit {
219 .comment-form-submit {
215 margin-top: 5px;
220 margin-top: 5px;
216 margin-left: 525px;
221 margin-left: 525px;
217 }
222 }
218
223
219 .file-comments {
224 .file-comments {
220 display: none;
225 display: none;
221 }
226 }
222
227
223 .comment-form .preview-box.unloaded,
228 .comment-form .preview-box.unloaded,
224 .comment-inline-form .preview-box.unloaded {
229 .comment-inline-form .preview-box.unloaded {
225 height: 50px;
230 height: 50px;
226 text-align: center;
231 text-align: center;
227 padding: 20px;
232 padding: 20px;
228 background-color: white;
233 background-color: white;
229 }
234 }
230
235
231 .comment-footer {
236 .comment-footer {
232 position: relative;
237 position: relative;
233 width: 100%;
238 width: 100%;
234 min-height: 42px;
239 min-height: 42px;
235
240
236 .status_box,
241 .status_box,
237 .cancel-button {
242 .cancel-button {
238 float: left;
243 float: left;
239 display: inline-block;
244 display: inline-block;
240 }
245 }
241
246
242 .action-buttons {
247 .action-buttons {
243 float: right;
248 float: right;
244 display: inline-block;
249 display: inline-block;
245 }
250 }
246 }
251 }
247
252
248 .comment-form {
253 .comment-form {
249
254
250 .comment {
255 .comment {
251 margin-left: 10px;
256 margin-left: 10px;
252 }
257 }
253
258
254 .comment-help {
259 .comment-help {
255 color: @grey4;
260 color: @grey4;
256 padding: 5px 0 5px 0;
261 padding: 5px 0 5px 0;
257 }
262 }
258
263
259 .comment-title {
264 .comment-title {
260 padding: 5px 0 5px 0;
265 padding: 5px 0 5px 0;
261 }
266 }
262
267
263 .comment-button {
268 .comment-button {
264 display: inline-block;
269 display: inline-block;
265 }
270 }
266
271
267 .comment-button .comment-button-input {
272 .comment-button .comment-button-input {
268 margin-right: 0;
273 margin-right: 0;
269 }
274 }
270
275
271 .comment-footer {
276 .comment-footer {
272 margin-bottom: 110px;
277 margin-bottom: 110px;
273 }
278 }
274 }
279 }
275
280
276
281
277 .comment-form-login {
282 .comment-form-login {
278 .comment-help {
283 .comment-help {
279 padding: 0.9em; //same as the button
284 padding: 0.9em; //same as the button
280 }
285 }
281
286
282 div.clearfix {
287 div.clearfix {
283 clear: both;
288 clear: both;
284 width: 100%;
289 width: 100%;
285 display: block;
290 display: block;
286 }
291 }
287 }
292 }
288
293
289 .preview-box {
294 .preview-box {
290 padding: 10px;
295 padding: 10px;
291 min-height: 100px;
296 min-height: 100px;
292 margin-bottom: 15px;
297 margin-bottom: 15px;
293 background-color: white;
298 background-color: white;
294 border: @border-thickness solid #ccc;
299 border: @border-thickness solid #ccc;
295 .border-radius(@border-radius);
300 .border-radius(@border-radius);
296 .box-sizing(border-box);
301 .box-sizing(border-box);
297 }
302 }
298
303
299 .add-another-button {
304 .add-another-button {
300 margin-left: 10px;
305 margin-left: 10px;
301 margin-top: 10px;
306 margin-top: 10px;
302 margin-bottom: 10px;
307 margin-bottom: 10px;
303 }
308 }
304
309
305 .comment .buttons {
310 .comment .buttons {
306 float: right;
311 float: right;
307 margin: -1px 0px 0px 0px;
312 margin: -1px 0px 0px 0px;
308 }
313 }
309
314
310 // Inline Comment Form
315 // Inline Comment Form
311 .injected_diff .comment-inline-form,
316 .injected_diff .comment-inline-form,
312 .comment-inline-form {
317 .comment-inline-form {
313 background-color: @grey6;
318 background-color: @grey6;
314 margin-top: 10px;
319 margin-top: 10px;
315 margin-bottom: 20px;
320 margin-bottom: 20px;
316 }
321 }
317
322
318 .inline-form {
323 .inline-form {
319 padding: 10px 7px;
324 padding: 10px 7px;
320 }
325 }
321
326
322 .inline-form div {
327 .inline-form div {
323 max-width: 100%;
328 max-width: 100%;
324 }
329 }
325
330
326 .overlay {
331 .overlay {
327 display: none;
332 display: none;
328 position: absolute;
333 position: absolute;
329 width: 100%;
334 width: 100%;
330 text-align: center;
335 text-align: center;
331 vertical-align: middle;
336 vertical-align: middle;
332 font-size: 16px;
337 font-size: 16px;
333 background: none repeat scroll 0 0 white;
338 background: none repeat scroll 0 0 white;
334
339
335 &.submitting {
340 &.submitting {
336 display: block;
341 display: block;
337 opacity: 0.5;
342 opacity: 0.5;
338 z-index: 100;
343 z-index: 100;
339 }
344 }
340 }
345 }
341 .comment-inline-form .overlay.submitting .overlay-text {
346 .comment-inline-form .overlay.submitting .overlay-text {
342 margin-top: 5%;
347 margin-top: 5%;
343 }
348 }
344
349
345 .comment-inline-form .clearfix,
350 .comment-inline-form .clearfix,
346 .comment-form .clearfix {
351 .comment-form .clearfix {
347 .border-radius(@border-radius);
352 .border-radius(@border-radius);
348 margin: 0px;
353 margin: 0px;
349 }
354 }
350
355
351 .hide-inline-form-button {
356 .hide-inline-form-button {
352 margin-left: 5px;
357 margin-left: 5px;
353 }
358 }
354 .comment-button .hide-inline-form {
359 .comment-button .hide-inline-form {
355 background: white;
360 background: white;
356 }
361 }
@@ -1,2076 +1,2087 b''
1 //Primary CSS
1 //Primary CSS
2
2
3 //--- IMPORTS ------------------//
3 //--- IMPORTS ------------------//
4
4
5 @import 'helpers';
5 @import 'helpers';
6 @import 'mixins';
6 @import 'mixins';
7 @import 'rcicons';
7 @import 'rcicons';
8 @import 'fonts';
8 @import 'fonts';
9 @import 'variables';
9 @import 'variables';
10 @import 'bootstrap-variables';
10 @import 'bootstrap-variables';
11 @import 'form-bootstrap';
11 @import 'form-bootstrap';
12 @import 'codemirror';
12 @import 'codemirror';
13 @import 'legacy_code_styles';
13 @import 'legacy_code_styles';
14 @import 'progress-bar';
14 @import 'progress-bar';
15
15
16 @import 'type';
16 @import 'type';
17 @import 'alerts';
17 @import 'alerts';
18 @import 'buttons';
18 @import 'buttons';
19 @import 'tags';
19 @import 'tags';
20 @import 'code-block';
20 @import 'code-block';
21 @import 'examples';
21 @import 'examples';
22 @import 'login';
22 @import 'login';
23 @import 'main-content';
23 @import 'main-content';
24 @import 'select2';
24 @import 'select2';
25 @import 'comments';
25 @import 'comments';
26 @import 'panels-bootstrap';
26 @import 'panels-bootstrap';
27 @import 'panels';
27 @import 'panels';
28
28
29
29
30 //--- BASE ------------------//
30 //--- BASE ------------------//
31 .noscript-error {
31 .noscript-error {
32 top: 0;
32 top: 0;
33 left: 0;
33 left: 0;
34 width: 100%;
34 width: 100%;
35 z-index: 101;
35 z-index: 101;
36 text-align: center;
36 text-align: center;
37 font-family: @text-semibold;
37 font-family: @text-semibold;
38 font-size: 120%;
38 font-size: 120%;
39 color: white;
39 color: white;
40 background-color: @alert2;
40 background-color: @alert2;
41 padding: 5px 0 5px 0;
41 padding: 5px 0 5px 0;
42 }
42 }
43
43
44 html {
44 html {
45 display: table;
45 display: table;
46 height: 100%;
46 height: 100%;
47 width: 100%;
47 width: 100%;
48 }
48 }
49
49
50 body {
50 body {
51 display: table-cell;
51 display: table-cell;
52 width: 100%;
52 width: 100%;
53 }
53 }
54
54
55 //--- LAYOUT ------------------//
55 //--- LAYOUT ------------------//
56
56
57 .hidden{
57 .hidden{
58 display: none !important;
58 display: none !important;
59 }
59 }
60
60
61 .box{
61 .box{
62 float: left;
62 float: left;
63 width: 100%;
63 width: 100%;
64 }
64 }
65
65
66 .browser-header {
66 .browser-header {
67 clear: both;
67 clear: both;
68 }
68 }
69 .main {
69 .main {
70 clear: both;
70 clear: both;
71 padding:0 0 @pagepadding;
71 padding:0 0 @pagepadding;
72 height: auto;
72 height: auto;
73
73
74 &:after { //clearfix
74 &:after { //clearfix
75 content:"";
75 content:"";
76 clear:both;
76 clear:both;
77 width:100%;
77 width:100%;
78 display:block;
78 display:block;
79 }
79 }
80 }
80 }
81
81
82 .action-link{
82 .action-link{
83 margin-left: @padding;
83 margin-left: @padding;
84 padding-left: @padding;
84 padding-left: @padding;
85 border-left: @border-thickness solid @border-default-color;
85 border-left: @border-thickness solid @border-default-color;
86 }
86 }
87
87
88 input + .action-link, .action-link.first{
88 input + .action-link, .action-link.first{
89 border-left: none;
89 border-left: none;
90 }
90 }
91
91
92 .action-link.last{
92 .action-link.last{
93 margin-right: @padding;
93 margin-right: @padding;
94 padding-right: @padding;
94 padding-right: @padding;
95 }
95 }
96
96
97 .action-link.active,
97 .action-link.active,
98 .action-link.active a{
98 .action-link.active a{
99 color: @grey4;
99 color: @grey4;
100 }
100 }
101
101
102 ul.simple-list{
102 ul.simple-list{
103 list-style: none;
103 list-style: none;
104 margin: 0;
104 margin: 0;
105 padding: 0;
105 padding: 0;
106 }
106 }
107
107
108 .main-content {
108 .main-content {
109 padding-bottom: @pagepadding;
109 padding-bottom: @pagepadding;
110 }
110 }
111
111
112 .wrapper {
112 .wrapper {
113 position: relative;
113 position: relative;
114 max-width: @wrapper-maxwidth;
114 max-width: @wrapper-maxwidth;
115 margin: 0 auto;
115 margin: 0 auto;
116 }
116 }
117
117
118 #content {
118 #content {
119 clear: both;
119 clear: both;
120 padding: 0 @contentpadding;
120 padding: 0 @contentpadding;
121 }
121 }
122
122
123 .advanced-settings-fields{
123 .advanced-settings-fields{
124 input{
124 input{
125 margin-left: @textmargin;
125 margin-left: @textmargin;
126 margin-right: @padding/2;
126 margin-right: @padding/2;
127 }
127 }
128 }
128 }
129
129
130 .cs_files_title {
130 .cs_files_title {
131 margin: @pagepadding 0 0;
131 margin: @pagepadding 0 0;
132 }
132 }
133
133
134 input.inline[type="file"] {
134 input.inline[type="file"] {
135 display: inline;
135 display: inline;
136 }
136 }
137
137
138 .error_page {
138 .error_page {
139 margin: 10% auto;
139 margin: 10% auto;
140
140
141 h1 {
141 h1 {
142 color: @grey2;
142 color: @grey2;
143 }
143 }
144
144
145 .error-branding {
145 .error-branding {
146 font-family: @text-semibold;
146 font-family: @text-semibold;
147 color: @grey4;
147 color: @grey4;
148 }
148 }
149
149
150 .error_message {
150 .error_message {
151 font-family: @text-regular;
151 font-family: @text-regular;
152 }
152 }
153
153
154 .sidebar {
154 .sidebar {
155 min-height: 275px;
155 min-height: 275px;
156 margin: 0;
156 margin: 0;
157 padding: 0 0 @sidebarpadding @sidebarpadding;
157 padding: 0 0 @sidebarpadding @sidebarpadding;
158 border: none;
158 border: none;
159 }
159 }
160
160
161 .main-content {
161 .main-content {
162 position: relative;
162 position: relative;
163 margin: 0 @sidebarpadding @sidebarpadding;
163 margin: 0 @sidebarpadding @sidebarpadding;
164 padding: 0 0 0 @sidebarpadding;
164 padding: 0 0 0 @sidebarpadding;
165 border-left: @border-thickness solid @grey5;
165 border-left: @border-thickness solid @grey5;
166
166
167 @media (max-width:767px) {
167 @media (max-width:767px) {
168 clear: both;
168 clear: both;
169 width: 100%;
169 width: 100%;
170 margin: 0;
170 margin: 0;
171 border: none;
171 border: none;
172 }
172 }
173 }
173 }
174
174
175 .inner-column {
175 .inner-column {
176 float: left;
176 float: left;
177 width: 29.75%;
177 width: 29.75%;
178 min-height: 150px;
178 min-height: 150px;
179 margin: @sidebarpadding 2% 0 0;
179 margin: @sidebarpadding 2% 0 0;
180 padding: 0 2% 0 0;
180 padding: 0 2% 0 0;
181 border-right: @border-thickness solid @grey5;
181 border-right: @border-thickness solid @grey5;
182
182
183 @media (max-width:767px) {
183 @media (max-width:767px) {
184 clear: both;
184 clear: both;
185 width: 100%;
185 width: 100%;
186 border: none;
186 border: none;
187 }
187 }
188
188
189 ul {
189 ul {
190 padding-left: 1.25em;
190 padding-left: 1.25em;
191 }
191 }
192
192
193 &:last-child {
193 &:last-child {
194 margin: @sidebarpadding 0 0;
194 margin: @sidebarpadding 0 0;
195 border: none;
195 border: none;
196 }
196 }
197
197
198 h4 {
198 h4 {
199 margin: 0 0 @padding;
199 margin: 0 0 @padding;
200 font-family: @text-semibold;
200 font-family: @text-semibold;
201 }
201 }
202 }
202 }
203 }
203 }
204 .error-page-logo {
204 .error-page-logo {
205 width: 130px;
205 width: 130px;
206 height: 160px;
206 height: 160px;
207 }
207 }
208
208
209 // HEADER
209 // HEADER
210 .header {
210 .header {
211
211
212 // TODO: johbo: Fix login pages, so that they work without a min-height
212 // TODO: johbo: Fix login pages, so that they work without a min-height
213 // for the header and then remove the min-height. I chose a smaller value
213 // for the header and then remove the min-height. I chose a smaller value
214 // intentionally here to avoid rendering issues in the main navigation.
214 // intentionally here to avoid rendering issues in the main navigation.
215 min-height: 49px;
215 min-height: 49px;
216
216
217 position: relative;
217 position: relative;
218 vertical-align: bottom;
218 vertical-align: bottom;
219 padding: 0 @header-padding;
219 padding: 0 @header-padding;
220 background-color: @grey2;
220 background-color: @grey2;
221 color: @grey5;
221 color: @grey5;
222
222
223 .title {
223 .title {
224 overflow: visible;
224 overflow: visible;
225 }
225 }
226
226
227 &:before,
227 &:before,
228 &:after {
228 &:after {
229 content: "";
229 content: "";
230 clear: both;
230 clear: both;
231 width: 100%;
231 width: 100%;
232 }
232 }
233
233
234 // TODO: johbo: Avoids breaking "Repositories" chooser
234 // TODO: johbo: Avoids breaking "Repositories" chooser
235 .select2-container .select2-choice .select2-arrow {
235 .select2-container .select2-choice .select2-arrow {
236 display: none;
236 display: none;
237 }
237 }
238 }
238 }
239
239
240 #header-inner {
240 #header-inner {
241 &.title {
241 &.title {
242 margin: 0;
242 margin: 0;
243 }
243 }
244 &:before,
244 &:before,
245 &:after {
245 &:after {
246 content: "";
246 content: "";
247 clear: both;
247 clear: both;
248 }
248 }
249 }
249 }
250
250
251 // Gists
251 // Gists
252 #files_data {
252 #files_data {
253 clear: both; //for firefox
253 clear: both; //for firefox
254 }
254 }
255 #gistid {
255 #gistid {
256 margin-right: @padding;
256 margin-right: @padding;
257 }
257 }
258
258
259 // Global Settings Editor
259 // Global Settings Editor
260 .textarea.editor {
260 .textarea.editor {
261 float: left;
261 float: left;
262 position: relative;
262 position: relative;
263 max-width: @texteditor-width;
263 max-width: @texteditor-width;
264
264
265 select {
265 select {
266 position: absolute;
266 position: absolute;
267 top:10px;
267 top:10px;
268 right:0;
268 right:0;
269 }
269 }
270
270
271 .CodeMirror {
271 .CodeMirror {
272 margin: 0;
272 margin: 0;
273 }
273 }
274
274
275 .help-block {
275 .help-block {
276 margin: 0 0 @padding;
276 margin: 0 0 @padding;
277 padding:.5em;
277 padding:.5em;
278 background-color: @grey6;
278 background-color: @grey6;
279 }
279 }
280 }
280 }
281
281
282 ul.auth_plugins {
282 ul.auth_plugins {
283 margin: @padding 0 @padding @legend-width;
283 margin: @padding 0 @padding @legend-width;
284 padding: 0;
284 padding: 0;
285
285
286 li {
286 li {
287 margin-bottom: @padding;
287 margin-bottom: @padding;
288 line-height: 1em;
288 line-height: 1em;
289 list-style-type: none;
289 list-style-type: none;
290
290
291 .auth_buttons .btn {
291 .auth_buttons .btn {
292 margin-right: @padding;
292 margin-right: @padding;
293 }
293 }
294
294
295 &:before { content: none; }
295 &:before { content: none; }
296 }
296 }
297 }
297 }
298
298
299 // Pull Requests
299 // Pull Requests
300
300
301 .pullrequestlist {
301 .pullrequestlist {
302 max-width: @pullrequest-width;
302 max-width: @pullrequest-width;
303 margin-bottom: @space;
303 margin-bottom: @space;
304
304
305 // Tweaks for "My Account" / "Pull requests"
305 // Tweaks for "My Account" / "Pull requests"
306 .prwrapper {
306 .prwrapper {
307 clear: left;
307 clear: left;
308
308
309 .pr {
309 .pr {
310 margin: 0;
310 margin: 0;
311 padding: 0;
311 padding: 0;
312 border-bottom: none;
312 border-bottom: none;
313 }
313 }
314
314
315 // TODO: johbo: Replace with something that makes up an inline form or
315 // TODO: johbo: Replace with something that makes up an inline form or
316 // similar.
316 // similar.
317 .repolist_actions {
317 .repolist_actions {
318 display: inline-block;
318 display: inline-block;
319 }
319 }
320 }
320 }
321
321
322 }
322 }
323
323
324 .pullrequests_section_head {
324 .pullrequests_section_head {
325 display: block;
325 display: block;
326 clear: both;
326 clear: both;
327 margin: @padding 0;
327 margin: @padding 0;
328 font-family: @text-bold;
328 font-family: @text-bold;
329 }
329 }
330
330
331 .pr-origininfo, .pr-targetinfo {
331 .pr-origininfo, .pr-targetinfo {
332 position: relative;
332 position: relative;
333
333
334 .tag {
334 .tag {
335 display: inline-block;
335 display: inline-block;
336 margin: 0 1em .5em 0;
336 margin: 0 1em .5em 0;
337 }
337 }
338
338
339 .clone-url {
339 .clone-url {
340 display: inline-block;
340 display: inline-block;
341 margin: 0 0 .5em 0;
341 margin: 0 0 .5em 0;
342 padding: 0;
342 padding: 0;
343 line-height: 1.2em;
343 line-height: 1.2em;
344 }
344 }
345 }
345 }
346
346
347 .pr-pullinfo {
347 .pr-pullinfo {
348 clear: both;
348 clear: both;
349 margin: .5em 0;
349 margin: .5em 0;
350 }
350 }
351
351
352 #pr-title-input {
352 #pr-title-input {
353 width: 72%;
353 width: 72%;
354 font-size: 1em;
354 font-size: 1em;
355 font-family: @text-bold;
355 font-family: @text-bold;
356 margin: 0;
356 margin: 0;
357 padding: 0 0 0 @padding/4;
357 padding: 0 0 0 @padding/4;
358 line-height: 1.7em;
358 line-height: 1.7em;
359 color: @text-color;
359 color: @text-color;
360 letter-spacing: .02em;
360 letter-spacing: .02em;
361 }
361 }
362
362
363 #pullrequest_title {
363 #pullrequest_title {
364 width: 100%;
364 width: 100%;
365 box-sizing: border-box;
365 box-sizing: border-box;
366 }
366 }
367
367
368 #pr_open_message {
368 #pr_open_message {
369 border: @border-thickness solid #fff;
369 border: @border-thickness solid #fff;
370 border-radius: @border-radius;
370 border-radius: @border-radius;
371 padding: @padding-large-vertical @padding-large-vertical @padding-large-vertical 0;
371 padding: @padding-large-vertical @padding-large-vertical @padding-large-vertical 0;
372 text-align: right;
372 text-align: right;
373 overflow: hidden;
373 overflow: hidden;
374 }
374 }
375
375
376 .pr-submit-button {
376 .pr-submit-button {
377 float: right;
377 float: right;
378 margin: 0 0 0 5px;
378 margin: 0 0 0 5px;
379 }
379 }
380
380
381 .pr-spacing-container {
381 .pr-spacing-container {
382 padding: 20px;
382 padding: 20px;
383 clear: both
383 clear: both
384 }
384 }
385
385
386 #pr-description-input {
386 #pr-description-input {
387 margin-bottom: 0;
387 margin-bottom: 0;
388 }
388 }
389
389
390 .pr-description-label {
390 .pr-description-label {
391 vertical-align: top;
391 vertical-align: top;
392 }
392 }
393
393
394 .perms_section_head {
394 .perms_section_head {
395 min-width: 625px;
395 min-width: 625px;
396
396
397 h2 {
397 h2 {
398 margin-bottom: 0;
398 margin-bottom: 0;
399 }
399 }
400
400
401 .label-checkbox {
401 .label-checkbox {
402 float: left;
402 float: left;
403 }
403 }
404
404
405 &.field {
405 &.field {
406 margin: @space 0 @padding;
406 margin: @space 0 @padding;
407 }
407 }
408
408
409 &:first-child.field {
409 &:first-child.field {
410 margin-top: 0;
410 margin-top: 0;
411
411
412 .label {
412 .label {
413 margin-top: 0;
413 margin-top: 0;
414 padding-top: 0;
414 padding-top: 0;
415 }
415 }
416
416
417 .radios {
417 .radios {
418 padding-top: 0;
418 padding-top: 0;
419 }
419 }
420 }
420 }
421
421
422 .radios {
422 .radios {
423 float: right;
423 float: right;
424 position: relative;
424 position: relative;
425 width: 405px;
425 width: 405px;
426 }
426 }
427 }
427 }
428
428
429 //--- MODULES ------------------//
429 //--- MODULES ------------------//
430
430
431
431
432 // Fixed Sidebar Column
432 // Fixed Sidebar Column
433 .sidebar-col-wrapper {
433 .sidebar-col-wrapper {
434 padding-left: @sidebar-all-width;
434 padding-left: @sidebar-all-width;
435
435
436 .sidebar {
436 .sidebar {
437 width: @sidebar-width;
437 width: @sidebar-width;
438 margin-left: -@sidebar-all-width;
438 margin-left: -@sidebar-all-width;
439 }
439 }
440 }
440 }
441
441
442 .sidebar-col-wrapper.scw-small {
442 .sidebar-col-wrapper.scw-small {
443 padding-left: @sidebar-small-all-width;
443 padding-left: @sidebar-small-all-width;
444
444
445 .sidebar {
445 .sidebar {
446 width: @sidebar-small-width;
446 width: @sidebar-small-width;
447 margin-left: -@sidebar-small-all-width;
447 margin-left: -@sidebar-small-all-width;
448 }
448 }
449 }
449 }
450
450
451
451
452 // FOOTER
452 // FOOTER
453 #footer {
453 #footer {
454 padding: 0;
454 padding: 0;
455 text-align: center;
455 text-align: center;
456 vertical-align: middle;
456 vertical-align: middle;
457 color: @grey2;
457 color: @grey2;
458 background-color: @grey6;
458 background-color: @grey6;
459
459
460 p {
460 p {
461 margin: 0;
461 margin: 0;
462 padding: 1em;
462 padding: 1em;
463 line-height: 1em;
463 line-height: 1em;
464 }
464 }
465
465
466 .server-instance { //server instance
466 .server-instance { //server instance
467 display: none;
467 display: none;
468 }
468 }
469
469
470 .title {
470 .title {
471 float: none;
471 float: none;
472 margin: 0 auto;
472 margin: 0 auto;
473 }
473 }
474 }
474 }
475
475
476 button.close {
476 button.close {
477 padding: 0;
477 padding: 0;
478 cursor: pointer;
478 cursor: pointer;
479 background: transparent;
479 background: transparent;
480 border: 0;
480 border: 0;
481 .box-shadow(none);
481 .box-shadow(none);
482 -webkit-appearance: none;
482 -webkit-appearance: none;
483 }
483 }
484
484
485 .close {
485 .close {
486 float: right;
486 float: right;
487 font-size: 21px;
487 font-size: 21px;
488 font-family: @text-bootstrap;
488 font-family: @text-bootstrap;
489 line-height: 1em;
489 line-height: 1em;
490 font-weight: bold;
490 font-weight: bold;
491 color: @grey2;
491 color: @grey2;
492
492
493 &:hover,
493 &:hover,
494 &:focus {
494 &:focus {
495 color: @grey1;
495 color: @grey1;
496 text-decoration: none;
496 text-decoration: none;
497 cursor: pointer;
497 cursor: pointer;
498 }
498 }
499 }
499 }
500
500
501 // GRID
501 // GRID
502 .sorting,
502 .sorting,
503 .sorting_desc,
503 .sorting_desc,
504 .sorting_asc {
504 .sorting_asc {
505 cursor: pointer;
505 cursor: pointer;
506 }
506 }
507 .sorting_desc:after {
507 .sorting_desc:after {
508 content: "\00A0\25B2";
508 content: "\00A0\25B2";
509 font-size: .75em;
509 font-size: .75em;
510 }
510 }
511 .sorting_asc:after {
511 .sorting_asc:after {
512 content: "\00A0\25BC";
512 content: "\00A0\25BC";
513 font-size: .68em;
513 font-size: .68em;
514 }
514 }
515
515
516
516
517 .user_auth_tokens {
517 .user_auth_tokens {
518
518
519 &.truncate {
519 &.truncate {
520 white-space: nowrap;
520 white-space: nowrap;
521 overflow: hidden;
521 overflow: hidden;
522 text-overflow: ellipsis;
522 text-overflow: ellipsis;
523 }
523 }
524
524
525 .fields .field .input {
525 .fields .field .input {
526 margin: 0;
526 margin: 0;
527 }
527 }
528
528
529 input#description {
529 input#description {
530 width: 100px;
530 width: 100px;
531 margin: 0;
531 margin: 0;
532 }
532 }
533
533
534 .drop-menu {
534 .drop-menu {
535 // TODO: johbo: Remove this, should work out of the box when
535 // TODO: johbo: Remove this, should work out of the box when
536 // having multiple inputs inline
536 // having multiple inputs inline
537 margin: 0 0 0 5px;
537 margin: 0 0 0 5px;
538 }
538 }
539 }
539 }
540 #user_list_table {
540 #user_list_table {
541 .closed {
541 .closed {
542 background-color: @grey6;
542 background-color: @grey6;
543 }
543 }
544 }
544 }
545
545
546
546
547 input {
547 input {
548 &.disabled {
548 &.disabled {
549 opacity: .5;
549 opacity: .5;
550 }
550 }
551 }
551 }
552
552
553 // remove extra padding in firefox
553 // remove extra padding in firefox
554 input::-moz-focus-inner { border:0; padding:0 }
554 input::-moz-focus-inner { border:0; padding:0 }
555
555
556 .adjacent input {
556 .adjacent input {
557 margin-bottom: @padding;
557 margin-bottom: @padding;
558 }
558 }
559
559
560 .permissions_boxes {
560 .permissions_boxes {
561 display: block;
561 display: block;
562 }
562 }
563
563
564 //TODO: lisa: this should be in tables
564 //TODO: lisa: this should be in tables
565 .show_more_col {
565 .show_more_col {
566 width: 20px;
566 width: 20px;
567 }
567 }
568
568
569 //FORMS
569 //FORMS
570
570
571 .medium-inline,
571 .medium-inline,
572 input#description.medium-inline {
572 input#description.medium-inline {
573 display: inline;
573 display: inline;
574 width: @medium-inline-input-width;
574 width: @medium-inline-input-width;
575 min-width: 100px;
575 min-width: 100px;
576 }
576 }
577
577
578 select {
578 select {
579 //reset
579 //reset
580 -webkit-appearance: none;
580 -webkit-appearance: none;
581 -moz-appearance: none;
581 -moz-appearance: none;
582
582
583 display: inline-block;
583 display: inline-block;
584 height: 28px;
584 height: 28px;
585 width: auto;
585 width: auto;
586 margin: 0 @padding @padding 0;
586 margin: 0 @padding @padding 0;
587 padding: 0 18px 0 8px;
587 padding: 0 18px 0 8px;
588 line-height:1em;
588 line-height:1em;
589 font-size: @basefontsize;
589 font-size: @basefontsize;
590 border: @border-thickness solid @rcblue;
590 border: @border-thickness solid @rcblue;
591 background:white url("../images/dt-arrow-dn.png") no-repeat 100% 50%;
591 background:white url("../images/dt-arrow-dn.png") no-repeat 100% 50%;
592 color: @rcblue;
592 color: @rcblue;
593
593
594 &:after {
594 &:after {
595 content: "\00A0\25BE";
595 content: "\00A0\25BE";
596 }
596 }
597
597
598 &:focus {
598 &:focus {
599 outline: none;
599 outline: none;
600 }
600 }
601 }
601 }
602
602
603 option {
603 option {
604 &:focus {
604 &:focus {
605 outline: none;
605 outline: none;
606 }
606 }
607 }
607 }
608
608
609 input,
609 input,
610 textarea {
610 textarea {
611 padding: @input-padding;
611 padding: @input-padding;
612 border: @input-border-thickness solid @border-highlight-color;
612 border: @input-border-thickness solid @border-highlight-color;
613 .border-radius (@border-radius);
613 .border-radius (@border-radius);
614 font-family: @text-light;
614 font-family: @text-light;
615 font-size: @basefontsize;
615 font-size: @basefontsize;
616
616
617 &.input-sm {
617 &.input-sm {
618 padding: 5px;
618 padding: 5px;
619 }
619 }
620
620
621 &#description {
621 &#description {
622 min-width: @input-description-minwidth;
622 min-width: @input-description-minwidth;
623 min-height: 1em;
623 min-height: 1em;
624 padding: 10px;
624 padding: 10px;
625 }
625 }
626 }
626 }
627
627
628 .field-sm {
628 .field-sm {
629 input,
629 input,
630 textarea {
630 textarea {
631 padding: 5px;
631 padding: 5px;
632 }
632 }
633 }
633 }
634
634
635 textarea {
635 textarea {
636 display: block;
636 display: block;
637 clear: both;
637 clear: both;
638 width: 100%;
638 width: 100%;
639 min-height: 100px;
639 min-height: 100px;
640 margin-bottom: @padding;
640 margin-bottom: @padding;
641 .box-sizing(border-box);
641 .box-sizing(border-box);
642 overflow: auto;
642 overflow: auto;
643 }
643 }
644
644
645 label {
645 label {
646 font-family: @text-light;
646 font-family: @text-light;
647 }
647 }
648
648
649 // GRAVATARS
649 // GRAVATARS
650 // centers gravatar on username to the right
650 // centers gravatar on username to the right
651
651
652 .gravatar {
652 .gravatar {
653 display: inline;
653 display: inline;
654 min-width: 16px;
654 min-width: 16px;
655 min-height: 16px;
655 min-height: 16px;
656 margin: -5px 0;
656 margin: -5px 0;
657 padding: 0;
657 padding: 0;
658 line-height: 1em;
658 line-height: 1em;
659 border: 1px solid @grey4;
659 border: 1px solid @grey4;
660
660
661 &.gravatar-large {
661 &.gravatar-large {
662 margin: -0.5em .25em -0.5em 0;
662 margin: -0.5em .25em -0.5em 0;
663 }
663 }
664
664
665 & + .user {
665 & + .user {
666 display: inline;
666 display: inline;
667 margin: 0;
667 margin: 0;
668 padding: 0 0 0 .17em;
668 padding: 0 0 0 .17em;
669 line-height: 1em;
669 line-height: 1em;
670 }
670 }
671 }
671 }
672
672
673 .rc-user { // gravatar + user wrapper
673 .rc-user { // gravatar + user wrapper
674 float: left;
674 position: relative;
675 position: relative;
675 min-width: 100px;
676 min-width: 100px;
676 max-width: 200px;
677 max-width: 200px;
677 min-height: (@gravatar-size + @border-thickness * 2); // account for border
678 min-height: (@gravatar-size + @border-thickness * 2); // account for border
678 display: block;
679 display: block;
679 padding: 0 0 0 (@gravatar-size + @basefontsize/2 + @border-thickness * 2);
680 padding: 0 0 0 (@gravatar-size + @basefontsize/2 + @border-thickness * 2);
680
681
681
682
682 .gravatar {
683 .gravatar {
683 display: block;
684 display: block;
684 position: absolute;
685 position: absolute;
685 top: 0;
686 top: 0;
686 left: 0;
687 left: 0;
687 min-width: @gravatar-size;
688 min-width: @gravatar-size;
688 min-height: @gravatar-size;
689 min-height: @gravatar-size;
689 margin: 0;
690 margin: 0;
690 }
691 }
691
692
692 .user {
693 .user {
693 display: block;
694 display: block;
694 max-width: 175px;
695 max-width: 175px;
695 padding-top: 2px;
696 padding-top: 2px;
696 overflow: hidden;
697 overflow: hidden;
697 text-overflow: ellipsis;
698 text-overflow: ellipsis;
698 }
699 }
699 }
700 }
700
701
701 .gist-gravatar,
702 .gist-gravatar,
702 .journal_container {
703 .journal_container {
703 .gravatar-large {
704 .gravatar-large {
704 margin: 0 .5em -10px 0;
705 margin: 0 .5em -10px 0;
705 }
706 }
706 }
707 }
707
708
708
709
709 // ADMIN SETTINGS
710 // ADMIN SETTINGS
710
711
711 // Tag Patterns
712 // Tag Patterns
712 .tag_patterns {
713 .tag_patterns {
713 .tag_input {
714 .tag_input {
714 margin-bottom: @padding;
715 margin-bottom: @padding;
715 }
716 }
716 }
717 }
717
718
718 .locked_input {
719 .locked_input {
719 position: relative;
720 position: relative;
720
721
721 input {
722 input {
722 display: inline;
723 display: inline;
723 margin-top: 3px;
724 margin-top: 3px;
724 }
725 }
725
726
726 br {
727 br {
727 display: none;
728 display: none;
728 }
729 }
729
730
730 .error-message {
731 .error-message {
731 float: left;
732 float: left;
732 width: 100%;
733 width: 100%;
733 }
734 }
734
735
735 .lock_input_button {
736 .lock_input_button {
736 display: inline;
737 display: inline;
737 }
738 }
738
739
739 .help-block {
740 .help-block {
740 clear: both;
741 clear: both;
741 }
742 }
742 }
743 }
743
744
744 // Notifications
745 // Notifications
745
746
746 .notifications_buttons {
747 .notifications_buttons {
747 margin: 0 0 @space 0;
748 margin: 0 0 @space 0;
748 padding: 0;
749 padding: 0;
749
750
750 .btn {
751 .btn {
751 display: inline-block;
752 display: inline-block;
752 }
753 }
753 }
754 }
754
755
755 .notification-list {
756 .notification-list {
756
757
757 div {
758 div {
758 display: inline-block;
759 display: inline-block;
759 vertical-align: middle;
760 vertical-align: middle;
760 }
761 }
761
762
762 .container {
763 .container {
763 display: block;
764 display: block;
764 margin: 0 0 @padding 0;
765 margin: 0 0 @padding 0;
765 }
766 }
766
767
767 .delete-notifications {
768 .delete-notifications {
768 margin-left: @padding;
769 margin-left: @padding;
769 text-align: right;
770 text-align: right;
770 cursor: pointer;
771 cursor: pointer;
771 }
772 }
772
773
773 .read-notifications {
774 .read-notifications {
774 margin-left: @padding/2;
775 margin-left: @padding/2;
775 text-align: right;
776 text-align: right;
776 width: 35px;
777 width: 35px;
777 cursor: pointer;
778 cursor: pointer;
778 }
779 }
779
780
780 .icon-minus-sign {
781 .icon-minus-sign {
781 color: @alert2;
782 color: @alert2;
782 }
783 }
783
784
784 .icon-ok-sign {
785 .icon-ok-sign {
785 color: @alert1;
786 color: @alert1;
786 }
787 }
787 }
788 }
788
789
789 .user_settings {
790 .user_settings {
790 float: left;
791 float: left;
791 clear: both;
792 clear: both;
792 display: block;
793 display: block;
793 width: 100%;
794 width: 100%;
794
795
795 .gravatar_box {
796 .gravatar_box {
796 margin-bottom: @padding;
797 margin-bottom: @padding;
797
798
798 &:after {
799 &:after {
799 content: " ";
800 content: " ";
800 clear: both;
801 clear: both;
801 width: 100%;
802 width: 100%;
802 }
803 }
803 }
804 }
804
805
805 .fields .field {
806 .fields .field {
806 clear: both;
807 clear: both;
807 }
808 }
808 }
809 }
809
810
810 .advanced_settings {
811 .advanced_settings {
811 margin-bottom: @space;
812 margin-bottom: @space;
812
813
813 .help-block {
814 .help-block {
814 margin-left: 0;
815 margin-left: 0;
815 }
816 }
816
817
817 button + .help-block {
818 button + .help-block {
818 margin-top: @padding;
819 margin-top: @padding;
819 }
820 }
820 }
821 }
821
822
822 // admin settings radio buttons and labels
823 // admin settings radio buttons and labels
823 .label-2 {
824 .label-2 {
824 float: left;
825 float: left;
825 width: @label2-width;
826 width: @label2-width;
826
827
827 label {
828 label {
828 color: @grey1;
829 color: @grey1;
829 }
830 }
830 }
831 }
831 .checkboxes {
832 .checkboxes {
832 float: left;
833 float: left;
833 width: @checkboxes-width;
834 width: @checkboxes-width;
834 margin-bottom: @padding;
835 margin-bottom: @padding;
835
836
836 .checkbox {
837 .checkbox {
837 width: 100%;
838 width: 100%;
838
839
839 label {
840 label {
840 margin: 0;
841 margin: 0;
841 padding: 0;
842 padding: 0;
842 }
843 }
843 }
844 }
844
845
845 .checkbox + .checkbox {
846 .checkbox + .checkbox {
846 display: inline-block;
847 display: inline-block;
847 }
848 }
848
849
849 label {
850 label {
850 margin-right: 1em;
851 margin-right: 1em;
851 }
852 }
852 }
853 }
853
854
854 // CHANGELOG
855 // CHANGELOG
855 .container_header {
856 .container_header {
856 float: left;
857 float: left;
857 display: block;
858 display: block;
858 width: 100%;
859 width: 100%;
859 margin: @padding 0 @padding;
860 margin: @padding 0 @padding;
860
861
861 #filter_changelog {
862 #filter_changelog {
862 float: left;
863 float: left;
863 margin-right: @padding;
864 margin-right: @padding;
864 }
865 }
865
866
866 .breadcrumbs_light {
867 .breadcrumbs_light {
867 display: inline-block;
868 display: inline-block;
868 }
869 }
869 }
870 }
870
871
871 .info_box {
872 .info_box {
872 float: right;
873 float: right;
873 }
874 }
874
875
875
876
876 #graph_nodes {
877 #graph_nodes {
877 padding-top: 43px;
878 padding-top: 43px;
878 }
879 }
879
880
880 #graph_content{
881 #graph_content{
881
882
882 // adjust for table headers so that graph renders properly
883 // adjust for table headers so that graph renders properly
883 // #graph_nodes padding - table cell padding
884 // #graph_nodes padding - table cell padding
884 padding-top: (@space - (@basefontsize * 2.4));
885 padding-top: (@space - (@basefontsize * 2.4));
885
886
886 &.graph_full_width {
887 &.graph_full_width {
887 width: 100%;
888 width: 100%;
888 max-width: 100%;
889 max-width: 100%;
889 }
890 }
890 }
891 }
891
892
892 #graph {
893 #graph {
893 .flag_status {
894 .flag_status {
894 margin: 0;
895 margin: 0;
895 }
896 }
896
897
897 .pagination-left {
898 .pagination-left {
898 float: left;
899 float: left;
899 clear: both;
900 clear: both;
900 }
901 }
901
902
902 .log-container {
903 .log-container {
903 max-width: 345px;
904 max-width: 345px;
904
905
905 .message{
906 .message{
906 max-width: 340px;
907 max-width: 340px;
907 }
908 }
908 }
909 }
909
910
910 .graph-col-wrapper {
911 .graph-col-wrapper {
911 padding-left: 110px;
912 padding-left: 110px;
912
913
913 #graph_nodes {
914 #graph_nodes {
914 width: 100px;
915 width: 100px;
915 margin-left: -110px;
916 margin-left: -110px;
916 float: left;
917 float: left;
917 clear: left;
918 clear: left;
918 }
919 }
919 }
920 }
920 }
921 }
921
922
922 #filter_changelog {
923 #filter_changelog {
923 float: left;
924 float: left;
924 }
925 }
925
926
926
927
927 //--- THEME ------------------//
928 //--- THEME ------------------//
928
929
929 #logo {
930 #logo {
930 float: left;
931 float: left;
931 margin: 9px 0 0 0;
932 margin: 9px 0 0 0;
932
933
933 .header {
934 .header {
934 background-color: transparent;
935 background-color: transparent;
935 }
936 }
936
937
937 a {
938 a {
938 display: inline-block;
939 display: inline-block;
939 }
940 }
940
941
941 img {
942 img {
942 height:30px;
943 height:30px;
943 }
944 }
944 }
945 }
945
946
946 .logo-wrapper {
947 .logo-wrapper {
947 float:left;
948 float:left;
948 }
949 }
949
950
950 .branding{
951 .branding{
951 float: left;
952 float: left;
952 padding: 9px 2px;
953 padding: 9px 2px;
953 line-height: 1em;
954 line-height: 1em;
954 font-size: @navigation-fontsize;
955 font-size: @navigation-fontsize;
955 }
956 }
956
957
957 img {
958 img {
958 border: none;
959 border: none;
959 outline: none;
960 outline: none;
960 }
961 }
961 user-profile-header
962 user-profile-header
962 label {
963 label {
963
964
964 input[type="checkbox"] {
965 input[type="checkbox"] {
965 margin-right: 1em;
966 margin-right: 1em;
966 }
967 }
967 input[type="radio"] {
968 input[type="radio"] {
968 margin-right: 1em;
969 margin-right: 1em;
969 }
970 }
970 }
971 }
971
972
972 .flag_status {
973 .flag_status {
973 margin: 2px 8px 6px 2px;
974 margin: 2px 8px 6px 2px;
974 &.under_review {
975 &.under_review {
975 .circle(5px, @alert3);
976 .circle(5px, @alert3);
976 }
977 }
977 &.approved {
978 &.approved {
978 .circle(5px, @alert1);
979 .circle(5px, @alert1);
979 }
980 }
980 &.rejected,
981 &.rejected,
981 &.forced_closed{
982 &.forced_closed{
982 .circle(5px, @alert2);
983 .circle(5px, @alert2);
983 }
984 }
984 &.not_reviewed {
985 &.not_reviewed {
985 .circle(5px, @grey5);
986 .circle(5px, @grey5);
986 }
987 }
987 }
988 }
988
989
989 .flag_status_comment_box {
990 .flag_status_comment_box {
990 margin: 5px 6px 0px 2px;
991 margin: 5px 6px 0px 2px;
991 }
992 }
992 .test_pattern_preview {
993 .test_pattern_preview {
993 margin: @space 0;
994 margin: @space 0;
994
995
995 p {
996 p {
996 margin-bottom: 0;
997 margin-bottom: 0;
997 border-bottom: @border-thickness solid @border-default-color;
998 border-bottom: @border-thickness solid @border-default-color;
998 color: @grey3;
999 color: @grey3;
999 }
1000 }
1000
1001
1001 .btn {
1002 .btn {
1002 margin-bottom: @padding;
1003 margin-bottom: @padding;
1003 }
1004 }
1004 }
1005 }
1005 #test_pattern_result {
1006 #test_pattern_result {
1006 display: none;
1007 display: none;
1007 &:extend(pre);
1008 &:extend(pre);
1008 padding: .9em;
1009 padding: .9em;
1009 color: @grey3;
1010 color: @grey3;
1010 background-color: @grey7;
1011 background-color: @grey7;
1011 border-right: @border-thickness solid @border-default-color;
1012 border-right: @border-thickness solid @border-default-color;
1012 border-bottom: @border-thickness solid @border-default-color;
1013 border-bottom: @border-thickness solid @border-default-color;
1013 border-left: @border-thickness solid @border-default-color;
1014 border-left: @border-thickness solid @border-default-color;
1014 }
1015 }
1015
1016
1016 #repo_vcs_settings {
1017 #repo_vcs_settings {
1017 #inherit_overlay_vcs_default {
1018 #inherit_overlay_vcs_default {
1018 display: none;
1019 display: none;
1019 }
1020 }
1020 #inherit_overlay_vcs_custom {
1021 #inherit_overlay_vcs_custom {
1021 display: custom;
1022 display: custom;
1022 }
1023 }
1023 &.inherited {
1024 &.inherited {
1024 #inherit_overlay_vcs_default {
1025 #inherit_overlay_vcs_default {
1025 display: block;
1026 display: block;
1026 }
1027 }
1027 #inherit_overlay_vcs_custom {
1028 #inherit_overlay_vcs_custom {
1028 display: none;
1029 display: none;
1029 }
1030 }
1030 }
1031 }
1031 }
1032 }
1032
1033
1033 .issue-tracker-link {
1034 .issue-tracker-link {
1034 color: @rcblue;
1035 color: @rcblue;
1035 }
1036 }
1036
1037
1037 // Issue Tracker Table Show/Hide
1038 // Issue Tracker Table Show/Hide
1038 #repo_issue_tracker {
1039 #repo_issue_tracker {
1039 #inherit_overlay {
1040 #inherit_overlay {
1040 display: none;
1041 display: none;
1041 }
1042 }
1042 #custom_overlay {
1043 #custom_overlay {
1043 display: custom;
1044 display: custom;
1044 }
1045 }
1045 &.inherited {
1046 &.inherited {
1046 #inherit_overlay {
1047 #inherit_overlay {
1047 display: block;
1048 display: block;
1048 }
1049 }
1049 #custom_overlay {
1050 #custom_overlay {
1050 display: none;
1051 display: none;
1051 }
1052 }
1052 }
1053 }
1053 }
1054 }
1054 table.issuetracker {
1055 table.issuetracker {
1055 &.readonly {
1056 &.readonly {
1056 tr, td {
1057 tr, td {
1057 color: @grey3;
1058 color: @grey3;
1058 }
1059 }
1059 }
1060 }
1060 .edit {
1061 .edit {
1061 display: none;
1062 display: none;
1062 }
1063 }
1063 .editopen {
1064 .editopen {
1064 .edit {
1065 .edit {
1065 display: inline;
1066 display: inline;
1066 }
1067 }
1067 .entry {
1068 .entry {
1068 display: none;
1069 display: none;
1069 }
1070 }
1070 }
1071 }
1071 tr td.td-action {
1072 tr td.td-action {
1072 min-width: 117px;
1073 min-width: 117px;
1073 }
1074 }
1074 td input {
1075 td input {
1075 max-width: none;
1076 max-width: none;
1076 min-width: 30px;
1077 min-width: 30px;
1077 width: 80%;
1078 width: 80%;
1078 }
1079 }
1079 .issuetracker_pref input {
1080 .issuetracker_pref input {
1080 width: 40%;
1081 width: 40%;
1081 }
1082 }
1082 input.edit_issuetracker_update {
1083 input.edit_issuetracker_update {
1083 margin-right: 0;
1084 margin-right: 0;
1084 width: auto;
1085 width: auto;
1085 }
1086 }
1086 }
1087 }
1087
1088
1088 //Permissions Settings
1089 //Permissions Settings
1089 #add_perm {
1090 #add_perm {
1090 margin: 0 0 @padding;
1091 margin: 0 0 @padding;
1091 cursor: pointer;
1092 cursor: pointer;
1092 }
1093 }
1093
1094
1094 .perm_ac {
1095 .perm_ac {
1095 input {
1096 input {
1096 width: 95%;
1097 width: 95%;
1097 }
1098 }
1098 }
1099 }
1099
1100
1100 .autocomplete-suggestions {
1101 .autocomplete-suggestions {
1101 width: auto !important; // overrides autocomplete.js
1102 width: auto !important; // overrides autocomplete.js
1102 margin: 0;
1103 margin: 0;
1103 border: @border-thickness solid @rcblue;
1104 border: @border-thickness solid @rcblue;
1104 border-radius: @border-radius;
1105 border-radius: @border-radius;
1105 color: @rcblue;
1106 color: @rcblue;
1106 background-color: white;
1107 background-color: white;
1107 }
1108 }
1108 .autocomplete-selected {
1109 .autocomplete-selected {
1109 background: #F0F0F0;
1110 background: #F0F0F0;
1110 }
1111 }
1111 .ac-container-wrap {
1112 .ac-container-wrap {
1112 margin: 0;
1113 margin: 0;
1113 padding: 8px;
1114 padding: 8px;
1114 border-bottom: @border-thickness solid @rclightblue;
1115 border-bottom: @border-thickness solid @rclightblue;
1115 list-style-type: none;
1116 list-style-type: none;
1116 cursor: pointer;
1117 cursor: pointer;
1117
1118
1118 &:hover {
1119 &:hover {
1119 background-color: @rclightblue;
1120 background-color: @rclightblue;
1120 }
1121 }
1121
1122
1122 img {
1123 img {
1123 margin-right: 1em;
1124 margin-right: 1em;
1124 }
1125 }
1125
1126
1126 strong {
1127 strong {
1127 font-weight: normal;
1128 font-weight: normal;
1128 }
1129 }
1129 }
1130 }
1130
1131
1131 // Settings Dropdown
1132 // Settings Dropdown
1132 .user-menu .container {
1133 .user-menu .container {
1133 padding: 0 4px;
1134 padding: 0 4px;
1134 margin: 0;
1135 margin: 0;
1135 }
1136 }
1136
1137
1137 .user-menu .gravatar {
1138 .user-menu .gravatar {
1138 cursor: pointer;
1139 cursor: pointer;
1139 }
1140 }
1140
1141
1141 .codeblock {
1142 .codeblock {
1142 margin-bottom: @padding;
1143 margin-bottom: @padding;
1143 clear: both;
1144 clear: both;
1144
1145
1145 .stats{
1146 .stats{
1146 overflow: hidden;
1147 overflow: hidden;
1147 }
1148 }
1148
1149
1149 .message{
1150 .message{
1150 textarea{
1151 textarea{
1151 margin: 0;
1152 margin: 0;
1152 }
1153 }
1153 }
1154 }
1154
1155
1155 .code-header {
1156 .code-header {
1156 .stats {
1157 .stats {
1157 line-height: 2em;
1158 line-height: 2em;
1158
1159
1159 .revision_id {
1160 .revision_id {
1160 margin-left: 0;
1161 margin-left: 0;
1161 }
1162 }
1162 .buttons {
1163 .buttons {
1163 padding-right: 0;
1164 padding-right: 0;
1164 }
1165 }
1165 }
1166 }
1166
1167
1167 .item{
1168 .item{
1168 margin-right: 0.5em;
1169 margin-right: 0.5em;
1169 }
1170 }
1170 }
1171 }
1171
1172
1172 #editor_container{
1173 #editor_container{
1173 position: relative;
1174 position: relative;
1174 margin: @padding;
1175 margin: @padding;
1175 }
1176 }
1176 }
1177 }
1177
1178
1178 #file_history_container {
1179 #file_history_container {
1179 display: none;
1180 display: none;
1180 }
1181 }
1181
1182
1182 .file-history-inner {
1183 .file-history-inner {
1183 margin-bottom: 10px;
1184 margin-bottom: 10px;
1184 }
1185 }
1185
1186
1186 // Pull Requests
1187 // Pull Requests
1187 .summary-details {
1188 .summary-details {
1188 width: 72%;
1189 width: 72%;
1189 }
1190 }
1190 .pr-summary {
1191 .pr-summary {
1191 border-bottom: @border-thickness solid @grey5;
1192 border-bottom: @border-thickness solid @grey5;
1192 margin-bottom: @space;
1193 margin-bottom: @space;
1193 }
1194 }
1194 .reviewers-title {
1195 .reviewers-title {
1195 width: 25%;
1196 width: 25%;
1196 min-width: 200px;
1197 min-width: 200px;
1197 }
1198 }
1198 .reviewers {
1199 .reviewers {
1199 width: 25%;
1200 width: 25%;
1200 min-width: 200px;
1201 min-width: 200px;
1201 }
1202 }
1202 .reviewers ul li {
1203 .reviewers ul li {
1203 position: relative;
1204 position: relative;
1204 width: 100%;
1205 width: 100%;
1205 margin-bottom: 8px;
1206 margin-bottom: 8px;
1206 }
1207 }
1207 .reviewers_member {
1208 .reviewers_member {
1208 width: 100%;
1209 width: 100%;
1209 overflow: auto;
1210 overflow: auto;
1210 }
1211 }
1211 .reviewer_status {
1212 .reviewer_status {
1212 display: inline-block;
1213 display: inline-block;
1213 vertical-align: top;
1214 vertical-align: top;
1214 width: 7%;
1215 width: 7%;
1215 min-width: 20px;
1216 min-width: 20px;
1216 height: 1.2em;
1217 height: 1.2em;
1217 margin-top: 3px;
1218 margin-top: 3px;
1218 line-height: 1em;
1219 line-height: 1em;
1219 }
1220 }
1220
1221
1221 .reviewer_name {
1222 .reviewer_name {
1222 display: inline-block;
1223 display: inline-block;
1223 max-width: 83%;
1224 max-width: 83%;
1224 padding-right: 20px;
1225 padding-right: 20px;
1225 vertical-align: middle;
1226 vertical-align: middle;
1227 line-height: 1;
1228
1229 .rc-user {
1230 min-width: 0;
1231 margin: -2px 1em 0 0;
1232 }
1233
1234 .reviewer {
1235 float: left;
1236 }
1226 }
1237 }
1227
1238
1228 .reviewer_member_remove {
1239 .reviewer_member_remove {
1229 position: absolute;
1240 position: absolute;
1230 right: 0;
1241 right: 0;
1231 top: 0;
1242 top: 0;
1232 width: 16px;
1243 width: 16px;
1233 margin-bottom: 10px;
1244 margin-bottom: 10px;
1234 padding: 0;
1245 padding: 0;
1235 color: black;
1246 color: black;
1236 }
1247 }
1237 .reviewer_member_status {
1248 .reviewer_member_status {
1238 margin-top: 5px;
1249 margin-top: 5px;
1239 }
1250 }
1240 .pr-summary #summary{
1251 .pr-summary #summary{
1241 width: 100%;
1252 width: 100%;
1242 }
1253 }
1243 .pr-summary .action_button:hover {
1254 .pr-summary .action_button:hover {
1244 border: 0;
1255 border: 0;
1245 cursor: pointer;
1256 cursor: pointer;
1246 }
1257 }
1247 .pr-details-title {
1258 .pr-details-title {
1248 padding-bottom: 8px;
1259 padding-bottom: 8px;
1249 border-bottom: @border-thickness solid @grey5;
1260 border-bottom: @border-thickness solid @grey5;
1250 .action_button {
1261 .action_button {
1251 color: @rcblue;
1262 color: @rcblue;
1252 }
1263 }
1253 }
1264 }
1254 .pr-details-content {
1265 .pr-details-content {
1255 margin-top: @textmargin;
1266 margin-top: @textmargin;
1256 margin-bottom: @textmargin/2;
1267 margin-bottom: @textmargin;
1257 }
1268 }
1258 .pr-description {
1269 .pr-description {
1259 white-space:pre-wrap;
1270 white-space:pre-wrap;
1260 }
1271 }
1261 .group_members {
1272 .group_members {
1262 margin-top: 0;
1273 margin-top: 0;
1263 padding: 0;
1274 padding: 0;
1264 list-style: outside none none;
1275 list-style: outside none none;
1265 }
1276 }
1266 .reviewer_ac .ac-input {
1277 .reviewer_ac .ac-input {
1267 width: 92%;
1278 width: 92%;
1268 margin-bottom: 1em;
1279 margin-bottom: 1em;
1269 }
1280 }
1270 #update_commits {
1281 #update_commits {
1271 float: right;
1282 float: right;
1272 }
1283 }
1273 .compare_view_commits tr{
1284 .compare_view_commits tr{
1274 height: 20px;
1285 height: 20px;
1275 }
1286 }
1276 .compare_view_commits td {
1287 .compare_view_commits td {
1277 vertical-align: top;
1288 vertical-align: top;
1278 padding-top: 10px;
1289 padding-top: 10px;
1279 }
1290 }
1280 .compare_view_commits .author {
1291 .compare_view_commits .author {
1281 margin-left: 5px;
1292 margin-left: 5px;
1282 }
1293 }
1283
1294
1284 .compare_view_files {
1295 .compare_view_files {
1285 width: 100%;
1296 width: 100%;
1286
1297
1287 td {
1298 td {
1288 vertical-align: middle;
1299 vertical-align: middle;
1289 }
1300 }
1290 }
1301 }
1291
1302
1292 .compare_view_filepath {
1303 .compare_view_filepath {
1293 color: @grey1;
1304 color: @grey1;
1294 }
1305 }
1295
1306
1296 .show_more {
1307 .show_more {
1297 display: inline-block;
1308 display: inline-block;
1298 position: relative;
1309 position: relative;
1299 vertical-align: middle;
1310 vertical-align: middle;
1300 width: 4px;
1311 width: 4px;
1301 height: @basefontsize;
1312 height: @basefontsize;
1302
1313
1303 &:after {
1314 &:after {
1304 content: "\00A0\25BE";
1315 content: "\00A0\25BE";
1305 display: inline-block;
1316 display: inline-block;
1306 width:10px;
1317 width:10px;
1307 line-height: 5px;
1318 line-height: 5px;
1308 font-size: 12px;
1319 font-size: 12px;
1309 cursor: pointer;
1320 cursor: pointer;
1310 }
1321 }
1311 }
1322 }
1312
1323
1313 .journal_more .show_more {
1324 .journal_more .show_more {
1314 display: inline;
1325 display: inline;
1315
1326
1316 &:after {
1327 &:after {
1317 content: none;
1328 content: none;
1318 }
1329 }
1319 }
1330 }
1320
1331
1321 .open .show_more:after,
1332 .open .show_more:after,
1322 .select2-dropdown-open .show_more:after {
1333 .select2-dropdown-open .show_more:after {
1323 .rotate(180deg);
1334 .rotate(180deg);
1324 margin-left: 4px;
1335 margin-left: 4px;
1325 }
1336 }
1326
1337
1327
1338
1328 .compare_view_commits .collapse_commit:after {
1339 .compare_view_commits .collapse_commit:after {
1329 cursor: pointer;
1340 cursor: pointer;
1330 content: "\00A0\25B4";
1341 content: "\00A0\25B4";
1331 margin-left: -3px;
1342 margin-left: -3px;
1332 font-size: 17px;
1343 font-size: 17px;
1333 color: @grey4;
1344 color: @grey4;
1334 }
1345 }
1335
1346
1336 .diff_links {
1347 .diff_links {
1337 margin-left: 8px;
1348 margin-left: 8px;
1338 }
1349 }
1339
1350
1340 p.ancestor {
1351 p.ancestor {
1341 margin: @padding 0;
1352 margin: @padding 0;
1342 }
1353 }
1343
1354
1344 .cs_icon_td input[type="checkbox"] {
1355 .cs_icon_td input[type="checkbox"] {
1345 display: none;
1356 display: none;
1346 }
1357 }
1347
1358
1348 .cs_icon_td .expand_file_icon:after {
1359 .cs_icon_td .expand_file_icon:after {
1349 cursor: pointer;
1360 cursor: pointer;
1350 content: "\00A0\25B6";
1361 content: "\00A0\25B6";
1351 font-size: 12px;
1362 font-size: 12px;
1352 color: @grey4;
1363 color: @grey4;
1353 }
1364 }
1354
1365
1355 .cs_icon_td .collapse_file_icon:after {
1366 .cs_icon_td .collapse_file_icon:after {
1356 cursor: pointer;
1367 cursor: pointer;
1357 content: "\00A0\25BC";
1368 content: "\00A0\25BC";
1358 font-size: 12px;
1369 font-size: 12px;
1359 color: @grey4;
1370 color: @grey4;
1360 }
1371 }
1361
1372
1362 /*new binary
1373 /*new binary
1363 NEW_FILENODE = 1
1374 NEW_FILENODE = 1
1364 DEL_FILENODE = 2
1375 DEL_FILENODE = 2
1365 MOD_FILENODE = 3
1376 MOD_FILENODE = 3
1366 RENAMED_FILENODE = 4
1377 RENAMED_FILENODE = 4
1367 COPIED_FILENODE = 5
1378 COPIED_FILENODE = 5
1368 CHMOD_FILENODE = 6
1379 CHMOD_FILENODE = 6
1369 BIN_FILENODE = 7
1380 BIN_FILENODE = 7
1370 */
1381 */
1371 .cs_files_expand {
1382 .cs_files_expand {
1372 font-size: @basefontsize + 5px;
1383 font-size: @basefontsize + 5px;
1373 line-height: 1.8em;
1384 line-height: 1.8em;
1374 float: right;
1385 float: right;
1375 }
1386 }
1376
1387
1377 .cs_files_expand span{
1388 .cs_files_expand span{
1378 color: @rcblue;
1389 color: @rcblue;
1379 cursor: pointer;
1390 cursor: pointer;
1380 }
1391 }
1381 .cs_files {
1392 .cs_files {
1382 clear: both;
1393 clear: both;
1383 padding-bottom: @padding;
1394 padding-bottom: @padding;
1384
1395
1385 .cur_cs {
1396 .cur_cs {
1386 margin: 10px 2px;
1397 margin: 10px 2px;
1387 font-weight: bold;
1398 font-weight: bold;
1388 }
1399 }
1389
1400
1390 .node {
1401 .node {
1391 float: left;
1402 float: left;
1392 }
1403 }
1393
1404
1394 .changes {
1405 .changes {
1395 float: right;
1406 float: right;
1396 color: white;
1407 color: white;
1397 font-size: @basefontsize - 4px;
1408 font-size: @basefontsize - 4px;
1398 margin-top: 4px;
1409 margin-top: 4px;
1399 opacity: 0.6;
1410 opacity: 0.6;
1400 filter: Alpha(opacity=60); /* IE8 and earlier */
1411 filter: Alpha(opacity=60); /* IE8 and earlier */
1401
1412
1402 .added {
1413 .added {
1403 background-color: @alert1;
1414 background-color: @alert1;
1404 float: left;
1415 float: left;
1405 text-align: center;
1416 text-align: center;
1406 }
1417 }
1407
1418
1408 .deleted {
1419 .deleted {
1409 background-color: @alert2;
1420 background-color: @alert2;
1410 float: left;
1421 float: left;
1411 text-align: center;
1422 text-align: center;
1412 }
1423 }
1413
1424
1414 .bin {
1425 .bin {
1415 background-color: @alert1;
1426 background-color: @alert1;
1416 text-align: center;
1427 text-align: center;
1417 }
1428 }
1418
1429
1419 /*new binary*/
1430 /*new binary*/
1420 .bin.bin1 {
1431 .bin.bin1 {
1421 background-color: @alert1;
1432 background-color: @alert1;
1422 text-align: center;
1433 text-align: center;
1423 }
1434 }
1424
1435
1425 /*deleted binary*/
1436 /*deleted binary*/
1426 .bin.bin2 {
1437 .bin.bin2 {
1427 background-color: @alert2;
1438 background-color: @alert2;
1428 text-align: center;
1439 text-align: center;
1429 }
1440 }
1430
1441
1431 /*mod binary*/
1442 /*mod binary*/
1432 .bin.bin3 {
1443 .bin.bin3 {
1433 background-color: @grey2;
1444 background-color: @grey2;
1434 text-align: center;
1445 text-align: center;
1435 }
1446 }
1436
1447
1437 /*rename file*/
1448 /*rename file*/
1438 .bin.bin4 {
1449 .bin.bin4 {
1439 background-color: @alert4;
1450 background-color: @alert4;
1440 text-align: center;
1451 text-align: center;
1441 }
1452 }
1442
1453
1443 /*copied file*/
1454 /*copied file*/
1444 .bin.bin5 {
1455 .bin.bin5 {
1445 background-color: @alert4;
1456 background-color: @alert4;
1446 text-align: center;
1457 text-align: center;
1447 }
1458 }
1448
1459
1449 /*chmod file*/
1460 /*chmod file*/
1450 .bin.bin6 {
1461 .bin.bin6 {
1451 background-color: @grey2;
1462 background-color: @grey2;
1452 text-align: center;
1463 text-align: center;
1453 }
1464 }
1454 }
1465 }
1455 }
1466 }
1456
1467
1457 .cs_files .cs_added, .cs_files .cs_A,
1468 .cs_files .cs_added, .cs_files .cs_A,
1458 .cs_files .cs_added, .cs_files .cs_M,
1469 .cs_files .cs_added, .cs_files .cs_M,
1459 .cs_files .cs_added, .cs_files .cs_D {
1470 .cs_files .cs_added, .cs_files .cs_D {
1460 height: 16px;
1471 height: 16px;
1461 padding-right: 10px;
1472 padding-right: 10px;
1462 margin-top: 7px;
1473 margin-top: 7px;
1463 text-align: left;
1474 text-align: left;
1464 }
1475 }
1465
1476
1466 .cs_icon_td {
1477 .cs_icon_td {
1467 min-width: 16px;
1478 min-width: 16px;
1468 width: 16px;
1479 width: 16px;
1469 }
1480 }
1470
1481
1471 .pull-request-merge {
1482 .pull-request-merge {
1472 padding: 10px 0;
1483 padding: 10px 0;
1473 margin-top: 10px;
1484 margin-top: 10px;
1474 margin-bottom: 20px;
1485 margin-bottom: 20px;
1475 }
1486 }
1476
1487
1477 .pull-request-merge .pull-request-wrap {
1488 .pull-request-merge .pull-request-wrap {
1478 height: 25px;
1489 height: 25px;
1479 padding: 5px 0;
1490 padding: 5px 0;
1480 }
1491 }
1481
1492
1482 .pull-request-merge span {
1493 .pull-request-merge span {
1483 margin-right: 10px;
1494 margin-right: 10px;
1484 }
1495 }
1485 #close_pull_request {
1496 #close_pull_request {
1486 margin-right: 0px;
1497 margin-right: 0px;
1487 }
1498 }
1488
1499
1489 .empty_data {
1500 .empty_data {
1490 color: @grey4;
1501 color: @grey4;
1491 }
1502 }
1492
1503
1493 #changeset_compare_view_content {
1504 #changeset_compare_view_content {
1494 margin-bottom: @space;
1505 margin-bottom: @space;
1495 clear: both;
1506 clear: both;
1496 width: 100%;
1507 width: 100%;
1497 box-sizing: border-box;
1508 box-sizing: border-box;
1498 .border-radius(@border-radius);
1509 .border-radius(@border-radius);
1499
1510
1500 .help-block {
1511 .help-block {
1501 margin: @padding 0;
1512 margin: @padding 0;
1502 color: @text-color;
1513 color: @text-color;
1503 }
1514 }
1504
1515
1505 .empty_data {
1516 .empty_data {
1506 margin: @padding 0;
1517 margin: @padding 0;
1507 }
1518 }
1508
1519
1509 .alert {
1520 .alert {
1510 margin-bottom: @space;
1521 margin-bottom: @space;
1511 }
1522 }
1512 }
1523 }
1513
1524
1514 .table_disp {
1525 .table_disp {
1515 .status {
1526 .status {
1516 width: auto;
1527 width: auto;
1517
1528
1518 .flag_status {
1529 .flag_status {
1519 float: left;
1530 float: left;
1520 }
1531 }
1521 }
1532 }
1522 }
1533 }
1523
1534
1524 .status_box_menu {
1535 .status_box_menu {
1525 margin: 0;
1536 margin: 0;
1526 }
1537 }
1527
1538
1528 .notification-table{
1539 .notification-table{
1529 margin-bottom: @space;
1540 margin-bottom: @space;
1530 display: table;
1541 display: table;
1531 width: 100%;
1542 width: 100%;
1532
1543
1533 .container{
1544 .container{
1534 display: table-row;
1545 display: table-row;
1535
1546
1536 .notification-header{
1547 .notification-header{
1537 border-bottom: @border-thickness solid @border-default-color;
1548 border-bottom: @border-thickness solid @border-default-color;
1538 }
1549 }
1539
1550
1540 .notification-subject{
1551 .notification-subject{
1541 display: table-cell;
1552 display: table-cell;
1542 }
1553 }
1543 }
1554 }
1544 }
1555 }
1545
1556
1546 // Notifications
1557 // Notifications
1547 .notification-header{
1558 .notification-header{
1548 display: table;
1559 display: table;
1549 width: 100%;
1560 width: 100%;
1550 padding: floor(@basefontsize/2) 0;
1561 padding: floor(@basefontsize/2) 0;
1551 line-height: 1em;
1562 line-height: 1em;
1552
1563
1553 .desc, .delete-notifications, .read-notifications{
1564 .desc, .delete-notifications, .read-notifications{
1554 display: table-cell;
1565 display: table-cell;
1555 text-align: left;
1566 text-align: left;
1556 }
1567 }
1557
1568
1558 .desc{
1569 .desc{
1559 width: 1163px;
1570 width: 1163px;
1560 }
1571 }
1561
1572
1562 .delete-notifications, .read-notifications{
1573 .delete-notifications, .read-notifications{
1563 width: 35px;
1574 width: 35px;
1564 min-width: 35px; //fixes when only one button is displayed
1575 min-width: 35px; //fixes when only one button is displayed
1565 }
1576 }
1566 }
1577 }
1567
1578
1568 .notification-body {
1579 .notification-body {
1569 .markdown-block,
1580 .markdown-block,
1570 .rst-block {
1581 .rst-block {
1571 padding: @padding 0;
1582 padding: @padding 0;
1572 }
1583 }
1573
1584
1574 .notification-subject {
1585 .notification-subject {
1575 padding: @textmargin 0;
1586 padding: @textmargin 0;
1576 border-bottom: @border-thickness solid @border-default-color;
1587 border-bottom: @border-thickness solid @border-default-color;
1577 }
1588 }
1578 }
1589 }
1579
1590
1580
1591
1581 .notifications_buttons{
1592 .notifications_buttons{
1582 float: right;
1593 float: right;
1583 }
1594 }
1584
1595
1585 // Repositories
1596 // Repositories
1586
1597
1587 #summary.fields{
1598 #summary.fields{
1588 display: table;
1599 display: table;
1589
1600
1590 .field{
1601 .field{
1591 display: table-row;
1602 display: table-row;
1592
1603
1593 .label-summary{
1604 .label-summary{
1594 display: table-cell;
1605 display: table-cell;
1595 min-width: @label-summary-minwidth;
1606 min-width: @label-summary-minwidth;
1596 padding-top: @padding/2;
1607 padding-top: @padding/2;
1597 padding-bottom: @padding/2;
1608 padding-bottom: @padding/2;
1598 padding-right: @padding/2;
1609 padding-right: @padding/2;
1599 }
1610 }
1600
1611
1601 .input{
1612 .input{
1602 display: table-cell;
1613 display: table-cell;
1603 padding: @padding/2;
1614 padding: @padding/2;
1604
1615
1605 input{
1616 input{
1606 min-width: 29em;
1617 min-width: 29em;
1607 padding: @padding/4;
1618 padding: @padding/4;
1608 }
1619 }
1609 }
1620 }
1610 .statistics, .downloads{
1621 .statistics, .downloads{
1611 .disabled{
1622 .disabled{
1612 color: @grey4;
1623 color: @grey4;
1613 }
1624 }
1614 }
1625 }
1615 }
1626 }
1616 }
1627 }
1617
1628
1618 #summary{
1629 #summary{
1619 width: 70%;
1630 width: 70%;
1620 }
1631 }
1621
1632
1622
1633
1623 // Journal
1634 // Journal
1624 .journal.title {
1635 .journal.title {
1625 h5 {
1636 h5 {
1626 float: left;
1637 float: left;
1627 margin: 0;
1638 margin: 0;
1628 width: 70%;
1639 width: 70%;
1629 }
1640 }
1630
1641
1631 ul {
1642 ul {
1632 float: right;
1643 float: right;
1633 display: inline-block;
1644 display: inline-block;
1634 margin: 0;
1645 margin: 0;
1635 width: 30%;
1646 width: 30%;
1636 text-align: right;
1647 text-align: right;
1637
1648
1638 li {
1649 li {
1639 display: inline;
1650 display: inline;
1640 font-size: @journal-fontsize;
1651 font-size: @journal-fontsize;
1641 line-height: 1em;
1652 line-height: 1em;
1642
1653
1643 &:before { content: none; }
1654 &:before { content: none; }
1644 }
1655 }
1645 }
1656 }
1646 }
1657 }
1647
1658
1648 .filterexample {
1659 .filterexample {
1649 position: absolute;
1660 position: absolute;
1650 top: 95px;
1661 top: 95px;
1651 left: @contentpadding;
1662 left: @contentpadding;
1652 color: @rcblue;
1663 color: @rcblue;
1653 font-size: 11px;
1664 font-size: 11px;
1654 font-family: @text-regular;
1665 font-family: @text-regular;
1655 cursor: help;
1666 cursor: help;
1656
1667
1657 &:hover {
1668 &:hover {
1658 color: @rcdarkblue;
1669 color: @rcdarkblue;
1659 }
1670 }
1660
1671
1661 @media (max-width:768px) {
1672 @media (max-width:768px) {
1662 position: relative;
1673 position: relative;
1663 top: auto;
1674 top: auto;
1664 left: auto;
1675 left: auto;
1665 display: block;
1676 display: block;
1666 }
1677 }
1667 }
1678 }
1668
1679
1669
1680
1670 #journal{
1681 #journal{
1671 margin-bottom: @space;
1682 margin-bottom: @space;
1672
1683
1673 .journal_day{
1684 .journal_day{
1674 margin-bottom: @textmargin/2;
1685 margin-bottom: @textmargin/2;
1675 padding-bottom: @textmargin/2;
1686 padding-bottom: @textmargin/2;
1676 font-size: @journal-fontsize;
1687 font-size: @journal-fontsize;
1677 border-bottom: @border-thickness solid @border-default-color;
1688 border-bottom: @border-thickness solid @border-default-color;
1678 }
1689 }
1679
1690
1680 .journal_container{
1691 .journal_container{
1681 margin-bottom: @space;
1692 margin-bottom: @space;
1682
1693
1683 .journal_user{
1694 .journal_user{
1684 display: inline-block;
1695 display: inline-block;
1685 }
1696 }
1686 .journal_action_container{
1697 .journal_action_container{
1687 display: block;
1698 display: block;
1688 margin-top: @textmargin;
1699 margin-top: @textmargin;
1689
1700
1690 div{
1701 div{
1691 display: inline;
1702 display: inline;
1692 }
1703 }
1693
1704
1694 div.journal_action_params{
1705 div.journal_action_params{
1695 display: block;
1706 display: block;
1696 }
1707 }
1697
1708
1698 div.journal_repo:after{
1709 div.journal_repo:after{
1699 content: "\A";
1710 content: "\A";
1700 white-space: pre;
1711 white-space: pre;
1701 }
1712 }
1702
1713
1703 div.date{
1714 div.date{
1704 display: block;
1715 display: block;
1705 margin-bottom: @textmargin;
1716 margin-bottom: @textmargin;
1706 }
1717 }
1707 }
1718 }
1708 }
1719 }
1709 }
1720 }
1710
1721
1711 // Files
1722 // Files
1712 .edit-file-title {
1723 .edit-file-title {
1713 border-bottom: @border-thickness solid @border-default-color;
1724 border-bottom: @border-thickness solid @border-default-color;
1714
1725
1715 .breadcrumbs {
1726 .breadcrumbs {
1716 margin-bottom: 0;
1727 margin-bottom: 0;
1717 }
1728 }
1718 }
1729 }
1719
1730
1720 .edit-file-fieldset {
1731 .edit-file-fieldset {
1721 margin-top: @sidebarpadding;
1732 margin-top: @sidebarpadding;
1722
1733
1723 .fieldset {
1734 .fieldset {
1724 .left-label {
1735 .left-label {
1725 width: 13%;
1736 width: 13%;
1726 }
1737 }
1727 .right-content {
1738 .right-content {
1728 width: 87%;
1739 width: 87%;
1729 max-width: 100%;
1740 max-width: 100%;
1730 }
1741 }
1731 .filename-label {
1742 .filename-label {
1732 margin-top: 13px;
1743 margin-top: 13px;
1733 }
1744 }
1734 .commit-message-label {
1745 .commit-message-label {
1735 margin-top: 4px;
1746 margin-top: 4px;
1736 }
1747 }
1737 .file-upload-input {
1748 .file-upload-input {
1738 input {
1749 input {
1739 display: none;
1750 display: none;
1740 }
1751 }
1741 }
1752 }
1742 p {
1753 p {
1743 margin-top: 5px;
1754 margin-top: 5px;
1744 }
1755 }
1745
1756
1746 }
1757 }
1747 .custom-path-link {
1758 .custom-path-link {
1748 margin-left: 5px;
1759 margin-left: 5px;
1749 }
1760 }
1750 #commit {
1761 #commit {
1751 resize: vertical;
1762 resize: vertical;
1752 }
1763 }
1753 }
1764 }
1754
1765
1755 .delete-file-preview {
1766 .delete-file-preview {
1756 max-height: 250px;
1767 max-height: 250px;
1757 }
1768 }
1758
1769
1759 .new-file,
1770 .new-file,
1760 #filter_activate,
1771 #filter_activate,
1761 #filter_deactivate {
1772 #filter_deactivate {
1762 float: left;
1773 float: left;
1763 margin: 0 0 0 15px;
1774 margin: 0 0 0 15px;
1764 }
1775 }
1765
1776
1766 h3.files_location{
1777 h3.files_location{
1767 line-height: 2.4em;
1778 line-height: 2.4em;
1768 }
1779 }
1769
1780
1770 .browser-nav {
1781 .browser-nav {
1771 display: table;
1782 display: table;
1772 margin-bottom: @space;
1783 margin-bottom: @space;
1773
1784
1774
1785
1775 .info_box {
1786 .info_box {
1776 display: inline-table;
1787 display: inline-table;
1777 height: 2.5em;
1788 height: 2.5em;
1778
1789
1779 .browser-cur-rev, .info_box_elem {
1790 .browser-cur-rev, .info_box_elem {
1780 display: table-cell;
1791 display: table-cell;
1781 vertical-align: middle;
1792 vertical-align: middle;
1782 }
1793 }
1783
1794
1784 .info_box_elem {
1795 .info_box_elem {
1785 border-top: @border-thickness solid @rcblue;
1796 border-top: @border-thickness solid @rcblue;
1786 border-bottom: @border-thickness solid @rcblue;
1797 border-bottom: @border-thickness solid @rcblue;
1787
1798
1788 #at_rev, a {
1799 #at_rev, a {
1789 padding: 0.6em 0.9em;
1800 padding: 0.6em 0.9em;
1790 margin: 0;
1801 margin: 0;
1791 .box-shadow(none);
1802 .box-shadow(none);
1792 border: 0;
1803 border: 0;
1793 height: 12px;
1804 height: 12px;
1794 }
1805 }
1795
1806
1796 input#at_rev {
1807 input#at_rev {
1797 max-width: 50px;
1808 max-width: 50px;
1798 text-align: right;
1809 text-align: right;
1799 }
1810 }
1800
1811
1801 &.previous {
1812 &.previous {
1802 border: @border-thickness solid @rcblue;
1813 border: @border-thickness solid @rcblue;
1803 .disabled {
1814 .disabled {
1804 color: @grey4;
1815 color: @grey4;
1805 cursor: not-allowed;
1816 cursor: not-allowed;
1806 }
1817 }
1807 }
1818 }
1808
1819
1809 &.next {
1820 &.next {
1810 border: @border-thickness solid @rcblue;
1821 border: @border-thickness solid @rcblue;
1811 .disabled {
1822 .disabled {
1812 color: @grey4;
1823 color: @grey4;
1813 cursor: not-allowed;
1824 cursor: not-allowed;
1814 }
1825 }
1815 }
1826 }
1816 }
1827 }
1817
1828
1818 .browser-cur-rev {
1829 .browser-cur-rev {
1819
1830
1820 span{
1831 span{
1821 margin: 0;
1832 margin: 0;
1822 color: @rcblue;
1833 color: @rcblue;
1823 height: 12px;
1834 height: 12px;
1824 display: inline-block;
1835 display: inline-block;
1825 padding: 0.7em 1em ;
1836 padding: 0.7em 1em ;
1826 border: @border-thickness solid @rcblue;
1837 border: @border-thickness solid @rcblue;
1827 margin-right: @padding;
1838 margin-right: @padding;
1828 }
1839 }
1829 }
1840 }
1830 }
1841 }
1831
1842
1832 .search_activate {
1843 .search_activate {
1833 display: table-cell;
1844 display: table-cell;
1834 vertical-align: middle;
1845 vertical-align: middle;
1835
1846
1836 input, label{
1847 input, label{
1837 margin: 0;
1848 margin: 0;
1838 padding: 0;
1849 padding: 0;
1839 }
1850 }
1840
1851
1841 input{
1852 input{
1842 margin-left: @textmargin;
1853 margin-left: @textmargin;
1843 }
1854 }
1844
1855
1845 }
1856 }
1846 }
1857 }
1847
1858
1848 .file_author{
1859 .file_author{
1849 margin-bottom: @padding;
1860 margin-bottom: @padding;
1850
1861
1851 div{
1862 div{
1852 display: inline-block;
1863 display: inline-block;
1853 margin-right: 0.5em;
1864 margin-right: 0.5em;
1854 }
1865 }
1855 }
1866 }
1856
1867
1857 .browser-cur-rev{
1868 .browser-cur-rev{
1858 margin-bottom: @textmargin;
1869 margin-bottom: @textmargin;
1859 }
1870 }
1860
1871
1861 #node_filter_box_loading{
1872 #node_filter_box_loading{
1862 .info_text;
1873 .info_text;
1863 }
1874 }
1864
1875
1865 .browser-search {
1876 .browser-search {
1866 margin: -25px 0px 5px 0px;
1877 margin: -25px 0px 5px 0px;
1867 }
1878 }
1868
1879
1869 .node-filter {
1880 .node-filter {
1870 font-size: @repo-title-fontsize;
1881 font-size: @repo-title-fontsize;
1871 padding: 4px 0px 0px 0px;
1882 padding: 4px 0px 0px 0px;
1872
1883
1873 .node-filter-path {
1884 .node-filter-path {
1874 float: left;
1885 float: left;
1875 color: @grey4;
1886 color: @grey4;
1876 }
1887 }
1877 .node-filter-input {
1888 .node-filter-input {
1878 float: left;
1889 float: left;
1879 margin: -2px 0px 0px 2px;
1890 margin: -2px 0px 0px 2px;
1880 input {
1891 input {
1881 padding: 2px;
1892 padding: 2px;
1882 border: none;
1893 border: none;
1883 font-size: @repo-title-fontsize;
1894 font-size: @repo-title-fontsize;
1884 }
1895 }
1885 }
1896 }
1886 }
1897 }
1887
1898
1888
1899
1889 .browser-result{
1900 .browser-result{
1890 td a{
1901 td a{
1891 margin-left: 0.5em;
1902 margin-left: 0.5em;
1892 display: inline-block;
1903 display: inline-block;
1893
1904
1894 em{
1905 em{
1895 font-family: @text-bold;
1906 font-family: @text-bold;
1896 }
1907 }
1897 }
1908 }
1898 }
1909 }
1899
1910
1900 .browser-highlight{
1911 .browser-highlight{
1901 background-color: @grey5-alpha;
1912 background-color: @grey5-alpha;
1902 }
1913 }
1903
1914
1904
1915
1905 // Search
1916 // Search
1906
1917
1907 .search-form{
1918 .search-form{
1908 #q {
1919 #q {
1909 width: @search-form-width;
1920 width: @search-form-width;
1910 }
1921 }
1911 .fields{
1922 .fields{
1912 margin: 0 0 @space;
1923 margin: 0 0 @space;
1913 }
1924 }
1914
1925
1915 label{
1926 label{
1916 display: inline-block;
1927 display: inline-block;
1917 margin-right: @textmargin;
1928 margin-right: @textmargin;
1918 padding-top: 0.25em;
1929 padding-top: 0.25em;
1919 }
1930 }
1920
1931
1921
1932
1922 .results{
1933 .results{
1923 clear: both;
1934 clear: both;
1924 margin: 0 0 @padding;
1935 margin: 0 0 @padding;
1925 }
1936 }
1926 }
1937 }
1927
1938
1928 div.search-feedback-items {
1939 div.search-feedback-items {
1929 display: inline-block;
1940 display: inline-block;
1930 padding:0px 0px 0px 96px;
1941 padding:0px 0px 0px 96px;
1931 }
1942 }
1932
1943
1933 div.search-code-body {
1944 div.search-code-body {
1934 background-color: #ffffff; padding: 5px 0 5px 10px;
1945 background-color: #ffffff; padding: 5px 0 5px 10px;
1935 pre {
1946 pre {
1936 .match { background-color: #faffa6;}
1947 .match { background-color: #faffa6;}
1937 .break { display: block; width: 100%; background-color: #DDE7EF; color: #747474; }
1948 .break { display: block; width: 100%; background-color: #DDE7EF; color: #747474; }
1938 }
1949 }
1939 }
1950 }
1940
1951
1941 .expand_commit.search {
1952 .expand_commit.search {
1942 .show_more.open {
1953 .show_more.open {
1943 height: auto;
1954 height: auto;
1944 max-height: none;
1955 max-height: none;
1945 }
1956 }
1946 }
1957 }
1947
1958
1948 .search-results {
1959 .search-results {
1949
1960
1950 h2 {
1961 h2 {
1951 margin-bottom: 0;
1962 margin-bottom: 0;
1952 }
1963 }
1953 .codeblock {
1964 .codeblock {
1954 border: none;
1965 border: none;
1955 background: transparent;
1966 background: transparent;
1956 }
1967 }
1957
1968
1958 .codeblock-header {
1969 .codeblock-header {
1959 border: none;
1970 border: none;
1960 background: transparent;
1971 background: transparent;
1961 }
1972 }
1962
1973
1963 .code-body {
1974 .code-body {
1964 border: @border-thickness solid @border-default-color;
1975 border: @border-thickness solid @border-default-color;
1965 .border-radius(@border-radius);
1976 .border-radius(@border-radius);
1966 }
1977 }
1967
1978
1968 .td-commit {
1979 .td-commit {
1969 &:extend(pre);
1980 &:extend(pre);
1970 border-bottom: @border-thickness solid @border-default-color;
1981 border-bottom: @border-thickness solid @border-default-color;
1971 }
1982 }
1972
1983
1973 .message {
1984 .message {
1974 height: auto;
1985 height: auto;
1975 max-width: 350px;
1986 max-width: 350px;
1976 white-space: normal;
1987 white-space: normal;
1977 text-overflow: initial;
1988 text-overflow: initial;
1978 overflow: visible;
1989 overflow: visible;
1979
1990
1980 .match { background-color: #faffa6;}
1991 .match { background-color: #faffa6;}
1981 .break { background-color: #DDE7EF; width: 100%; color: #747474; display: block; }
1992 .break { background-color: #DDE7EF; width: 100%; color: #747474; display: block; }
1982 }
1993 }
1983
1994
1984 }
1995 }
1985
1996
1986 table.rctable td.td-search-results div {
1997 table.rctable td.td-search-results div {
1987 max-width: 100%;
1998 max-width: 100%;
1988 }
1999 }
1989
2000
1990 #tip-box, .tip-box{
2001 #tip-box, .tip-box{
1991 padding: @menupadding/2;
2002 padding: @menupadding/2;
1992 display: block;
2003 display: block;
1993 border: @border-thickness solid @border-highlight-color;
2004 border: @border-thickness solid @border-highlight-color;
1994 .border-radius(@border-radius);
2005 .border-radius(@border-radius);
1995 background-color: white;
2006 background-color: white;
1996 z-index: 99;
2007 z-index: 99;
1997 white-space: pre-wrap;
2008 white-space: pre-wrap;
1998 }
2009 }
1999
2010
2000 #linktt {
2011 #linktt {
2001 width: 79px;
2012 width: 79px;
2002 }
2013 }
2003
2014
2004 #help_kb .modal-content{
2015 #help_kb .modal-content{
2005 max-width: 750px;
2016 max-width: 750px;
2006 margin: 10% auto;
2017 margin: 10% auto;
2007
2018
2008 table{
2019 table{
2009 td,th{
2020 td,th{
2010 border-bottom: none;
2021 border-bottom: none;
2011 line-height: 2.5em;
2022 line-height: 2.5em;
2012 }
2023 }
2013 th{
2024 th{
2014 padding-bottom: @textmargin/2;
2025 padding-bottom: @textmargin/2;
2015 }
2026 }
2016 td.keys{
2027 td.keys{
2017 text-align: center;
2028 text-align: center;
2018 }
2029 }
2019 }
2030 }
2020
2031
2021 .block-left{
2032 .block-left{
2022 width: 45%;
2033 width: 45%;
2023 margin-right: 5%;
2034 margin-right: 5%;
2024 }
2035 }
2025 .modal-footer{
2036 .modal-footer{
2026 clear: both;
2037 clear: both;
2027 }
2038 }
2028 .key.tag{
2039 .key.tag{
2029 padding: 0.5em;
2040 padding: 0.5em;
2030 background-color: @rcblue;
2041 background-color: @rcblue;
2031 color: white;
2042 color: white;
2032 border-color: @rcblue;
2043 border-color: @rcblue;
2033 .box-shadow(none);
2044 .box-shadow(none);
2034 }
2045 }
2035 }
2046 }
2036
2047
2037
2048
2038
2049
2039 //--- IMPORTS FOR REFACTORED STYLES ------------------//
2050 //--- IMPORTS FOR REFACTORED STYLES ------------------//
2040
2051
2041 @import 'statistics-graph';
2052 @import 'statistics-graph';
2042 @import 'tables';
2053 @import 'tables';
2043 @import 'forms';
2054 @import 'forms';
2044 @import 'diff';
2055 @import 'diff';
2045 @import 'summary';
2056 @import 'summary';
2046 @import 'navigation';
2057 @import 'navigation';
2047
2058
2048 //--- SHOW/HIDE SECTIONS --//
2059 //--- SHOW/HIDE SECTIONS --//
2049
2060
2050 .btn-collapse {
2061 .btn-collapse {
2051 float: right;
2062 float: right;
2052 text-align: right;
2063 text-align: right;
2053 font-family: @text-light;
2064 font-family: @text-light;
2054 font-size: @basefontsize;
2065 font-size: @basefontsize;
2055 cursor: pointer;
2066 cursor: pointer;
2056 border: none;
2067 border: none;
2057 color: @rcblue;
2068 color: @rcblue;
2058 }
2069 }
2059
2070
2060 table.rctable,
2071 table.rctable,
2061 table.dataTable {
2072 table.dataTable {
2062 .btn-collapse {
2073 .btn-collapse {
2063 float: right;
2074 float: right;
2064 text-align: right;
2075 text-align: right;
2065 }
2076 }
2066 }
2077 }
2067
2078
2068
2079
2069 // TODO: johbo: Fix for IE10, this avoids that we see a border
2080 // TODO: johbo: Fix for IE10, this avoids that we see a border
2070 // and padding around checkboxes and radio boxes. Move to the right place,
2081 // and padding around checkboxes and radio boxes. Move to the right place,
2071 // or better: Remove this once we did the form refactoring.
2082 // or better: Remove this once we did the form refactoring.
2072 input[type=checkbox],
2083 input[type=checkbox],
2073 input[type=radio] {
2084 input[type=radio] {
2074 padding: 0;
2085 padding: 0;
2075 border: none;
2086 border: none;
2076 }
2087 }
@@ -1,47 +1,49 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%namespace name="base" file="/base/base.html"/>
2 <%namespace name="base" file="/base/base.html"/>
3
3
4 % if c.forks_pager:
4 % if c.forks_pager:
5 <table class="rctable fork_summary">
5 <table class="rctable fork_summary">
6 <tr>
6 <tr>
7 <th>${_('Owner')}</th>
7 <th>${_('Fork')}</th>
8 <th>${_('Fork')}</th>
8 <th>${_('Description')}</th>
9 <th>${_('Description')}</th>
9 <th>${_('Forked')}</th>
10 <th>${_('Forked')}</th>
10 <th></th>
11 <th></th>
11 </tr>
12 </tr>
12 % for f in c.forks_pager:
13 % for f in c.forks_pager:
13 <tr>
14 <tr>
14 <td class="td-user fork_user">
15 <td class="td-user fork_user">
15 ${base.gravatar_with_user(f.user.email, 16)}
16 ${base.gravatar_with_user(f.user.email, 16)}
16 &frasl;
17 </td>
18 <td class="td-componentname">
17 ${h.link_to(f.repo_name,h.url('summary_home',repo_name=f.repo_name))}
19 ${h.link_to(f.repo_name,h.url('summary_home',repo_name=f.repo_name))}
18 </td>
20 </td>
19 <td class="td-description">
21 <td class="td-description">
20 <div class="truncate">${f.description}</div>
22 <div class="truncate">${f.description}</div>
21 </td>
23 </td>
22 <td class="td-time follower_date">
24 <td class="td-time follower_date">
23 ${h.age_component(f.created_on)}
25 ${h.age_component(f.created_on)}
24 </td>
26 </td>
25 <td class="td-compare">
27 <td class="td-compare">
26 <a title="${_('Compare fork with %s' % c.repo_name)}"
28 <a title="${_('Compare fork with %s' % c.repo_name)}"
27 href="${h.url('compare_url',repo_name=c.repo_name, source_ref_type=c.rhodecode_db_repo.landing_rev[0],source_ref=c.rhodecode_db_repo.landing_rev[1],target_repo=f.repo_name,target_ref_type=c.rhodecode_db_repo.landing_rev[0],target_ref=c.rhodecode_db_repo.landing_rev[1], merge=1)}"
29 href="${h.url('compare_url',repo_name=c.repo_name, source_ref_type=c.rhodecode_db_repo.landing_rev[0],source_ref=c.rhodecode_db_repo.landing_rev[1],target_repo=f.repo_name,target_ref_type=c.rhodecode_db_repo.landing_rev[0],target_ref=c.rhodecode_db_repo.landing_rev[1], merge=1)}"
28 class="btn-link"><i class="icon-loop"></i> ${_('Compare fork')}</a>
30 class="btn-link"><i class="icon-loop"></i> ${_('Compare fork')}</a>
29 </td>
31 </td>
30 </tr>
32 </tr>
31 % endfor
33 % endfor
32 </table>
34 </table>
33 <div class="pagination-wh pagination-left">
35 <div class="pagination-wh pagination-left">
34 <script type="text/javascript">
36 <script type="text/javascript">
35 $(document).pjax('#forks .pager_link','#forks');
37 $(document).pjax('#forks .pager_link','#forks');
36 $(document).on('pjax:success',function(){
38 $(document).on('pjax:success',function(){
37 show_more_event();
39 show_more_event();
38 timeagoActivate();
40 timeagoActivate();
39 tooltip_activate();
41 tooltip_activate();
40 show_changeset_tooltip();
42 show_changeset_tooltip();
41 });
43 });
42 </script>
44 </script>
43 ${c.forks_pager.pager('$link_previous ~2~ $link_next')}
45 ${c.forks_pager.pager('$link_previous ~2~ $link_next')}
44 </div>
46 </div>
45 % else:
47 % else:
46 ${_('There are no forks yet')}
48 ${_('There are no forks yet')}
47 % endif
49 % endif
@@ -1,568 +1,568 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${_('%s Pull Request #%s') % (c.repo_name, c.pull_request.pull_request_id)}
4 ${_('%s Pull Request #%s') % (c.repo_name, c.pull_request.pull_request_id)}
5 %if c.rhodecode_name:
5 %if c.rhodecode_name:
6 &middot; ${h.branding(c.rhodecode_name)}
6 &middot; ${h.branding(c.rhodecode_name)}
7 %endif
7 %endif
8 </%def>
8 </%def>
9
9
10 <%def name="breadcrumbs_links()">
10 <%def name="breadcrumbs_links()">
11 <span id="pr-title">
11 <span id="pr-title">
12 ${c.pull_request.title}
12 ${c.pull_request.title}
13 %if c.pull_request.is_closed():
13 %if c.pull_request.is_closed():
14 (${_('Closed')})
14 (${_('Closed')})
15 %endif
15 %endif
16 </span>
16 </span>
17 <div id="pr-title-edit" class="input" style="display: none;">
17 <div id="pr-title-edit" class="input" style="display: none;">
18 ${h.text('pullrequest_title', id_="pr-title-input", class_="large", value=c.pull_request.title)}
18 ${h.text('pullrequest_title', id_="pr-title-input", class_="large", value=c.pull_request.title)}
19 </div>
19 </div>
20 </%def>
20 </%def>
21
21
22 <%def name="menu_bar_nav()">
22 <%def name="menu_bar_nav()">
23 ${self.menu_items(active='repositories')}
23 ${self.menu_items(active='repositories')}
24 </%def>
24 </%def>
25
25
26 <%def name="menu_bar_subnav()">
26 <%def name="menu_bar_subnav()">
27 ${self.repo_menu(active='showpullrequest')}
27 ${self.repo_menu(active='showpullrequest')}
28 </%def>
28 </%def>
29
29
30 <%def name="main()">
30 <%def name="main()">
31 <script type="text/javascript">
31 <script type="text/javascript">
32 // TODO: marcink switch this to pyroutes
32 // TODO: marcink switch this to pyroutes
33 AJAX_COMMENT_DELETE_URL = "${url('pullrequest_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}";
33 AJAX_COMMENT_DELETE_URL = "${url('pullrequest_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}";
34 templateContext.pull_request_data.pull_request_id = ${c.pull_request.pull_request_id};
34 templateContext.pull_request_data.pull_request_id = ${c.pull_request.pull_request_id};
35 </script>
35 </script>
36 <div class="box">
36 <div class="box">
37 <div class="title">
37 <div class="title">
38 ${self.repo_page_title(c.rhodecode_db_repo)}
38 ${self.repo_page_title(c.rhodecode_db_repo)}
39 </div>
39 </div>
40
40
41 ${self.breadcrumbs()}
41 ${self.breadcrumbs()}
42
42
43
43
44 <div class="box pr-summary">
44 <div class="box pr-summary">
45 <div class="summary-details block-left">
45 <div class="summary-details block-left">
46 <%summary = lambda n:{False:'summary-short'}.get(n)%>
46 <%summary = lambda n:{False:'summary-short'}.get(n)%>
47 <div class="pr-details-title">
47 <div class="pr-details-title">
48 ${_('Pull request #%s') % c.pull_request.pull_request_id} ${_('From')} ${h.format_date(c.pull_request.created_on)}
48 ${_('Pull request #%s') % c.pull_request.pull_request_id} ${_('From')} ${h.format_date(c.pull_request.created_on)}
49 %if c.allowed_to_update:
49 %if c.allowed_to_update:
50 <span id="open_edit_pullrequest" class="block-right action_button">${_('Edit')}</span>
50 <span id="open_edit_pullrequest" class="block-right action_button">${_('Edit')}</span>
51 <span id="close_edit_pullrequest" class="block-right action_button" style="display: none;">${_('Close')}</span>
51 <span id="close_edit_pullrequest" class="block-right action_button" style="display: none;">${_('Close')}</span>
52 %endif
52 %endif
53 </div>
53 </div>
54
54
55 <div id="summary" class="fields pr-details-content">
55 <div id="summary" class="fields pr-details-content">
56 <div class="field">
56 <div class="field">
57 <div class="label-summary">
57 <div class="label-summary">
58 <label>${_('Origin')}:</label>
58 <label>${_('Origin')}:</label>
59 </div>
59 </div>
60 <div class="input">
60 <div class="input">
61 <div class="pr-origininfo">
61 <div class="pr-origininfo">
62 ## branch link is only valid if it is a branch
62 ## branch link is only valid if it is a branch
63 <span class="tag">
63 <span class="tag">
64 %if c.pull_request.source_ref_parts.type == 'branch':
64 %if c.pull_request.source_ref_parts.type == 'branch':
65 <a href="${h.url('changelog_home', repo_name=c.pull_request.source_repo.repo_name, branch=c.pull_request.source_ref_parts.name)}">${c.pull_request.source_ref_parts.type}: ${c.pull_request.source_ref_parts.name}</a>
65 <a href="${h.url('changelog_home', repo_name=c.pull_request.source_repo.repo_name, branch=c.pull_request.source_ref_parts.name)}">${c.pull_request.source_ref_parts.type}: ${c.pull_request.source_ref_parts.name}</a>
66 %else:
66 %else:
67 ${c.pull_request.source_ref_parts.type}: ${c.pull_request.source_ref_parts.name}
67 ${c.pull_request.source_ref_parts.type}: ${c.pull_request.source_ref_parts.name}
68 %endif
68 %endif
69 </span>
69 </span>
70 <span class="clone-url">
70 <span class="clone-url">
71 <a href="${h.url('summary_home', repo_name=c.pull_request.source_repo.repo_name)}">${c.pull_request.source_repo.clone_url()}</a>
71 <a href="${h.url('summary_home', repo_name=c.pull_request.source_repo.repo_name)}">${c.pull_request.source_repo.clone_url()}</a>
72 </span>
72 </span>
73 </div>
73 </div>
74 <div class="pr-pullinfo">
74 <div class="pr-pullinfo">
75 %if h.is_hg(c.pull_request.source_repo):
75 %if h.is_hg(c.pull_request.source_repo):
76 <input type="text" value="hg pull -r ${h.short_id(c.source_ref)} ${c.pull_request.source_repo.clone_url()}" readonly="readonly">
76 <input type="text" value="hg pull -r ${h.short_id(c.source_ref)} ${c.pull_request.source_repo.clone_url()}" readonly="readonly">
77 %elif h.is_git(c.pull_request.source_repo):
77 %elif h.is_git(c.pull_request.source_repo):
78 <input type="text" value="git pull ${c.pull_request.source_repo.clone_url()} ${c.pull_request.source_ref_parts.name}" readonly="readonly">
78 <input type="text" value="git pull ${c.pull_request.source_repo.clone_url()} ${c.pull_request.source_ref_parts.name}" readonly="readonly">
79 %endif
79 %endif
80 </div>
80 </div>
81 </div>
81 </div>
82 </div>
82 </div>
83 <div class="field">
83 <div class="field">
84 <div class="label-summary">
84 <div class="label-summary">
85 <label>${_('Target')}:</label>
85 <label>${_('Target')}:</label>
86 </div>
86 </div>
87 <div class="input">
87 <div class="input">
88 <div class="pr-targetinfo">
88 <div class="pr-targetinfo">
89 ## branch link is only valid if it is a branch
89 ## branch link is only valid if it is a branch
90 <span class="tag">
90 <span class="tag">
91 %if c.pull_request.target_ref_parts.type == 'branch':
91 %if c.pull_request.target_ref_parts.type == 'branch':
92 <a href="${h.url('changelog_home', repo_name=c.pull_request.target_repo.repo_name, branch=c.pull_request.target_ref_parts.name)}">${c.pull_request.target_ref_parts.type}: ${c.pull_request.target_ref_parts.name}</a>
92 <a href="${h.url('changelog_home', repo_name=c.pull_request.target_repo.repo_name, branch=c.pull_request.target_ref_parts.name)}">${c.pull_request.target_ref_parts.type}: ${c.pull_request.target_ref_parts.name}</a>
93 %else:
93 %else:
94 ${c.pull_request.target_ref_parts.type}: ${c.pull_request.target_ref_parts.name}
94 ${c.pull_request.target_ref_parts.type}: ${c.pull_request.target_ref_parts.name}
95 %endif
95 %endif
96 </span>
96 </span>
97 <span class="clone-url">
97 <span class="clone-url">
98 <a href="${h.url('summary_home', repo_name=c.pull_request.target_repo.repo_name)}">${c.pull_request.target_repo.clone_url()}</a>
98 <a href="${h.url('summary_home', repo_name=c.pull_request.target_repo.repo_name)}">${c.pull_request.target_repo.clone_url()}</a>
99 </span>
99 </span>
100 </div>
100 </div>
101 </div>
101 </div>
102 </div>
102 </div>
103 <div class="field">
103 <div class="field">
104 <div class="label-summary">
104 <div class="label-summary">
105 <label>${_('Review')}:</label>
105 <label>${_('Review')}:</label>
106 </div>
106 </div>
107 <div class="input">
107 <div class="input">
108 %if c.pull_request_review_status:
108 %if c.pull_request_review_status:
109 <div class="${'flag_status %s' % c.pull_request_review_status} tooltip pull-left"></div>
109 <div class="${'flag_status %s' % c.pull_request_review_status} tooltip pull-left"></div>
110 <span class="changeset-status-lbl tooltip">
110 <span class="changeset-status-lbl tooltip">
111 %if c.pull_request.is_closed():
111 %if c.pull_request.is_closed():
112 ${_('Closed')},
112 ${_('Closed')},
113 %endif
113 %endif
114 ${h.commit_status_lbl(c.pull_request_review_status)}
114 ${h.commit_status_lbl(c.pull_request_review_status)}
115 </span>
115 </span>
116 - ${ungettext('calculated based on %s reviewer vote', 'calculated based on %s reviewers votes', len(c.pull_request_reviewers)) % len(c.pull_request_reviewers)}
116 - ${ungettext('calculated based on %s reviewer vote', 'calculated based on %s reviewers votes', len(c.pull_request_reviewers)) % len(c.pull_request_reviewers)}
117 %endif
117 %endif
118 </div>
118 </div>
119 </div>
119 </div>
120 <div class="field">
120 <div class="field">
121 <div class="pr-description-label label-summary">
121 <div class="pr-description-label label-summary">
122 <label>${_('Description')}:</label>
122 <label>${_('Description')}:</label>
123 </div>
123 </div>
124 <div id="pr-desc" class="input">
124 <div id="pr-desc" class="input">
125 <div class="pr-description">${h.urlify_commit_message(c.pull_request.description, c.repo_name)}</div>
125 <div class="pr-description">${h.urlify_commit_message(c.pull_request.description, c.repo_name)}</div>
126 </div>
126 </div>
127 <div id="pr-desc-edit" class="input textarea editor" style="display: none;">
127 <div id="pr-desc-edit" class="input textarea editor" style="display: none;">
128 <textarea id="pr-description-input" size="30">${c.pull_request.description}</textarea>
128 <textarea id="pr-description-input" size="30">${c.pull_request.description}</textarea>
129 </div>
129 </div>
130 </div>
130 </div>
131 <div class="field">
131 <div class="field">
132 <div class="label-summary">
132 <div class="label-summary">
133 <label>${_('Comments')}:</label>
133 <label>${_('Comments')}:</label>
134 </div>
134 </div>
135 <div class="input">
135 <div class="input">
136 <div>
136 <div>
137 <div class="comments-number">
137 <div class="comments-number">
138 %if c.comments:
138 %if c.comments:
139 <a href="#comments">${ungettext("%d Pull request comment", "%d Pull request comments", len(c.comments)) % len(c.comments)}</a>,
139 <a href="#comments">${ungettext("%d Pull request comment", "%d Pull request comments", len(c.comments)) % len(c.comments)}</a>,
140 %else:
140 %else:
141 ${ungettext("%d Pull request comment", "%d Pull request comments", len(c.comments)) % len(c.comments)}
141 ${ungettext("%d Pull request comment", "%d Pull request comments", len(c.comments)) % len(c.comments)}
142 %endif
142 %endif
143 %if c.inline_cnt:
143 %if c.inline_cnt:
144 ## this is replaced with a proper link to first comment via JS linkifyComments() func
144 ## this is replaced with a proper link to first comment via JS linkifyComments() func
145 <a href="#inline-comments" id="inline-comments-counter">${ungettext("%d Inline Comment", "%d Inline Comments", c.inline_cnt) % c.inline_cnt}</a>
145 <a href="#inline-comments" id="inline-comments-counter">${ungettext("%d Inline Comment", "%d Inline Comments", c.inline_cnt) % c.inline_cnt}</a>
146 %else:
146 %else:
147 ${ungettext("%d Inline Comment", "%d Inline Comments", c.inline_cnt) % c.inline_cnt}
147 ${ungettext("%d Inline Comment", "%d Inline Comments", c.inline_cnt) % c.inline_cnt}
148 %endif
148 %endif
149
149
150 % if c.outdated_cnt:
150 % if c.outdated_cnt:
151 ,${ungettext("%d Outdated Comment", "%d Outdated Comments", c.outdated_cnt) % c.outdated_cnt} <span id="show-outdated-comments" class="btn btn-link">${_('(Show)')}</span>
151 ,${ungettext("%d Outdated Comment", "%d Outdated Comments", c.outdated_cnt) % c.outdated_cnt} <span id="show-outdated-comments" class="btn btn-link">${_('(Show)')}</span>
152 % endif
152 % endif
153 </div>
153 </div>
154 </div>
154 </div>
155 </div>
155 </div>
156 </div>
156 </div>
157 <div id="pr-save" class="field" style="display: none;">
157 <div id="pr-save" class="field" style="display: none;">
158 <div class="label-summary"></div>
158 <div class="label-summary"></div>
159 <div class="input">
159 <div class="input">
160 <span id="edit_pull_request" class="btn btn-small">${_('Save Changes')}</span>
160 <span id="edit_pull_request" class="btn btn-small">${_('Save Changes')}</span>
161 </div>
161 </div>
162 </div>
162 </div>
163 </div>
163 </div>
164 </div>
164 </div>
165 <div>
165 <div>
166 ## AUTHOR
166 ## AUTHOR
167 <div class="reviewers-title block-right">
167 <div class="reviewers-title block-right">
168 <div class="pr-details-title">
168 <div class="pr-details-title">
169 ${_('Author')}
169 ${_('Author')}
170 </div>
170 </div>
171 </div>
171 </div>
172 <div class="block-right pr-details-content reviewers">
172 <div class="block-right pr-details-content reviewers">
173 <ul class="group_members">
173 <ul class="group_members">
174 <li>
174 <li>
175 ${self.gravatar_with_user(c.pull_request.author.email, 16)}
175 ${self.gravatar_with_user(c.pull_request.author.email, 16)}
176 </li>
176 </li>
177 </ul>
177 </ul>
178 </div>
178 </div>
179 ## REVIEWERS
179 ## REVIEWERS
180 <div class="reviewers-title block-right">
180 <div class="reviewers-title block-right">
181 <div class="pr-details-title">
181 <div class="pr-details-title">
182 ${_('Pull request reviewers')}
182 ${_('Pull request reviewers')}
183 %if c.allowed_to_update:
183 %if c.allowed_to_update:
184 <span id="open_edit_reviewers" class="block-right action_button">${_('Edit')}</span>
184 <span id="open_edit_reviewers" class="block-right action_button">${_('Edit')}</span>
185 <span id="close_edit_reviewers" class="block-right action_button" style="display: none;">${_('Close')}</span>
185 <span id="close_edit_reviewers" class="block-right action_button" style="display: none;">${_('Close')}</span>
186 %endif
186 %endif
187 </div>
187 </div>
188 </div>
188 </div>
189 <div id="reviewers" class="block-right pr-details-content reviewers">
189 <div id="reviewers" class="block-right pr-details-content reviewers">
190 ## members goes here !
190 ## members goes here !
191 <ul id="review_members" class="group_members">
191 <ul id="review_members" class="group_members">
192 %for member,status in c.pull_request_reviewers:
192 %for member,status in c.pull_request_reviewers:
193 <li id="reviewer_${member.user_id}">
193 <li id="reviewer_${member.user_id}">
194 <div class="reviewers_member">
194 <div class="reviewers_member">
195 <div class="reviewer_status tooltip" title="${h.tooltip(h.commit_status_lbl(status[0][1].status if status else 'not_reviewed'))}">
195 <div class="reviewer_status tooltip" title="${h.tooltip(h.commit_status_lbl(status[0][1].status if status else 'not_reviewed'))}">
196 <div class="${'flag_status %s' % (status[0][1].status if status else 'not_reviewed')} pull-left reviewer_member_status"></div>
196 <div class="${'flag_status %s' % (status[0][1].status if status else 'not_reviewed')} pull-left reviewer_member_status"></div>
197 </div>
197 </div>
198 <span id="reviewer_${member.user_id}_name" class="reviewer_name">
198 <div id="reviewer_${member.user_id}_name" class="reviewer_name">
199 ${self.gravatar_with_user(member.email, 16)}
199 ${self.gravatar_with_user(member.email, 16)} <div class="reviewer">(${_('owner') if c.pull_request.user_id == member.user_id else _('reviewer')})</div>
200 (${_('owner') if c.pull_request.user_id == member.user_id else _('reviewer')})</span>
200 </div>
201 <input id="reviewer_${member.user_id}_input" type="hidden" value="${member.user_id}" name="review_members" />
201 <input id="reviewer_${member.user_id}_input" type="hidden" value="${member.user_id}" name="review_members" />
202 %if c.allowed_to_update:
202 %if c.allowed_to_update:
203 <div class="reviewer_member_remove action_button" onclick="removeReviewMember(${member.user_id}, true)" style="visibility: hidden;">
203 <div class="reviewer_member_remove action_button" onclick="removeReviewMember(${member.user_id}, true)" style="visibility: hidden;">
204 <i class="icon-remove-sign" ></i>
204 <i class="icon-remove-sign" ></i>
205 </div>
205 </div>
206 %endif
206 %endif
207 </div>
207 </div>
208 </li>
208 </li>
209 %endfor
209 %endfor
210 </ul>
210 </ul>
211 %if not c.pull_request.is_closed():
211 %if not c.pull_request.is_closed():
212 <div id="add_reviewer_input" class='ac' style="display: none;">
212 <div id="add_reviewer_input" class='ac' style="display: none;">
213 %if c.allowed_to_update:
213 %if c.allowed_to_update:
214 <div class="reviewer_ac">
214 <div class="reviewer_ac">
215 ${h.text('user', class_='ac-input', placeholder=_('Add reviewer'))}
215 ${h.text('user', class_='ac-input', placeholder=_('Add reviewer'))}
216 <div id="reviewers_container"></div>
216 <div id="reviewers_container"></div>
217 </div>
217 </div>
218 <div>
218 <div>
219 <span id="update_pull_request" class="btn btn-small">${_('Save Changes')}</span>
219 <span id="update_pull_request" class="btn btn-small">${_('Save Changes')}</span>
220 </div>
220 </div>
221 %endif
221 %endif
222 </div>
222 </div>
223 %endif
223 %endif
224 </div>
224 </div>
225 </div>
225 </div>
226 </div>
226 </div>
227 <div class="box">
227 <div class="box">
228 ##DIFF
228 ##DIFF
229 <div class="table" >
229 <div class="table" >
230 <div id="changeset_compare_view_content">
230 <div id="changeset_compare_view_content">
231 ##CS
231 ##CS
232 % if c.missing_requirements:
232 % if c.missing_requirements:
233 <div class="box">
233 <div class="box">
234 <div class="alert alert-warning">
234 <div class="alert alert-warning">
235 <div>
235 <div>
236 <strong>${_('Missing requirements:')}</strong>
236 <strong>${_('Missing requirements:')}</strong>
237 ${_('These commits cannot be displayed, because this repository uses the Mercurial largefiles extension, which was not enabled.')}
237 ${_('These commits cannot be displayed, because this repository uses the Mercurial largefiles extension, which was not enabled.')}
238 </div>
238 </div>
239 </div>
239 </div>
240 </div>
240 </div>
241 % elif c.missing_commits:
241 % elif c.missing_commits:
242 <div class="box">
242 <div class="box">
243 <div class="alert alert-warning">
243 <div class="alert alert-warning">
244 <div>
244 <div>
245 <strong>${_('Missing commits')}:</strong>
245 <strong>${_('Missing commits')}:</strong>
246 ${_('This pull request cannot be displayed, because one or more commits no longer exist in the source repository.')}
246 ${_('This pull request cannot be displayed, because one or more commits no longer exist in the source repository.')}
247 ${_('Please update this pull request, push the commits back into the source repository, or consider closing this pull request.')}
247 ${_('Please update this pull request, push the commits back into the source repository, or consider closing this pull request.')}
248 </div>
248 </div>
249 </div>
249 </div>
250 </div>
250 </div>
251 % endif
251 % endif
252 <div class="compare_view_commits_title">
252 <div class="compare_view_commits_title">
253 % if c.allowed_to_update and not c.pull_request.is_closed():
253 % if c.allowed_to_update and not c.pull_request.is_closed():
254 <button id="update_commits" class="btn btn-small">${_('Update commits')}</button>
254 <button id="update_commits" class="btn btn-small">${_('Update commits')}</button>
255 % endif
255 % endif
256 % if len(c.commit_ranges):
256 % if len(c.commit_ranges):
257 <h2>${ungettext('Compare View: %s commit','Compare View: %s commits', len(c.commit_ranges)) % len(c.commit_ranges)}</h2>
257 <h2>${ungettext('Compare View: %s commit','Compare View: %s commits', len(c.commit_ranges)) % len(c.commit_ranges)}</h2>
258 % endif
258 % endif
259 </div>
259 </div>
260 % if not c.missing_commits:
260 % if not c.missing_commits:
261 <%include file="/compare/compare_commits.html" />
261 <%include file="/compare/compare_commits.html" />
262 ## FILES
262 ## FILES
263 <div class="cs_files_title">
263 <div class="cs_files_title">
264 <span class="cs_files_expand">
264 <span class="cs_files_expand">
265 <span id="expand_all_files">${_('Expand All')}</span> | <span id="collapse_all_files">${_('Collapse All')}</span>
265 <span id="expand_all_files">${_('Expand All')}</span> | <span id="collapse_all_files">${_('Collapse All')}</span>
266 </span>
266 </span>
267 <h2>
267 <h2>
268 ${diff_block.diff_summary_text(len(c.files), c.lines_added, c.lines_deleted, c.limited_diff)}
268 ${diff_block.diff_summary_text(len(c.files), c.lines_added, c.lines_deleted, c.limited_diff)}
269 </h2>
269 </h2>
270 </div>
270 </div>
271 % endif
271 % endif
272 <div class="cs_files">
272 <div class="cs_files">
273 %if not c.files and not c.missing_commits:
273 %if not c.files and not c.missing_commits:
274 <span class="empty_data">${_('No files')}</span>
274 <span class="empty_data">${_('No files')}</span>
275 %endif
275 %endif
276 <table class="compare_view_files">
276 <table class="compare_view_files">
277 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
277 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
278 %for FID, change, path, stats in c.files:
278 %for FID, change, path, stats in c.files:
279 <tr class="cs_${change} collapse_file" fid="${FID}">
279 <tr class="cs_${change} collapse_file" fid="${FID}">
280 <td class="cs_icon_td">
280 <td class="cs_icon_td">
281 <span class="collapse_file_icon" fid="${FID}"></span>
281 <span class="collapse_file_icon" fid="${FID}"></span>
282 </td>
282 </td>
283 <td class="cs_icon_td">
283 <td class="cs_icon_td">
284 <div class="flag_status not_reviewed hidden"></div>
284 <div class="flag_status not_reviewed hidden"></div>
285 </td>
285 </td>
286 <td class="cs_${change}" id="a_${FID}">
286 <td class="cs_${change}" id="a_${FID}">
287 <div class="node">
287 <div class="node">
288 <a href="#a_${FID}">
288 <a href="#a_${FID}">
289 <i class="icon-file-${change.lower()}"></i>
289 <i class="icon-file-${change.lower()}"></i>
290 ${h.safe_unicode(path)}
290 ${h.safe_unicode(path)}
291 </a>
291 </a>
292 </div>
292 </div>
293 </td>
293 </td>
294 <td>
294 <td>
295 <div class="changes pull-right">${h.fancy_file_stats(stats)}</div>
295 <div class="changes pull-right">${h.fancy_file_stats(stats)}</div>
296 <div class="comment-bubble pull-right" data-path="${path}">
296 <div class="comment-bubble pull-right" data-path="${path}">
297 <i class="icon-comment"></i>
297 <i class="icon-comment"></i>
298 </div>
298 </div>
299 </td>
299 </td>
300 </tr>
300 </tr>
301 <tr fid="${FID}" id="diff_${FID}" class="diff_links">
301 <tr fid="${FID}" id="diff_${FID}" class="diff_links">
302 <td></td>
302 <td></td>
303 <td></td>
303 <td></td>
304 <td class="cs_${change}">
304 <td class="cs_${change}">
305 %if c.target_repo.repo_name == c.repo_name:
305 %if c.target_repo.repo_name == c.repo_name:
306 ${diff_block.diff_menu(c.repo_name, h.safe_unicode(path), c.target_ref, c.source_ref, change)}
306 ${diff_block.diff_menu(c.repo_name, h.safe_unicode(path), c.target_ref, c.source_ref, change)}
307 %else:
307 %else:
308 ## this is slightly different case later, since the other repo can have this
308 ## this is slightly different case later, since the other repo can have this
309 ## file in other state than the origin repo
309 ## file in other state than the origin repo
310 ${diff_block.diff_menu(c.target_repo.repo_name, h.safe_unicode(path), c.target_ref, c.source_ref, change)}
310 ${diff_block.diff_menu(c.target_repo.repo_name, h.safe_unicode(path), c.target_ref, c.source_ref, change)}
311 %endif
311 %endif
312 </td>
312 </td>
313 <td class="td-actions rc-form">
313 <td class="td-actions rc-form">
314 </td>
314 </td>
315 </tr>
315 </tr>
316 <tr id="tr_${FID}">
316 <tr id="tr_${FID}">
317 <td></td>
317 <td></td>
318 <td></td>
318 <td></td>
319 <td class="injected_diff" colspan="2">
319 <td class="injected_diff" colspan="2">
320 ${diff_block.diff_block_simple([c.changes[FID]])}
320 ${diff_block.diff_block_simple([c.changes[FID]])}
321 </td>
321 </td>
322 </tr>
322 </tr>
323
323
324 ## Loop through inline comments
324 ## Loop through inline comments
325 % if c.outdated_comments.get(path,False):
325 % if c.outdated_comments.get(path,False):
326 <tr class="outdated">
326 <tr class="outdated">
327 <td></td>
327 <td></td>
328 <td></td>
328 <td></td>
329 <td colspan="2">
329 <td colspan="2">
330 <p>${_('Outdated Inline Comments')}:</p>
330 <p>${_('Outdated Inline Comments')}:</p>
331 </td>
331 </td>
332 </tr>
332 </tr>
333 <tr class="outdated">
333 <tr class="outdated">
334 <td></td>
334 <td></td>
335 <td></td>
335 <td></td>
336 <td colspan="2" class="outdated_comment_block">
336 <td colspan="2" class="outdated_comment_block">
337 % for line, comments in c.outdated_comments[path].iteritems():
337 % for line, comments in c.outdated_comments[path].iteritems():
338 <div class="inline-comment-placeholder" path="${path}" target_id="${h.safeid(h.safe_unicode(path))}">
338 <div class="inline-comment-placeholder" path="${path}" target_id="${h.safeid(h.safe_unicode(path))}">
339 % for co in comments:
339 % for co in comments:
340 ${comment.comment_block_outdated(co)}
340 ${comment.comment_block_outdated(co)}
341 % endfor
341 % endfor
342 </div>
342 </div>
343 % endfor
343 % endfor
344 </td>
344 </td>
345 </tr>
345 </tr>
346 % endif
346 % endif
347 %endfor
347 %endfor
348 ## Loop through inline comments for deleted files
348 ## Loop through inline comments for deleted files
349 %for path in c.deleted_files:
349 %for path in c.deleted_files:
350 <tr class="outdated deleted">
350 <tr class="outdated deleted">
351 <td></td>
351 <td></td>
352 <td></td>
352 <td></td>
353 <td>${path}</td>
353 <td>${path}</td>
354 </tr>
354 </tr>
355 <tr class="outdated deleted">
355 <tr class="outdated deleted">
356 <td></td>
356 <td></td>
357 <td></td>
357 <td></td>
358 <td>(${_('Removed')})</td>
358 <td>(${_('Removed')})</td>
359 </tr>
359 </tr>
360 % if path in c.outdated_comments:
360 % if path in c.outdated_comments:
361 <tr class="outdated deleted">
361 <tr class="outdated deleted">
362 <td></td>
362 <td></td>
363 <td></td>
363 <td></td>
364 <td colspan="2">
364 <td colspan="2">
365 <p>${_('Outdated Inline Comments')}:</p>
365 <p>${_('Outdated Inline Comments')}:</p>
366 </td>
366 </td>
367 </tr>
367 </tr>
368 <tr class="outdated">
368 <tr class="outdated">
369 <td></td>
369 <td></td>
370 <td></td>
370 <td></td>
371 <td colspan="2" class="outdated_comment_block">
371 <td colspan="2" class="outdated_comment_block">
372 % for line, comments in c.outdated_comments[path].iteritems():
372 % for line, comments in c.outdated_comments[path].iteritems():
373 <div class="inline-comment-placeholder" path="${path}" target_id="${h.safeid(h.safe_unicode(path))}">
373 <div class="inline-comment-placeholder" path="${path}" target_id="${h.safeid(h.safe_unicode(path))}">
374 % for co in comments:
374 % for co in comments:
375 ${comment.comment_block_outdated(co)}
375 ${comment.comment_block_outdated(co)}
376 % endfor
376 % endfor
377 </div>
377 </div>
378 % endfor
378 % endfor
379 </td>
379 </td>
380 </tr>
380 </tr>
381 % endif
381 % endif
382 %endfor
382 %endfor
383 </table>
383 </table>
384 </div>
384 </div>
385 % if c.limited_diff:
385 % if c.limited_diff:
386 <h5>${_('Commit was too big and was cut off...')} <a href="${h.url.current(fulldiff=1, **request.GET.mixed())}" onclick="return confirm('${_("Showing a huge diff might take some time and resources")}')">${_('Show full diff')}</a></h5>
386 <h5>${_('Commit was too big and was cut off...')} <a href="${h.url.current(fulldiff=1, **request.GET.mixed())}" onclick="return confirm('${_("Showing a huge diff might take some time and resources")}')">${_('Show full diff')}</a></h5>
387 % endif
387 % endif
388 </div>
388 </div>
389 </div>
389 </div>
390
390
391 % if c.limited_diff:
391 % if c.limited_diff:
392 <p>${_('Commit was too big and was cut off...')} <a href="${h.url.current(fulldiff=1, **request.GET.mixed())}" onclick="return confirm('${_("Showing a huge diff might take some time and resources")}')">${_('Show full diff')}</a></p>
392 <p>${_('Commit was too big and was cut off...')} <a href="${h.url.current(fulldiff=1, **request.GET.mixed())}" onclick="return confirm('${_("Showing a huge diff might take some time and resources")}')">${_('Show full diff')}</a></p>
393 % endif
393 % endif
394
394
395 ## template for inline comment form
395 ## template for inline comment form
396 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
396 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
397 ${comment.comment_inline_form()}
397 ${comment.comment_inline_form()}
398
398
399 ## render comments and inlines
399 ## render comments and inlines
400 ${comment.generate_comments(include_pull_request=True, is_pull_request=True)}
400 ${comment.generate_comments(include_pull_request=True, is_pull_request=True)}
401
401
402 % if not c.pull_request.is_closed():
402 % if not c.pull_request.is_closed():
403 ## main comment form and it status
403 ## main comment form and it status
404 ${comment.comments(h.url('pullrequest_comment', repo_name=c.repo_name,
404 ${comment.comments(h.url('pullrequest_comment', repo_name=c.repo_name,
405 pull_request_id=c.pull_request.pull_request_id),
405 pull_request_id=c.pull_request.pull_request_id),
406 c.pull_request_review_status,
406 c.pull_request_review_status,
407 is_pull_request=True, change_status=c.allowed_to_change_status)}
407 is_pull_request=True, change_status=c.allowed_to_change_status)}
408 %endif
408 %endif
409
409
410 <script type="text/javascript">
410 <script type="text/javascript">
411 if (location.href.indexOf('#') != -1) {
411 if (location.href.indexOf('#') != -1) {
412 var id = '#'+location.href.substring(location.href.indexOf('#') + 1).split('#');
412 var id = '#'+location.href.substring(location.href.indexOf('#') + 1).split('#');
413 var line = $('html').find(id);
413 var line = $('html').find(id);
414 offsetScroll(line, 70);
414 offsetScroll(line, 70);
415 }
415 }
416 $(function(){
416 $(function(){
417 ReviewerAutoComplete('user');
417 ReviewerAutoComplete('user');
418 // custom code mirror
418 // custom code mirror
419 var codeMirrorInstance = initPullRequestsCodeMirror('#pr-description-input');
419 var codeMirrorInstance = initPullRequestsCodeMirror('#pr-description-input');
420
420
421 var PRDetails = {
421 var PRDetails = {
422 editButton: $('#open_edit_pullrequest'),
422 editButton: $('#open_edit_pullrequest'),
423 closeButton: $('#close_edit_pullrequest'),
423 closeButton: $('#close_edit_pullrequest'),
424 viewFields: $('#pr-desc, #pr-title'),
424 viewFields: $('#pr-desc, #pr-title'),
425 editFields: $('#pr-desc-edit, #pr-title-edit, #pr-save'),
425 editFields: $('#pr-desc-edit, #pr-title-edit, #pr-save'),
426
426
427 init: function() {
427 init: function() {
428 var that = this;
428 var that = this;
429 this.editButton.on('click', function(e) { that.edit(); });
429 this.editButton.on('click', function(e) { that.edit(); });
430 this.closeButton.on('click', function(e) { that.view(); });
430 this.closeButton.on('click', function(e) { that.view(); });
431 },
431 },
432
432
433 edit: function(event) {
433 edit: function(event) {
434 this.viewFields.hide();
434 this.viewFields.hide();
435 this.editButton.hide();
435 this.editButton.hide();
436 this.editFields.show();
436 this.editFields.show();
437 codeMirrorInstance.refresh();
437 codeMirrorInstance.refresh();
438 },
438 },
439
439
440 view: function(event) {
440 view: function(event) {
441 this.editFields.hide();
441 this.editFields.hide();
442 this.closeButton.hide();
442 this.closeButton.hide();
443 this.viewFields.show();
443 this.viewFields.show();
444 }
444 }
445 };
445 };
446
446
447 var ReviewersPanel = {
447 var ReviewersPanel = {
448 editButton: $('#open_edit_reviewers'),
448 editButton: $('#open_edit_reviewers'),
449 closeButton: $('#close_edit_reviewers'),
449 closeButton: $('#close_edit_reviewers'),
450 addButton: $('#add_reviewer_input'),
450 addButton: $('#add_reviewer_input'),
451 removeButtons: $('.reviewer_member_remove'),
451 removeButtons: $('.reviewer_member_remove'),
452
452
453 init: function() {
453 init: function() {
454 var that = this;
454 var that = this;
455 this.editButton.on('click', function(e) { that.edit(); });
455 this.editButton.on('click', function(e) { that.edit(); });
456 this.closeButton.on('click', function(e) { that.close(); });
456 this.closeButton.on('click', function(e) { that.close(); });
457 },
457 },
458
458
459 edit: function(event) {
459 edit: function(event) {
460 this.editButton.hide();
460 this.editButton.hide();
461 this.closeButton.show();
461 this.closeButton.show();
462 this.addButton.show();
462 this.addButton.show();
463 this.removeButtons.css('visibility', 'visible');
463 this.removeButtons.css('visibility', 'visible');
464 },
464 },
465
465
466 close: function(event) {
466 close: function(event) {
467 this.editButton.show();
467 this.editButton.show();
468 this.closeButton.hide();
468 this.closeButton.hide();
469 this.addButton.hide();
469 this.addButton.hide();
470 this.removeButtons.css('visibility', 'hidden');
470 this.removeButtons.css('visibility', 'hidden');
471 }
471 }
472 };
472 };
473
473
474 PRDetails.init();
474 PRDetails.init();
475 ReviewersPanel.init();
475 ReviewersPanel.init();
476
476
477 $('#show-outdated-comments').on('click', function(e){
477 $('#show-outdated-comments').on('click', function(e){
478 var button = $(this);
478 var button = $(this);
479 var outdated = $('.outdated');
479 var outdated = $('.outdated');
480 if (button.html() === "(Show)") {
480 if (button.html() === "(Show)") {
481 button.html("(Hide)");
481 button.html("(Hide)");
482 outdated.show();
482 outdated.show();
483 } else {
483 } else {
484 button.html("(Show)");
484 button.html("(Show)");
485 outdated.hide();
485 outdated.hide();
486 }
486 }
487 });
487 });
488
488
489 $('.show-inline-comments').on('change', function(e){
489 $('.show-inline-comments').on('change', function(e){
490 var show = 'none';
490 var show = 'none';
491 var target = e.currentTarget;
491 var target = e.currentTarget;
492 if(target.checked){
492 if(target.checked){
493 show = ''
493 show = ''
494 }
494 }
495 var boxid = $(target).attr('id_for');
495 var boxid = $(target).attr('id_for');
496 var comments = $('#{0} .inline-comments'.format(boxid));
496 var comments = $('#{0} .inline-comments'.format(boxid));
497 var fn_display = function(idx){
497 var fn_display = function(idx){
498 $(this).css('display', show);
498 $(this).css('display', show);
499 };
499 };
500 $(comments).each(fn_display);
500 $(comments).each(fn_display);
501 var btns = $('#{0} .inline-comments-button'.format(boxid));
501 var btns = $('#{0} .inline-comments-button'.format(boxid));
502 $(btns).each(fn_display);
502 $(btns).each(fn_display);
503 });
503 });
504
504
505 // inject comments into their proper positions
505 // inject comments into their proper positions
506 var file_comments = $('.inline-comment-placeholder');
506 var file_comments = $('.inline-comment-placeholder');
507 %if c.pull_request.is_closed():
507 %if c.pull_request.is_closed():
508 renderInlineComments(file_comments, false);
508 renderInlineComments(file_comments, false);
509 %else:
509 %else:
510 renderInlineComments(file_comments, true);
510 renderInlineComments(file_comments, true);
511 %endif
511 %endif
512 var commentTotals = {};
512 var commentTotals = {};
513 $.each(file_comments, function(i, comment) {
513 $.each(file_comments, function(i, comment) {
514 var path = $(comment).attr('path');
514 var path = $(comment).attr('path');
515 var comms = $(comment).children().length;
515 var comms = $(comment).children().length;
516 if (path in commentTotals) {
516 if (path in commentTotals) {
517 commentTotals[path] += comms;
517 commentTotals[path] += comms;
518 } else {
518 } else {
519 commentTotals[path] = comms;
519 commentTotals[path] = comms;
520 }
520 }
521 });
521 });
522 $.each(commentTotals, function(path, total) {
522 $.each(commentTotals, function(path, total) {
523 var elem = $('.comment-bubble[data-path="'+ path +'"]');
523 var elem = $('.comment-bubble[data-path="'+ path +'"]');
524 elem.css('visibility', 'visible');
524 elem.css('visibility', 'visible');
525 elem.html(elem.html() + ' ' + total );
525 elem.html(elem.html() + ' ' + total );
526 });
526 });
527
527
528 $('#merge_pull_request_form').submit(function() {
528 $('#merge_pull_request_form').submit(function() {
529 if (!$('#merge_pull_request').attr('disabled')) {
529 if (!$('#merge_pull_request').attr('disabled')) {
530 $('#merge_pull_request').attr('disabled', 'disabled');
530 $('#merge_pull_request').attr('disabled', 'disabled');
531 }
531 }
532 return true;
532 return true;
533 });
533 });
534
534
535 $('#edit_pull_request').on('click', function(e){
535 $('#edit_pull_request').on('click', function(e){
536 var title = $('#pr-title-input').val();
536 var title = $('#pr-title-input').val();
537 var description = codeMirrorInstance.getValue();
537 var description = codeMirrorInstance.getValue();
538 editPullRequest(
538 editPullRequest(
539 "${c.repo_name}", "${c.pull_request.pull_request_id}",
539 "${c.repo_name}", "${c.pull_request.pull_request_id}",
540 title, description);
540 title, description);
541 });
541 });
542
542
543 $('#update_pull_request').on('click', function(e){
543 $('#update_pull_request').on('click', function(e){
544 updateReviewers(undefined, "${c.repo_name}", "${c.pull_request.pull_request_id}");
544 updateReviewers(undefined, "${c.repo_name}", "${c.pull_request.pull_request_id}");
545 });
545 });
546
546
547 $('#update_commits').on('click', function(e){
547 $('#update_commits').on('click', function(e){
548 var isDisabled = !$(e.currentTarget).attr('disabled');
548 var isDisabled = !$(e.currentTarget).attr('disabled');
549 $(e.currentTarget).text(_TM['Updating...']);
549 $(e.currentTarget).text(_TM['Updating...']);
550 $(e.currentTarget).attr('disabled', 'disabled');
550 $(e.currentTarget).attr('disabled', 'disabled');
551 if(isDisabled){
551 if(isDisabled){
552 updateCommits("${c.repo_name}", "${c.pull_request.pull_request_id}");
552 updateCommits("${c.repo_name}", "${c.pull_request.pull_request_id}");
553 }
553 }
554
554
555 });
555 });
556 // fixing issue with caches on firefox
556 // fixing issue with caches on firefox
557 $('#update_commits').removeAttr("disabled");
557 $('#update_commits').removeAttr("disabled");
558
558
559 $('#close_pull_request').on('click', function(e){
559 $('#close_pull_request').on('click', function(e){
560 closePullRequest("${c.repo_name}", "${c.pull_request.pull_request_id}");
560 closePullRequest("${c.repo_name}", "${c.pull_request.pull_request_id}");
561 });
561 });
562 })
562 })
563 </script>
563 </script>
564
564
565 </div>
565 </div>
566 </div>
566 </div>
567
567
568 </%def>
568 </%def>
@@ -1,241 +1,243 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Import early to make sure things are patched up properly
3 # Import early to make sure things are patched up properly
4 from setuptools import setup, find_packages
4 from setuptools import setup, find_packages
5
5
6 import os
6 import os
7 import sys
7 import sys
8 import platform
8 import platform
9
9
10 if sys.version_info < (2, 7):
10 if sys.version_info < (2, 7):
11 raise Exception('RhodeCode requires Python 2.7 or later')
11 raise Exception('RhodeCode requires Python 2.7 or later')
12
12
13
13
14 here = os.path.abspath(os.path.dirname(__file__))
14 here = os.path.abspath(os.path.dirname(__file__))
15
15
16
16
17 def _get_meta_var(name, data, callback_handler=None):
17 def _get_meta_var(name, data, callback_handler=None):
18 import re
18 import re
19 matches = re.compile(r'(?:%s)\s*=\s*(.*)' % name).search(data)
19 matches = re.compile(r'(?:%s)\s*=\s*(.*)' % name).search(data)
20 if matches:
20 if matches:
21 if not callable(callback_handler):
21 if not callable(callback_handler):
22 callback_handler = lambda v: v
22 callback_handler = lambda v: v
23
23
24 return callback_handler(eval(matches.groups()[0]))
24 return callback_handler(eval(matches.groups()[0]))
25
25
26 _meta = open(os.path.join(here, 'rhodecode', '__init__.py'), 'rb')
26 _meta = open(os.path.join(here, 'rhodecode', '__init__.py'), 'rb')
27 _metadata = _meta.read()
27 _metadata = _meta.read()
28 _meta.close()
28 _meta.close()
29
29
30 callback = lambda V: ('.'.join(map(str, V[:3])) + '.'.join(V[3:]))
30 callback = lambda V: ('.'.join(map(str, V[:3])) + '.'.join(V[3:]))
31 __version__ = open(os.path.join('rhodecode', 'VERSION')).read().strip()
31 __version__ = open(os.path.join('rhodecode', 'VERSION')).read().strip()
32 __license__ = _get_meta_var('__license__', _metadata)
32 __license__ = _get_meta_var('__license__', _metadata)
33 __author__ = _get_meta_var('__author__', _metadata)
33 __author__ = _get_meta_var('__author__', _metadata)
34 __url__ = _get_meta_var('__url__', _metadata)
34 __url__ = _get_meta_var('__url__', _metadata)
35 # defines current platform
35 # defines current platform
36 __platform__ = platform.system()
36 __platform__ = platform.system()
37
37
38 # Cygwin has different platform identifiers, but they all contain the
38 # Cygwin has different platform identifiers, but they all contain the
39 # term "CYGWIN"
39 # term "CYGWIN"
40 is_windows = __platform__ == 'Windows' or 'CYGWIN' in __platform__
40 is_windows = __platform__ == 'Windows' or 'CYGWIN' in __platform__
41
41
42 requirements = [
42 requirements = [
43 'Babel',
43 'Babel',
44 'Beaker',
44 'Beaker',
45 'FormEncode',
45 'FormEncode',
46 'Mako',
46 'Mako',
47 'Markdown',
47 'Markdown',
48 'MarkupSafe',
48 'MarkupSafe',
49 'MySQL-python',
49 'MySQL-python',
50 'Paste',
50 'Paste',
51 'PasteDeploy',
51 'PasteDeploy',
52 'PasteScript',
52 'PasteScript',
53 'Pygments',
53 'Pygments',
54 'Pylons',
54 'Pylons',
55 'Pyro4',
55 'Pyro4',
56 'Routes',
56 'Routes',
57 'SQLAlchemy',
57 'SQLAlchemy',
58 'Tempita',
58 'Tempita',
59 'URLObject',
59 'URLObject',
60 'WebError',
60 'WebError',
61 'WebHelpers',
61 'WebHelpers',
62 'WebHelpers2',
62 'WebHelpers2',
63 'WebOb',
63 'WebOb',
64 'WebTest',
64 'WebTest',
65 'Whoosh',
65 'Whoosh',
66 'alembic',
66 'alembic',
67 'amqplib',
67 'amqplib',
68 'anyjson',
68 'anyjson',
69 'appenlight-client',
69 'appenlight-client',
70 'authomatic',
70 'authomatic',
71 'backport_ipaddress',
71 'backport_ipaddress',
72 'celery',
72 'celery',
73 'colander',
73 'colander',
74 'decorator',
74 'decorator',
75 'docutils',
75 'docutils',
76 'gunicorn',
76 'infrae.cache',
77 'infrae.cache',
77 'ipython',
78 'ipython',
78 'iso8601',
79 'iso8601',
79 'kombu',
80 'kombu',
80 'msgpack-python',
81 'msgpack-python',
81 'packaging',
82 'packaging',
82 'psycopg2',
83 'psycopg2',
83 'pycrypto',
84 'pycrypto',
84 'pycurl',
85 'pycurl',
85 'pyparsing',
86 'pyparsing',
86 'pyramid',
87 'pyramid',
87 'pyramid-debugtoolbar',
88 'pyramid-debugtoolbar',
88 'pyramid-mako',
89 'pyramid-mako',
89 'pyramid-beaker',
90 'pyramid-beaker',
90 'pysqlite',
91 'pysqlite',
91 'python-dateutil',
92 'python-dateutil',
92 'python-ldap',
93 'python-ldap',
93 'python-memcached',
94 'python-memcached',
94 'python-pam',
95 'python-pam',
95 'recaptcha-client',
96 'recaptcha-client',
96 'repoze.lru',
97 'repoze.lru',
97 'requests',
98 'requests',
98 'simplejson',
99 'simplejson',
99 'waitress',
100 'waitress',
100 'zope.cachedescriptors',
101 'zope.cachedescriptors',
101 ]
102 ]
102
103
103 if is_windows:
104 if is_windows:
104 pass
105 pass
105 else:
106 else:
106 requirements.append('psutil')
107 requirements.append('psutil')
107 requirements.append('py-bcrypt')
108 requirements.append('py-bcrypt')
108
109
109 test_requirements = [
110 test_requirements = [
110 'WebTest',
111 'WebTest',
111 'configobj',
112 'configobj',
112 'cssselect',
113 'cssselect',
113 'flake8',
114 'flake8',
114 'lxml',
115 'lxml',
115 'mock',
116 'mock',
116 'pytest',
117 'pytest',
118 'pytest-cov',
117 'pytest-runner',
119 'pytest-runner',
118 ]
120 ]
119
121
120 setup_requirements = [
122 setup_requirements = [
121 'PasteScript',
123 'PasteScript',
122 'pytest-runner',
124 'pytest-runner',
123 ]
125 ]
124
126
125 dependency_links = [
127 dependency_links = [
126 ]
128 ]
127
129
128 classifiers = [
130 classifiers = [
129 'Development Status :: 6 - Mature',
131 'Development Status :: 6 - Mature',
130 'Environment :: Web Environment',
132 'Environment :: Web Environment',
131 'Framework :: Pylons',
133 'Framework :: Pylons',
132 'Intended Audience :: Developers',
134 'Intended Audience :: Developers',
133 'Operating System :: OS Independent',
135 'Operating System :: OS Independent',
134 'Programming Language :: Python',
136 'Programming Language :: Python',
135 'Programming Language :: Python :: 2.7',
137 'Programming Language :: Python :: 2.7',
136 ]
138 ]
137
139
138
140
139 # additional files from project that goes somewhere in the filesystem
141 # additional files from project that goes somewhere in the filesystem
140 # relative to sys.prefix
142 # relative to sys.prefix
141 data_files = []
143 data_files = []
142
144
143 # additional files that goes into package itself
145 # additional files that goes into package itself
144 package_data = {'rhodecode': ['i18n/*/LC_MESSAGES/*.mo', ], }
146 package_data = {'rhodecode': ['i18n/*/LC_MESSAGES/*.mo', ], }
145
147
146 description = ('RhodeCode is a fast and powerful management tool '
148 description = ('RhodeCode is a fast and powerful management tool '
147 'for Mercurial and GIT with a built in push/pull server, '
149 'for Mercurial and GIT with a built in push/pull server, '
148 'full text search and code-review.')
150 'full text search and code-review.')
149
151
150 keywords = ' '.join([
152 keywords = ' '.join([
151 'rhodecode', 'rhodiumcode', 'mercurial', 'git', 'code review',
153 'rhodecode', 'rhodiumcode', 'mercurial', 'git', 'code review',
152 'repo groups', 'ldap', 'repository management', 'hgweb replacement',
154 'repo groups', 'ldap', 'repository management', 'hgweb replacement',
153 'hgwebdir', 'gitweb replacement', 'serving hgweb',
155 'hgwebdir', 'gitweb replacement', 'serving hgweb',
154 ])
156 ])
155
157
156 # long description
158 # long description
157 README_FILE = 'README.rst'
159 README_FILE = 'README.rst'
158 CHANGELOG_FILE = 'CHANGES.rst'
160 CHANGELOG_FILE = 'CHANGES.rst'
159 try:
161 try:
160 long_description = open(README_FILE).read() + '\n\n' + \
162 long_description = open(README_FILE).read() + '\n\n' + \
161 open(CHANGELOG_FILE).read()
163 open(CHANGELOG_FILE).read()
162
164
163 except IOError, err:
165 except IOError, err:
164 sys.stderr.write(
166 sys.stderr.write(
165 '[WARNING] Cannot find file specified as long_description (%s)\n or '
167 '[WARNING] Cannot find file specified as long_description (%s)\n or '
166 'changelog (%s) skipping that file' % (README_FILE, CHANGELOG_FILE)
168 'changelog (%s) skipping that file' % (README_FILE, CHANGELOG_FILE)
167 )
169 )
168 long_description = description
170 long_description = description
169
171
170 # packages
172 # packages
171 packages = find_packages()
173 packages = find_packages()
172
174
173 paster_commands = [
175 paster_commands = [
174 'make-config=rhodecode.lib.paster_commands.make_config:Command',
176 'make-config=rhodecode.lib.paster_commands.make_config:Command',
175 'setup-rhodecode=rhodecode.lib.paster_commands.setup_rhodecode:Command',
177 'setup-rhodecode=rhodecode.lib.paster_commands.setup_rhodecode:Command',
176 'update-repoinfo=rhodecode.lib.paster_commands.update_repoinfo:Command',
178 'update-repoinfo=rhodecode.lib.paster_commands.update_repoinfo:Command',
177 'cache-keys=rhodecode.lib.paster_commands.cache_keys:Command',
179 'cache-keys=rhodecode.lib.paster_commands.cache_keys:Command',
178 'ishell=rhodecode.lib.paster_commands.ishell:Command',
180 'ishell=rhodecode.lib.paster_commands.ishell:Command',
179 'upgrade-db=rhodecode.lib.dbmigrate:UpgradeDb',
181 'upgrade-db=rhodecode.lib.dbmigrate:UpgradeDb',
180 'celeryd=rhodecode.lib.celerypylons.commands:CeleryDaemonCommand',
182 'celeryd=rhodecode.lib.celerypylons.commands:CeleryDaemonCommand',
181 ]
183 ]
182
184
183 setup(
185 setup(
184 name='rhodecode-enterprise-ce',
186 name='rhodecode-enterprise-ce',
185 version=__version__,
187 version=__version__,
186 description=description,
188 description=description,
187 long_description=long_description,
189 long_description=long_description,
188 keywords=keywords,
190 keywords=keywords,
189 license=__license__,
191 license=__license__,
190 author=__author__,
192 author=__author__,
191 author_email='marcin@rhodecode.com',
193 author_email='marcin@rhodecode.com',
192 dependency_links=dependency_links,
194 dependency_links=dependency_links,
193 url=__url__,
195 url=__url__,
194 install_requires=requirements,
196 install_requires=requirements,
195 tests_require=test_requirements,
197 tests_require=test_requirements,
196 classifiers=classifiers,
198 classifiers=classifiers,
197 setup_requires=setup_requirements,
199 setup_requires=setup_requirements,
198 data_files=data_files,
200 data_files=data_files,
199 packages=packages,
201 packages=packages,
200 include_package_data=True,
202 include_package_data=True,
201 package_data=package_data,
203 package_data=package_data,
202 message_extractors={
204 message_extractors={
203 'rhodecode': [
205 'rhodecode': [
204 ('**.py', 'python', None),
206 ('**.py', 'python', None),
205 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
207 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
206 ('templates/**.html', 'mako', {'input_encoding': 'utf-8'}),
208 ('templates/**.html', 'mako', {'input_encoding': 'utf-8'}),
207 ('public/**', 'ignore', None),
209 ('public/**', 'ignore', None),
208 ]
210 ]
209 },
211 },
210 zip_safe=False,
212 zip_safe=False,
211 paster_plugins=['PasteScript', 'Pylons'],
213 paster_plugins=['PasteScript', 'Pylons'],
212 entry_points={
214 entry_points={
213 'enterprise.plugins1': [
215 'enterprise.plugins1': [
214 'crowd=rhodecode.authentication.plugins.auth_crowd:plugin_factory',
216 'crowd=rhodecode.authentication.plugins.auth_crowd:plugin_factory',
215 'jasig_cas=rhodecode.authentication.plugins.auth_jasig_cas:plugin_factory',
217 'jasig_cas=rhodecode.authentication.plugins.auth_jasig_cas:plugin_factory',
216 'ldap=rhodecode.authentication.plugins.auth_ldap:plugin_factory',
218 'ldap=rhodecode.authentication.plugins.auth_ldap:plugin_factory',
217 'pam=rhodecode.authentication.plugins.auth_pam:plugin_factory',
219 'pam=rhodecode.authentication.plugins.auth_pam:plugin_factory',
218 'rhodecode=rhodecode.authentication.plugins.auth_rhodecode:plugin_factory',
220 'rhodecode=rhodecode.authentication.plugins.auth_rhodecode:plugin_factory',
219 ],
221 ],
220 'paste.app_factory': [
222 'paste.app_factory': [
221 'main=rhodecode.config.middleware:make_pyramid_app',
223 'main=rhodecode.config.middleware:make_pyramid_app',
222 'pylons=rhodecode.config.middleware:make_app',
224 'pylons=rhodecode.config.middleware:make_app',
223 ],
225 ],
224 'paste.app_install': [
226 'paste.app_install': [
225 'main=pylons.util:PylonsInstaller',
227 'main=pylons.util:PylonsInstaller',
226 'pylons=pylons.util:PylonsInstaller',
228 'pylons=pylons.util:PylonsInstaller',
227 ],
229 ],
228 'paste.global_paster_command': paster_commands,
230 'paste.global_paster_command': paster_commands,
229 'pytest11': [
231 'pytest11': [
230 'pylons=rhodecode.tests.pylons_plugin',
232 'pylons=rhodecode.tests.pylons_plugin',
231 'enterprise=rhodecode.tests.plugin',
233 'enterprise=rhodecode.tests.plugin',
232 ],
234 ],
233 'console_scripts': [
235 'console_scripts': [
234 'rcserver=rhodecode.rcserver:main',
236 'rcserver=rhodecode.rcserver:main',
235 ],
237 ],
236 'beaker.backends': [
238 'beaker.backends': [
237 'memorylru_base=rhodecode.lib.memory_lru_debug:MemoryLRUNamespaceManagerBase',
239 'memorylru_base=rhodecode.lib.memory_lru_debug:MemoryLRUNamespaceManagerBase',
238 'memorylru_debug=rhodecode.lib.memory_lru_debug:MemoryLRUNamespaceManagerDebug'
240 'memorylru_debug=rhodecode.lib.memory_lru_debug:MemoryLRUNamespaceManagerDebug'
239 ]
241 ]
240 },
242 },
241 )
243 )
General Comments 0
You need to be logged in to leave comments. Login now