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 | 1 | [bumpversion] |
|
2 |
current_version = 4.0. |
|
|
2 | current_version = 4.0.1 | |
|
3 | 3 | message = release: Bump version {current_version} to {new_version} |
|
4 | 4 | |
|
5 | 5 | [bumpversion:file:rhodecode/VERSION] |
|
6 |
@@ -1,39 +1,47 b'' | |||
|
1 | 1 | |
|
2 | 2 | WEBPACK=./node_modules/webpack/bin/webpack.js |
|
3 | 3 | GRUNT=grunt |
|
4 | 4 | NODE_PATH=./node_modules |
|
5 | 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 | 6 | CI_PREFIX=enterprise |
|
7 | 7 | |
|
8 |
.PHONY: |
|
|
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: | |
|
11 | @echo "TODO: describe Makefile" | |
|
14 | docs-clean: | |
|
15 | (cd docs; make clean) | |
|
16 | ||
|
17 | ci-docs: docs; | |
|
18 | ||
|
12 | 19 | |
|
13 | 20 | clean: test-clean |
|
14 | 21 | find . -type f \( -iname '*.c' -o -iname '*.pyc' -o -iname '*.so' \) -exec rm '{}' ';' |
|
15 | 22 | |
|
16 | 23 | test: test-clean test-lint test-only |
|
17 | 24 | |
|
18 | 25 | test-clean: |
|
19 | 26 | rm -rf coverage.xml htmlcov junit.xml pylint.log result |
|
20 | 27 | |
|
21 | 28 | test-lint: |
|
22 | 29 | if [ "$$IN_NIX_SHELL" = "1" ]; then \ |
|
23 | 30 | $(FLAKE8); \ |
|
24 | 31 | else \ |
|
25 | 32 | $(FLAKE8) --format=pylint --exit-zero > pylint.log; \ |
|
26 | 33 | fi |
|
27 | 34 | |
|
28 | 35 | test-only: |
|
29 | 36 | PYTHONHASHSEED=random py.test -vv -r xw --cov=rhodecode --cov-report=term-missing --cov-report=html rhodecode/tests/ |
|
30 | 37 | |
|
31 | 38 | web-build: |
|
32 | 39 | NODE_PATH=$(NODE_PATH) $(GRUNT) |
|
33 | 40 | |
|
34 | 41 | web-test: |
|
35 | 42 | @echo "no test for our javascript, yet!" |
|
36 | 43 | |
|
37 | 44 | docs-bootstrap: |
|
38 | 45 | (cd docs; nix-build default.nix -o result) |
|
39 | 46 | @echo "Please go to docs folder and run make html" |
|
47 |
@@ -1,575 +1,577 b'' | |||
|
1 | 1 | ################################################################################ |
|
2 | 2 | ################################################################################ |
|
3 | 3 | # RhodeCode Enterprise - configuration file # |
|
4 | 4 | # Built-in functions and variables # |
|
5 | 5 | # The %(here)s variable will be replaced with the parent directory of this file# |
|
6 | 6 | # # |
|
7 | 7 | ################################################################################ |
|
8 | 8 | |
|
9 | 9 | [DEFAULT] |
|
10 | 10 | debug = true |
|
11 | 11 | pdebug = false |
|
12 | 12 | ################################################################################ |
|
13 | 13 | ## Uncomment and replace with the email address which should receive ## |
|
14 | 14 | ## any error reports after an application crash ## |
|
15 | 15 | ## Additionally these settings will be used by the RhodeCode mailing system ## |
|
16 | 16 | ################################################################################ |
|
17 | 17 | #email_to = admin@localhost |
|
18 | 18 | #error_email_from = paste_error@localhost |
|
19 | 19 | #app_email_from = rhodecode-noreply@localhost |
|
20 | 20 | #error_message = |
|
21 | 21 | #email_prefix = [RhodeCode] |
|
22 | 22 | |
|
23 | 23 | #smtp_server = mail.server.com |
|
24 | 24 | #smtp_username = |
|
25 | 25 | #smtp_password = |
|
26 | 26 | #smtp_port = |
|
27 | 27 | #smtp_use_tls = false |
|
28 | 28 | #smtp_use_ssl = true |
|
29 | 29 | ## Specify available auth parameters here (e.g. LOGIN PLAIN CRAM-MD5, etc.) |
|
30 | 30 | #smtp_auth = |
|
31 | 31 | |
|
32 | 32 | [server:main] |
|
33 | 33 | ## COMMON ## |
|
34 | 34 | host = 127.0.0.1 |
|
35 | 35 | port = 5000 |
|
36 | 36 | |
|
37 | 37 | ########################## |
|
38 | 38 | ## WAITRESS WSGI SERVER ## |
|
39 | 39 | ########################## |
|
40 | 40 | use = egg:waitress#main |
|
41 | 41 | ## number of worker threads |
|
42 | 42 | threads = 5 |
|
43 | 43 | ## MAX BODY SIZE 100GB |
|
44 | 44 | max_request_body_size = 107374182400 |
|
45 | 45 | ## Use poll instead of select, fixes file descriptors limits problems. |
|
46 | 46 | ## May not work on old windows systems. |
|
47 | 47 | asyncore_use_poll = true |
|
48 | 48 | |
|
49 | 49 | |
|
50 | 50 | ########################## |
|
51 | 51 | ## GUNICORN WSGI SERVER ## |
|
52 | 52 | ########################## |
|
53 | 53 | ## run with gunicorn --log-config <inifile.ini> --paste <inifile.ini> |
|
54 | 54 | #use = egg:gunicorn#main |
|
55 | 55 | ## Sets the number of process workers. You must set `instance_id = *` |
|
56 | 56 | ## when this option is set to more than one worker, recommended |
|
57 | 57 | ## value is (2 * NUMBER_OF_CPUS + 1), eg 2CPU = 5 workers |
|
58 | 58 | ## The `instance_id = *` must be set in the [app:main] section below |
|
59 | 59 | #workers = 1 |
|
60 | 60 | ## number of threads for each of the worker, must be set to 1 for gevent |
|
61 | 61 | ## generally recommened to be at 1 |
|
62 | 62 | #threads = 1 |
|
63 | 63 | ## process name |
|
64 | 64 | #proc_name = rhodecode |
|
65 | 65 | ## type of worker class, one of sync, gevent |
|
66 | 66 | ## recommended for bigger setup is using of of other than sync one |
|
67 | 67 | #worker_class = sync |
|
68 | ## The maximum number of simultaneous clients. Valid only for Gevent | |
|
69 | #worker_connections = 10 | |
|
68 | 70 | ## max number of requests that worker will handle before being gracefully |
|
69 | 71 | ## restarted, could prevent memory leaks |
|
70 | 72 | #max_requests = 1000 |
|
71 | 73 | #max_requests_jitter = 30 |
|
72 | 74 | ## ammount of time a worker can spend with handling a request before it |
|
73 | 75 | ## gets killed and restarted. Set to 6hrs |
|
74 | 76 | #timeout = 21600 |
|
75 | 77 | |
|
76 | 78 | |
|
77 | 79 | ## prefix middleware for RhodeCode, disables force_https flag. |
|
78 | 80 | ## allows to set RhodeCode under a prefix in server. |
|
79 | 81 | ## eg https://server.com/<prefix>. Enable `filter-with =` option below as well. |
|
80 | 82 | #[filter:proxy-prefix] |
|
81 | 83 | #use = egg:PasteDeploy#prefix |
|
82 | 84 | #prefix = /<your-prefix> |
|
83 | 85 | |
|
84 | 86 | [app:main] |
|
85 | 87 | use = egg:rhodecode-enterprise-ce |
|
86 | 88 | ## enable proxy prefix middleware, defined below |
|
87 | 89 | #filter-with = proxy-prefix |
|
88 | 90 | |
|
89 | 91 | # During development the we want to have the debug toolbar enabled |
|
90 | 92 | pyramid.includes = |
|
91 | 93 | pyramid_debugtoolbar |
|
92 | 94 | rhodecode.utils.debugtoolbar |
|
93 | 95 | rhodecode.lib.middleware.request_wrapper |
|
94 | 96 | |
|
95 | 97 | pyramid.reload_templates = true |
|
96 | 98 | |
|
97 | 99 | debugtoolbar.hosts = 0.0.0.0/0 |
|
98 | 100 | debugtoolbar.exclude_prefixes = |
|
99 | 101 | /css |
|
100 | 102 | /fonts |
|
101 | 103 | /images |
|
102 | 104 | /js |
|
103 | 105 | |
|
104 | 106 | ## RHODECODE PLUGINS ## |
|
105 | 107 | rhodecode.includes = |
|
106 | 108 | rhodecode.api |
|
107 | 109 | |
|
108 | 110 | |
|
109 | 111 | # api prefix url |
|
110 | 112 | rhodecode.api.url = /_admin/api |
|
111 | 113 | |
|
112 | 114 | |
|
113 | 115 | ## END RHODECODE PLUGINS ## |
|
114 | 116 | |
|
115 | 117 | full_stack = true |
|
116 | 118 | |
|
117 | 119 | ## Serve static files via RhodeCode, disable to serve them via HTTP server |
|
118 | 120 | static_files = true |
|
119 | 121 | |
|
120 | 122 | ## Optional Languages |
|
121 | 123 | ## en(default), be, de, es, fr, it, ja, pl, pt, ru, zh |
|
122 | 124 | lang = en |
|
123 | 125 | |
|
124 | 126 | ## perform a full repository scan on each server start, this should be |
|
125 | 127 | ## set to false after first startup, to allow faster server restarts. |
|
126 | 128 | startup.import_repos = false |
|
127 | 129 | |
|
128 | 130 | ## Uncomment and set this path to use archive download cache. |
|
129 | 131 | ## Once enabled, generated archives will be cached at this location |
|
130 | 132 | ## and served from the cache during subsequent requests for the same archive of |
|
131 | 133 | ## the repository. |
|
132 | 134 | #archive_cache_dir = /tmp/tarballcache |
|
133 | 135 | |
|
134 | 136 | ## change this to unique ID for security |
|
135 | 137 | app_instance_uuid = rc-production |
|
136 | 138 | |
|
137 | 139 | ## cut off limit for large diffs (size in bytes) |
|
138 | 140 | cut_off_limit_diff = 1024000 |
|
139 | 141 | cut_off_limit_file = 256000 |
|
140 | 142 | |
|
141 | 143 | ## use cache version of scm repo everywhere |
|
142 | 144 | vcs_full_cache = true |
|
143 | 145 | |
|
144 | 146 | ## force https in RhodeCode, fixes https redirects, assumes it's always https |
|
145 | 147 | ## Normally this is controlled by proper http flags sent from http server |
|
146 | 148 | force_https = false |
|
147 | 149 | |
|
148 | 150 | ## use Strict-Transport-Security headers |
|
149 | 151 | use_htsts = false |
|
150 | 152 | |
|
151 | 153 | ## number of commits stats will parse on each iteration |
|
152 | 154 | commit_parse_limit = 25 |
|
153 | 155 | |
|
154 | 156 | ## git rev filter option, --all is the default filter, if you need to |
|
155 | 157 | ## hide all refs in changelog switch this to --branches --tags |
|
156 | 158 | git_rev_filter = --branches --tags |
|
157 | 159 | |
|
158 | 160 | # Set to true if your repos are exposed using the dumb protocol |
|
159 | 161 | git_update_server_info = false |
|
160 | 162 | |
|
161 | 163 | ## RSS/ATOM feed options |
|
162 | 164 | rss_cut_off_limit = 256000 |
|
163 | 165 | rss_items_per_page = 10 |
|
164 | 166 | rss_include_diff = false |
|
165 | 167 | |
|
166 | 168 | ## gist URL alias, used to create nicer urls for gist. This should be an |
|
167 | 169 | ## url that does rewrites to _admin/gists/<gistid>. |
|
168 | 170 | ## example: http://gist.rhodecode.org/{gistid}. Empty means use the internal |
|
169 | 171 | ## RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/<gistid> |
|
170 | 172 | gist_alias_url = |
|
171 | 173 | |
|
172 | 174 | ## List of controllers (using glob pattern syntax) that AUTH TOKENS could be |
|
173 | 175 | ## used for access. |
|
174 | 176 | ## Adding ?auth_token = <token> to the url authenticates this request as if it |
|
175 | 177 | ## came from the the logged in user who own this authentication token. |
|
176 | 178 | ## |
|
177 | 179 | ## Syntax is <ControllerClass>:<function_pattern>. |
|
178 | 180 | ## To enable access to raw_files put `FilesController:raw`. |
|
179 | 181 | ## To enable access to patches add `ChangesetController:changeset_patch`. |
|
180 | 182 | ## The list should be "," separated and on a single line. |
|
181 | 183 | ## |
|
182 | 184 | ## Recommended controllers to enable: |
|
183 | 185 | # ChangesetController:changeset_patch, |
|
184 | 186 | # ChangesetController:changeset_raw, |
|
185 | 187 | # FilesController:raw, |
|
186 | 188 | # FilesController:archivefile, |
|
187 | 189 | # GistsController:*, |
|
188 | 190 | api_access_controllers_whitelist = |
|
189 | 191 | |
|
190 | 192 | ## default encoding used to convert from and to unicode |
|
191 | 193 | ## can be also a comma separated list of encoding in case of mixed encodings |
|
192 | 194 | default_encoding = UTF-8 |
|
193 | 195 | |
|
194 | 196 | ## instance-id prefix |
|
195 | 197 | ## a prefix key for this instance used for cache invalidation when running |
|
196 | 198 | ## multiple instances of rhodecode, make sure it's globally unique for |
|
197 | 199 | ## all running rhodecode instances. Leave empty if you don't use it |
|
198 | 200 | instance_id = |
|
199 | 201 | |
|
200 | 202 | ## alternative return HTTP header for failed authentication. Default HTTP |
|
201 | 203 | ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with |
|
202 | 204 | ## handling that causing a series of failed authentication calls. |
|
203 | 205 | ## Set this variable to 403 to return HTTPForbidden, or any other HTTP code |
|
204 | 206 | ## This will be served instead of default 401 on bad authnetication |
|
205 | 207 | auth_ret_code = |
|
206 | 208 | |
|
207 | 209 | ## use special detection method when serving auth_ret_code, instead of serving |
|
208 | 210 | ## ret_code directly, use 401 initially (Which triggers credentials prompt) |
|
209 | 211 | ## and then serve auth_ret_code to clients |
|
210 | 212 | auth_ret_code_detection = false |
|
211 | 213 | |
|
212 | 214 | ## locking return code. When repository is locked return this HTTP code. 2XX |
|
213 | 215 | ## codes don't break the transactions while 4XX codes do |
|
214 | 216 | lock_ret_code = 423 |
|
215 | 217 | |
|
216 | 218 | ## allows to change the repository location in settings page |
|
217 | 219 | allow_repo_location_change = true |
|
218 | 220 | |
|
219 | 221 | ## allows to setup custom hooks in settings page |
|
220 | 222 | allow_custom_hooks_settings = true |
|
221 | 223 | |
|
222 | 224 | ## generated license token, goto license page in RhodeCode settings to obtain |
|
223 | 225 | ## new token |
|
224 | 226 | license_token = |
|
225 | 227 | |
|
226 | 228 | ## supervisor connection uri, for managing supervisor and logs. |
|
227 | 229 | supervisor.uri = |
|
228 | 230 | ## supervisord group name/id we only want this RC instance to handle |
|
229 | 231 | supervisor.group_id = dev |
|
230 | 232 | |
|
231 | 233 | ## Display extended labs settings |
|
232 | 234 | labs_settings_active = true |
|
233 | 235 | |
|
234 | 236 | #################################### |
|
235 | 237 | ### CELERY CONFIG #### |
|
236 | 238 | #################################### |
|
237 | 239 | use_celery = false |
|
238 | 240 | broker.host = localhost |
|
239 | 241 | broker.vhost = rabbitmqhost |
|
240 | 242 | broker.port = 5672 |
|
241 | 243 | broker.user = rabbitmq |
|
242 | 244 | broker.password = qweqwe |
|
243 | 245 | |
|
244 | 246 | celery.imports = rhodecode.lib.celerylib.tasks |
|
245 | 247 | |
|
246 | 248 | celery.result.backend = amqp |
|
247 | 249 | celery.result.dburi = amqp:// |
|
248 | 250 | celery.result.serialier = json |
|
249 | 251 | |
|
250 | 252 | #celery.send.task.error.emails = true |
|
251 | 253 | #celery.amqp.task.result.expires = 18000 |
|
252 | 254 | |
|
253 | 255 | celeryd.concurrency = 2 |
|
254 | 256 | #celeryd.log.file = celeryd.log |
|
255 | 257 | celeryd.log.level = debug |
|
256 | 258 | celeryd.max.tasks.per.child = 1 |
|
257 | 259 | |
|
258 | 260 | ## tasks will never be sent to the queue, but executed locally instead. |
|
259 | 261 | celery.always.eager = false |
|
260 | 262 | |
|
261 | 263 | #################################### |
|
262 | 264 | ### BEAKER CACHE #### |
|
263 | 265 | #################################### |
|
264 | 266 | # default cache dir for templates. Putting this into a ramdisk |
|
265 | 267 | ## can boost performance, eg. %(here)s/data_ramdisk |
|
266 | 268 | cache_dir = %(here)s/data |
|
267 | 269 | |
|
268 | 270 | ## locking and default file storage for Beaker. Putting this into a ramdisk |
|
269 | 271 | ## can boost performance, eg. %(here)s/data_ramdisk/cache/beaker_data |
|
270 | 272 | beaker.cache.data_dir = %(here)s/data/cache/beaker_data |
|
271 | 273 | beaker.cache.lock_dir = %(here)s/data/cache/beaker_lock |
|
272 | 274 | |
|
273 | 275 | beaker.cache.regions = super_short_term, short_term, long_term, sql_cache_short, auth_plugins, repo_cache_long |
|
274 | 276 | |
|
275 | 277 | beaker.cache.super_short_term.type = memory |
|
276 | 278 | beaker.cache.super_short_term.expire = 10 |
|
277 | 279 | beaker.cache.super_short_term.key_length = 256 |
|
278 | 280 | |
|
279 | 281 | beaker.cache.short_term.type = memory |
|
280 | 282 | beaker.cache.short_term.expire = 60 |
|
281 | 283 | beaker.cache.short_term.key_length = 256 |
|
282 | 284 | |
|
283 | 285 | beaker.cache.long_term.type = memory |
|
284 | 286 | beaker.cache.long_term.expire = 36000 |
|
285 | 287 | beaker.cache.long_term.key_length = 256 |
|
286 | 288 | |
|
287 | 289 | beaker.cache.sql_cache_short.type = memory |
|
288 | 290 | beaker.cache.sql_cache_short.expire = 10 |
|
289 | 291 | beaker.cache.sql_cache_short.key_length = 256 |
|
290 | 292 | |
|
291 | 293 | # default is memory cache, configure only if required |
|
292 | 294 | # using multi-node or multi-worker setup |
|
293 | 295 | #beaker.cache.auth_plugins.type = ext:database |
|
294 | 296 | #beaker.cache.auth_plugins.lock_dir = %(here)s/data/cache/auth_plugin_lock |
|
295 | 297 | #beaker.cache.auth_plugins.url = postgresql://postgres:secret@localhost/rhodecode |
|
296 | 298 | #beaker.cache.auth_plugins.url = mysql://root:secret@127.0.0.1/rhodecode |
|
297 | 299 | #beaker.cache.auth_plugins.sa.pool_recycle = 3600 |
|
298 | 300 | #beaker.cache.auth_plugins.sa.pool_size = 10 |
|
299 | 301 | #beaker.cache.auth_plugins.sa.max_overflow = 0 |
|
300 | 302 | |
|
301 | 303 | beaker.cache.repo_cache_long.type = memorylru_base |
|
302 | 304 | beaker.cache.repo_cache_long.max_items = 4096 |
|
303 | 305 | beaker.cache.repo_cache_long.expire = 2592000 |
|
304 | 306 | |
|
305 | 307 | # default is memorylru_base cache, configure only if required |
|
306 | 308 | # using multi-node or multi-worker setup |
|
307 | 309 | #beaker.cache.repo_cache_long.type = ext:memcached |
|
308 | 310 | #beaker.cache.repo_cache_long.url = localhost:11211 |
|
309 | 311 | #beaker.cache.repo_cache_long.expire = 1209600 |
|
310 | 312 | #beaker.cache.repo_cache_long.key_length = 256 |
|
311 | 313 | |
|
312 | 314 | #################################### |
|
313 | 315 | ### BEAKER SESSION #### |
|
314 | 316 | #################################### |
|
315 | 317 | |
|
316 | 318 | ## .session.type is type of storage options for the session, current allowed |
|
317 | 319 | ## types are file, ext:memcached, ext:database, and memory(default). |
|
318 | 320 | beaker.session.type = file |
|
319 | 321 | beaker.session.data_dir = %(here)s/data/sessions/data |
|
320 | 322 | |
|
321 | 323 | ## db based session, fast, and allows easy management over logged in users ## |
|
322 | 324 | #beaker.session.type = ext:database |
|
323 | 325 | #beaker.session.table_name = db_session |
|
324 | 326 | #beaker.session.sa.url = postgresql://postgres:secret@localhost/rhodecode |
|
325 | 327 | #beaker.session.sa.url = mysql://root:secret@127.0.0.1/rhodecode |
|
326 | 328 | #beaker.session.sa.pool_recycle = 3600 |
|
327 | 329 | #beaker.session.sa.echo = false |
|
328 | 330 | |
|
329 | 331 | beaker.session.key = rhodecode |
|
330 | 332 | beaker.session.secret = develop-rc-uytcxaz |
|
331 | 333 | beaker.session.lock_dir = %(here)s/data/sessions/lock |
|
332 | 334 | |
|
333 | 335 | ## Secure encrypted cookie. Requires AES and AES python libraries |
|
334 | 336 | ## you must disable beaker.session.secret to use this |
|
335 | 337 | #beaker.session.encrypt_key = <key_for_encryption> |
|
336 | 338 | #beaker.session.validate_key = <validation_key> |
|
337 | 339 | |
|
338 | 340 | ## sets session as invalid(also logging out user) if it haven not been |
|
339 | 341 | ## accessed for given amount of time in seconds |
|
340 | 342 | beaker.session.timeout = 2592000 |
|
341 | 343 | beaker.session.httponly = true |
|
342 | 344 | #beaker.session.cookie_path = /<your-prefix> |
|
343 | 345 | |
|
344 | 346 | ## uncomment for https secure cookie |
|
345 | 347 | beaker.session.secure = false |
|
346 | 348 | |
|
347 | 349 | ## auto save the session to not to use .save() |
|
348 | 350 | beaker.session.auto = false |
|
349 | 351 | |
|
350 | 352 | ## default cookie expiration time in seconds, set to `true` to set expire |
|
351 | 353 | ## at browser close |
|
352 | 354 | #beaker.session.cookie_expires = 3600 |
|
353 | 355 | |
|
354 | 356 | ################################### |
|
355 | 357 | ## SEARCH INDEXING CONFIGURATION ## |
|
356 | 358 | ################################### |
|
357 | 359 | |
|
358 | 360 | search.module = rhodecode.lib.index.whoosh |
|
359 | 361 | search.location = %(here)s/data/index |
|
360 | 362 | |
|
361 | 363 | ################################### |
|
362 | 364 | ## ERROR AND LOG HANDLING SYSTEM ## |
|
363 | 365 | ################################### |
|
364 | 366 | |
|
365 | 367 | ## Appenlight is tailored to work with RhodeCode, see |
|
366 | 368 | ## http://appenlight.com for details how to obtain an account |
|
367 | 369 | |
|
368 | 370 | ## appenlight integration enabled |
|
369 | 371 | appenlight = false |
|
370 | 372 | |
|
371 | 373 | appenlight.server_url = https://api.appenlight.com |
|
372 | 374 | appenlight.api_key = YOUR_API_KEY |
|
373 | 375 | ;appenlight.transport_config = https://api.appenlight.com?threaded=1&timeout=5 |
|
374 | 376 | |
|
375 | 377 | # used for JS client |
|
376 | 378 | appenlight.api_public_key = YOUR_API_PUBLIC_KEY |
|
377 | 379 | |
|
378 | 380 | ## TWEAK AMOUNT OF INFO SENT HERE |
|
379 | 381 | |
|
380 | 382 | ## enables 404 error logging (default False) |
|
381 | 383 | appenlight.report_404 = false |
|
382 | 384 | |
|
383 | 385 | ## time in seconds after request is considered being slow (default 1) |
|
384 | 386 | appenlight.slow_request_time = 1 |
|
385 | 387 | |
|
386 | 388 | ## record slow requests in application |
|
387 | 389 | ## (needs to be enabled for slow datastore recording and time tracking) |
|
388 | 390 | appenlight.slow_requests = true |
|
389 | 391 | |
|
390 | 392 | ## enable hooking to application loggers |
|
391 | 393 | appenlight.logging = true |
|
392 | 394 | |
|
393 | 395 | ## minimum log level for log capture |
|
394 | 396 | appenlight.logging.level = WARNING |
|
395 | 397 | |
|
396 | 398 | ## send logs only from erroneous/slow requests |
|
397 | 399 | ## (saves API quota for intensive logging) |
|
398 | 400 | appenlight.logging_on_error = false |
|
399 | 401 | |
|
400 | 402 | ## list of additonal keywords that should be grabbed from environ object |
|
401 | 403 | ## can be string with comma separated list of words in lowercase |
|
402 | 404 | ## (by default client will always send following info: |
|
403 | 405 | ## 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that |
|
404 | 406 | ## start with HTTP* this list be extended with additional keywords here |
|
405 | 407 | appenlight.environ_keys_whitelist = |
|
406 | 408 | |
|
407 | 409 | ## list of keywords that should be blanked from request object |
|
408 | 410 | ## can be string with comma separated list of words in lowercase |
|
409 | 411 | ## (by default client will always blank keys that contain following words |
|
410 | 412 | ## 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf' |
|
411 | 413 | ## this list be extended with additional keywords set here |
|
412 | 414 | appenlight.request_keys_blacklist = |
|
413 | 415 | |
|
414 | 416 | ## list of namespaces that should be ignores when gathering log entries |
|
415 | 417 | ## can be string with comma separated list of namespaces |
|
416 | 418 | ## (by default the client ignores own entries: appenlight_client.client) |
|
417 | 419 | appenlight.log_namespace_blacklist = |
|
418 | 420 | |
|
419 | 421 | |
|
420 | 422 | ################################################################################ |
|
421 | 423 | ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ## |
|
422 | 424 | ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ## |
|
423 | 425 | ## execute malicious code after an exception is raised. ## |
|
424 | 426 | ################################################################################ |
|
425 | 427 | #set debug = false |
|
426 | 428 | |
|
427 | 429 | |
|
428 | 430 | ############## |
|
429 | 431 | ## STYLING ## |
|
430 | 432 | ############## |
|
431 | 433 | debug_style = true |
|
432 | 434 | |
|
433 | 435 | ######################################################### |
|
434 | 436 | ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ### |
|
435 | 437 | ######################################################### |
|
436 | 438 | sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30 |
|
437 | 439 | #sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode |
|
438 | 440 | #sqlalchemy.db1.url = mysql://root:qweqwe@localhost/rhodecode |
|
439 | 441 | |
|
440 | 442 | # see sqlalchemy docs for other advanced settings |
|
441 | 443 | |
|
442 | 444 | ## print the sql statements to output |
|
443 | 445 | sqlalchemy.db1.echo = false |
|
444 | 446 | ## recycle the connections after this ammount of seconds |
|
445 | 447 | sqlalchemy.db1.pool_recycle = 3600 |
|
446 | 448 | sqlalchemy.db1.convert_unicode = true |
|
447 | 449 | |
|
448 | 450 | ## the number of connections to keep open inside the connection pool. |
|
449 | 451 | ## 0 indicates no limit |
|
450 | 452 | #sqlalchemy.db1.pool_size = 5 |
|
451 | 453 | |
|
452 | 454 | ## the number of connections to allow in connection pool "overflow", that is |
|
453 | 455 | ## connections that can be opened above and beyond the pool_size setting, |
|
454 | 456 | ## which defaults to five. |
|
455 | 457 | #sqlalchemy.db1.max_overflow = 10 |
|
456 | 458 | |
|
457 | 459 | |
|
458 | 460 | ################## |
|
459 | 461 | ### VCS CONFIG ### |
|
460 | 462 | ################## |
|
461 | 463 | vcs.server.enable = true |
|
462 | 464 | vcs.server = localhost:9900 |
|
463 | 465 | # Available protocols: pyro4, http |
|
464 | 466 | vcs.server.protocol = pyro4 |
|
465 | 467 | |
|
466 | 468 | # available impl: |
|
467 | 469 | # vcsserver.scm_app (EE only, for testing), |
|
468 | 470 | # rhodecode.lib.middleware.utils.scm_app_http |
|
469 | 471 | # pyro4 |
|
470 | 472 | #vcs.scm_app_implementation = rhodecode.lib.middleware.utils.scm_app_http |
|
471 | 473 | |
|
472 | 474 | vcs.server.log_level = debug |
|
473 | 475 | vcs.start_server = true |
|
474 | 476 | vcs.backends = hg, git, svn |
|
475 | 477 | vcs.connection_timeout = 3600 |
|
476 | 478 | ## Compatibility version when creating SVN repositories. Defaults to newest version when commented out. |
|
477 | 479 | ## Available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible |
|
478 | 480 | #vcs.svn.compatible_version = pre-1.8-compatible |
|
479 | 481 | |
|
480 | 482 | ################################ |
|
481 | 483 | ### LOGGING CONFIGURATION #### |
|
482 | 484 | ################################ |
|
483 | 485 | [loggers] |
|
484 | 486 | keys = root, routes, rhodecode, sqlalchemy, beaker, pyro4, templates, whoosh_indexer |
|
485 | 487 | |
|
486 | 488 | [handlers] |
|
487 | 489 | keys = console, console_sql |
|
488 | 490 | |
|
489 | 491 | [formatters] |
|
490 | 492 | keys = generic, color_formatter, color_formatter_sql |
|
491 | 493 | |
|
492 | 494 | ############# |
|
493 | 495 | ## LOGGERS ## |
|
494 | 496 | ############# |
|
495 | 497 | [logger_root] |
|
496 | 498 | level = NOTSET |
|
497 | 499 | handlers = console |
|
498 | 500 | |
|
499 | 501 | [logger_routes] |
|
500 | 502 | level = DEBUG |
|
501 | 503 | handlers = |
|
502 | 504 | qualname = routes.middleware |
|
503 | 505 | ## "level = DEBUG" logs the route matched and routing variables. |
|
504 | 506 | propagate = 1 |
|
505 | 507 | |
|
506 | 508 | [logger_beaker] |
|
507 | 509 | level = DEBUG |
|
508 | 510 | handlers = |
|
509 | 511 | qualname = beaker.container |
|
510 | 512 | propagate = 1 |
|
511 | 513 | |
|
512 | 514 | [logger_pyro4] |
|
513 | 515 | level = DEBUG |
|
514 | 516 | handlers = |
|
515 | 517 | qualname = Pyro4 |
|
516 | 518 | propagate = 1 |
|
517 | 519 | |
|
518 | 520 | [logger_templates] |
|
519 | 521 | level = INFO |
|
520 | 522 | handlers = |
|
521 | 523 | qualname = pylons.templating |
|
522 | 524 | propagate = 1 |
|
523 | 525 | |
|
524 | 526 | [logger_rhodecode] |
|
525 | 527 | level = DEBUG |
|
526 | 528 | handlers = |
|
527 | 529 | qualname = rhodecode |
|
528 | 530 | propagate = 1 |
|
529 | 531 | |
|
530 | 532 | [logger_sqlalchemy] |
|
531 | 533 | level = INFO |
|
532 | 534 | handlers = console_sql |
|
533 | 535 | qualname = sqlalchemy.engine |
|
534 | 536 | propagate = 0 |
|
535 | 537 | |
|
536 | 538 | [logger_whoosh_indexer] |
|
537 | 539 | level = DEBUG |
|
538 | 540 | handlers = |
|
539 | 541 | qualname = whoosh_indexer |
|
540 | 542 | propagate = 1 |
|
541 | 543 | |
|
542 | 544 | ############## |
|
543 | 545 | ## HANDLERS ## |
|
544 | 546 | ############## |
|
545 | 547 | |
|
546 | 548 | [handler_console] |
|
547 | 549 | class = StreamHandler |
|
548 | 550 | args = (sys.stderr,) |
|
549 | 551 | level = DEBUG |
|
550 | 552 | formatter = color_formatter |
|
551 | 553 | |
|
552 | 554 | [handler_console_sql] |
|
553 | 555 | class = StreamHandler |
|
554 | 556 | args = (sys.stderr,) |
|
555 | 557 | level = DEBUG |
|
556 | 558 | formatter = color_formatter_sql |
|
557 | 559 | |
|
558 | 560 | ################ |
|
559 | 561 | ## FORMATTERS ## |
|
560 | 562 | ################ |
|
561 | 563 | |
|
562 | 564 | [formatter_generic] |
|
563 | 565 | class = rhodecode.lib.logging_formatter.Pyro4AwareFormatter |
|
564 | 566 | format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s |
|
565 | 567 | datefmt = %Y-%m-%d %H:%M:%S |
|
566 | 568 | |
|
567 | 569 | [formatter_color_formatter] |
|
568 | 570 | class = rhodecode.lib.logging_formatter.ColorFormatter |
|
569 | 571 | format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s |
|
570 | 572 | datefmt = %Y-%m-%d %H:%M:%S |
|
571 | 573 | |
|
572 | 574 | [formatter_color_formatter_sql] |
|
573 | 575 | class = rhodecode.lib.logging_formatter.ColorFormatterSql |
|
574 | 576 | format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s |
|
575 | 577 | datefmt = %Y-%m-%d %H:%M:%S |
@@ -1,541 +1,551 b'' | |||
|
1 | 1 | ################################################################################ |
|
2 | 2 | ################################################################################ |
|
3 | 3 | # RhodeCode Enterprise - configuration file # |
|
4 | 4 | # Built-in functions and variables # |
|
5 | 5 | # The %(here)s variable will be replaced with the parent directory of this file# |
|
6 | 6 | # # |
|
7 | 7 | ################################################################################ |
|
8 | 8 | |
|
9 | 9 | [DEFAULT] |
|
10 | 10 | debug = true |
|
11 | 11 | pdebug = false |
|
12 | 12 | ################################################################################ |
|
13 | 13 | ## Uncomment and replace with the email address which should receive ## |
|
14 | 14 | ## any error reports after an application crash ## |
|
15 | 15 | ## Additionally these settings will be used by the RhodeCode mailing system ## |
|
16 | 16 | ################################################################################ |
|
17 | 17 | #email_to = admin@localhost |
|
18 | 18 | #error_email_from = paste_error@localhost |
|
19 | 19 | #app_email_from = rhodecode-noreply@localhost |
|
20 | 20 | #error_message = |
|
21 | 21 | #email_prefix = [RhodeCode] |
|
22 | 22 | |
|
23 | 23 | #smtp_server = mail.server.com |
|
24 | 24 | #smtp_username = |
|
25 | 25 | #smtp_password = |
|
26 | 26 | #smtp_port = |
|
27 | 27 | #smtp_use_tls = false |
|
28 | 28 | #smtp_use_ssl = true |
|
29 | 29 | ## Specify available auth parameters here (e.g. LOGIN PLAIN CRAM-MD5, etc.) |
|
30 | 30 | #smtp_auth = |
|
31 | 31 | |
|
32 | 32 | [server:main] |
|
33 | 33 | ## COMMON ## |
|
34 | 34 | host = 127.0.0.1 |
|
35 | 35 | port = 5000 |
|
36 | 36 | |
|
37 | 37 | ########################## |
|
38 | 38 | ## WAITRESS WSGI SERVER ## |
|
39 | 39 | ########################## |
|
40 | 40 | use = egg:waitress#main |
|
41 | 41 | ## number of worker threads |
|
42 | 42 | threads = 5 |
|
43 | 43 | ## MAX BODY SIZE 100GB |
|
44 | 44 | max_request_body_size = 107374182400 |
|
45 | 45 | ## Use poll instead of select, fixes file descriptors limits problems. |
|
46 | 46 | ## May not work on old windows systems. |
|
47 | 47 | asyncore_use_poll = true |
|
48 | 48 | |
|
49 | 49 | |
|
50 | 50 | ########################## |
|
51 | 51 | ## GUNICORN WSGI SERVER ## |
|
52 | 52 | ########################## |
|
53 | 53 | ## run with gunicorn --log-config <inifile.ini> --paste <inifile.ini> |
|
54 | 54 | #use = egg:gunicorn#main |
|
55 | 55 | ## Sets the number of process workers. You must set `instance_id = *` |
|
56 | 56 | ## when this option is set to more than one worker, recommended |
|
57 | 57 | ## value is (2 * NUMBER_OF_CPUS + 1), eg 2CPU = 5 workers |
|
58 | 58 | ## The `instance_id = *` must be set in the [app:main] section below |
|
59 | 59 | #workers = 1 |
|
60 | 60 | ## number of threads for each of the worker, must be set to 1 for gevent |
|
61 | 61 | ## generally recommened to be at 1 |
|
62 | 62 | #threads = 1 |
|
63 | 63 | ## process name |
|
64 | 64 | #proc_name = rhodecode |
|
65 | 65 | ## type of worker class, one of sync, gevent |
|
66 | 66 | ## recommended for bigger setup is using of of other than sync one |
|
67 | 67 | #worker_class = sync |
|
68 | ## The maximum number of simultaneous clients. Valid only for Gevent | |
|
69 | #worker_connections = 10 | |
|
68 | 70 | ## max number of requests that worker will handle before being gracefully |
|
69 | 71 | ## restarted, could prevent memory leaks |
|
70 | 72 | #max_requests = 1000 |
|
71 | 73 | #max_requests_jitter = 30 |
|
72 | 74 | ## ammount of time a worker can spend with handling a request before it |
|
73 | 75 | ## gets killed and restarted. Set to 6hrs |
|
74 | 76 | #timeout = 21600 |
|
75 | 77 | |
|
76 | 78 | |
|
77 | 79 | ## prefix middleware for RhodeCode, disables force_https flag. |
|
78 | 80 | ## allows to set RhodeCode under a prefix in server. |
|
79 | 81 | ## eg https://server.com/<prefix>. Enable `filter-with =` option below as well. |
|
80 | 82 | #[filter:proxy-prefix] |
|
81 | 83 | #use = egg:PasteDeploy#prefix |
|
82 | 84 | #prefix = /<your-prefix> |
|
83 | 85 | |
|
84 | 86 | [app:main] |
|
85 | 87 | use = egg:rhodecode-enterprise-ce |
|
86 | 88 | ## enable proxy prefix middleware, defined below |
|
87 | 89 | #filter-with = proxy-prefix |
|
88 | 90 | |
|
89 | 91 | full_stack = true |
|
90 | 92 | |
|
91 | 93 | ## Serve static files via RhodeCode, disable to serve them via HTTP server |
|
92 | 94 | static_files = true |
|
93 | 95 | |
|
94 | 96 | ## Optional Languages |
|
95 | 97 | ## en(default), be, de, es, fr, it, ja, pl, pt, ru, zh |
|
96 | 98 | lang = en |
|
97 | 99 | |
|
98 | 100 | ## perform a full repository scan on each server start, this should be |
|
99 | 101 | ## set to false after first startup, to allow faster server restarts. |
|
100 | 102 | startup.import_repos = false |
|
101 | 103 | |
|
102 | 104 | ## Uncomment and set this path to use archive download cache. |
|
103 | 105 | ## Once enabled, generated archives will be cached at this location |
|
104 | 106 | ## and served from the cache during subsequent requests for the same archive of |
|
105 | 107 | ## the repository. |
|
106 | 108 | #archive_cache_dir = /tmp/tarballcache |
|
107 | 109 | |
|
108 | 110 | ## change this to unique ID for security |
|
109 | 111 | app_instance_uuid = rc-production |
|
110 | 112 | |
|
111 | 113 | ## cut off limit for large diffs (size in bytes) |
|
112 | 114 | cut_off_limit_diff = 1024000 |
|
113 | 115 | cut_off_limit_file = 256000 |
|
114 | 116 | |
|
115 | 117 | ## use cache version of scm repo everywhere |
|
116 | 118 | vcs_full_cache = true |
|
117 | 119 | |
|
118 | 120 | ## force https in RhodeCode, fixes https redirects, assumes it's always https |
|
119 | 121 | ## Normally this is controlled by proper http flags sent from http server |
|
120 | 122 | force_https = false |
|
121 | 123 | |
|
122 | 124 | ## use Strict-Transport-Security headers |
|
123 | 125 | use_htsts = false |
|
124 | 126 | |
|
125 | 127 | ## number of commits stats will parse on each iteration |
|
126 | 128 | commit_parse_limit = 25 |
|
127 | 129 | |
|
128 | 130 | ## git rev filter option, --all is the default filter, if you need to |
|
129 | 131 | ## hide all refs in changelog switch this to --branches --tags |
|
130 | 132 | git_rev_filter = --branches --tags |
|
131 | 133 | |
|
132 | 134 | # Set to true if your repos are exposed using the dumb protocol |
|
133 | 135 | git_update_server_info = false |
|
134 | 136 | |
|
135 | 137 | ## RSS/ATOM feed options |
|
136 | 138 | rss_cut_off_limit = 256000 |
|
137 | 139 | rss_items_per_page = 10 |
|
138 | 140 | rss_include_diff = false |
|
139 | 141 | |
|
140 | 142 | ## gist URL alias, used to create nicer urls for gist. This should be an |
|
141 | 143 | ## url that does rewrites to _admin/gists/<gistid>. |
|
142 | 144 | ## example: http://gist.rhodecode.org/{gistid}. Empty means use the internal |
|
143 | 145 | ## RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/<gistid> |
|
144 | 146 | gist_alias_url = |
|
145 | 147 | |
|
146 | 148 | ## List of controllers (using glob pattern syntax) that AUTH TOKENS could be |
|
147 | 149 | ## used for access. |
|
148 | 150 | ## Adding ?auth_token = <token> to the url authenticates this request as if it |
|
149 | 151 | ## came from the the logged in user who own this authentication token. |
|
150 | 152 | ## |
|
151 | 153 | ## Syntax is <ControllerClass>:<function_pattern>. |
|
152 | 154 | ## To enable access to raw_files put `FilesController:raw`. |
|
153 | 155 | ## To enable access to patches add `ChangesetController:changeset_patch`. |
|
154 | 156 | ## The list should be "," separated and on a single line. |
|
155 | 157 | ## |
|
156 | 158 | ## Recommended controllers to enable: |
|
157 | 159 | # ChangesetController:changeset_patch, |
|
158 | 160 | # ChangesetController:changeset_raw, |
|
159 | 161 | # FilesController:raw, |
|
160 | 162 | # FilesController:archivefile, |
|
161 | 163 | # GistsController:*, |
|
162 | 164 | api_access_controllers_whitelist = |
|
163 | 165 | |
|
164 | 166 | ## default encoding used to convert from and to unicode |
|
165 | 167 | ## can be also a comma separated list of encoding in case of mixed encodings |
|
166 | 168 | default_encoding = UTF-8 |
|
167 | 169 | |
|
168 | 170 | ## instance-id prefix |
|
169 | 171 | ## a prefix key for this instance used for cache invalidation when running |
|
170 | 172 | ## multiple instances of rhodecode, make sure it's globally unique for |
|
171 | 173 | ## all running rhodecode instances. Leave empty if you don't use it |
|
172 | 174 | instance_id = |
|
173 | 175 | |
|
174 | 176 | ## alternative return HTTP header for failed authentication. Default HTTP |
|
175 | 177 | ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with |
|
176 | 178 | ## handling that causing a series of failed authentication calls. |
|
177 | 179 | ## Set this variable to 403 to return HTTPForbidden, or any other HTTP code |
|
178 | 180 | ## This will be served instead of default 401 on bad authnetication |
|
179 | 181 | auth_ret_code = |
|
180 | 182 | |
|
181 | 183 | ## use special detection method when serving auth_ret_code, instead of serving |
|
182 | 184 | ## ret_code directly, use 401 initially (Which triggers credentials prompt) |
|
183 | 185 | ## and then serve auth_ret_code to clients |
|
184 | 186 | auth_ret_code_detection = false |
|
185 | 187 | |
|
186 | 188 | ## locking return code. When repository is locked return this HTTP code. 2XX |
|
187 | 189 | ## codes don't break the transactions while 4XX codes do |
|
188 | 190 | lock_ret_code = 423 |
|
189 | 191 | |
|
190 | 192 | ## allows to change the repository location in settings page |
|
191 | 193 | allow_repo_location_change = true |
|
192 | 194 | |
|
193 | 195 | ## allows to setup custom hooks in settings page |
|
194 | 196 | allow_custom_hooks_settings = true |
|
195 | 197 | |
|
196 | 198 | ## generated license token, goto license page in RhodeCode settings to obtain |
|
197 | 199 | ## new token |
|
198 | 200 | license_token = |
|
199 | 201 | |
|
200 | 202 | ## supervisor connection uri, for managing supervisor and logs. |
|
201 | 203 | supervisor.uri = |
|
202 | 204 | ## supervisord group name/id we only want this RC instance to handle |
|
203 | 205 | supervisor.group_id = prod |
|
204 | 206 | |
|
205 | 207 | ## Display extended labs settings |
|
206 | 208 | labs_settings_active = true |
|
207 | 209 | |
|
208 | 210 | #################################### |
|
209 | 211 | ### CELERY CONFIG #### |
|
210 | 212 | #################################### |
|
211 | 213 | use_celery = false |
|
212 | 214 | broker.host = localhost |
|
213 | 215 | broker.vhost = rabbitmqhost |
|
214 | 216 | broker.port = 5672 |
|
215 | 217 | broker.user = rabbitmq |
|
216 | 218 | broker.password = qweqwe |
|
217 | 219 | |
|
218 | 220 | celery.imports = rhodecode.lib.celerylib.tasks |
|
219 | 221 | |
|
220 | 222 | celery.result.backend = amqp |
|
221 | 223 | celery.result.dburi = amqp:// |
|
222 | 224 | celery.result.serialier = json |
|
223 | 225 | |
|
224 | 226 | #celery.send.task.error.emails = true |
|
225 | 227 | #celery.amqp.task.result.expires = 18000 |
|
226 | 228 | |
|
227 | 229 | celeryd.concurrency = 2 |
|
228 | 230 | #celeryd.log.file = celeryd.log |
|
229 | 231 | celeryd.log.level = debug |
|
230 | 232 | celeryd.max.tasks.per.child = 1 |
|
231 | 233 | |
|
232 | 234 | ## tasks will never be sent to the queue, but executed locally instead. |
|
233 | 235 | celery.always.eager = false |
|
234 | 236 | |
|
235 | 237 | #################################### |
|
236 | 238 | ### BEAKER CACHE #### |
|
237 | 239 | #################################### |
|
238 | 240 | # default cache dir for templates. Putting this into a ramdisk |
|
239 | 241 | ## can boost performance, eg. %(here)s/data_ramdisk |
|
240 | 242 | cache_dir = %(here)s/data |
|
241 | 243 | |
|
242 | 244 | ## locking and default file storage for Beaker. Putting this into a ramdisk |
|
243 | 245 | ## can boost performance, eg. %(here)s/data_ramdisk/cache/beaker_data |
|
244 | 246 | beaker.cache.data_dir = %(here)s/data/cache/beaker_data |
|
245 | 247 | beaker.cache.lock_dir = %(here)s/data/cache/beaker_lock |
|
246 | 248 | |
|
247 | 249 | beaker.cache.regions = super_short_term, short_term, long_term, sql_cache_short, auth_plugins, repo_cache_long |
|
248 | 250 | |
|
249 | 251 | beaker.cache.super_short_term.type = memory |
|
250 | 252 | beaker.cache.super_short_term.expire = 10 |
|
251 | 253 | beaker.cache.super_short_term.key_length = 256 |
|
252 | 254 | |
|
253 | 255 | beaker.cache.short_term.type = memory |
|
254 | 256 | beaker.cache.short_term.expire = 60 |
|
255 | 257 | beaker.cache.short_term.key_length = 256 |
|
256 | 258 | |
|
257 | 259 | beaker.cache.long_term.type = memory |
|
258 | 260 | beaker.cache.long_term.expire = 36000 |
|
259 | 261 | beaker.cache.long_term.key_length = 256 |
|
260 | 262 | |
|
261 | 263 | beaker.cache.sql_cache_short.type = memory |
|
262 | 264 | beaker.cache.sql_cache_short.expire = 10 |
|
263 | 265 | beaker.cache.sql_cache_short.key_length = 256 |
|
264 | 266 | |
|
265 | 267 | # default is memory cache, configure only if required |
|
266 | 268 | # using multi-node or multi-worker setup |
|
267 | 269 | #beaker.cache.auth_plugins.type = ext:database |
|
268 | 270 | #beaker.cache.auth_plugins.lock_dir = %(here)s/data/cache/auth_plugin_lock |
|
269 | 271 | #beaker.cache.auth_plugins.url = postgresql://postgres:secret@localhost/rhodecode |
|
270 | 272 | #beaker.cache.auth_plugins.url = mysql://root:secret@127.0.0.1/rhodecode |
|
271 | 273 | #beaker.cache.auth_plugins.sa.pool_recycle = 3600 |
|
272 | 274 | #beaker.cache.auth_plugins.sa.pool_size = 10 |
|
273 | 275 | #beaker.cache.auth_plugins.sa.max_overflow = 0 |
|
274 | 276 | |
|
275 | 277 | beaker.cache.repo_cache_long.type = memorylru_base |
|
276 | 278 | beaker.cache.repo_cache_long.max_items = 4096 |
|
277 | 279 | beaker.cache.repo_cache_long.expire = 2592000 |
|
278 | 280 | |
|
279 | 281 | # default is memorylru_base cache, configure only if required |
|
280 | 282 | # using multi-node or multi-worker setup |
|
281 | 283 | #beaker.cache.repo_cache_long.type = ext:memcached |
|
282 | 284 | #beaker.cache.repo_cache_long.url = localhost:11211 |
|
283 | 285 | #beaker.cache.repo_cache_long.expire = 1209600 |
|
284 | 286 | #beaker.cache.repo_cache_long.key_length = 256 |
|
285 | 287 | |
|
286 | 288 | #################################### |
|
287 | 289 | ### BEAKER SESSION #### |
|
288 | 290 | #################################### |
|
289 | 291 | |
|
290 | 292 | ## .session.type is type of storage options for the session, current allowed |
|
291 |
## types are file |
|
|
292 |
|
|
|
293 | ## types are file, ext:memcached, ext:database, and memory(default). | |
|
294 | beaker.session.type = file | |
|
295 | beaker.session.data_dir = %(here)s/data/sessions/data | |
|
293 | 296 | |
|
294 | 297 | ## db based session, fast, and allows easy management over logged in users ## |
|
295 | 298 | #beaker.session.type = ext:database |
|
296 | #beaker.session.lock_dir = %(here)s/data/cache/session_db_lock | |
|
297 | 299 | #beaker.session.table_name = db_session |
|
298 | 300 | #beaker.session.sa.url = postgresql://postgres:secret@localhost/rhodecode |
|
299 | 301 | #beaker.session.sa.url = mysql://root:secret@127.0.0.1/rhodecode |
|
300 | 302 | #beaker.session.sa.pool_recycle = 3600 |
|
301 | 303 | #beaker.session.sa.echo = false |
|
302 | 304 | |
|
303 | 305 | beaker.session.key = rhodecode |
|
304 | 306 | beaker.session.secret = production-rc-uytcxaz |
|
307 | #beaker.session.lock_dir = %(here)s/data/sessions/lock | |
|
305 | 308 | |
|
306 | 309 | ## Secure encrypted cookie. Requires AES and AES python libraries |
|
307 | 310 | ## you must disable beaker.session.secret to use this |
|
308 | 311 | #beaker.session.encrypt_key = <key_for_encryption> |
|
309 | 312 | #beaker.session.validate_key = <validation_key> |
|
310 | 313 | |
|
311 | 314 | ## sets session as invalid(also logging out user) if it haven not been |
|
312 | 315 | ## accessed for given amount of time in seconds |
|
313 | 316 | beaker.session.timeout = 2592000 |
|
314 | 317 | beaker.session.httponly = true |
|
315 | 318 | #beaker.session.cookie_path = /<your-prefix> |
|
316 | 319 | |
|
317 | 320 | ## uncomment for https secure cookie |
|
318 | 321 | beaker.session.secure = false |
|
319 | 322 | |
|
320 | 323 | ## auto save the session to not to use .save() |
|
321 | 324 | beaker.session.auto = false |
|
322 | 325 | |
|
323 | 326 | ## default cookie expiration time in seconds, set to `true` to set expire |
|
324 | 327 | ## at browser close |
|
325 | 328 | #beaker.session.cookie_expires = 3600 |
|
326 | 329 | |
|
327 | 330 | ################################### |
|
328 | 331 | ## SEARCH INDEXING CONFIGURATION ## |
|
329 | 332 | ################################### |
|
330 | 333 | |
|
331 | 334 | search.module = rhodecode.lib.index.whoosh |
|
332 | 335 | search.location = %(here)s/data/index |
|
333 | 336 | |
|
334 | 337 | ################################### |
|
335 | 338 | ## ERROR AND LOG HANDLING SYSTEM ## |
|
336 | 339 | ################################### |
|
337 | 340 | |
|
338 | 341 | ## Appenlight is tailored to work with RhodeCode, see |
|
339 | 342 | ## http://appenlight.com for details how to obtain an account |
|
340 | 343 | |
|
341 | 344 | ## appenlight integration enabled |
|
342 | 345 | appenlight = false |
|
343 | 346 | |
|
344 | 347 | appenlight.server_url = https://api.appenlight.com |
|
345 | 348 | appenlight.api_key = YOUR_API_KEY |
|
346 | 349 | ;appenlight.transport_config = https://api.appenlight.com?threaded=1&timeout=5 |
|
347 | 350 | |
|
348 | 351 | # used for JS client |
|
349 | 352 | appenlight.api_public_key = YOUR_API_PUBLIC_KEY |
|
350 | 353 | |
|
351 | 354 | ## TWEAK AMOUNT OF INFO SENT HERE |
|
352 | 355 | |
|
353 | 356 | ## enables 404 error logging (default False) |
|
354 | 357 | appenlight.report_404 = false |
|
355 | 358 | |
|
356 | 359 | ## time in seconds after request is considered being slow (default 1) |
|
357 | 360 | appenlight.slow_request_time = 1 |
|
358 | 361 | |
|
359 | 362 | ## record slow requests in application |
|
360 | 363 | ## (needs to be enabled for slow datastore recording and time tracking) |
|
361 | 364 | appenlight.slow_requests = true |
|
362 | 365 | |
|
363 | 366 | ## enable hooking to application loggers |
|
364 | 367 | appenlight.logging = true |
|
365 | 368 | |
|
366 | 369 | ## minimum log level for log capture |
|
367 | 370 | appenlight.logging.level = WARNING |
|
368 | 371 | |
|
369 | 372 | ## send logs only from erroneous/slow requests |
|
370 | 373 | ## (saves API quota for intensive logging) |
|
371 | 374 | appenlight.logging_on_error = false |
|
372 | 375 | |
|
373 | 376 | ## list of additonal keywords that should be grabbed from environ object |
|
374 | 377 | ## can be string with comma separated list of words in lowercase |
|
375 | 378 | ## (by default client will always send following info: |
|
376 | 379 | ## 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that |
|
377 | 380 | ## start with HTTP* this list be extended with additional keywords here |
|
378 | 381 | appenlight.environ_keys_whitelist = |
|
379 | 382 | |
|
380 | 383 | ## list of keywords that should be blanked from request object |
|
381 | 384 | ## can be string with comma separated list of words in lowercase |
|
382 | 385 | ## (by default client will always blank keys that contain following words |
|
383 | 386 | ## 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf' |
|
384 | 387 | ## this list be extended with additional keywords set here |
|
385 | 388 | appenlight.request_keys_blacklist = |
|
386 | 389 | |
|
387 | 390 | ## list of namespaces that should be ignores when gathering log entries |
|
388 | 391 | ## can be string with comma separated list of namespaces |
|
389 | 392 | ## (by default the client ignores own entries: appenlight_client.client) |
|
390 | 393 | appenlight.log_namespace_blacklist = |
|
391 | 394 | |
|
392 | 395 | |
|
393 | 396 | ################################################################################ |
|
394 | 397 | ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ## |
|
395 | 398 | ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ## |
|
396 | 399 | ## execute malicious code after an exception is raised. ## |
|
397 | 400 | ################################################################################ |
|
398 | 401 | set debug = false |
|
399 | 402 | |
|
400 | 403 | |
|
401 | 404 | ############## |
|
402 | 405 | ## STYLING ## |
|
403 | 406 | ############## |
|
404 | 407 | debug_style = false |
|
405 | 408 | |
|
406 | 409 | ######################################################### |
|
407 | 410 | ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ### |
|
408 | 411 | ######################################################### |
|
409 | 412 | #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30 |
|
410 | 413 | sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode |
|
411 | 414 | #sqlalchemy.db1.url = mysql://root:qweqwe@localhost/rhodecode |
|
412 | 415 | |
|
413 | 416 | # see sqlalchemy docs for other advanced settings |
|
414 | 417 | |
|
415 | 418 | ## print the sql statements to output |
|
416 | 419 | sqlalchemy.db1.echo = false |
|
417 | 420 | ## recycle the connections after this ammount of seconds |
|
418 | 421 | sqlalchemy.db1.pool_recycle = 3600 |
|
419 | 422 | sqlalchemy.db1.convert_unicode = true |
|
420 | 423 | |
|
421 | 424 | ## the number of connections to keep open inside the connection pool. |
|
422 | 425 | ## 0 indicates no limit |
|
423 | 426 | #sqlalchemy.db1.pool_size = 5 |
|
424 | 427 | |
|
425 | 428 | ## the number of connections to allow in connection pool "overflow", that is |
|
426 | 429 | ## connections that can be opened above and beyond the pool_size setting, |
|
427 | 430 | ## which defaults to five. |
|
428 | 431 | #sqlalchemy.db1.max_overflow = 10 |
|
429 | 432 | |
|
430 | 433 | |
|
431 | 434 | ################## |
|
432 | 435 | ### VCS CONFIG ### |
|
433 | 436 | ################## |
|
434 | 437 | vcs.server.enable = true |
|
435 | 438 | vcs.server = localhost:9900 |
|
436 | 439 | # Available protocols: pyro4, http |
|
437 | 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 | 448 | vcs.server.log_level = info |
|
439 | 449 | vcs.start_server = false |
|
440 | 450 | vcs.backends = hg, git, svn |
|
441 | 451 | vcs.connection_timeout = 3600 |
|
442 | 452 | ## Compatibility version when creating SVN repositories. Defaults to newest version when commented out. |
|
443 | 453 | ## Available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible |
|
444 | 454 | #vcs.svn.compatible_version = pre-1.8-compatible |
|
445 | 455 | |
|
446 | 456 | ################################ |
|
447 | 457 | ### LOGGING CONFIGURATION #### |
|
448 | 458 | ################################ |
|
449 | 459 | [loggers] |
|
450 | 460 | keys = root, routes, rhodecode, sqlalchemy, beaker, pyro4, templates, whoosh_indexer |
|
451 | 461 | |
|
452 | 462 | [handlers] |
|
453 | 463 | keys = console, console_sql |
|
454 | 464 | |
|
455 | 465 | [formatters] |
|
456 | 466 | keys = generic, color_formatter, color_formatter_sql |
|
457 | 467 | |
|
458 | 468 | ############# |
|
459 | 469 | ## LOGGERS ## |
|
460 | 470 | ############# |
|
461 | 471 | [logger_root] |
|
462 | 472 | level = NOTSET |
|
463 | 473 | handlers = console |
|
464 | 474 | |
|
465 | 475 | [logger_routes] |
|
466 | 476 | level = DEBUG |
|
467 | 477 | handlers = |
|
468 | 478 | qualname = routes.middleware |
|
469 | 479 | ## "level = DEBUG" logs the route matched and routing variables. |
|
470 | 480 | propagate = 1 |
|
471 | 481 | |
|
472 | 482 | [logger_beaker] |
|
473 | 483 | level = DEBUG |
|
474 | 484 | handlers = |
|
475 | 485 | qualname = beaker.container |
|
476 | 486 | propagate = 1 |
|
477 | 487 | |
|
478 | 488 | [logger_pyro4] |
|
479 | 489 | level = DEBUG |
|
480 | 490 | handlers = |
|
481 | 491 | qualname = Pyro4 |
|
482 | 492 | propagate = 1 |
|
483 | 493 | |
|
484 | 494 | [logger_templates] |
|
485 | 495 | level = INFO |
|
486 | 496 | handlers = |
|
487 | 497 | qualname = pylons.templating |
|
488 | 498 | propagate = 1 |
|
489 | 499 | |
|
490 | 500 | [logger_rhodecode] |
|
491 | 501 | level = DEBUG |
|
492 | 502 | handlers = |
|
493 | 503 | qualname = rhodecode |
|
494 | 504 | propagate = 1 |
|
495 | 505 | |
|
496 | 506 | [logger_sqlalchemy] |
|
497 | 507 | level = INFO |
|
498 | 508 | handlers = console_sql |
|
499 | 509 | qualname = sqlalchemy.engine |
|
500 | 510 | propagate = 0 |
|
501 | 511 | |
|
502 | 512 | [logger_whoosh_indexer] |
|
503 | 513 | level = DEBUG |
|
504 | 514 | handlers = |
|
505 | 515 | qualname = whoosh_indexer |
|
506 | 516 | propagate = 1 |
|
507 | 517 | |
|
508 | 518 | ############## |
|
509 | 519 | ## HANDLERS ## |
|
510 | 520 | ############## |
|
511 | 521 | |
|
512 | 522 | [handler_console] |
|
513 | 523 | class = StreamHandler |
|
514 | 524 | args = (sys.stderr,) |
|
515 | 525 | level = INFO |
|
516 | 526 | formatter = generic |
|
517 | 527 | |
|
518 | 528 | [handler_console_sql] |
|
519 | 529 | class = StreamHandler |
|
520 | 530 | args = (sys.stderr,) |
|
521 | 531 | level = WARN |
|
522 | 532 | formatter = generic |
|
523 | 533 | |
|
524 | 534 | ################ |
|
525 | 535 | ## FORMATTERS ## |
|
526 | 536 | ################ |
|
527 | 537 | |
|
528 | 538 | [formatter_generic] |
|
529 | 539 | class = rhodecode.lib.logging_formatter.Pyro4AwareFormatter |
|
530 | 540 | format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s |
|
531 | 541 | datefmt = %Y-%m-%d %H:%M:%S |
|
532 | 542 | |
|
533 | 543 | [formatter_color_formatter] |
|
534 | 544 | class = rhodecode.lib.logging_formatter.ColorFormatter |
|
535 | 545 | format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s |
|
536 | 546 | datefmt = %Y-%m-%d %H:%M:%S |
|
537 | 547 | |
|
538 | 548 | [formatter_color_formatter_sql] |
|
539 | 549 | class = rhodecode.lib.logging_formatter.ColorFormatterSql |
|
540 | 550 | format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s |
|
541 | 551 | datefmt = %Y-%m-%d %H:%M:%S |
@@ -1,75 +1,76 b'' | |||
|
1 | 1 | .. _rhodecode-release-notes-ref: |
|
2 | 2 | |
|
3 | 3 | Release Notes |
|
4 | 4 | ============= |
|
5 | 5 | |
|
6 | 6 | |RCE| 4.x Versions |
|
7 | 7 | ------------------ |
|
8 | 8 | |
|
9 | release-notes-4.0.1.rst | |
|
9 | 10 | release-notes-4.0.0.rst |
|
10 | 11 | |
|
11 | 12 | |RCE| 3.x Versions |
|
12 | 13 | ------------------ |
|
13 | 14 | |
|
14 | 15 | .. toctree:: |
|
15 | 16 | :maxdepth: 1 |
|
16 | 17 | |
|
17 | 18 | release-notes-3.8.4.rst |
|
18 | 19 | release-notes-3.8.3.rst |
|
19 | 20 | release-notes-3.8.2.rst |
|
20 | 21 | release-notes-3.8.1.rst |
|
21 | 22 | release-notes-3.8.0.rst |
|
22 | 23 | release-notes-3.7.1.rst |
|
23 | 24 | release-notes-3.7.0.rst |
|
24 | 25 | release-notes-3.6.1.rst |
|
25 | 26 | release-notes-3.6.0.rst |
|
26 | 27 | release-notes-3.5.2.rst |
|
27 | 28 | release-notes-3.5.1.rst |
|
28 | 29 | release-notes-3.5.0.rst |
|
29 | 30 | release-notes-3.4.1.rst |
|
30 | 31 | release-notes-3.4.0.rst |
|
31 | 32 | release-notes-3.3.4.rst |
|
32 | 33 | release-notes-3.3.3.rst |
|
33 | 34 | release-notes-3.3.2.rst |
|
34 | 35 | release-notes-3.3.1.rst |
|
35 | 36 | release-notes-3.3.0.rst |
|
36 | 37 | release-notes-3.2.3.rst |
|
37 | 38 | release-notes-3.2.2.rst |
|
38 | 39 | release-notes-3.2.1.rst |
|
39 | 40 | release-notes-3.2.0.rst |
|
40 | 41 | release-notes-3.1.1.rst |
|
41 | 42 | release-notes-3.1.0.rst |
|
42 | 43 | release-notes-3.0.2.rst |
|
43 | 44 | release-notes-3.0.1.rst |
|
44 | 45 | release-notes-3.0.0.rst |
|
45 | 46 | |
|
46 | 47 | |RCE| 2.x Versions |
|
47 | 48 | ------------------ |
|
48 | 49 | |
|
49 | 50 | .. toctree:: |
|
50 | 51 | :maxdepth: 1 |
|
51 | 52 | |
|
52 | 53 | release-notes-2.2.8.rst |
|
53 | 54 | release-notes-2.2.7.rst |
|
54 | 55 | release-notes-2.2.6.rst |
|
55 | 56 | release-notes-2.2.5.rst |
|
56 | 57 | release-notes-2.2.4.rst |
|
57 | 58 | release-notes-2.2.3.rst |
|
58 | 59 | release-notes-2.2.2.rst |
|
59 | 60 | release-notes-2.2.1.rst |
|
60 | 61 | release-notes-2.2.0.rst |
|
61 | 62 | release-notes-2.1.0.rst |
|
62 | 63 | release-notes-2.0.2.rst |
|
63 | 64 | release-notes-2.0.1.rst |
|
64 | 65 | release-notes-2.0.0.rst |
|
65 | 66 | |
|
66 | 67 | |RCE| 1.x Versions |
|
67 | 68 | ------------------ |
|
68 | 69 | |
|
69 | 70 | .. toctree:: |
|
70 | 71 | :maxdepth: 1 |
|
71 | 72 | |
|
72 | 73 | release-notes-1.7.2.rst |
|
73 | 74 | release-notes-1.7.1.rst |
|
74 | 75 | release-notes-1.7.0.rst |
|
75 | 76 | release-notes-1.6.0.rst |
@@ -1,1273 +1,1273 b'' | |||
|
1 | 1 | { |
|
2 | 2 | Babel = super.buildPythonPackage { |
|
3 | 3 | name = "Babel-1.3"; |
|
4 | 4 | buildInputs = with self; []; |
|
5 | 5 | doCheck = false; |
|
6 | 6 | propagatedBuildInputs = with self; [pytz]; |
|
7 | 7 | src = fetchurl { |
|
8 | 8 | url = "https://pypi.python.org/packages/33/27/e3978243a03a76398c384c83f7ca879bc6e8f1511233a621fcada135606e/Babel-1.3.tar.gz"; |
|
9 | 9 | md5 = "5264ceb02717843cbc9ffce8e6e06bdb"; |
|
10 | 10 | }; |
|
11 | 11 | }; |
|
12 | 12 | Beaker = super.buildPythonPackage { |
|
13 | 13 | name = "Beaker-1.7.0"; |
|
14 | 14 | buildInputs = with self; []; |
|
15 | 15 | doCheck = false; |
|
16 | 16 | propagatedBuildInputs = with self; []; |
|
17 | 17 | src = fetchurl { |
|
18 | 18 | url = "https://pypi.python.org/packages/97/8e/409d2e7c009b8aa803dc9e6f239f1db7c3cdf578249087a404e7c27a505d/Beaker-1.7.0.tar.gz"; |
|
19 | 19 | md5 = "386be3f7fe427358881eee4622b428b3"; |
|
20 | 20 | }; |
|
21 | 21 | }; |
|
22 | 22 | CProfileV = super.buildPythonPackage { |
|
23 | 23 | name = "CProfileV-1.0.6"; |
|
24 | 24 | buildInputs = with self; []; |
|
25 | 25 | doCheck = false; |
|
26 | 26 | propagatedBuildInputs = with self; [bottle]; |
|
27 | 27 | src = fetchurl { |
|
28 | 28 | url = "https://pypi.python.org/packages/eb/df/983a0b6cfd3ac94abf023f5011cb04f33613ace196e33f53c86cf91850d5/CProfileV-1.0.6.tar.gz"; |
|
29 | 29 | md5 = "08c7c242b6e64237bc53c5d13537e03d"; |
|
30 | 30 | }; |
|
31 | 31 | }; |
|
32 | 32 | Fabric = super.buildPythonPackage { |
|
33 | 33 | name = "Fabric-1.10.0"; |
|
34 | 34 | buildInputs = with self; []; |
|
35 | 35 | doCheck = false; |
|
36 | 36 | propagatedBuildInputs = with self; [paramiko]; |
|
37 | 37 | src = fetchurl { |
|
38 | 38 | url = "https://pypi.python.org/packages/e3/5f/b6ebdb5241d5ec9eab582a5c8a01255c1107da396f849e538801d2fe64a5/Fabric-1.10.0.tar.gz"; |
|
39 | 39 | md5 = "2cb96473387f0e7aa035210892352f4a"; |
|
40 | 40 | }; |
|
41 | 41 | }; |
|
42 | 42 | FormEncode = super.buildPythonPackage { |
|
43 | 43 | name = "FormEncode-1.2.4"; |
|
44 | 44 | buildInputs = with self; []; |
|
45 | 45 | doCheck = false; |
|
46 | 46 | propagatedBuildInputs = with self; []; |
|
47 | 47 | src = fetchurl { |
|
48 | 48 | url = "https://pypi.python.org/packages/8e/59/0174271a6f004512e0201188593e6d319db139d14cb7490e488bbb078015/FormEncode-1.2.4.tar.gz"; |
|
49 | 49 | md5 = "6bc17fb9aed8aea198975e888e2077f4"; |
|
50 | 50 | }; |
|
51 | 51 | }; |
|
52 | 52 | Jinja2 = super.buildPythonPackage { |
|
53 | 53 | name = "Jinja2-2.7.3"; |
|
54 | 54 | buildInputs = with self; []; |
|
55 | 55 | doCheck = false; |
|
56 | 56 | propagatedBuildInputs = with self; [MarkupSafe]; |
|
57 | 57 | src = fetchurl { |
|
58 | 58 | url = "https://pypi.python.org/packages/b0/73/eab0bca302d6d6a0b5c402f47ad1760dc9cb2dd14bbc1873ad48db258e4d/Jinja2-2.7.3.tar.gz"; |
|
59 | 59 | md5 = "b9dffd2f3b43d673802fe857c8445b1a"; |
|
60 | 60 | }; |
|
61 | 61 | }; |
|
62 | 62 | Mako = super.buildPythonPackage { |
|
63 | 63 | name = "Mako-1.0.1"; |
|
64 | 64 | buildInputs = with self; []; |
|
65 | 65 | doCheck = false; |
|
66 | 66 | propagatedBuildInputs = with self; [MarkupSafe]; |
|
67 | 67 | src = fetchurl { |
|
68 | 68 | url = "https://pypi.python.org/packages/8e/a4/aa56533ecaa5f22ca92428f74e074d0c9337282933c722391902c8f9e0f8/Mako-1.0.1.tar.gz"; |
|
69 | 69 | md5 = "9f0aafd177b039ef67b90ea350497a54"; |
|
70 | 70 | }; |
|
71 | 71 | }; |
|
72 | 72 | Markdown = super.buildPythonPackage { |
|
73 | 73 | name = "Markdown-2.6.2"; |
|
74 | 74 | buildInputs = with self; []; |
|
75 | 75 | doCheck = false; |
|
76 | 76 | propagatedBuildInputs = with self; []; |
|
77 | 77 | src = fetchurl { |
|
78 | 78 | url = "https://pypi.python.org/packages/62/8b/83658b5f6c220d5fcde9f9852d46ea54765d734cfbc5a9f4c05bfc36db4d/Markdown-2.6.2.tar.gz"; |
|
79 | 79 | md5 = "256d19afcc564dc4ce4c229bb762f7ae"; |
|
80 | 80 | }; |
|
81 | 81 | }; |
|
82 | 82 | MarkupSafe = super.buildPythonPackage { |
|
83 | 83 | name = "MarkupSafe-0.23"; |
|
84 | 84 | buildInputs = with self; []; |
|
85 | 85 | doCheck = false; |
|
86 | 86 | propagatedBuildInputs = with self; []; |
|
87 | 87 | src = fetchurl { |
|
88 | 88 | url = "https://pypi.python.org/packages/c0/41/bae1254e0396c0cc8cf1751cb7d9afc90a602353695af5952530482c963f/MarkupSafe-0.23.tar.gz"; |
|
89 | 89 | md5 = "f5ab3deee4c37cd6a922fb81e730da6e"; |
|
90 | 90 | }; |
|
91 | 91 | }; |
|
92 | 92 | MySQL-python = super.buildPythonPackage { |
|
93 | 93 | name = "MySQL-python-1.2.5"; |
|
94 | 94 | buildInputs = with self; []; |
|
95 | 95 | doCheck = false; |
|
96 | 96 | propagatedBuildInputs = with self; []; |
|
97 | 97 | src = fetchurl { |
|
98 | 98 | url = "https://pypi.python.org/packages/a5/e9/51b544da85a36a68debe7a7091f068d802fc515a3a202652828c73453cad/MySQL-python-1.2.5.zip"; |
|
99 | 99 | md5 = "654f75b302db6ed8dc5a898c625e030c"; |
|
100 | 100 | }; |
|
101 | 101 | }; |
|
102 | 102 | Paste = super.buildPythonPackage { |
|
103 | 103 | name = "Paste-2.0.2"; |
|
104 | 104 | buildInputs = with self; []; |
|
105 | 105 | doCheck = false; |
|
106 | 106 | propagatedBuildInputs = with self; [six]; |
|
107 | 107 | src = fetchurl { |
|
108 | 108 | url = "https://pypi.python.org/packages/d5/8d/0f8ac40687b97ff3e07ebd1369be20bdb3f93864d2dc3c2ff542edb4ce50/Paste-2.0.2.tar.gz"; |
|
109 | 109 | md5 = "4bfc8a7eaf858f6309d2ac0f40fc951c"; |
|
110 | 110 | }; |
|
111 | 111 | }; |
|
112 | 112 | PasteDeploy = super.buildPythonPackage { |
|
113 | 113 | name = "PasteDeploy-1.5.2"; |
|
114 | 114 | buildInputs = with self; []; |
|
115 | 115 | doCheck = false; |
|
116 | 116 | propagatedBuildInputs = with self; []; |
|
117 | 117 | src = fetchurl { |
|
118 | 118 | url = "https://pypi.python.org/packages/0f/90/8e20cdae206c543ea10793cbf4136eb9a8b3f417e04e40a29d72d9922cbd/PasteDeploy-1.5.2.tar.gz"; |
|
119 | 119 | md5 = "352b7205c78c8de4987578d19431af3b"; |
|
120 | 120 | }; |
|
121 | 121 | }; |
|
122 | 122 | PasteScript = super.buildPythonPackage { |
|
123 | 123 | name = "PasteScript-1.7.5"; |
|
124 | 124 | buildInputs = with self; []; |
|
125 | 125 | doCheck = false; |
|
126 | 126 | propagatedBuildInputs = with self; [Paste PasteDeploy]; |
|
127 | 127 | src = fetchurl { |
|
128 | 128 | url = "https://pypi.python.org/packages/a5/05/fc60efa7c2f17a1dbaeccb2a903a1e90902d92b9d00eebabe3095829d806/PasteScript-1.7.5.tar.gz"; |
|
129 | 129 | md5 = "4c72d78dcb6bb993f30536842c16af4d"; |
|
130 | 130 | }; |
|
131 | 131 | }; |
|
132 | 132 | Pygments = super.buildPythonPackage { |
|
133 | 133 | name = "Pygments-2.0.2"; |
|
134 | 134 | buildInputs = with self; []; |
|
135 | 135 | doCheck = false; |
|
136 | 136 | propagatedBuildInputs = with self; []; |
|
137 | 137 | src = fetchurl { |
|
138 | 138 | url = "https://pypi.python.org/packages/f4/c6/bdbc5a8a112256b2b6136af304dbae93d8b1ef8738ff2d12a51018800e46/Pygments-2.0.2.tar.gz"; |
|
139 | 139 | md5 = "238587a1370d62405edabd0794b3ec4a"; |
|
140 | 140 | }; |
|
141 | 141 | }; |
|
142 | 142 | Pylons = super.buildPythonPackage { |
|
143 | 143 | name = "Pylons-1.0.1"; |
|
144 | 144 | buildInputs = with self; []; |
|
145 | 145 | doCheck = false; |
|
146 | 146 | propagatedBuildInputs = with self; [Routes WebHelpers Beaker Paste PasteDeploy PasteScript FormEncode simplejson decorator nose Mako WebError WebTest Tempita MarkupSafe WebOb]; |
|
147 | 147 | src = fetchurl { |
|
148 | 148 | url = "https://pypi.python.org/packages/a2/69/b835a6bad00acbfeed3f33c6e44fa3f936efc998c795bfb15c61a79ecf62/Pylons-1.0.1.tar.gz"; |
|
149 | 149 | md5 = "6cb880d75fa81213192142b07a6e4915"; |
|
150 | 150 | }; |
|
151 | 151 | }; |
|
152 | 152 | Pyro4 = super.buildPythonPackage { |
|
153 | 153 | name = "Pyro4-4.41"; |
|
154 | 154 | buildInputs = with self; []; |
|
155 | 155 | doCheck = false; |
|
156 | 156 | propagatedBuildInputs = with self; [serpent]; |
|
157 | 157 | src = fetchurl { |
|
158 | 158 | url = "https://pypi.python.org/packages/56/2b/89b566b4bf3e7f8ba790db2d1223852f8cb454c52cab7693dd41f608ca2a/Pyro4-4.41.tar.gz"; |
|
159 | 159 | md5 = "ed69e9bfafa9c06c049a87cb0c4c2b6c"; |
|
160 | 160 | }; |
|
161 | 161 | }; |
|
162 | 162 | Routes = super.buildPythonPackage { |
|
163 | 163 | name = "Routes-1.13"; |
|
164 | 164 | buildInputs = with self; []; |
|
165 | 165 | doCheck = false; |
|
166 | 166 | propagatedBuildInputs = with self; [repoze.lru]; |
|
167 | 167 | src = fetchurl { |
|
168 | 168 | url = "https://pypi.python.org/packages/88/d3/259c3b3cde8837eb9441ab5f574a660e8a4acea8f54a078441d4d2acac1c/Routes-1.13.tar.gz"; |
|
169 | 169 | md5 = "d527b0ab7dd9172b1275a41f97448783"; |
|
170 | 170 | }; |
|
171 | 171 | }; |
|
172 | 172 | SQLAlchemy = super.buildPythonPackage { |
|
173 | 173 | name = "SQLAlchemy-0.9.9"; |
|
174 | 174 | buildInputs = with self; []; |
|
175 | 175 | doCheck = false; |
|
176 | 176 | propagatedBuildInputs = with self; []; |
|
177 | 177 | src = fetchurl { |
|
178 | 178 | url = "https://pypi.python.org/packages/28/f7/1bbfd0d8597e8c358d5e15a166a486ad82fc5579b4e67b6ef7c05b1d182b/SQLAlchemy-0.9.9.tar.gz"; |
|
179 | 179 | md5 = "8a10a9bd13ed3336ef7333ac2cc679ff"; |
|
180 | 180 | }; |
|
181 | 181 | }; |
|
182 | 182 | Sphinx = super.buildPythonPackage { |
|
183 | 183 | name = "Sphinx-1.2.2"; |
|
184 | 184 | buildInputs = with self; []; |
|
185 | 185 | doCheck = false; |
|
186 | 186 | propagatedBuildInputs = with self; [Pygments docutils Jinja2]; |
|
187 | 187 | src = fetchurl { |
|
188 | 188 | url = "https://pypi.python.org/packages/0a/50/34017e6efcd372893a416aba14b84a1a149fc7074537b0e9cb6ca7b7abe9/Sphinx-1.2.2.tar.gz"; |
|
189 | 189 | md5 = "3dc73ccaa8d0bfb2d62fb671b1f7e8a4"; |
|
190 | 190 | }; |
|
191 | 191 | }; |
|
192 | 192 | Tempita = super.buildPythonPackage { |
|
193 | 193 | name = "Tempita-0.5.2"; |
|
194 | 194 | buildInputs = with self; []; |
|
195 | 195 | doCheck = false; |
|
196 | 196 | propagatedBuildInputs = with self; []; |
|
197 | 197 | src = fetchurl { |
|
198 | 198 | url = "https://pypi.python.org/packages/56/c8/8ed6eee83dbddf7b0fc64dd5d4454bc05e6ccaafff47991f73f2894d9ff4/Tempita-0.5.2.tar.gz"; |
|
199 | 199 | md5 = "4c2f17bb9d481821c41b6fbee904cea1"; |
|
200 | 200 | }; |
|
201 | 201 | }; |
|
202 | 202 | URLObject = super.buildPythonPackage { |
|
203 | 203 | name = "URLObject-2.4.0"; |
|
204 | 204 | buildInputs = with self; []; |
|
205 | 205 | doCheck = false; |
|
206 | 206 | propagatedBuildInputs = with self; []; |
|
207 | 207 | src = fetchurl { |
|
208 | 208 | url = "https://pypi.python.org/packages/cb/b6/e25e58500f9caef85d664bec71ec67c116897bfebf8622c32cb75d1ca199/URLObject-2.4.0.tar.gz"; |
|
209 | 209 | md5 = "2ed819738a9f0a3051f31dc9924e3065"; |
|
210 | 210 | }; |
|
211 | 211 | }; |
|
212 | 212 | WebError = super.buildPythonPackage { |
|
213 | 213 | name = "WebError-0.10.3"; |
|
214 | 214 | buildInputs = with self; []; |
|
215 | 215 | doCheck = false; |
|
216 | 216 | propagatedBuildInputs = with self; [WebOb Tempita Pygments Paste]; |
|
217 | 217 | src = fetchurl { |
|
218 | 218 | url = "https://pypi.python.org/packages/35/76/e7e5c2ce7e9c7f31b54c1ff295a495886d1279a002557d74dd8957346a79/WebError-0.10.3.tar.gz"; |
|
219 | 219 | md5 = "84b9990b0baae6fd440b1e60cdd06f9a"; |
|
220 | 220 | }; |
|
221 | 221 | }; |
|
222 | 222 | WebHelpers = super.buildPythonPackage { |
|
223 | 223 | name = "WebHelpers-1.3"; |
|
224 | 224 | buildInputs = with self; []; |
|
225 | 225 | doCheck = false; |
|
226 | 226 | propagatedBuildInputs = with self; [MarkupSafe]; |
|
227 | 227 | src = fetchurl { |
|
228 | 228 | url = "https://pypi.python.org/packages/ee/68/4d07672821d514184357f1552f2dad923324f597e722de3b016ca4f7844f/WebHelpers-1.3.tar.gz"; |
|
229 | 229 | md5 = "32749ffadfc40fea51075a7def32588b"; |
|
230 | 230 | }; |
|
231 | 231 | }; |
|
232 | 232 | WebHelpers2 = super.buildPythonPackage { |
|
233 | 233 | name = "WebHelpers2-2.0"; |
|
234 | 234 | buildInputs = with self; []; |
|
235 | 235 | doCheck = false; |
|
236 | 236 | propagatedBuildInputs = with self; [MarkupSafe six]; |
|
237 | 237 | src = fetchurl { |
|
238 | 238 | url = "https://pypi.python.org/packages/ff/30/56342c6ea522439e3662427c8d7b5e5b390dff4ff2dc92d8afcb8ab68b75/WebHelpers2-2.0.tar.gz"; |
|
239 | 239 | md5 = "0f6b68d70c12ee0aed48c00b24da13d3"; |
|
240 | 240 | }; |
|
241 | 241 | }; |
|
242 | 242 | WebOb = super.buildPythonPackage { |
|
243 | 243 | name = "WebOb-1.3.1"; |
|
244 | 244 | buildInputs = with self; []; |
|
245 | 245 | doCheck = false; |
|
246 | 246 | propagatedBuildInputs = with self; []; |
|
247 | 247 | src = fetchurl { |
|
248 | 248 | url = "https://pypi.python.org/packages/16/78/adfc0380b8a0d75b2d543fa7085ba98a573b1ae486d9def88d172b81b9fa/WebOb-1.3.1.tar.gz"; |
|
249 | 249 | md5 = "20918251c5726956ba8fef22d1556177"; |
|
250 | 250 | }; |
|
251 | 251 | }; |
|
252 | 252 | WebTest = super.buildPythonPackage { |
|
253 | 253 | name = "WebTest-1.4.3"; |
|
254 | 254 | buildInputs = with self; []; |
|
255 | 255 | doCheck = false; |
|
256 | 256 | propagatedBuildInputs = with self; [WebOb]; |
|
257 | 257 | src = fetchurl { |
|
258 | 258 | url = "https://pypi.python.org/packages/51/3d/84fd0f628df10b30c7db87895f56d0158e5411206b721ca903cb51bfd948/WebTest-1.4.3.zip"; |
|
259 | 259 | md5 = "631ce728bed92c681a4020a36adbc353"; |
|
260 | 260 | }; |
|
261 | 261 | }; |
|
262 | 262 | Whoosh = super.buildPythonPackage { |
|
263 | 263 | name = "Whoosh-2.7.0"; |
|
264 | 264 | buildInputs = with self; []; |
|
265 | 265 | doCheck = false; |
|
266 | 266 | propagatedBuildInputs = with self; []; |
|
267 | 267 | src = fetchurl { |
|
268 | 268 | url = "https://pypi.python.org/packages/1c/dc/2f0231ff3875ded36df8c1ab851451e51a237dc0e5a86d3d96036158da94/Whoosh-2.7.0.zip"; |
|
269 | 269 | md5 = "7abfd970f16fadc7311960f3fa0bc7a9"; |
|
270 | 270 | }; |
|
271 | 271 | }; |
|
272 | 272 | alembic = super.buildPythonPackage { |
|
273 | 273 | name = "alembic-0.8.4"; |
|
274 | 274 | buildInputs = with self; []; |
|
275 | 275 | doCheck = false; |
|
276 | 276 | propagatedBuildInputs = with self; [SQLAlchemy Mako python-editor]; |
|
277 | 277 | src = fetchurl { |
|
278 | 278 | url = "https://pypi.python.org/packages/ca/7e/299b4499b5c75e5a38c5845145ad24755bebfb8eec07a2e1c366b7181eeb/alembic-0.8.4.tar.gz"; |
|
279 | 279 | md5 = "5f95d8ee62b443f9b37eb5bee76c582d"; |
|
280 | 280 | }; |
|
281 | 281 | }; |
|
282 | 282 | amqplib = super.buildPythonPackage { |
|
283 | 283 | name = "amqplib-1.0.2"; |
|
284 | 284 | buildInputs = with self; []; |
|
285 | 285 | doCheck = false; |
|
286 | 286 | propagatedBuildInputs = with self; []; |
|
287 | 287 | src = fetchurl { |
|
288 | 288 | url = "https://pypi.python.org/packages/75/b7/8c2429bf8d92354a0118614f9a4d15e53bc69ebedce534284111de5a0102/amqplib-1.0.2.tgz"; |
|
289 | 289 | md5 = "5c92f17fbedd99b2b4a836d4352d1e2f"; |
|
290 | 290 | }; |
|
291 | 291 | }; |
|
292 | 292 | anyjson = super.buildPythonPackage { |
|
293 | 293 | name = "anyjson-0.3.3"; |
|
294 | 294 | buildInputs = with self; []; |
|
295 | 295 | doCheck = false; |
|
296 | 296 | propagatedBuildInputs = with self; []; |
|
297 | 297 | src = fetchurl { |
|
298 | 298 | url = "https://pypi.python.org/packages/c3/4d/d4089e1a3dd25b46bebdb55a992b0797cff657b4477bc32ce28038fdecbc/anyjson-0.3.3.tar.gz"; |
|
299 | 299 | md5 = "2ea28d6ec311aeeebaf993cb3008b27c"; |
|
300 | 300 | }; |
|
301 | 301 | }; |
|
302 | 302 | appenlight-client = super.buildPythonPackage { |
|
303 | 303 | name = "appenlight-client-0.6.14"; |
|
304 | 304 | buildInputs = with self; []; |
|
305 | 305 | doCheck = false; |
|
306 | 306 | propagatedBuildInputs = with self; [WebOb requests]; |
|
307 | 307 | src = fetchurl { |
|
308 | 308 | url = "https://pypi.python.org/packages/4d/e0/23fee3ebada8143f707e65c06bcb82992040ee64ea8355e044ed55ebf0c1/appenlight_client-0.6.14.tar.gz"; |
|
309 | 309 | md5 = "578c69b09f4356d898fff1199b98a95c"; |
|
310 | 310 | }; |
|
311 | 311 | }; |
|
312 | 312 | authomatic = super.buildPythonPackage { |
|
313 | 313 | name = "authomatic-0.1.0.post1"; |
|
314 | 314 | buildInputs = with self; []; |
|
315 | 315 | doCheck = false; |
|
316 | 316 | propagatedBuildInputs = with self; []; |
|
317 | 317 | src = fetchurl { |
|
318 | 318 | url = "https://pypi.python.org/packages/08/1a/8a930461e604c2d5a7a871e1ac59fa82ccf994c32e807230c8d2fb07815a/Authomatic-0.1.0.post1.tar.gz"; |
|
319 | 319 | md5 = "be3f3ce08747d776aae6d6cc8dcb49a9"; |
|
320 | 320 | }; |
|
321 | 321 | }; |
|
322 | 322 | backport-ipaddress = super.buildPythonPackage { |
|
323 | 323 | name = "backport-ipaddress-0.1"; |
|
324 | 324 | buildInputs = with self; []; |
|
325 | 325 | doCheck = false; |
|
326 | 326 | propagatedBuildInputs = with self; []; |
|
327 | 327 | src = fetchurl { |
|
328 | 328 | url = "https://pypi.python.org/packages/d3/30/54c6dab05a4dec44db25ff309f1fbb6b7a8bde3f2bade38bb9da67bbab8f/backport_ipaddress-0.1.tar.gz"; |
|
329 | 329 | md5 = "9c1f45f4361f71b124d7293a60006c05"; |
|
330 | 330 | }; |
|
331 | 331 | }; |
|
332 | 332 | bottle = super.buildPythonPackage { |
|
333 | 333 | name = "bottle-0.12.8"; |
|
334 | 334 | buildInputs = with self; []; |
|
335 | 335 | doCheck = false; |
|
336 | 336 | propagatedBuildInputs = with self; []; |
|
337 | 337 | src = fetchurl { |
|
338 | 338 | url = "https://pypi.python.org/packages/52/df/e4a408f3a7af396d186d4ecd3b389dd764f0f943b4fa8d257bfe7b49d343/bottle-0.12.8.tar.gz"; |
|
339 | 339 | md5 = "13132c0a8f607bf860810a6ee9064c5b"; |
|
340 | 340 | }; |
|
341 | 341 | }; |
|
342 | 342 | bumpversion = super.buildPythonPackage { |
|
343 | 343 | name = "bumpversion-0.5.3"; |
|
344 | 344 | buildInputs = with self; []; |
|
345 | 345 | doCheck = false; |
|
346 | 346 | propagatedBuildInputs = with self; []; |
|
347 | 347 | src = fetchurl { |
|
348 | 348 | url = "https://pypi.python.org/packages/14/41/8c9da3549f8e00c84f0432c3a8cf8ed6898374714676aab91501d48760db/bumpversion-0.5.3.tar.gz"; |
|
349 | 349 | md5 = "c66a3492eafcf5ad4b024be9fca29820"; |
|
350 | 350 | }; |
|
351 | 351 | }; |
|
352 | 352 | celery = super.buildPythonPackage { |
|
353 | 353 | name = "celery-2.2.10"; |
|
354 | 354 | buildInputs = with self; []; |
|
355 | 355 | doCheck = false; |
|
356 | 356 | propagatedBuildInputs = with self; [python-dateutil anyjson kombu pyparsing]; |
|
357 | 357 | src = fetchurl { |
|
358 | 358 | url = "https://pypi.python.org/packages/b1/64/860fd50e45844c83442e7953effcddeff66b2851d90b2d784f7201c111b8/celery-2.2.10.tar.gz"; |
|
359 | 359 | md5 = "898bc87e54f278055b561316ba73e222"; |
|
360 | 360 | }; |
|
361 | 361 | }; |
|
362 | 362 | certifi = super.buildPythonPackage { |
|
363 |
name = "certifi-2016. |
|
|
363 | name = "certifi-2016.2.28"; | |
|
364 | 364 | buildInputs = with self; []; |
|
365 | 365 | doCheck = false; |
|
366 | 366 | propagatedBuildInputs = with self; []; |
|
367 | 367 | src = fetchurl { |
|
368 | 368 | url = "https://pypi.python.org/packages/5c/f8/f6c54727c74579c6bbe5926f5deb9677c5810a33e11da58d1a4e2d09d041/certifi-2016.2.28.tar.gz"; |
|
369 | 369 | md5 = "5d672aa766e1f773c75cfeccd02d3650"; |
|
370 | 370 | }; |
|
371 | 371 | }; |
|
372 | 372 | click = super.buildPythonPackage { |
|
373 | 373 | name = "click-5.1"; |
|
374 | 374 | buildInputs = with self; []; |
|
375 | 375 | doCheck = false; |
|
376 | 376 | propagatedBuildInputs = with self; []; |
|
377 | 377 | src = fetchurl { |
|
378 | 378 | url = "https://pypi.python.org/packages/b7/34/a496632c4fb6c1ee76efedf77bb8d28b29363d839953d95095b12defe791/click-5.1.tar.gz"; |
|
379 | 379 | md5 = "9c5323008cccfe232a8b161fc8196d41"; |
|
380 | 380 | }; |
|
381 | 381 | }; |
|
382 | 382 | colander = super.buildPythonPackage { |
|
383 | 383 | name = "colander-1.2"; |
|
384 | 384 | buildInputs = with self; []; |
|
385 | 385 | doCheck = false; |
|
386 | 386 | propagatedBuildInputs = with self; [translationstring iso8601]; |
|
387 | 387 | src = fetchurl { |
|
388 | 388 | url = "https://pypi.python.org/packages/14/23/c9ceba07a6a1dc0eefbb215fc0dc64aabc2b22ee756bc0f0c13278fa0887/colander-1.2.tar.gz"; |
|
389 | 389 | md5 = "83db21b07936a0726e588dae1914b9ed"; |
|
390 | 390 | }; |
|
391 | 391 | }; |
|
392 | 392 | configobj = super.buildPythonPackage { |
|
393 | 393 | name = "configobj-5.0.6"; |
|
394 | 394 | buildInputs = with self; []; |
|
395 | 395 | doCheck = false; |
|
396 | 396 | propagatedBuildInputs = with self; [six]; |
|
397 | 397 | src = fetchurl { |
|
398 | 398 | url = "https://pypi.python.org/packages/64/61/079eb60459c44929e684fa7d9e2fdca403f67d64dd9dbac27296be2e0fab/configobj-5.0.6.tar.gz"; |
|
399 | 399 | md5 = "e472a3a1c2a67bb0ec9b5d54c13a47d6"; |
|
400 | 400 | }; |
|
401 | 401 | }; |
|
402 | 402 | cov-core = super.buildPythonPackage { |
|
403 | 403 | name = "cov-core-1.15.0"; |
|
404 | 404 | buildInputs = with self; []; |
|
405 | 405 | doCheck = false; |
|
406 | 406 | propagatedBuildInputs = with self; [coverage]; |
|
407 | 407 | src = fetchurl { |
|
408 | 408 | url = "https://pypi.python.org/packages/4b/87/13e75a47b4ba1be06f29f6d807ca99638bedc6b57fa491cd3de891ca2923/cov-core-1.15.0.tar.gz"; |
|
409 | 409 | md5 = "f519d4cb4c4e52856afb14af52919fe6"; |
|
410 | 410 | }; |
|
411 | 411 | }; |
|
412 | 412 | coverage = super.buildPythonPackage { |
|
413 | 413 | name = "coverage-3.7.1"; |
|
414 | 414 | buildInputs = with self; []; |
|
415 | 415 | doCheck = false; |
|
416 | 416 | propagatedBuildInputs = with self; []; |
|
417 | 417 | src = fetchurl { |
|
418 | 418 | url = "https://pypi.python.org/packages/09/4f/89b06c7fdc09687bca507dc411c342556ef9c5a3b26756137a4878ff19bf/coverage-3.7.1.tar.gz"; |
|
419 | 419 | md5 = "c47b36ceb17eaff3ecfab3bcd347d0df"; |
|
420 | 420 | }; |
|
421 | 421 | }; |
|
422 | 422 | cssselect = super.buildPythonPackage { |
|
423 | 423 | name = "cssselect-0.9.1"; |
|
424 | 424 | buildInputs = with self; []; |
|
425 | 425 | doCheck = false; |
|
426 | 426 | propagatedBuildInputs = with self; []; |
|
427 | 427 | src = fetchurl { |
|
428 | 428 | url = "https://pypi.python.org/packages/aa/e5/9ee1460d485b94a6d55732eb7ad5b6c084caf73dd6f9cb0bb7d2a78fafe8/cssselect-0.9.1.tar.gz"; |
|
429 | 429 | md5 = "c74f45966277dc7a0f768b9b0f3522ac"; |
|
430 | 430 | }; |
|
431 | 431 | }; |
|
432 | 432 | decorator = super.buildPythonPackage { |
|
433 | 433 | name = "decorator-3.4.2"; |
|
434 | 434 | buildInputs = with self; []; |
|
435 | 435 | doCheck = false; |
|
436 | 436 | propagatedBuildInputs = with self; []; |
|
437 | 437 | src = fetchurl { |
|
438 | 438 | url = "https://pypi.python.org/packages/35/3a/42566eb7a2cbac774399871af04e11d7ae3fc2579e7dae85213b8d1d1c57/decorator-3.4.2.tar.gz"; |
|
439 | 439 | md5 = "9e0536870d2b83ae27d58dbf22582f4d"; |
|
440 | 440 | }; |
|
441 | 441 | }; |
|
442 | 442 | docutils = super.buildPythonPackage { |
|
443 | 443 | name = "docutils-0.12"; |
|
444 | 444 | buildInputs = with self; []; |
|
445 | 445 | doCheck = false; |
|
446 | 446 | propagatedBuildInputs = with self; []; |
|
447 | 447 | src = fetchurl { |
|
448 | 448 | url = "https://pypi.python.org/packages/37/38/ceda70135b9144d84884ae2fc5886c6baac4edea39550f28bcd144c1234d/docutils-0.12.tar.gz"; |
|
449 | 449 | md5 = "4622263b62c5c771c03502afa3157768"; |
|
450 | 450 | }; |
|
451 | 451 | }; |
|
452 | 452 | dogpile.cache = super.buildPythonPackage { |
|
453 | 453 | name = "dogpile.cache-0.5.7"; |
|
454 | 454 | buildInputs = with self; []; |
|
455 | 455 | doCheck = false; |
|
456 | 456 | propagatedBuildInputs = with self; [dogpile.core]; |
|
457 | 457 | src = fetchurl { |
|
458 | 458 | url = "https://pypi.python.org/packages/07/74/2a83bedf758156d9c95d112691bbad870d3b77ccbcfb781b4ef836ea7d96/dogpile.cache-0.5.7.tar.gz"; |
|
459 | 459 | md5 = "3e58ce41af574aab41d78e9c4190f194"; |
|
460 | 460 | }; |
|
461 | 461 | }; |
|
462 | 462 | dogpile.core = super.buildPythonPackage { |
|
463 | 463 | name = "dogpile.core-0.4.1"; |
|
464 | 464 | buildInputs = with self; []; |
|
465 | 465 | doCheck = false; |
|
466 | 466 | propagatedBuildInputs = with self; []; |
|
467 | 467 | src = fetchurl { |
|
468 | 468 | url = "https://pypi.python.org/packages/0e/77/e72abc04c22aedf874301861e5c1e761231c288b5de369c18be8f4b5c9bb/dogpile.core-0.4.1.tar.gz"; |
|
469 | 469 | md5 = "01cb19f52bba3e95c9b560f39341f045"; |
|
470 | 470 | }; |
|
471 | 471 | }; |
|
472 | 472 | dulwich = super.buildPythonPackage { |
|
473 | 473 | name = "dulwich-0.12.0"; |
|
474 | 474 | buildInputs = with self; []; |
|
475 | 475 | doCheck = false; |
|
476 | 476 | propagatedBuildInputs = with self; []; |
|
477 | 477 | src = fetchurl { |
|
478 | 478 | url = "https://pypi.python.org/packages/6f/04/fbe561b6d45c0ec758330d5b7f5ba4b6cb4f1ca1ab49859d2fc16320da75/dulwich-0.12.0.tar.gz"; |
|
479 | 479 | md5 = "f3a8a12bd9f9dd8c233e18f3d49436fa"; |
|
480 | 480 | }; |
|
481 | 481 | }; |
|
482 | 482 | ecdsa = super.buildPythonPackage { |
|
483 | 483 | name = "ecdsa-0.11"; |
|
484 | 484 | buildInputs = with self; []; |
|
485 | 485 | doCheck = false; |
|
486 | 486 | propagatedBuildInputs = with self; []; |
|
487 | 487 | src = fetchurl { |
|
488 | 488 | url = "https://pypi.python.org/packages/6c/3f/92fe5dcdcaa7bd117be21e5520c9a54375112b66ec000d209e9e9519fad1/ecdsa-0.11.tar.gz"; |
|
489 | 489 | md5 = "8ef586fe4dbb156697d756900cb41d7c"; |
|
490 | 490 | }; |
|
491 | 491 | }; |
|
492 | 492 | elasticsearch = super.buildPythonPackage { |
|
493 | 493 | name = "elasticsearch-1.9.0"; |
|
494 | 494 | buildInputs = with self; []; |
|
495 | 495 | doCheck = false; |
|
496 | 496 | propagatedBuildInputs = with self; [urllib3]; |
|
497 | 497 | src = fetchurl { |
|
498 | 498 | url = "https://pypi.python.org/packages/13/9b/540e311b31a10c2a904acfb08030c656047e5c7ba479d35df2799e5dccfe/elasticsearch-1.9.0.tar.gz"; |
|
499 | 499 | md5 = "3550390baea1639479f79758d66ab032"; |
|
500 | 500 | }; |
|
501 | 501 | }; |
|
502 | 502 | flake8 = super.buildPythonPackage { |
|
503 | 503 | name = "flake8-2.4.1"; |
|
504 | 504 | buildInputs = with self; []; |
|
505 | 505 | doCheck = false; |
|
506 | 506 | propagatedBuildInputs = with self; [pyflakes pep8 mccabe]; |
|
507 | 507 | src = fetchurl { |
|
508 | 508 | url = "https://pypi.python.org/packages/8f/b5/9a73c66c7dba273bac8758398f060c008a25f3e84531063b42503b5d0a95/flake8-2.4.1.tar.gz"; |
|
509 | 509 | md5 = "ed45d3db81a3b7c88bd63c6e37ca1d65"; |
|
510 | 510 | }; |
|
511 | 511 | }; |
|
512 | 512 | future = super.buildPythonPackage { |
|
513 | 513 | name = "future-0.14.3"; |
|
514 | 514 | buildInputs = with self; []; |
|
515 | 515 | doCheck = false; |
|
516 | 516 | propagatedBuildInputs = with self; []; |
|
517 | 517 | src = fetchurl { |
|
518 | 518 | url = "https://pypi.python.org/packages/83/80/8ef3a11a15f8eaafafa0937b20c1b3f73527e69ab6b3fa1cf94a5a96aabb/future-0.14.3.tar.gz"; |
|
519 | 519 | md5 = "e94079b0bd1fc054929e8769fc0f6083"; |
|
520 | 520 | }; |
|
521 | 521 | }; |
|
522 | 522 | futures = super.buildPythonPackage { |
|
523 | 523 | name = "futures-3.0.2"; |
|
524 | 524 | buildInputs = with self; []; |
|
525 | 525 | doCheck = false; |
|
526 | 526 | propagatedBuildInputs = with self; []; |
|
527 | 527 | src = fetchurl { |
|
528 | 528 | url = "https://pypi.python.org/packages/f8/e7/fc0fcbeb9193ba2d4de00b065e7fd5aecd0679e93ce95a07322b2b1434f4/futures-3.0.2.tar.gz"; |
|
529 | 529 | md5 = "42aaf1e4de48d6e871d77dc1f9d96d5a"; |
|
530 | 530 | }; |
|
531 | 531 | }; |
|
532 | 532 | gnureadline = super.buildPythonPackage { |
|
533 | 533 | name = "gnureadline-6.3.3"; |
|
534 | 534 | buildInputs = with self; []; |
|
535 | 535 | doCheck = false; |
|
536 | 536 | propagatedBuildInputs = with self; []; |
|
537 | 537 | src = fetchurl { |
|
538 | 538 | url = "https://pypi.python.org/packages/3a/ee/2c3f568b0a74974791ac590ec742ef6133e2fbd287a074ba72a53fa5e97c/gnureadline-6.3.3.tar.gz"; |
|
539 | 539 | md5 = "c4af83c9a3fbeac8f2da9b5a7c60e51c"; |
|
540 | 540 | }; |
|
541 | 541 | }; |
|
542 | 542 | gprof2dot = super.buildPythonPackage { |
|
543 | 543 | name = "gprof2dot-2015.12.1"; |
|
544 | 544 | buildInputs = with self; []; |
|
545 | 545 | doCheck = false; |
|
546 | 546 | propagatedBuildInputs = with self; []; |
|
547 | 547 | src = fetchurl { |
|
548 | 548 | url = "https://pypi.python.org/packages/b9/34/7bf93c1952d40fa5c95ad963f4d8344b61ef58558632402eca18e6c14127/gprof2dot-2015.12.1.tar.gz"; |
|
549 | 549 | md5 = "e23bf4e2f94db032750c193384b4165b"; |
|
550 | 550 | }; |
|
551 | 551 | }; |
|
552 | 552 | greenlet = super.buildPythonPackage { |
|
553 | 553 | name = "greenlet-0.4.7"; |
|
554 | 554 | buildInputs = with self; []; |
|
555 | 555 | doCheck = false; |
|
556 | 556 | propagatedBuildInputs = with self; []; |
|
557 | 557 | src = fetchurl { |
|
558 | 558 | url = "https://pypi.python.org/packages/7a/9f/a1a0d9bdf3203ae1502c5a8434fe89d323599d78a106985bc327351a69d4/greenlet-0.4.7.zip"; |
|
559 | 559 | md5 = "c2333a8ff30fa75c5d5ec0e67b461086"; |
|
560 | 560 | }; |
|
561 | 561 | }; |
|
562 | 562 | gunicorn = super.buildPythonPackage { |
|
563 | 563 | name = "gunicorn-19.6.0"; |
|
564 | 564 | buildInputs = with self; []; |
|
565 | 565 | doCheck = false; |
|
566 | 566 | propagatedBuildInputs = with self; []; |
|
567 | 567 | src = fetchurl { |
|
568 | 568 | url = "https://pypi.python.org/packages/84/ce/7ea5396efad1cef682bbc4068e72a0276341d9d9d0f501da609fab9fcb80/gunicorn-19.6.0.tar.gz"; |
|
569 | 569 | md5 = "338e5e8a83ea0f0625f768dba4597530"; |
|
570 | 570 | }; |
|
571 | 571 | }; |
|
572 | 572 | infrae.cache = super.buildPythonPackage { |
|
573 | 573 | name = "infrae.cache-1.0.1"; |
|
574 | 574 | buildInputs = with self; []; |
|
575 | 575 | doCheck = false; |
|
576 | 576 | propagatedBuildInputs = with self; [Beaker repoze.lru]; |
|
577 | 577 | src = fetchurl { |
|
578 | 578 | url = "https://pypi.python.org/packages/bb/f0/e7d5e984cf6592fd2807dc7bc44a93f9d18e04e6a61f87fdfb2622422d74/infrae.cache-1.0.1.tar.gz"; |
|
579 | 579 | md5 = "b09076a766747e6ed2a755cc62088e32"; |
|
580 | 580 | }; |
|
581 | 581 | }; |
|
582 | 582 | invoke = super.buildPythonPackage { |
|
583 | 583 | name = "invoke-0.11.1"; |
|
584 | 584 | buildInputs = with self; []; |
|
585 | 585 | doCheck = false; |
|
586 | 586 | propagatedBuildInputs = with self; []; |
|
587 | 587 | src = fetchurl { |
|
588 | 588 | url = "https://pypi.python.org/packages/d3/bb/36a5558ea19882073def7b0edeef4a0e6282056fed96506dd10b1d532bd4/invoke-0.11.1.tar.gz"; |
|
589 | 589 | md5 = "3d4ecbe26779ceef1046ecf702c9c4a8"; |
|
590 | 590 | }; |
|
591 | 591 | }; |
|
592 | 592 | ipdb = super.buildPythonPackage { |
|
593 | 593 | name = "ipdb-0.8"; |
|
594 | 594 | buildInputs = with self; []; |
|
595 | 595 | doCheck = false; |
|
596 | 596 | propagatedBuildInputs = with self; [ipython]; |
|
597 | 597 | src = fetchurl { |
|
598 | 598 | url = "https://pypi.python.org/packages/f0/25/d7dd430ced6cd8dc242a933c8682b5dbf32eb4011d82f87e34209e5ec845/ipdb-0.8.zip"; |
|
599 | 599 | md5 = "96dca0712efa01aa5eaf6b22071dd3ed"; |
|
600 | 600 | }; |
|
601 | 601 | }; |
|
602 | 602 | ipython = super.buildPythonPackage { |
|
603 | 603 | name = "ipython-3.1.0"; |
|
604 | 604 | buildInputs = with self; []; |
|
605 | 605 | doCheck = false; |
|
606 | 606 | propagatedBuildInputs = with self; [gnureadline]; |
|
607 | 607 | src = fetchurl { |
|
608 | 608 | url = "https://pypi.python.org/packages/06/91/120c0835254c120af89f066afaabf81289bc2726c1fc3ca0555df6882f58/ipython-3.1.0.tar.gz"; |
|
609 | 609 | md5 = "a749d90c16068687b0ec45a27e72ef8f"; |
|
610 | 610 | }; |
|
611 | 611 | }; |
|
612 | 612 | iso8601 = super.buildPythonPackage { |
|
613 | 613 | name = "iso8601-0.1.11"; |
|
614 | 614 | buildInputs = with self; []; |
|
615 | 615 | doCheck = false; |
|
616 | 616 | propagatedBuildInputs = with self; []; |
|
617 | 617 | src = fetchurl { |
|
618 | 618 | url = "https://pypi.python.org/packages/c0/75/c9209ee4d1b5975eb8c2cba4428bde6b61bd55664a98290dd015cdb18e98/iso8601-0.1.11.tar.gz"; |
|
619 | 619 | md5 = "b06d11cd14a64096f907086044f0fe38"; |
|
620 | 620 | }; |
|
621 | 621 | }; |
|
622 | 622 | itsdangerous = super.buildPythonPackage { |
|
623 | 623 | name = "itsdangerous-0.24"; |
|
624 | 624 | buildInputs = with self; []; |
|
625 | 625 | doCheck = false; |
|
626 | 626 | propagatedBuildInputs = with self; []; |
|
627 | 627 | src = fetchurl { |
|
628 | 628 | url = "https://pypi.python.org/packages/dc/b4/a60bcdba945c00f6d608d8975131ab3f25b22f2bcfe1dab221165194b2d4/itsdangerous-0.24.tar.gz"; |
|
629 | 629 | md5 = "a3d55aa79369aef5345c036a8a26307f"; |
|
630 | 630 | }; |
|
631 | 631 | }; |
|
632 | 632 | kombu = super.buildPythonPackage { |
|
633 | 633 | name = "kombu-1.5.1"; |
|
634 | 634 | buildInputs = with self; []; |
|
635 | 635 | doCheck = false; |
|
636 | 636 | propagatedBuildInputs = with self; [anyjson amqplib]; |
|
637 | 637 | src = fetchurl { |
|
638 | 638 | url = "https://pypi.python.org/packages/19/53/74bf2a624644b45f0850a638752514fc10a8e1cbd738f10804951a6df3f5/kombu-1.5.1.tar.gz"; |
|
639 | 639 | md5 = "50662f3c7e9395b3d0721fb75d100b63"; |
|
640 | 640 | }; |
|
641 | 641 | }; |
|
642 | 642 | lxml = super.buildPythonPackage { |
|
643 | 643 | name = "lxml-3.4.4"; |
|
644 | 644 | buildInputs = with self; []; |
|
645 | 645 | doCheck = false; |
|
646 | 646 | propagatedBuildInputs = with self; []; |
|
647 | 647 | src = fetchurl { |
|
648 | 648 | url = "https://pypi.python.org/packages/63/c7/4f2a2a4ad6c6fa99b14be6b3c1cece9142e2d915aa7c43c908677afc8fa4/lxml-3.4.4.tar.gz"; |
|
649 | 649 | md5 = "a9a65972afc173ec7a39c585f4eea69c"; |
|
650 | 650 | }; |
|
651 | 651 | }; |
|
652 | 652 | mccabe = super.buildPythonPackage { |
|
653 | 653 | name = "mccabe-0.3"; |
|
654 | 654 | buildInputs = with self; []; |
|
655 | 655 | doCheck = false; |
|
656 | 656 | propagatedBuildInputs = with self; []; |
|
657 | 657 | src = fetchurl { |
|
658 | 658 | url = "https://pypi.python.org/packages/c9/2e/75231479e11a906b64ac43bad9d0bb534d00080b18bdca8db9da46e1faf7/mccabe-0.3.tar.gz"; |
|
659 | 659 | md5 = "81640948ff226f8c12b3277059489157"; |
|
660 | 660 | }; |
|
661 | 661 | }; |
|
662 | 662 | meld3 = super.buildPythonPackage { |
|
663 | 663 | name = "meld3-1.0.2"; |
|
664 | 664 | buildInputs = with self; []; |
|
665 | 665 | doCheck = false; |
|
666 | 666 | propagatedBuildInputs = with self; []; |
|
667 | 667 | src = fetchurl { |
|
668 | 668 | url = "https://pypi.python.org/packages/45/a0/317c6422b26c12fe0161e936fc35f36552069ba8e6f7ecbd99bbffe32a5f/meld3-1.0.2.tar.gz"; |
|
669 | 669 | md5 = "3ccc78cd79cffd63a751ad7684c02c91"; |
|
670 | 670 | }; |
|
671 | 671 | }; |
|
672 | 672 | mock = super.buildPythonPackage { |
|
673 | 673 | name = "mock-1.0.1"; |
|
674 | 674 | buildInputs = with self; []; |
|
675 | 675 | doCheck = false; |
|
676 | 676 | propagatedBuildInputs = with self; []; |
|
677 | 677 | src = fetchurl { |
|
678 | 678 | url = "https://pypi.python.org/packages/15/45/30273ee91feb60dabb8fbb2da7868520525f02cf910279b3047182feed80/mock-1.0.1.zip"; |
|
679 | 679 | md5 = "869f08d003c289a97c1a6610faf5e913"; |
|
680 | 680 | }; |
|
681 | 681 | }; |
|
682 | 682 | msgpack-python = super.buildPythonPackage { |
|
683 | 683 | name = "msgpack-python-0.4.6"; |
|
684 | 684 | buildInputs = with self; []; |
|
685 | 685 | doCheck = false; |
|
686 | 686 | propagatedBuildInputs = with self; []; |
|
687 | 687 | src = fetchurl { |
|
688 | 688 | url = "https://pypi.python.org/packages/15/ce/ff2840885789ef8035f66cd506ea05bdb228340307d5e71a7b1e3f82224c/msgpack-python-0.4.6.tar.gz"; |
|
689 | 689 | md5 = "8b317669314cf1bc881716cccdaccb30"; |
|
690 | 690 | }; |
|
691 | 691 | }; |
|
692 | 692 | nose = super.buildPythonPackage { |
|
693 | 693 | name = "nose-1.3.6"; |
|
694 | 694 | buildInputs = with self; []; |
|
695 | 695 | doCheck = false; |
|
696 | 696 | propagatedBuildInputs = with self; []; |
|
697 | 697 | src = fetchurl { |
|
698 | 698 | url = "https://pypi.python.org/packages/70/c7/469e68148d17a0d3db5ed49150242fd70a74a8147b8f3f8b87776e028d99/nose-1.3.6.tar.gz"; |
|
699 | 699 | md5 = "0ca546d81ca8309080fc80cb389e7a16"; |
|
700 | 700 | }; |
|
701 | 701 | }; |
|
702 | 702 | objgraph = super.buildPythonPackage { |
|
703 | 703 | name = "objgraph-2.0.0"; |
|
704 | 704 | buildInputs = with self; []; |
|
705 | 705 | doCheck = false; |
|
706 | 706 | propagatedBuildInputs = with self; []; |
|
707 | 707 | src = fetchurl { |
|
708 | 708 | url = "https://pypi.python.org/packages/d7/33/ace750b59247496ed769b170586c5def7202683f3d98e737b75b767ff29e/objgraph-2.0.0.tar.gz"; |
|
709 | 709 | md5 = "25b0d5e5adc74aa63ead15699614159c"; |
|
710 | 710 | }; |
|
711 | 711 | }; |
|
712 | 712 | packaging = super.buildPythonPackage { |
|
713 | 713 | name = "packaging-15.2"; |
|
714 | 714 | buildInputs = with self; []; |
|
715 | 715 | doCheck = false; |
|
716 | 716 | propagatedBuildInputs = with self; []; |
|
717 | 717 | src = fetchurl { |
|
718 | 718 | url = "https://pypi.python.org/packages/24/c4/185da1304f07047dc9e0c46c31db75c0351bd73458ac3efad7da3dbcfbe1/packaging-15.2.tar.gz"; |
|
719 | 719 | md5 = "c16093476f6ced42128bf610e5db3784"; |
|
720 | 720 | }; |
|
721 | 721 | }; |
|
722 | 722 | paramiko = super.buildPythonPackage { |
|
723 | 723 | name = "paramiko-1.15.1"; |
|
724 | 724 | buildInputs = with self; []; |
|
725 | 725 | doCheck = false; |
|
726 | 726 | propagatedBuildInputs = with self; [pycrypto ecdsa]; |
|
727 | 727 | src = fetchurl { |
|
728 | 728 | url = "https://pypi.python.org/packages/04/2b/a22d2a560c1951abbbf95a0628e245945565f70dc082d9e784666887222c/paramiko-1.15.1.tar.gz"; |
|
729 | 729 | md5 = "48c274c3f9b1282932567b21f6acf3b5"; |
|
730 | 730 | }; |
|
731 | 731 | }; |
|
732 | 732 | pep8 = super.buildPythonPackage { |
|
733 | 733 | name = "pep8-1.5.7"; |
|
734 | 734 | buildInputs = with self; []; |
|
735 | 735 | doCheck = false; |
|
736 | 736 | propagatedBuildInputs = with self; []; |
|
737 | 737 | src = fetchurl { |
|
738 | 738 | url = "https://pypi.python.org/packages/8b/de/259f5e735897ada1683489dd514b2a1c91aaa74e5e6b68f80acf128a6368/pep8-1.5.7.tar.gz"; |
|
739 | 739 | md5 = "f6adbdd69365ecca20513c709f9b7c93"; |
|
740 | 740 | }; |
|
741 | 741 | }; |
|
742 | 742 | psutil = super.buildPythonPackage { |
|
743 | 743 | name = "psutil-2.2.1"; |
|
744 | 744 | buildInputs = with self; []; |
|
745 | 745 | doCheck = false; |
|
746 | 746 | propagatedBuildInputs = with self; []; |
|
747 | 747 | src = fetchurl { |
|
748 | 748 | url = "https://pypi.python.org/packages/df/47/ee54ef14dd40f8ce831a7581001a5096494dc99fe71586260ca6b531fe86/psutil-2.2.1.tar.gz"; |
|
749 | 749 | md5 = "1a2b58cd9e3a53528bb6148f0c4d5244"; |
|
750 | 750 | }; |
|
751 | 751 | }; |
|
752 | 752 | psycopg2 = super.buildPythonPackage { |
|
753 | 753 | name = "psycopg2-2.6"; |
|
754 | 754 | buildInputs = with self; []; |
|
755 | 755 | doCheck = false; |
|
756 | 756 | propagatedBuildInputs = with self; []; |
|
757 | 757 | src = fetchurl { |
|
758 | 758 | url = "https://pypi.python.org/packages/dd/c7/9016ff8ff69da269b1848276eebfb264af5badf6b38caad805426771f04d/psycopg2-2.6.tar.gz"; |
|
759 | 759 | md5 = "fbbb039a8765d561a1c04969bbae7c74"; |
|
760 | 760 | }; |
|
761 | 761 | }; |
|
762 | 762 | py = super.buildPythonPackage { |
|
763 | 763 | name = "py-1.4.29"; |
|
764 | 764 | buildInputs = with self; []; |
|
765 | 765 | doCheck = false; |
|
766 | 766 | propagatedBuildInputs = with self; []; |
|
767 | 767 | src = fetchurl { |
|
768 | 768 | url = "https://pypi.python.org/packages/2a/bc/a1a4a332ac10069b8e5e25136a35e08a03f01fd6ab03d819889d79a1fd65/py-1.4.29.tar.gz"; |
|
769 | 769 | md5 = "c28e0accba523a29b35a48bb703fb96c"; |
|
770 | 770 | }; |
|
771 | 771 | }; |
|
772 | 772 | py-bcrypt = super.buildPythonPackage { |
|
773 | 773 | name = "py-bcrypt-0.4"; |
|
774 | 774 | buildInputs = with self; []; |
|
775 | 775 | doCheck = false; |
|
776 | 776 | propagatedBuildInputs = with self; []; |
|
777 | 777 | src = fetchurl { |
|
778 | 778 | url = "https://pypi.python.org/packages/68/b1/1c3068c5c4d2e35c48b38dcc865301ebfdf45f54507086ac65ced1fd3b3d/py-bcrypt-0.4.tar.gz"; |
|
779 | 779 | md5 = "dd8b367d6b716a2ea2e72392525f4e36"; |
|
780 | 780 | }; |
|
781 | 781 | }; |
|
782 | 782 | pycrypto = super.buildPythonPackage { |
|
783 | 783 | name = "pycrypto-2.6.1"; |
|
784 | 784 | buildInputs = with self; []; |
|
785 | 785 | doCheck = false; |
|
786 | 786 | propagatedBuildInputs = with self; []; |
|
787 | 787 | src = fetchurl { |
|
788 | 788 | url = "https://pypi.python.org/packages/60/db/645aa9af249f059cc3a368b118de33889219e0362141e75d4eaf6f80f163/pycrypto-2.6.1.tar.gz"; |
|
789 | 789 | md5 = "55a61a054aa66812daf5161a0d5d7eda"; |
|
790 | 790 | }; |
|
791 | 791 | }; |
|
792 | 792 | pycurl = super.buildPythonPackage { |
|
793 | 793 | name = "pycurl-7.19.5"; |
|
794 | 794 | buildInputs = with self; []; |
|
795 | 795 | doCheck = false; |
|
796 | 796 | propagatedBuildInputs = with self; []; |
|
797 | 797 | src = fetchurl { |
|
798 | 798 | url = "https://pypi.python.org/packages/6c/48/13bad289ef6f4869b1d8fc11ae54de8cfb3cc4a2eb9f7419c506f763be46/pycurl-7.19.5.tar.gz"; |
|
799 | 799 | md5 = "47b4eac84118e2606658122104e62072"; |
|
800 | 800 | }; |
|
801 | 801 | }; |
|
802 | 802 | pyelasticsearch = super.buildPythonPackage { |
|
803 | 803 | name = "pyelasticsearch-1.4"; |
|
804 | 804 | buildInputs = with self; []; |
|
805 | 805 | doCheck = false; |
|
806 | 806 | propagatedBuildInputs = with self; [certifi elasticsearch urllib3 simplejson six]; |
|
807 | 807 | src = fetchurl { |
|
808 | 808 | url = "https://pypi.python.org/packages/2f/3a/7643cfcfc4cbdbb20ada800bbd54ac9705d0c047d7b8f8d5eeeb3047b4eb/pyelasticsearch-1.4.tar.gz"; |
|
809 | 809 | md5 = "ed61ebb7b253364e55b4923d11e17049"; |
|
810 | 810 | }; |
|
811 | 811 | }; |
|
812 | 812 | pyflakes = super.buildPythonPackage { |
|
813 | 813 | name = "pyflakes-0.8.1"; |
|
814 | 814 | buildInputs = with self; []; |
|
815 | 815 | doCheck = false; |
|
816 | 816 | propagatedBuildInputs = with self; []; |
|
817 | 817 | src = fetchurl { |
|
818 | 818 | url = "https://pypi.python.org/packages/75/22/a90ec0252f4f87f3ffb6336504de71fe16a49d69c4538dae2f12b9360a38/pyflakes-0.8.1.tar.gz"; |
|
819 | 819 | md5 = "905fe91ad14b912807e8fdc2ac2e2c23"; |
|
820 | 820 | }; |
|
821 | 821 | }; |
|
822 | 822 | pyparsing = super.buildPythonPackage { |
|
823 | 823 | name = "pyparsing-1.5.7"; |
|
824 | 824 | buildInputs = with self; []; |
|
825 | 825 | doCheck = false; |
|
826 | 826 | propagatedBuildInputs = with self; []; |
|
827 | 827 | src = fetchurl { |
|
828 | 828 | url = "https://pypi.python.org/packages/2e/26/e8fb5b4256a5f5036be7ce115ef8db8d06bc537becfbdc46c6af008314ee/pyparsing-1.5.7.zip"; |
|
829 | 829 | md5 = "b86854857a368d6ccb4d5b6e76d0637f"; |
|
830 | 830 | }; |
|
831 | 831 | }; |
|
832 | 832 | pyramid = super.buildPythonPackage { |
|
833 | 833 | name = "pyramid-1.6.1"; |
|
834 | 834 | buildInputs = with self; []; |
|
835 | 835 | doCheck = false; |
|
836 | 836 | propagatedBuildInputs = with self; [setuptools WebOb repoze.lru zope.interface zope.deprecation venusian translationstring PasteDeploy]; |
|
837 | 837 | src = fetchurl { |
|
838 | 838 | url = "https://pypi.python.org/packages/30/b3/fcc4a2a4800cbf21989e00454b5828cf1f7fe35c63e0810b350e56d4c475/pyramid-1.6.1.tar.gz"; |
|
839 | 839 | md5 = "b18688ff3cc33efdbb098a35b45dd122"; |
|
840 | 840 | }; |
|
841 | 841 | }; |
|
842 | 842 | pyramid-beaker = super.buildPythonPackage { |
|
843 | 843 | name = "pyramid-beaker-0.8"; |
|
844 | 844 | buildInputs = with self; []; |
|
845 | 845 | doCheck = false; |
|
846 | 846 | propagatedBuildInputs = with self; [pyramid Beaker]; |
|
847 | 847 | src = fetchurl { |
|
848 | 848 | url = "https://pypi.python.org/packages/d9/6e/b85426e00fd3d57f4545f74e1c3828552d8700f13ededeef9233f7bca8be/pyramid_beaker-0.8.tar.gz"; |
|
849 | 849 | md5 = "22f14be31b06549f80890e2c63a93834"; |
|
850 | 850 | }; |
|
851 | 851 | }; |
|
852 | 852 | pyramid-debugtoolbar = super.buildPythonPackage { |
|
853 | 853 | name = "pyramid-debugtoolbar-2.4.2"; |
|
854 | 854 | buildInputs = with self; []; |
|
855 | 855 | doCheck = false; |
|
856 | 856 | propagatedBuildInputs = with self; [pyramid pyramid-mako repoze.lru Pygments]; |
|
857 | 857 | src = fetchurl { |
|
858 | 858 | url = "https://pypi.python.org/packages/89/00/ed5426ee41ed747ba3ffd30e8230841a6878286ea67d480b1444d24f06a2/pyramid_debugtoolbar-2.4.2.tar.gz"; |
|
859 | 859 | md5 = "073ea67086cc4bd5decc3a000853642d"; |
|
860 | 860 | }; |
|
861 | 861 | }; |
|
862 | 862 | pyramid-jinja2 = super.buildPythonPackage { |
|
863 | 863 | name = "pyramid-jinja2-2.5"; |
|
864 | 864 | buildInputs = with self; []; |
|
865 | 865 | doCheck = false; |
|
866 | 866 | propagatedBuildInputs = with self; [pyramid zope.deprecation Jinja2 MarkupSafe]; |
|
867 | 867 | src = fetchurl { |
|
868 | 868 | url = "https://pypi.python.org/packages/a1/80/595e26ffab7deba7208676b6936b7e5a721875710f982e59899013cae1ed/pyramid_jinja2-2.5.tar.gz"; |
|
869 | 869 | md5 = "07cb6547204ac5e6f0b22a954ccee928"; |
|
870 | 870 | }; |
|
871 | 871 | }; |
|
872 | 872 | pyramid-mako = super.buildPythonPackage { |
|
873 | 873 | name = "pyramid-mako-1.0.2"; |
|
874 | 874 | buildInputs = with self; []; |
|
875 | 875 | doCheck = false; |
|
876 | 876 | propagatedBuildInputs = with self; [pyramid Mako]; |
|
877 | 877 | src = fetchurl { |
|
878 | 878 | url = "https://pypi.python.org/packages/f1/92/7e69bcf09676d286a71cb3bbb887b16595b96f9ba7adbdc239ffdd4b1eb9/pyramid_mako-1.0.2.tar.gz"; |
|
879 | 879 | md5 = "ee25343a97eb76bd90abdc2a774eb48a"; |
|
880 | 880 | }; |
|
881 | 881 | }; |
|
882 | 882 | pysqlite = super.buildPythonPackage { |
|
883 | 883 | name = "pysqlite-2.6.3"; |
|
884 | 884 | buildInputs = with self; []; |
|
885 | 885 | doCheck = false; |
|
886 | 886 | propagatedBuildInputs = with self; []; |
|
887 | 887 | src = fetchurl { |
|
888 | 888 | url = "https://pypi.python.org/packages/5c/a6/1c429cd4c8069cf4bfbd0eb4d592b3f4042155a8202df83d7e9b93aa3dc2/pysqlite-2.6.3.tar.gz"; |
|
889 | 889 | md5 = "7ff1cedee74646b50117acff87aa1cfa"; |
|
890 | 890 | }; |
|
891 | 891 | }; |
|
892 | 892 | pytest = super.buildPythonPackage { |
|
893 | 893 | name = "pytest-2.8.5"; |
|
894 | 894 | buildInputs = with self; []; |
|
895 | 895 | doCheck = false; |
|
896 | 896 | propagatedBuildInputs = with self; [py]; |
|
897 | 897 | src = fetchurl { |
|
898 | 898 | url = "https://pypi.python.org/packages/b1/3d/d7ea9b0c51e0cacded856e49859f0a13452747491e842c236bbab3714afe/pytest-2.8.5.zip"; |
|
899 | 899 | md5 = "8493b06f700862f1294298d6c1b715a9"; |
|
900 | 900 | }; |
|
901 | 901 | }; |
|
902 | 902 | pytest-catchlog = super.buildPythonPackage { |
|
903 | 903 | name = "pytest-catchlog-1.2.2"; |
|
904 | 904 | buildInputs = with self; []; |
|
905 | 905 | doCheck = false; |
|
906 | 906 | propagatedBuildInputs = with self; [py pytest]; |
|
907 | 907 | src = fetchurl { |
|
908 | 908 | url = "https://pypi.python.org/packages/f2/2b/2faccdb1a978fab9dd0bf31cca9f6847fbe9184a0bdcc3011ac41dd44191/pytest-catchlog-1.2.2.zip"; |
|
909 | 909 | md5 = "09d890c54c7456c818102b7ff8c182c8"; |
|
910 | 910 | }; |
|
911 | 911 | }; |
|
912 | 912 | pytest-cov = super.buildPythonPackage { |
|
913 | 913 | name = "pytest-cov-1.8.1"; |
|
914 | 914 | buildInputs = with self; []; |
|
915 | 915 | doCheck = false; |
|
916 | 916 | propagatedBuildInputs = with self; [py pytest coverage cov-core]; |
|
917 | 917 | src = fetchurl { |
|
918 | 918 | url = "https://pypi.python.org/packages/11/4b/b04646e97f1721878eb21e9f779102d84dd044d324382263b1770a3e4838/pytest-cov-1.8.1.tar.gz"; |
|
919 | 919 | md5 = "76c778afa2494088270348be42d759fc"; |
|
920 | 920 | }; |
|
921 | 921 | }; |
|
922 | 922 | pytest-profiling = super.buildPythonPackage { |
|
923 | 923 | name = "pytest-profiling-1.0.1"; |
|
924 | 924 | buildInputs = with self; []; |
|
925 | 925 | doCheck = false; |
|
926 | 926 | propagatedBuildInputs = with self; [six pytest gprof2dot]; |
|
927 | 927 | src = fetchurl { |
|
928 | 928 | url = "https://pypi.python.org/packages/d8/67/8ffab73406e22870e07fa4dc8dce1d7689b26dba8efd00161c9b6fc01ec0/pytest-profiling-1.0.1.tar.gz"; |
|
929 | 929 | md5 = "354404eb5b3fd4dc5eb7fffbb3d9b68b"; |
|
930 | 930 | }; |
|
931 | 931 | }; |
|
932 | 932 | pytest-runner = super.buildPythonPackage { |
|
933 | 933 | name = "pytest-runner-2.7.1"; |
|
934 | 934 | buildInputs = with self; []; |
|
935 | 935 | doCheck = false; |
|
936 | 936 | propagatedBuildInputs = with self; []; |
|
937 | 937 | src = fetchurl { |
|
938 | 938 | url = "https://pypi.python.org/packages/99/6b/c4ff4418d3424d4475b7af60724fd4a5cdd91ed8e489dc9443281f0052bc/pytest-runner-2.7.1.tar.gz"; |
|
939 | 939 | md5 = "e56f0bc8d79a6bd91772b44ef4215c7e"; |
|
940 | 940 | }; |
|
941 | 941 | }; |
|
942 | 942 | pytest-timeout = super.buildPythonPackage { |
|
943 | 943 | name = "pytest-timeout-0.4"; |
|
944 | 944 | buildInputs = with self; []; |
|
945 | 945 | doCheck = false; |
|
946 | 946 | propagatedBuildInputs = with self; [pytest]; |
|
947 | 947 | src = fetchurl { |
|
948 | 948 | url = "https://pypi.python.org/packages/24/48/5f6bd4b8026a26e1dd427243d560a29a0f1b24a5c7cffca4bf049a7bb65b/pytest-timeout-0.4.tar.gz"; |
|
949 | 949 | md5 = "03b28aff69cbbfb959ed35ade5fde262"; |
|
950 | 950 | }; |
|
951 | 951 | }; |
|
952 | 952 | python-dateutil = super.buildPythonPackage { |
|
953 | 953 | name = "python-dateutil-1.5"; |
|
954 | 954 | buildInputs = with self; []; |
|
955 | 955 | doCheck = false; |
|
956 | 956 | propagatedBuildInputs = with self; []; |
|
957 | 957 | src = fetchurl { |
|
958 | 958 | url = "https://pypi.python.org/packages/b4/7c/df59c89a753eb33c7c44e1dd42de0e9bc2ccdd5a4d576e0bfad97cc280cb/python-dateutil-1.5.tar.gz"; |
|
959 | 959 | md5 = "0dcb1de5e5cad69490a3b6ab63f0cfa5"; |
|
960 | 960 | }; |
|
961 | 961 | }; |
|
962 | 962 | python-editor = super.buildPythonPackage { |
|
963 | 963 | name = "python-editor-1.0"; |
|
964 | 964 | buildInputs = with self; []; |
|
965 | 965 | doCheck = false; |
|
966 | 966 | propagatedBuildInputs = with self; []; |
|
967 | 967 | src = fetchurl { |
|
968 | 968 | url = "https://pypi.python.org/packages/f5/d9/01eb441489c8bd2adb33ee4f3aea299a3db531a584cb39c57a0ecf516d9c/python-editor-1.0.tar.gz"; |
|
969 | 969 | md5 = "a5ead611360b17b52507297d8590b4e8"; |
|
970 | 970 | }; |
|
971 | 971 | }; |
|
972 | 972 | python-ldap = super.buildPythonPackage { |
|
973 | 973 | name = "python-ldap-2.4.19"; |
|
974 | 974 | buildInputs = with self; []; |
|
975 | 975 | doCheck = false; |
|
976 | 976 | propagatedBuildInputs = with self; [setuptools]; |
|
977 | 977 | src = fetchurl { |
|
978 | 978 | url = "https://pypi.python.org/packages/42/81/1b64838c82e64f14d4e246ff00b52e650a35c012551b891ada2b85d40737/python-ldap-2.4.19.tar.gz"; |
|
979 | 979 | md5 = "b941bf31d09739492aa19ef679e94ae3"; |
|
980 | 980 | }; |
|
981 | 981 | }; |
|
982 | 982 | python-memcached = super.buildPythonPackage { |
|
983 | 983 | name = "python-memcached-1.57"; |
|
984 | 984 | buildInputs = with self; []; |
|
985 | 985 | doCheck = false; |
|
986 | 986 | propagatedBuildInputs = with self; [six]; |
|
987 | 987 | src = fetchurl { |
|
988 | 988 | url = "https://pypi.python.org/packages/52/9d/eebc0dcbc5c7c66840ad207dfc1baa376dadb74912484bff73819cce01e6/python-memcached-1.57.tar.gz"; |
|
989 | 989 | md5 = "de21f64b42b2d961f3d4ad7beb5468a1"; |
|
990 | 990 | }; |
|
991 | 991 | }; |
|
992 | 992 | python-pam = super.buildPythonPackage { |
|
993 | 993 | name = "python-pam-1.8.2"; |
|
994 | 994 | buildInputs = with self; []; |
|
995 | 995 | doCheck = false; |
|
996 | 996 | propagatedBuildInputs = with self; []; |
|
997 | 997 | src = fetchurl { |
|
998 | 998 | url = "https://pypi.python.org/packages/de/8c/f8f5d38b4f26893af267ea0b39023d4951705ab0413a39e0cf7cf4900505/python-pam-1.8.2.tar.gz"; |
|
999 | 999 | md5 = "db71b6b999246fb05d78ecfbe166629d"; |
|
1000 | 1000 | }; |
|
1001 | 1001 | }; |
|
1002 | 1002 | pytz = super.buildPythonPackage { |
|
1003 | 1003 | name = "pytz-2015.4"; |
|
1004 | 1004 | buildInputs = with self; []; |
|
1005 | 1005 | doCheck = false; |
|
1006 | 1006 | propagatedBuildInputs = with self; []; |
|
1007 | 1007 | src = fetchurl { |
|
1008 | 1008 | url = "https://pypi.python.org/packages/7e/1a/f43b5c92df7b156822030fed151327ea096bcf417e45acc23bd1df43472f/pytz-2015.4.zip"; |
|
1009 | 1009 | md5 = "233f2a2b370d03f9b5911700cc9ebf3c"; |
|
1010 | 1010 | }; |
|
1011 | 1011 | }; |
|
1012 | 1012 | pyzmq = super.buildPythonPackage { |
|
1013 | 1013 | name = "pyzmq-14.6.0"; |
|
1014 | 1014 | buildInputs = with self; []; |
|
1015 | 1015 | doCheck = false; |
|
1016 | 1016 | propagatedBuildInputs = with self; []; |
|
1017 | 1017 | src = fetchurl { |
|
1018 | 1018 | url = "https://pypi.python.org/packages/8a/3b/5463d5a9d712cd8bbdac335daece0d69f6a6792da4e3dd89956c0db4e4e6/pyzmq-14.6.0.tar.gz"; |
|
1019 | 1019 | md5 = "395b5de95a931afa5b14c9349a5b8024"; |
|
1020 | 1020 | }; |
|
1021 | 1021 | }; |
|
1022 | 1022 | recaptcha-client = super.buildPythonPackage { |
|
1023 | 1023 | name = "recaptcha-client-1.0.6"; |
|
1024 | 1024 | buildInputs = with self; []; |
|
1025 | 1025 | doCheck = false; |
|
1026 | 1026 | propagatedBuildInputs = with self; []; |
|
1027 | 1027 | src = fetchurl { |
|
1028 | 1028 | url = "https://pypi.python.org/packages/0a/ea/5f2fbbfd894bdac1c68ef8d92019066cfcf9fbff5fe3d728d2b5c25c8db4/recaptcha-client-1.0.6.tar.gz"; |
|
1029 | 1029 | md5 = "74228180f7e1fb76c4d7089160b0d919"; |
|
1030 | 1030 | }; |
|
1031 | 1031 | }; |
|
1032 | 1032 | repoze.lru = super.buildPythonPackage { |
|
1033 | 1033 | name = "repoze.lru-0.6"; |
|
1034 | 1034 | buildInputs = with self; []; |
|
1035 | 1035 | doCheck = false; |
|
1036 | 1036 | propagatedBuildInputs = with self; []; |
|
1037 | 1037 | src = fetchurl { |
|
1038 | 1038 | url = "https://pypi.python.org/packages/6e/1e/aa15cc90217e086dc8769872c8778b409812ff036bf021b15795638939e4/repoze.lru-0.6.tar.gz"; |
|
1039 | 1039 | md5 = "2c3b64b17a8e18b405f55d46173e14dd"; |
|
1040 | 1040 | }; |
|
1041 | 1041 | }; |
|
1042 | 1042 | requests = super.buildPythonPackage { |
|
1043 | 1043 | name = "requests-2.9.1"; |
|
1044 | 1044 | buildInputs = with self; []; |
|
1045 | 1045 | doCheck = false; |
|
1046 | 1046 | propagatedBuildInputs = with self; []; |
|
1047 | 1047 | src = fetchurl { |
|
1048 | 1048 | url = "https://pypi.python.org/packages/f9/6d/07c44fb1ebe04d069459a189e7dab9e4abfe9432adcd4477367c25332748/requests-2.9.1.tar.gz"; |
|
1049 | 1049 | md5 = "0b7f480d19012ec52bab78292efd976d"; |
|
1050 | 1050 | }; |
|
1051 | 1051 | }; |
|
1052 | 1052 | rhodecode-enterprise-ce = super.buildPythonPackage { |
|
1053 |
name = "rhodecode-enterprise-ce-4.0. |
|
|
1054 |
buildInputs = with self; [WebTest configobj cssselect flake8 lxml mock pytest pytest-runner |
|
|
1053 | name = "rhodecode-enterprise-ce-4.0.1"; | |
|
1054 | buildInputs = with self; [WebTest configobj cssselect flake8 lxml mock pytest pytest-cov pytest-runner]; | |
|
1055 | 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 | 1057 | src = ./.; |
|
1058 | 1058 | }; |
|
1059 | 1059 | rhodecode-tools = super.buildPythonPackage { |
|
1060 | 1060 | name = "rhodecode-tools-0.7.1"; |
|
1061 | 1061 | buildInputs = with self; []; |
|
1062 | 1062 | doCheck = false; |
|
1063 | 1063 | propagatedBuildInputs = with self; [click future six Mako MarkupSafe requests Whoosh pyelasticsearch]; |
|
1064 | 1064 | src = fetchurl { |
|
1065 | 1065 | url = "https://code.rhodecode.com/rhodecode-tools-ce/archive/v0.7.1.zip"; |
|
1066 | 1066 | md5 = "91daea803aaa264ce7a8213bc2220d4c"; |
|
1067 | 1067 | }; |
|
1068 | 1068 | }; |
|
1069 | 1069 | serpent = super.buildPythonPackage { |
|
1070 | 1070 | name = "serpent-1.12"; |
|
1071 | 1071 | buildInputs = with self; []; |
|
1072 | 1072 | doCheck = false; |
|
1073 | 1073 | propagatedBuildInputs = with self; []; |
|
1074 | 1074 | src = fetchurl { |
|
1075 | 1075 | url = "https://pypi.python.org/packages/3b/19/1e0e83b47c09edaef8398655088036e7e67386b5c48770218ebb339fbbd5/serpent-1.12.tar.gz"; |
|
1076 | 1076 | md5 = "05869ac7b062828b34f8f927f0457b65"; |
|
1077 | 1077 | }; |
|
1078 | 1078 | }; |
|
1079 | 1079 | setproctitle = super.buildPythonPackage { |
|
1080 | 1080 | name = "setproctitle-1.1.8"; |
|
1081 | 1081 | buildInputs = with self; []; |
|
1082 | 1082 | doCheck = false; |
|
1083 | 1083 | propagatedBuildInputs = with self; []; |
|
1084 | 1084 | src = fetchurl { |
|
1085 | 1085 | url = "https://pypi.python.org/packages/33/c3/ad367a4f4f1ca90468863ae727ac62f6edb558fc09a003d344a02cfc6ea6/setproctitle-1.1.8.tar.gz"; |
|
1086 | 1086 | md5 = "728f4c8c6031bbe56083a48594027edd"; |
|
1087 | 1087 | }; |
|
1088 | 1088 | }; |
|
1089 | 1089 | setuptools = super.buildPythonPackage { |
|
1090 | 1090 | name = "setuptools-20.8.1"; |
|
1091 | 1091 | buildInputs = with self; []; |
|
1092 | 1092 | doCheck = false; |
|
1093 | 1093 | propagatedBuildInputs = with self; []; |
|
1094 | 1094 | src = fetchurl { |
|
1095 | 1095 | url = "https://pypi.python.org/packages/c4/19/c1bdc88b53da654df43770f941079dbab4e4788c2dcb5658fb86259894c7/setuptools-20.8.1.zip"; |
|
1096 | 1096 | md5 = "fe58a5cac0df20bb83942b252a4b0543"; |
|
1097 | 1097 | }; |
|
1098 | 1098 | }; |
|
1099 | 1099 | setuptools-scm = super.buildPythonPackage { |
|
1100 | 1100 | name = "setuptools-scm-1.11.0"; |
|
1101 | 1101 | buildInputs = with self; []; |
|
1102 | 1102 | doCheck = false; |
|
1103 | 1103 | propagatedBuildInputs = with self; []; |
|
1104 | 1104 | src = fetchurl { |
|
1105 | 1105 | url = "https://pypi.python.org/packages/cd/5f/e3a038292358058d83d764a47d09114aa5a8003ed4529518f9e580f1a94f/setuptools_scm-1.11.0.tar.gz"; |
|
1106 | 1106 | md5 = "4c5c896ba52e134bbc3507bac6400087"; |
|
1107 | 1107 | }; |
|
1108 | 1108 | }; |
|
1109 | 1109 | simplejson = super.buildPythonPackage { |
|
1110 | 1110 | name = "simplejson-3.7.2"; |
|
1111 | 1111 | buildInputs = with self; []; |
|
1112 | 1112 | doCheck = false; |
|
1113 | 1113 | propagatedBuildInputs = with self; []; |
|
1114 | 1114 | src = fetchurl { |
|
1115 | 1115 | url = "https://pypi.python.org/packages/6d/89/7f13f099344eea9d6722779a1f165087cb559598107844b1ac5dbd831fb1/simplejson-3.7.2.tar.gz"; |
|
1116 | 1116 | md5 = "a5fc7d05d4cb38492285553def5d4b46"; |
|
1117 | 1117 | }; |
|
1118 | 1118 | }; |
|
1119 | 1119 | six = super.buildPythonPackage { |
|
1120 | 1120 | name = "six-1.9.0"; |
|
1121 | 1121 | buildInputs = with self; []; |
|
1122 | 1122 | doCheck = false; |
|
1123 | 1123 | propagatedBuildInputs = with self; []; |
|
1124 | 1124 | src = fetchurl { |
|
1125 | 1125 | url = "https://pypi.python.org/packages/16/64/1dc5e5976b17466fd7d712e59cbe9fb1e18bec153109e5ba3ed6c9102f1a/six-1.9.0.tar.gz"; |
|
1126 | 1126 | md5 = "476881ef4012262dfc8adc645ee786c4"; |
|
1127 | 1127 | }; |
|
1128 | 1128 | }; |
|
1129 | 1129 | subprocess32 = super.buildPythonPackage { |
|
1130 | 1130 | name = "subprocess32-3.2.6"; |
|
1131 | 1131 | buildInputs = with self; []; |
|
1132 | 1132 | doCheck = false; |
|
1133 | 1133 | propagatedBuildInputs = with self; []; |
|
1134 | 1134 | src = fetchurl { |
|
1135 | 1135 | url = "https://pypi.python.org/packages/28/8d/33ccbff51053f59ae6c357310cac0e79246bbed1d345ecc6188b176d72c3/subprocess32-3.2.6.tar.gz"; |
|
1136 | 1136 | md5 = "754c5ab9f533e764f931136974b618f1"; |
|
1137 | 1137 | }; |
|
1138 | 1138 | }; |
|
1139 | 1139 | supervisor = super.buildPythonPackage { |
|
1140 | 1140 | name = "supervisor-3.1.3"; |
|
1141 | 1141 | buildInputs = with self; []; |
|
1142 | 1142 | doCheck = false; |
|
1143 | 1143 | propagatedBuildInputs = with self; [meld3]; |
|
1144 | 1144 | src = fetchurl { |
|
1145 | 1145 | url = "https://pypi.python.org/packages/a6/41/65ad5bd66230b173eb4d0b8810230f3a9c59ef52ae066e540b6b99895db7/supervisor-3.1.3.tar.gz"; |
|
1146 | 1146 | md5 = "aad263c4fbc070de63dd354864d5e552"; |
|
1147 | 1147 | }; |
|
1148 | 1148 | }; |
|
1149 | 1149 | transifex-client = super.buildPythonPackage { |
|
1150 | 1150 | name = "transifex-client-0.10"; |
|
1151 | 1151 | buildInputs = with self; []; |
|
1152 | 1152 | doCheck = false; |
|
1153 | 1153 | propagatedBuildInputs = with self; []; |
|
1154 | 1154 | src = fetchurl { |
|
1155 | 1155 | url = "https://pypi.python.org/packages/f3/4e/7b925192aee656fb3e04fa6381c8b3dc40198047c3b4a356f6cfd642c809/transifex-client-0.10.tar.gz"; |
|
1156 | 1156 | md5 = "5549538d84b8eede6b254cd81ae024fa"; |
|
1157 | 1157 | }; |
|
1158 | 1158 | }; |
|
1159 | 1159 | translationstring = super.buildPythonPackage { |
|
1160 | 1160 | name = "translationstring-1.3"; |
|
1161 | 1161 | buildInputs = with self; []; |
|
1162 | 1162 | doCheck = false; |
|
1163 | 1163 | propagatedBuildInputs = with self; []; |
|
1164 | 1164 | src = fetchurl { |
|
1165 | 1165 | url = "https://pypi.python.org/packages/5e/eb/bee578cc150b44c653b63f5ebe258b5d0d812ddac12497e5f80fcad5d0b4/translationstring-1.3.tar.gz"; |
|
1166 | 1166 | md5 = "a4b62e0f3c189c783a1685b3027f7c90"; |
|
1167 | 1167 | }; |
|
1168 | 1168 | }; |
|
1169 | 1169 | trollius = super.buildPythonPackage { |
|
1170 | 1170 | name = "trollius-1.0.4"; |
|
1171 | 1171 | buildInputs = with self; []; |
|
1172 | 1172 | doCheck = false; |
|
1173 | 1173 | propagatedBuildInputs = with self; [futures]; |
|
1174 | 1174 | src = fetchurl { |
|
1175 | 1175 | url = "https://pypi.python.org/packages/aa/e6/4141db437f55e6ee7a3fb69663239e3fde7841a811b4bef293145ad6c836/trollius-1.0.4.tar.gz"; |
|
1176 | 1176 | md5 = "3631a464d49d0cbfd30ab2918ef2b783"; |
|
1177 | 1177 | }; |
|
1178 | 1178 | }; |
|
1179 | 1179 | uWSGI = super.buildPythonPackage { |
|
1180 | 1180 | name = "uWSGI-2.0.11.2"; |
|
1181 | 1181 | buildInputs = with self; []; |
|
1182 | 1182 | doCheck = false; |
|
1183 | 1183 | propagatedBuildInputs = with self; []; |
|
1184 | 1184 | src = fetchurl { |
|
1185 | 1185 | url = "https://pypi.python.org/packages/9b/78/918db0cfab0546afa580c1e565209c49aaf1476bbfe491314eadbe47c556/uwsgi-2.0.11.2.tar.gz"; |
|
1186 | 1186 | md5 = "1f02dcbee7f6f61de4b1fd68350cf16f"; |
|
1187 | 1187 | }; |
|
1188 | 1188 | }; |
|
1189 | 1189 | urllib3 = super.buildPythonPackage { |
|
1190 | 1190 | name = "urllib3-1.15.1"; |
|
1191 | 1191 | buildInputs = with self; []; |
|
1192 | 1192 | doCheck = false; |
|
1193 | 1193 | propagatedBuildInputs = with self; []; |
|
1194 | 1194 | src = fetchurl { |
|
1195 | 1195 | url = "https://pypi.python.org/packages/49/26/a7d12ea00cb4b9fa1e13b5980e5a04a1fe7c477eb8f657ce0b757a7a497d/urllib3-1.15.1.tar.gz"; |
|
1196 | 1196 | md5 = "5be254b0dbb55d1307ede99e1895c8dd"; |
|
1197 | 1197 | }; |
|
1198 | 1198 | }; |
|
1199 | 1199 | venusian = super.buildPythonPackage { |
|
1200 | 1200 | name = "venusian-1.0"; |
|
1201 | 1201 | buildInputs = with self; []; |
|
1202 | 1202 | doCheck = false; |
|
1203 | 1203 | propagatedBuildInputs = with self; []; |
|
1204 | 1204 | src = fetchurl { |
|
1205 | 1205 | url = "https://pypi.python.org/packages/86/20/1948e0dfc4930ddde3da8c33612f6a5717c0b4bc28f591a5c5cf014dd390/venusian-1.0.tar.gz"; |
|
1206 | 1206 | md5 = "dccf2eafb7113759d60c86faf5538756"; |
|
1207 | 1207 | }; |
|
1208 | 1208 | }; |
|
1209 | 1209 | waitress = super.buildPythonPackage { |
|
1210 | 1210 | name = "waitress-0.8.9"; |
|
1211 | 1211 | buildInputs = with self; []; |
|
1212 | 1212 | doCheck = false; |
|
1213 | 1213 | propagatedBuildInputs = with self; [setuptools]; |
|
1214 | 1214 | src = fetchurl { |
|
1215 | 1215 | url = "https://pypi.python.org/packages/ee/65/fc9dee74a909a1187ca51e4f15ad9c4d35476e4ab5813f73421505c48053/waitress-0.8.9.tar.gz"; |
|
1216 | 1216 | md5 = "da3f2e62b3676be5dd630703a68e2a04"; |
|
1217 | 1217 | }; |
|
1218 | 1218 | }; |
|
1219 | 1219 | wsgiref = super.buildPythonPackage { |
|
1220 | 1220 | name = "wsgiref-0.1.2"; |
|
1221 | 1221 | buildInputs = with self; []; |
|
1222 | 1222 | doCheck = false; |
|
1223 | 1223 | propagatedBuildInputs = with self; []; |
|
1224 | 1224 | src = fetchurl { |
|
1225 | 1225 | url = "https://pypi.python.org/packages/41/9e/309259ce8dff8c596e8c26df86dbc4e848b9249fd36797fd60be456f03fc/wsgiref-0.1.2.zip"; |
|
1226 | 1226 | md5 = "29b146e6ebd0f9fb119fe321f7bcf6cb"; |
|
1227 | 1227 | }; |
|
1228 | 1228 | }; |
|
1229 | 1229 | zope.cachedescriptors = super.buildPythonPackage { |
|
1230 | 1230 | name = "zope.cachedescriptors-4.0.0"; |
|
1231 | 1231 | buildInputs = with self; []; |
|
1232 | 1232 | doCheck = false; |
|
1233 | 1233 | propagatedBuildInputs = with self; [setuptools]; |
|
1234 | 1234 | src = fetchurl { |
|
1235 | 1235 | url = "https://pypi.python.org/packages/40/33/694b6644c37f28553f4b9f20b3c3a20fb709a22574dff20b5bdffb09ecd5/zope.cachedescriptors-4.0.0.tar.gz"; |
|
1236 | 1236 | md5 = "8d308de8c936792c8e758058fcb7d0f0"; |
|
1237 | 1237 | }; |
|
1238 | 1238 | }; |
|
1239 | 1239 | zope.deprecation = super.buildPythonPackage { |
|
1240 | 1240 | name = "zope.deprecation-4.1.2"; |
|
1241 | 1241 | buildInputs = with self; []; |
|
1242 | 1242 | doCheck = false; |
|
1243 | 1243 | propagatedBuildInputs = with self; [setuptools]; |
|
1244 | 1244 | src = fetchurl { |
|
1245 | 1245 | url = "https://pypi.python.org/packages/c1/d3/3919492d5e57d8dd01b36f30b34fc8404a30577392b1eb817c303499ad20/zope.deprecation-4.1.2.tar.gz"; |
|
1246 | 1246 | md5 = "e9a663ded58f4f9f7881beb56cae2782"; |
|
1247 | 1247 | }; |
|
1248 | 1248 | }; |
|
1249 | 1249 | zope.event = super.buildPythonPackage { |
|
1250 | 1250 | name = "zope.event-4.0.3"; |
|
1251 | 1251 | buildInputs = with self; []; |
|
1252 | 1252 | doCheck = false; |
|
1253 | 1253 | propagatedBuildInputs = with self; [setuptools]; |
|
1254 | 1254 | src = fetchurl { |
|
1255 | 1255 | url = "https://pypi.python.org/packages/c1/29/91ba884d7d6d96691df592e9e9c2bfa57a47040ec1ff47eff18c85137152/zope.event-4.0.3.tar.gz"; |
|
1256 | 1256 | md5 = "9a3780916332b18b8b85f522bcc3e249"; |
|
1257 | 1257 | }; |
|
1258 | 1258 | }; |
|
1259 | 1259 | zope.interface = super.buildPythonPackage { |
|
1260 | 1260 | name = "zope.interface-4.1.3"; |
|
1261 | 1261 | buildInputs = with self; []; |
|
1262 | 1262 | doCheck = false; |
|
1263 | 1263 | propagatedBuildInputs = with self; [setuptools]; |
|
1264 | 1264 | src = fetchurl { |
|
1265 | 1265 | url = "https://pypi.python.org/packages/9d/81/2509ca3c6f59080123c1a8a97125eb48414022618cec0e64eb1313727bfe/zope.interface-4.1.3.tar.gz"; |
|
1266 | 1266 | md5 = "9ae3d24c0c7415deb249dd1a132f0f79"; |
|
1267 | 1267 | }; |
|
1268 | 1268 | }; |
|
1269 | 1269 | |
|
1270 | 1270 | ### Test requirements |
|
1271 | 1271 | |
|
1272 | 1272 | |
|
1273 | 1273 | } |
@@ -1,166 +1,163 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | |
|
3 | 3 | # Copyright (C) 2012-2016 RhodeCode GmbH |
|
4 | 4 | # |
|
5 | 5 | # This program is free software: you can redistribute it and/or modify |
|
6 | 6 | # it under the terms of the GNU Affero General Public License, version 3 |
|
7 | 7 | # (only), as published by the Free Software Foundation. |
|
8 | 8 | # |
|
9 | 9 | # This program is distributed in the hope that it will be useful, |
|
10 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 | 12 | # GNU General Public License for more details. |
|
13 | 13 | # |
|
14 | 14 | # You should have received a copy of the GNU Affero General Public License |
|
15 | 15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | 16 | # |
|
17 | 17 | # This program is dual-licensed. If you wish to learn more about the |
|
18 | 18 | # RhodeCode Enterprise Edition, including its added features, Support services, |
|
19 | 19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ |
|
20 | 20 | |
|
21 | 21 | """ |
|
22 | 22 | RhodeCode authentication plugin for Jasig CAS |
|
23 | 23 | http://www.jasig.org/cas |
|
24 | 24 | """ |
|
25 | 25 | |
|
26 | 26 | |
|
27 | 27 | import colander |
|
28 | 28 | import logging |
|
29 | 29 | import rhodecode |
|
30 | 30 | import urllib |
|
31 | 31 | import urllib2 |
|
32 | 32 | |
|
33 | 33 | from pylons.i18n.translation import lazy_ugettext as _ |
|
34 | 34 | from sqlalchemy.ext.hybrid import hybrid_property |
|
35 | 35 | |
|
36 | 36 | from rhodecode.authentication.base import RhodeCodeExternalAuthPlugin |
|
37 | 37 | from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase |
|
38 | 38 | from rhodecode.authentication.routes import AuthnPluginResourceBase |
|
39 | from rhodecode.lib.ext_json import formatted_json | |
|
40 | 39 | from rhodecode.lib.utils2 import safe_unicode |
|
41 | 40 | from rhodecode.model.db import User |
|
42 | 41 | |
|
43 | 42 | log = logging.getLogger(__name__) |
|
44 | 43 | |
|
45 | 44 | |
|
46 | 45 | def plugin_factory(plugin_id, *args, **kwds): |
|
47 | 46 | """ |
|
48 | 47 | Factory function that is called during plugin discovery. |
|
49 | 48 | It returns the plugin instance. |
|
50 | 49 | """ |
|
51 | 50 | plugin = RhodeCodeAuthPlugin(plugin_id) |
|
52 | 51 | return plugin |
|
53 | 52 | |
|
54 | 53 | |
|
55 | 54 | class JasigCasAuthnResource(AuthnPluginResourceBase): |
|
56 | 55 | pass |
|
57 | 56 | |
|
58 | 57 | |
|
59 | 58 | class JasigCasSettingsSchema(AuthnPluginSettingsSchemaBase): |
|
60 | 59 | service_url = colander.SchemaNode( |
|
61 | 60 | colander.String(), |
|
62 | 61 | default='https://domain.com/cas/v1/tickets', |
|
63 | 62 | description=_('The url of the Jasig CAS REST service'), |
|
64 | 63 | title=_('URL'), |
|
65 | 64 | widget='string') |
|
66 | 65 | |
|
67 | 66 | |
|
68 | 67 | class RhodeCodeAuthPlugin(RhodeCodeExternalAuthPlugin): |
|
69 | 68 | |
|
70 | 69 | def includeme(self, config): |
|
71 | 70 | config.add_authn_plugin(self) |
|
72 | 71 | config.add_authn_resource(self.get_id(), JasigCasAuthnResource(self)) |
|
73 | 72 | config.add_view( |
|
74 | 73 | 'rhodecode.authentication.views.AuthnPluginViewBase', |
|
75 | 74 | attr='settings_get', |
|
76 | 75 | request_method='GET', |
|
77 | 76 | route_name='auth_home', |
|
78 | 77 | context=JasigCasAuthnResource) |
|
79 | 78 | config.add_view( |
|
80 | 79 | 'rhodecode.authentication.views.AuthnPluginViewBase', |
|
81 | 80 | attr='settings_post', |
|
82 | 81 | request_method='POST', |
|
83 | 82 | route_name='auth_home', |
|
84 | 83 | context=JasigCasAuthnResource) |
|
85 | 84 | |
|
86 | 85 | def get_settings_schema(self): |
|
87 | 86 | return JasigCasSettingsSchema() |
|
88 | 87 | |
|
89 | 88 | def get_display_name(self): |
|
90 | 89 | return _('Jasig-CAS') |
|
91 | 90 | |
|
92 | 91 | @hybrid_property |
|
93 | 92 | def name(self): |
|
94 | 93 | return "jasig-cas" |
|
95 | 94 | |
|
96 | 95 | @hybrid_property |
|
97 | 96 | def is_container_auth(self): |
|
98 | 97 | return True |
|
99 | 98 | |
|
100 | 99 | def use_fake_password(self): |
|
101 | 100 | return True |
|
102 | 101 | |
|
103 | 102 | def user_activation_state(self): |
|
104 | 103 | def_user_perms = User.get_default_user().AuthUser.permissions['global'] |
|
105 | 104 | return 'hg.extern_activate.auto' in def_user_perms |
|
106 | 105 | |
|
107 | 106 | def auth(self, userobj, username, password, settings, **kwargs): |
|
108 | 107 | """ |
|
109 | 108 | Given a user object (which may be null), username, a plaintext password, |
|
110 | 109 | and a settings object (containing all the keys needed as listed in settings()), |
|
111 | 110 | authenticate this user's login attempt. |
|
112 | 111 | |
|
113 | 112 | Return None on failure. On success, return a dictionary of the form: |
|
114 | 113 | |
|
115 | 114 | see: RhodeCodeAuthPluginBase.auth_func_attrs |
|
116 | 115 | This is later validated for correctness |
|
117 | 116 | """ |
|
118 | 117 | if not username or not password: |
|
119 | 118 | log.debug('Empty username or password skipping...') |
|
120 | 119 | return None |
|
121 | 120 | |
|
122 |
log.debug("Jasig CAS settings: |
|
|
121 | log.debug("Jasig CAS settings: %s", settings) | |
|
123 | 122 | params = urllib.urlencode({'username': username, 'password': password}) |
|
124 | 123 | headers = {"Content-type": "application/x-www-form-urlencoded", |
|
125 | 124 | "Accept": "text/plain", |
|
126 | 125 | "User-Agent": "RhodeCode-auth-%s" % rhodecode.__version__} |
|
127 | 126 | url = settings["service_url"] |
|
128 | 127 | |
|
129 | log.debug("Sent Jasig CAS: \n%s" | |
|
130 | % (formatted_json({"url": url, | |
|
131 | "body": params, | |
|
132 | "headers": headers}))) | |
|
128 | log.debug("Sent Jasig CAS: \n%s", | |
|
129 | {"url": url, "body": params, "headers": headers}) | |
|
133 | 130 | request = urllib2.Request(url, params, headers) |
|
134 | 131 | try: |
|
135 | 132 | response = urllib2.urlopen(request) |
|
136 | 133 | except urllib2.HTTPError as e: |
|
137 | 134 | log.debug("HTTPError when requesting Jasig CAS (status code: %d)" % e.code) |
|
138 | 135 | return None |
|
139 | 136 | except urllib2.URLError as e: |
|
140 | 137 | log.debug("URLError when requesting Jasig CAS url: %s " % url) |
|
141 | 138 | return None |
|
142 | 139 | |
|
143 | 140 | # old attrs fetched from RhodeCode database |
|
144 | 141 | admin = getattr(userobj, 'admin', False) |
|
145 | 142 | active = getattr(userobj, 'active', True) |
|
146 | 143 | email = getattr(userobj, 'email', '') |
|
147 | 144 | username = getattr(userobj, 'username', username) |
|
148 | 145 | firstname = getattr(userobj, 'firstname', '') |
|
149 | 146 | lastname = getattr(userobj, 'lastname', '') |
|
150 | 147 | extern_type = getattr(userobj, 'extern_type', '') |
|
151 | 148 | |
|
152 | 149 | user_attrs = { |
|
153 | 150 | 'username': username, |
|
154 | 151 | 'firstname': safe_unicode(firstname or username), |
|
155 | 152 | 'lastname': safe_unicode(lastname or ''), |
|
156 | 153 | 'groups': [], |
|
157 | 154 | 'email': email or '', |
|
158 | 155 | 'admin': admin or False, |
|
159 | 156 | 'active': active, |
|
160 | 157 | 'active_from_extern': True, |
|
161 | 158 | 'extern_name': username, |
|
162 | 159 | 'extern_type': extern_type, |
|
163 | 160 | } |
|
164 | 161 | |
|
165 | 162 | log.info('user %s authenticated correctly' % user_attrs['username']) |
|
166 | 163 | return user_attrs |
@@ -1,448 +1,447 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | |
|
3 | 3 | # Copyright (C) 2010-2016 RhodeCode GmbH |
|
4 | 4 | # |
|
5 | 5 | # This program is free software: you can redistribute it and/or modify |
|
6 | 6 | # it under the terms of the GNU Affero General Public License, version 3 |
|
7 | 7 | # (only), as published by the Free Software Foundation. |
|
8 | 8 | # |
|
9 | 9 | # This program is distributed in the hope that it will be useful, |
|
10 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 | 12 | # GNU General Public License for more details. |
|
13 | 13 | # |
|
14 | 14 | # You should have received a copy of the GNU Affero General Public License |
|
15 | 15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | 16 | # |
|
17 | 17 | # This program is dual-licensed. If you wish to learn more about the |
|
18 | 18 | # RhodeCode Enterprise Edition, including its added features, Support services, |
|
19 | 19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ |
|
20 | 20 | |
|
21 | 21 | """ |
|
22 | 22 | RhodeCode authentication plugin for LDAP |
|
23 | 23 | """ |
|
24 | 24 | |
|
25 | 25 | |
|
26 | 26 | import colander |
|
27 | 27 | import logging |
|
28 | 28 | import traceback |
|
29 | 29 | |
|
30 | 30 | from pylons.i18n.translation import lazy_ugettext as _ |
|
31 | 31 | from sqlalchemy.ext.hybrid import hybrid_property |
|
32 | 32 | |
|
33 | 33 | from rhodecode.authentication.base import RhodeCodeExternalAuthPlugin |
|
34 | 34 | from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase |
|
35 | 35 | from rhodecode.authentication.routes import AuthnPluginResourceBase |
|
36 | 36 | from rhodecode.lib.exceptions import ( |
|
37 | 37 | LdapConnectionError, LdapUsernameError, LdapPasswordError, LdapImportError |
|
38 | 38 | ) |
|
39 | from rhodecode.lib.ext_json import formatted_json | |
|
40 | 39 | from rhodecode.lib.utils2 import safe_unicode, safe_str |
|
41 | 40 | from rhodecode.model.db import User |
|
42 | 41 | from rhodecode.model.validators import Missing |
|
43 | 42 | |
|
44 | 43 | log = logging.getLogger(__name__) |
|
45 | 44 | |
|
46 | 45 | try: |
|
47 | 46 | import ldap |
|
48 | 47 | except ImportError: |
|
49 | 48 | # means that python-ldap is not installed |
|
50 | 49 | ldap = Missing() |
|
51 | 50 | |
|
52 | 51 | |
|
53 | 52 | def plugin_factory(plugin_id, *args, **kwds): |
|
54 | 53 | """ |
|
55 | 54 | Factory function that is called during plugin discovery. |
|
56 | 55 | It returns the plugin instance. |
|
57 | 56 | """ |
|
58 | 57 | plugin = RhodeCodeAuthPlugin(plugin_id) |
|
59 | 58 | return plugin |
|
60 | 59 | |
|
61 | 60 | |
|
62 | 61 | class LdapAuthnResource(AuthnPluginResourceBase): |
|
63 | 62 | pass |
|
64 | 63 | |
|
65 | 64 | |
|
66 | 65 | class LdapSettingsSchema(AuthnPluginSettingsSchemaBase): |
|
67 | 66 | tls_kind_choices = ['PLAIN', 'LDAPS', 'START_TLS'] |
|
68 | 67 | tls_reqcert_choices = ['NEVER', 'ALLOW', 'TRY', 'DEMAND', 'HARD'] |
|
69 | 68 | search_scope_choices = ['BASE', 'ONELEVEL', 'SUBTREE'] |
|
70 | 69 | |
|
71 | 70 | host = colander.SchemaNode( |
|
72 | 71 | colander.String(), |
|
73 | 72 | default='', |
|
74 | 73 | description=_('Host of the LDAP Server'), |
|
75 | 74 | title=_('LDAP Host'), |
|
76 | 75 | widget='string') |
|
77 | 76 | port = colander.SchemaNode( |
|
78 | 77 | colander.Int(), |
|
79 | 78 | default=389, |
|
80 | 79 | description=_('Port that the LDAP server is listening on'), |
|
81 | 80 | title=_('Port'), |
|
82 | 81 | validator=colander.Range(min=0, max=65536), |
|
83 | 82 | widget='int') |
|
84 | 83 | dn_user = colander.SchemaNode( |
|
85 | 84 | colander.String(), |
|
86 | 85 | default='', |
|
87 | 86 | description=_('User to connect to LDAP'), |
|
88 | 87 | missing='', |
|
89 | 88 | title=_('Account'), |
|
90 | 89 | widget='string') |
|
91 | 90 | dn_pass = colander.SchemaNode( |
|
92 | 91 | colander.String(), |
|
93 | 92 | default='', |
|
94 | 93 | description=_('Password to connect to LDAP'), |
|
95 | 94 | missing='', |
|
96 | 95 | title=_('Password'), |
|
97 | 96 | widget='password') |
|
98 | 97 | tls_kind = colander.SchemaNode( |
|
99 | 98 | colander.String(), |
|
100 | 99 | default=tls_kind_choices[0], |
|
101 | 100 | description=_('TLS Type'), |
|
102 | 101 | title=_('Connection Security'), |
|
103 | 102 | validator=colander.OneOf(tls_kind_choices), |
|
104 | 103 | widget='select') |
|
105 | 104 | tls_reqcert = colander.SchemaNode( |
|
106 | 105 | colander.String(), |
|
107 | 106 | default=tls_reqcert_choices[0], |
|
108 | 107 | description=_('Require Cert over TLS?'), |
|
109 | 108 | title=_('Certificate Checks'), |
|
110 | 109 | validator=colander.OneOf(tls_reqcert_choices), |
|
111 | 110 | widget='select') |
|
112 | 111 | base_dn = colander.SchemaNode( |
|
113 | 112 | colander.String(), |
|
114 | 113 | default='', |
|
115 | 114 | description=_('Base DN to search (e.g., dc=mydomain,dc=com)'), |
|
116 | 115 | missing='', |
|
117 | 116 | title=_('Base DN'), |
|
118 | 117 | widget='string') |
|
119 | 118 | filter = colander.SchemaNode( |
|
120 | 119 | colander.String(), |
|
121 | 120 | default='', |
|
122 | 121 | description=_('Filter to narrow results (e.g., ou=Users, etc)'), |
|
123 | 122 | missing='', |
|
124 | 123 | title=_('LDAP Search Filter'), |
|
125 | 124 | widget='string') |
|
126 | 125 | search_scope = colander.SchemaNode( |
|
127 | 126 | colander.String(), |
|
128 | 127 | default=search_scope_choices[0], |
|
129 | 128 | description=_('How deep to search LDAP'), |
|
130 | 129 | title=_('LDAP Search Scope'), |
|
131 | 130 | validator=colander.OneOf(search_scope_choices), |
|
132 | 131 | widget='select') |
|
133 | 132 | attr_login = colander.SchemaNode( |
|
134 | 133 | colander.String(), |
|
135 | 134 | default='', |
|
136 | 135 | description=_('LDAP Attribute to map to user name'), |
|
137 | 136 | title=_('Login Attribute'), |
|
138 | 137 | missing_msg=_('The LDAP Login attribute of the CN must be specified'), |
|
139 | 138 | widget='string') |
|
140 | 139 | attr_firstname = colander.SchemaNode( |
|
141 | 140 | colander.String(), |
|
142 | 141 | default='', |
|
143 | 142 | description=_('LDAP Attribute to map to first name'), |
|
144 | 143 | missing='', |
|
145 | 144 | title=_('First Name Attribute'), |
|
146 | 145 | widget='string') |
|
147 | 146 | attr_lastname = colander.SchemaNode( |
|
148 | 147 | colander.String(), |
|
149 | 148 | default='', |
|
150 | 149 | description=_('LDAP Attribute to map to last name'), |
|
151 | 150 | missing='', |
|
152 | 151 | title=_('Last Name Attribute'), |
|
153 | 152 | widget='string') |
|
154 | 153 | attr_email = colander.SchemaNode( |
|
155 | 154 | colander.String(), |
|
156 | 155 | default='', |
|
157 | 156 | description=_('LDAP Attribute to map to email address'), |
|
158 | 157 | missing='', |
|
159 | 158 | title=_('Email Attribute'), |
|
160 | 159 | widget='string') |
|
161 | 160 | |
|
162 | 161 | |
|
163 | 162 | class AuthLdap(object): |
|
164 | 163 | |
|
165 | 164 | def _build_servers(self): |
|
166 | 165 | return ', '.join( |
|
167 | 166 | ["{}://{}:{}".format( |
|
168 | 167 | self.ldap_server_type, host.strip(), self.LDAP_SERVER_PORT) |
|
169 | 168 | for host in self.SERVER_ADDRESSES]) |
|
170 | 169 | |
|
171 | 170 | def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='', |
|
172 | 171 | tls_kind='PLAIN', tls_reqcert='DEMAND', ldap_version=3, |
|
173 | 172 | search_scope='SUBTREE', attr_login='uid', |
|
174 | 173 | ldap_filter='(&(objectClass=user)(!(objectClass=computer)))'): |
|
175 | 174 | if isinstance(ldap, Missing): |
|
176 | 175 | raise LdapImportError("Missing or incompatible ldap library") |
|
177 | 176 | |
|
178 | 177 | self.ldap_version = ldap_version |
|
179 | 178 | self.ldap_server_type = 'ldap' |
|
180 | 179 | |
|
181 | 180 | self.TLS_KIND = tls_kind |
|
182 | 181 | |
|
183 | 182 | if self.TLS_KIND == 'LDAPS': |
|
184 | 183 | port = port or 689 |
|
185 | 184 | self.ldap_server_type += 's' |
|
186 | 185 | |
|
187 | 186 | OPT_X_TLS_DEMAND = 2 |
|
188 | 187 | self.TLS_REQCERT = getattr(ldap, 'OPT_X_TLS_%s' % tls_reqcert, |
|
189 | 188 | OPT_X_TLS_DEMAND) |
|
190 | 189 | # split server into list |
|
191 | 190 | self.SERVER_ADDRESSES = server.split(',') |
|
192 | 191 | self.LDAP_SERVER_PORT = port |
|
193 | 192 | |
|
194 | 193 | # USE FOR READ ONLY BIND TO LDAP SERVER |
|
195 | 194 | self.attr_login = attr_login |
|
196 | 195 | |
|
197 | 196 | self.LDAP_BIND_DN = safe_str(bind_dn) |
|
198 | 197 | self.LDAP_BIND_PASS = safe_str(bind_pass) |
|
199 | 198 | self.LDAP_SERVER = self._build_servers() |
|
200 | 199 | self.SEARCH_SCOPE = getattr(ldap, 'SCOPE_%s' % search_scope) |
|
201 | 200 | self.BASE_DN = safe_str(base_dn) |
|
202 | 201 | self.LDAP_FILTER = safe_str(ldap_filter) |
|
203 | 202 | |
|
204 | 203 | def _get_ldap_server(self): |
|
205 | 204 | if hasattr(ldap, 'OPT_X_TLS_CACERTDIR'): |
|
206 | 205 | ldap.set_option(ldap.OPT_X_TLS_CACERTDIR, |
|
207 | 206 | '/etc/openldap/cacerts') |
|
208 | 207 | ldap.set_option(ldap.OPT_REFERRALS, ldap.OPT_OFF) |
|
209 | 208 | ldap.set_option(ldap.OPT_RESTART, ldap.OPT_ON) |
|
210 | 209 | ldap.set_option(ldap.OPT_TIMEOUT, 20) |
|
211 | 210 | ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 10) |
|
212 | 211 | ldap.set_option(ldap.OPT_TIMELIMIT, 15) |
|
213 | 212 | if self.TLS_KIND != 'PLAIN': |
|
214 | 213 | ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, self.TLS_REQCERT) |
|
215 | 214 | server = ldap.initialize(self.LDAP_SERVER) |
|
216 | 215 | if self.ldap_version == 2: |
|
217 | 216 | server.protocol = ldap.VERSION2 |
|
218 | 217 | else: |
|
219 | 218 | server.protocol = ldap.VERSION3 |
|
220 | 219 | |
|
221 | 220 | if self.TLS_KIND == 'START_TLS': |
|
222 | 221 | server.start_tls_s() |
|
223 | 222 | |
|
224 | 223 | if self.LDAP_BIND_DN and self.LDAP_BIND_PASS: |
|
225 | 224 | log.debug('Trying simple_bind with password and given DN: %s', |
|
226 | 225 | self.LDAP_BIND_DN) |
|
227 | 226 | server.simple_bind_s(self.LDAP_BIND_DN, self.LDAP_BIND_PASS) |
|
228 | 227 | |
|
229 | 228 | return server |
|
230 | 229 | |
|
231 | 230 | def get_uid(self, username): |
|
232 | 231 | from rhodecode.lib.helpers import chop_at |
|
233 | 232 | uid = username |
|
234 | 233 | for server_addr in self.SERVER_ADDRESSES: |
|
235 | 234 | uid = chop_at(username, "@%s" % server_addr) |
|
236 | 235 | return uid |
|
237 | 236 | |
|
238 | 237 | def fetch_attrs_from_simple_bind(self, server, dn, username, password): |
|
239 | 238 | try: |
|
240 | 239 | log.debug('Trying simple bind with %s', dn) |
|
241 | 240 | server.simple_bind_s(dn, safe_str(password)) |
|
242 | 241 | user = server.search_ext_s( |
|
243 | 242 | dn, ldap.SCOPE_BASE, '(objectClass=*)', )[0] |
|
244 | 243 | _, attrs = user |
|
245 | 244 | return attrs |
|
246 | 245 | |
|
247 | 246 | except ldap.INVALID_CREDENTIALS: |
|
248 | 247 | log.debug( |
|
249 | 248 | "LDAP rejected password for user '%s': %s, org_exc:", |
|
250 | 249 | username, dn, exc_info=True) |
|
251 | 250 | |
|
252 | 251 | def authenticate_ldap(self, username, password): |
|
253 | 252 | """ |
|
254 | 253 | Authenticate a user via LDAP and return his/her LDAP properties. |
|
255 | 254 | |
|
256 | 255 | Raises AuthenticationError if the credentials are rejected, or |
|
257 | 256 | EnvironmentError if the LDAP server can't be reached. |
|
258 | 257 | |
|
259 | 258 | :param username: username |
|
260 | 259 | :param password: password |
|
261 | 260 | """ |
|
262 | 261 | |
|
263 | 262 | uid = self.get_uid(username) |
|
264 | 263 | |
|
265 | 264 | if not password: |
|
266 | 265 | msg = "Authenticating user %s with blank password not allowed" |
|
267 | 266 | log.warning(msg, username) |
|
268 | 267 | raise LdapPasswordError(msg) |
|
269 | 268 | if "," in username: |
|
270 | 269 | raise LdapUsernameError("invalid character in username: ,") |
|
271 | 270 | try: |
|
272 | 271 | server = self._get_ldap_server() |
|
273 | 272 | filter_ = '(&%s(%s=%s))' % ( |
|
274 | 273 | self.LDAP_FILTER, self.attr_login, username) |
|
275 | 274 | log.debug("Authenticating %r filter %s at %s", self.BASE_DN, |
|
276 | 275 | filter_, self.LDAP_SERVER) |
|
277 | 276 | lobjects = server.search_ext_s( |
|
278 | 277 | self.BASE_DN, self.SEARCH_SCOPE, filter_) |
|
279 | 278 | |
|
280 | 279 | if not lobjects: |
|
281 | 280 | raise ldap.NO_SUCH_OBJECT() |
|
282 | 281 | |
|
283 | 282 | for (dn, _attrs) in lobjects: |
|
284 | 283 | if dn is None: |
|
285 | 284 | continue |
|
286 | 285 | |
|
287 | 286 | user_attrs = self.fetch_attrs_from_simple_bind( |
|
288 | 287 | server, dn, username, password) |
|
289 | 288 | if user_attrs: |
|
290 | 289 | break |
|
291 | 290 | |
|
292 | 291 | else: |
|
293 | 292 | log.debug("No matching LDAP objects for authentication " |
|
294 | 293 | "of '%s' (%s)", uid, username) |
|
295 | 294 | raise LdapPasswordError('Failed to authenticate user ' |
|
296 | 295 | 'with given password') |
|
297 | 296 | |
|
298 | 297 | except ldap.NO_SUCH_OBJECT: |
|
299 | 298 | log.debug("LDAP says no such user '%s' (%s), org_exc:", |
|
300 | 299 | uid, username, exc_info=True) |
|
301 | 300 | raise LdapUsernameError() |
|
302 | 301 | except ldap.SERVER_DOWN: |
|
303 | 302 | org_exc = traceback.format_exc() |
|
304 | 303 | raise LdapConnectionError( |
|
305 | 304 | "LDAP can't access authentication " |
|
306 | 305 | "server, org_exc:%s" % org_exc) |
|
307 | 306 | |
|
308 | 307 | return dn, user_attrs |
|
309 | 308 | |
|
310 | 309 | |
|
311 | 310 | class RhodeCodeAuthPlugin(RhodeCodeExternalAuthPlugin): |
|
312 | 311 | # used to define dynamic binding in the |
|
313 | 312 | DYNAMIC_BIND_VAR = '$login' |
|
314 | 313 | |
|
315 | 314 | def includeme(self, config): |
|
316 | 315 | config.add_authn_plugin(self) |
|
317 | 316 | config.add_authn_resource(self.get_id(), LdapAuthnResource(self)) |
|
318 | 317 | config.add_view( |
|
319 | 318 | 'rhodecode.authentication.views.AuthnPluginViewBase', |
|
320 | 319 | attr='settings_get', |
|
321 | 320 | request_method='GET', |
|
322 | 321 | route_name='auth_home', |
|
323 | 322 | context=LdapAuthnResource) |
|
324 | 323 | config.add_view( |
|
325 | 324 | 'rhodecode.authentication.views.AuthnPluginViewBase', |
|
326 | 325 | attr='settings_post', |
|
327 | 326 | request_method='POST', |
|
328 | 327 | route_name='auth_home', |
|
329 | 328 | context=LdapAuthnResource) |
|
330 | 329 | |
|
331 | 330 | def get_settings_schema(self): |
|
332 | 331 | return LdapSettingsSchema() |
|
333 | 332 | |
|
334 | 333 | def get_display_name(self): |
|
335 | 334 | return _('LDAP') |
|
336 | 335 | |
|
337 | 336 | @hybrid_property |
|
338 | 337 | def name(self): |
|
339 | 338 | return "ldap" |
|
340 | 339 | |
|
341 | 340 | def use_fake_password(self): |
|
342 | 341 | return True |
|
343 | 342 | |
|
344 | 343 | def user_activation_state(self): |
|
345 | 344 | def_user_perms = User.get_default_user().AuthUser.permissions['global'] |
|
346 | 345 | return 'hg.extern_activate.auto' in def_user_perms |
|
347 | 346 | |
|
348 | 347 | def try_dynamic_binding(self, username, password, current_args): |
|
349 | 348 | """ |
|
350 | 349 | Detects marker inside our original bind, and uses dynamic auth if |
|
351 | 350 | present |
|
352 | 351 | """ |
|
353 | 352 | |
|
354 | 353 | org_bind = current_args['bind_dn'] |
|
355 | 354 | passwd = current_args['bind_pass'] |
|
356 | 355 | |
|
357 | 356 | def has_bind_marker(username): |
|
358 | 357 | if self.DYNAMIC_BIND_VAR in username: |
|
359 | 358 | return True |
|
360 | 359 | |
|
361 | 360 | # we only passed in user with "special" variable |
|
362 | 361 | if org_bind and has_bind_marker(org_bind) and not passwd: |
|
363 | 362 | log.debug('Using dynamic user/password binding for ldap ' |
|
364 | 363 | 'authentication. Replacing `%s` with username', |
|
365 | 364 | self.DYNAMIC_BIND_VAR) |
|
366 | 365 | current_args['bind_dn'] = org_bind.replace( |
|
367 | 366 | self.DYNAMIC_BIND_VAR, username) |
|
368 | 367 | current_args['bind_pass'] = password |
|
369 | 368 | |
|
370 | 369 | return current_args |
|
371 | 370 | |
|
372 | 371 | def auth(self, userobj, username, password, settings, **kwargs): |
|
373 | 372 | """ |
|
374 | 373 | Given a user object (which may be null), username, a plaintext password, |
|
375 | 374 | and a settings object (containing all the keys needed as listed in |
|
376 | 375 | settings()), authenticate this user's login attempt. |
|
377 | 376 | |
|
378 | 377 | Return None on failure. On success, return a dictionary of the form: |
|
379 | 378 | |
|
380 | 379 | see: RhodeCodeAuthPluginBase.auth_func_attrs |
|
381 | 380 | This is later validated for correctness |
|
382 | 381 | """ |
|
383 | 382 | |
|
384 | 383 | if not username or not password: |
|
385 | 384 | log.debug('Empty username or password skipping...') |
|
386 | 385 | return None |
|
387 | 386 | |
|
388 | 387 | ldap_args = { |
|
389 | 388 | 'server': settings.get('host', ''), |
|
390 | 389 | 'base_dn': settings.get('base_dn', ''), |
|
391 | 390 | 'port': settings.get('port'), |
|
392 | 391 | 'bind_dn': settings.get('dn_user'), |
|
393 | 392 | 'bind_pass': settings.get('dn_pass'), |
|
394 | 393 | 'tls_kind': settings.get('tls_kind'), |
|
395 | 394 | 'tls_reqcert': settings.get('tls_reqcert'), |
|
396 | 395 | 'search_scope': settings.get('search_scope'), |
|
397 | 396 | 'attr_login': settings.get('attr_login'), |
|
398 | 397 | 'ldap_version': 3, |
|
399 | 398 | 'ldap_filter': settings.get('filter'), |
|
400 | 399 | } |
|
401 | 400 | |
|
402 | 401 | ldap_attrs = self.try_dynamic_binding(username, password, ldap_args) |
|
403 | 402 | |
|
404 | 403 | log.debug('Checking for ldap authentication.') |
|
405 | 404 | |
|
406 | 405 | try: |
|
407 | 406 | aldap = AuthLdap(**ldap_args) |
|
408 | 407 | (user_dn, ldap_attrs) = aldap.authenticate_ldap(username, password) |
|
409 | 408 | log.debug('Got ldap DN response %s', user_dn) |
|
410 | 409 | |
|
411 | 410 | def get_ldap_attr(k): |
|
412 | 411 | return ldap_attrs.get(settings.get(k), [''])[0] |
|
413 | 412 | |
|
414 | 413 | # old attrs fetched from RhodeCode database |
|
415 | 414 | admin = getattr(userobj, 'admin', False) |
|
416 | 415 | active = getattr(userobj, 'active', True) |
|
417 | 416 | email = getattr(userobj, 'email', '') |
|
418 | 417 | username = getattr(userobj, 'username', username) |
|
419 | 418 | firstname = getattr(userobj, 'firstname', '') |
|
420 | 419 | lastname = getattr(userobj, 'lastname', '') |
|
421 | 420 | extern_type = getattr(userobj, 'extern_type', '') |
|
422 | 421 | |
|
423 | 422 | groups = [] |
|
424 | 423 | user_attrs = { |
|
425 | 424 | 'username': username, |
|
426 | 425 | 'firstname': safe_unicode( |
|
427 | 426 | get_ldap_attr('attr_firstname') or firstname), |
|
428 | 427 | 'lastname': safe_unicode( |
|
429 | 428 | get_ldap_attr('attr_lastname') or lastname), |
|
430 | 429 | 'groups': groups, |
|
431 | 430 | 'email': get_ldap_attr('attr_email' or email), |
|
432 | 431 | 'admin': admin, |
|
433 | 432 | 'active': active, |
|
434 | 433 | "active_from_extern": None, |
|
435 | 434 | 'extern_name': user_dn, |
|
436 | 435 | 'extern_type': extern_type, |
|
437 | 436 | } |
|
438 |
log.debug('ldap user: |
|
|
437 | log.debug('ldap user: %s', user_attrs) | |
|
439 | 438 | log.info('user %s authenticated correctly', user_attrs['username']) |
|
440 | 439 | |
|
441 | 440 | return user_attrs |
|
442 | 441 | |
|
443 | 442 | except (LdapUsernameError, LdapPasswordError, LdapImportError): |
|
444 | 443 | log.exception("LDAP related exception") |
|
445 | 444 | return None |
|
446 | 445 | except (Exception,): |
|
447 | 446 | log.exception("Other exception") |
|
448 | 447 | return None |
@@ -1,156 +1,155 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | |
|
3 | 3 | # Copyright (C) 2012-2016 RhodeCode GmbH |
|
4 | 4 | # |
|
5 | 5 | # This program is free software: you can redistribute it and/or modify |
|
6 | 6 | # it under the terms of the GNU Affero General Public License, version 3 |
|
7 | 7 | # (only), as published by the Free Software Foundation. |
|
8 | 8 | # |
|
9 | 9 | # This program is distributed in the hope that it will be useful, |
|
10 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 | 12 | # GNU General Public License for more details. |
|
13 | 13 | # |
|
14 | 14 | # You should have received a copy of the GNU Affero General Public License |
|
15 | 15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | 16 | # |
|
17 | 17 | # This program is dual-licensed. If you wish to learn more about the |
|
18 | 18 | # RhodeCode Enterprise Edition, including its added features, Support services, |
|
19 | 19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ |
|
20 | 20 | """ |
|
21 | 21 | RhodeCode authentication library for PAM |
|
22 | 22 | """ |
|
23 | 23 | |
|
24 | 24 | import colander |
|
25 | 25 | import grp |
|
26 | 26 | import logging |
|
27 | 27 | import pam |
|
28 | 28 | import pwd |
|
29 | 29 | import re |
|
30 | 30 | import socket |
|
31 | 31 | |
|
32 | 32 | from pylons.i18n.translation import lazy_ugettext as _ |
|
33 | 33 | from sqlalchemy.ext.hybrid import hybrid_property |
|
34 | 34 | |
|
35 | 35 | from rhodecode.authentication.base import RhodeCodeExternalAuthPlugin |
|
36 | 36 | from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase |
|
37 | 37 | from rhodecode.authentication.routes import AuthnPluginResourceBase |
|
38 | from rhodecode.lib.ext_json import formatted_json | |
|
39 | 38 | |
|
40 | 39 | log = logging.getLogger(__name__) |
|
41 | 40 | |
|
42 | 41 | |
|
43 | 42 | def plugin_factory(plugin_id, *args, **kwds): |
|
44 | 43 | """ |
|
45 | 44 | Factory function that is called during plugin discovery. |
|
46 | 45 | It returns the plugin instance. |
|
47 | 46 | """ |
|
48 | 47 | plugin = RhodeCodeAuthPlugin(plugin_id) |
|
49 | 48 | return plugin |
|
50 | 49 | |
|
51 | 50 | |
|
52 | 51 | class PamAuthnResource(AuthnPluginResourceBase): |
|
53 | 52 | pass |
|
54 | 53 | |
|
55 | 54 | |
|
56 | 55 | class PamSettingsSchema(AuthnPluginSettingsSchemaBase): |
|
57 | 56 | service = colander.SchemaNode( |
|
58 | 57 | colander.String(), |
|
59 | 58 | default='login', |
|
60 | 59 | description=_('PAM service name to use for authentication.'), |
|
61 | 60 | title=_('PAM service name'), |
|
62 | 61 | widget='string') |
|
63 | 62 | gecos = colander.SchemaNode( |
|
64 | 63 | colander.String(), |
|
65 | 64 | default='(?P<last_name>.+),\s*(?P<first_name>\w+)', |
|
66 | 65 | description=_('Regular expression for extracting user name/email etc. ' |
|
67 | 66 | 'from Unix userinfo.'), |
|
68 | 67 | title=_('Gecos Regex'), |
|
69 | 68 | widget='string') |
|
70 | 69 | |
|
71 | 70 | |
|
72 | 71 | class RhodeCodeAuthPlugin(RhodeCodeExternalAuthPlugin): |
|
73 | 72 | # PAM authentication can be slow. Repository operations involve a lot of |
|
74 | 73 | # auth calls. Little caching helps speedup push/pull operations significantly |
|
75 | 74 | AUTH_CACHE_TTL = 4 |
|
76 | 75 | |
|
77 | 76 | def includeme(self, config): |
|
78 | 77 | config.add_authn_plugin(self) |
|
79 | 78 | config.add_authn_resource(self.get_id(), PamAuthnResource(self)) |
|
80 | 79 | config.add_view( |
|
81 | 80 | 'rhodecode.authentication.views.AuthnPluginViewBase', |
|
82 | 81 | attr='settings_get', |
|
83 | 82 | request_method='GET', |
|
84 | 83 | route_name='auth_home', |
|
85 | 84 | context=PamAuthnResource) |
|
86 | 85 | config.add_view( |
|
87 | 86 | 'rhodecode.authentication.views.AuthnPluginViewBase', |
|
88 | 87 | attr='settings_post', |
|
89 | 88 | request_method='POST', |
|
90 | 89 | route_name='auth_home', |
|
91 | 90 | context=PamAuthnResource) |
|
92 | 91 | |
|
93 | 92 | def get_display_name(self): |
|
94 | 93 | return _('PAM') |
|
95 | 94 | |
|
96 | 95 | @hybrid_property |
|
97 | 96 | def name(self): |
|
98 | 97 | return "pam" |
|
99 | 98 | |
|
100 | 99 | def get_settings_schema(self): |
|
101 | 100 | return PamSettingsSchema() |
|
102 | 101 | |
|
103 | 102 | def use_fake_password(self): |
|
104 | 103 | return True |
|
105 | 104 | |
|
106 | 105 | def auth(self, userobj, username, password, settings, **kwargs): |
|
107 | 106 | if not username or not password: |
|
108 | 107 | log.debug('Empty username or password skipping...') |
|
109 | 108 | return None |
|
110 | 109 | |
|
111 | 110 | auth_result = pam.authenticate(username, password, settings["service"]) |
|
112 | 111 | |
|
113 | 112 | if not auth_result: |
|
114 | 113 | log.error("PAM was unable to authenticate user: %s" % (username, )) |
|
115 | 114 | return None |
|
116 | 115 | |
|
117 | 116 | log.debug('Got PAM response %s' % (auth_result, )) |
|
118 | 117 | |
|
119 | 118 | # old attrs fetched from RhodeCode database |
|
120 | 119 | default_email = "%s@%s" % (username, socket.gethostname()) |
|
121 | 120 | admin = getattr(userobj, 'admin', False) |
|
122 | 121 | active = getattr(userobj, 'active', True) |
|
123 | 122 | email = getattr(userobj, 'email', '') or default_email |
|
124 | 123 | username = getattr(userobj, 'username', username) |
|
125 | 124 | firstname = getattr(userobj, 'firstname', '') |
|
126 | 125 | lastname = getattr(userobj, 'lastname', '') |
|
127 | 126 | extern_type = getattr(userobj, 'extern_type', '') |
|
128 | 127 | |
|
129 | 128 | user_attrs = { |
|
130 | 129 | 'username': username, |
|
131 | 130 | 'firstname': firstname, |
|
132 | 131 | 'lastname': lastname, |
|
133 | 132 | 'groups': [g.gr_name for g in grp.getgrall() |
|
134 | 133 | if username in g.gr_mem], |
|
135 | 134 | 'email': email, |
|
136 | 135 | 'admin': admin, |
|
137 | 136 | 'active': active, |
|
138 | 137 | 'active_from_extern': None, |
|
139 | 138 | 'extern_name': username, |
|
140 | 139 | 'extern_type': extern_type, |
|
141 | 140 | } |
|
142 | 141 | |
|
143 | 142 | try: |
|
144 | 143 | user_data = pwd.getpwnam(username) |
|
145 | 144 | regex = settings["gecos"] |
|
146 | 145 | match = re.search(regex, user_data.pw_gecos) |
|
147 | 146 | if match: |
|
148 | 147 | user_attrs["firstname"] = match.group('first_name') |
|
149 | 148 | user_attrs["lastname"] = match.group('last_name') |
|
150 | 149 | except Exception: |
|
151 | 150 | log.warning("Cannot extract additional info for PAM user") |
|
152 | 151 | pass |
|
153 | 152 | |
|
154 |
log.debug("pamuser: |
|
|
153 | log.debug("pamuser: %s", user_attrs) | |
|
155 | 154 | log.info('user %s authenticated correctly' % user_attrs['username']) |
|
156 | 155 | return user_attrs |
@@ -1,440 +1,441 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | |
|
3 | 3 | # Copyright (C) 2014-2016 RhodeCode GmbH |
|
4 | 4 | # |
|
5 | 5 | # This program is free software: you can redistribute it and/or modify |
|
6 | 6 | # it under the terms of the GNU Affero General Public License, version 3 |
|
7 | 7 | # (only), as published by the Free Software Foundation. |
|
8 | 8 | # |
|
9 | 9 | # This program is distributed in the hope that it will be useful, |
|
10 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 | 12 | # GNU General Public License for more details. |
|
13 | 13 | # |
|
14 | 14 | # You should have received a copy of the GNU Affero General Public License |
|
15 | 15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | 16 | # |
|
17 | 17 | # This program is dual-licensed. If you wish to learn more about the |
|
18 | 18 | # RhodeCode Enterprise Edition, including its added features, Support services, |
|
19 | 19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ |
|
20 | 20 | |
|
21 | 21 | """ |
|
22 | 22 | SimpleVCS middleware for handling protocol request (push/clone etc.) |
|
23 | 23 | It's implemented with basic auth function |
|
24 | 24 | """ |
|
25 | 25 | |
|
26 | 26 | import os |
|
27 | 27 | import logging |
|
28 | 28 | import importlib |
|
29 | 29 | from functools import wraps |
|
30 | 30 | |
|
31 | 31 | from paste.httpheaders import REMOTE_USER, AUTH_TYPE |
|
32 | 32 | from webob.exc import ( |
|
33 | 33 | HTTPNotFound, HTTPForbidden, HTTPNotAcceptable, HTTPInternalServerError) |
|
34 | 34 | |
|
35 | 35 | import rhodecode |
|
36 | 36 | from rhodecode.authentication.base import authenticate, VCS_TYPE |
|
37 | 37 | from rhodecode.lib.auth import AuthUser, HasPermissionAnyMiddleware |
|
38 | 38 | from rhodecode.lib.base import BasicAuth, get_ip_addr, vcs_operation_context |
|
39 | 39 | from rhodecode.lib.exceptions import ( |
|
40 | 40 | HTTPLockedRC, HTTPRequirementError, UserCreationError, |
|
41 | 41 | NotAllowedToCreateUserError) |
|
42 | 42 | from rhodecode.lib.hooks_daemon import prepare_callback_daemon |
|
43 | 43 | from rhodecode.lib.middleware import appenlight |
|
44 | 44 | from rhodecode.lib.middleware.utils import scm_app |
|
45 | 45 | from rhodecode.lib.utils import is_valid_repo |
|
46 | 46 | from rhodecode.lib.utils2 import safe_str, fix_PATH, str2bool |
|
47 | 47 | from rhodecode.model import meta |
|
48 | 48 | from rhodecode.model.db import User, Repository |
|
49 | 49 | from rhodecode.model.scm import ScmModel |
|
50 | 50 | from rhodecode.model.settings import SettingsModel |
|
51 | 51 | |
|
52 | 52 | log = logging.getLogger(__name__) |
|
53 | 53 | |
|
54 | 54 | |
|
55 | 55 | def initialize_generator(factory): |
|
56 | 56 | """ |
|
57 | 57 | Initializes the returned generator by draining its first element. |
|
58 | 58 | |
|
59 | 59 | This can be used to give a generator an initializer, which is the code |
|
60 | 60 | up to the first yield statement. This decorator enforces that the first |
|
61 | 61 | produced element has the value ``"__init__"`` to make its special |
|
62 | 62 | purpose very explicit in the using code. |
|
63 | 63 | """ |
|
64 | 64 | |
|
65 | 65 | @wraps(factory) |
|
66 | 66 | def wrapper(*args, **kwargs): |
|
67 | 67 | gen = factory(*args, **kwargs) |
|
68 | 68 | try: |
|
69 | 69 | init = gen.next() |
|
70 | 70 | except StopIteration: |
|
71 | 71 | raise ValueError('Generator must yield at least one element.') |
|
72 | 72 | if init != "__init__": |
|
73 | 73 | raise ValueError('First yielded element must be "__init__".') |
|
74 | 74 | return gen |
|
75 | 75 | return wrapper |
|
76 | 76 | |
|
77 | 77 | |
|
78 | 78 | class SimpleVCS(object): |
|
79 | 79 | """Common functionality for SCM HTTP handlers.""" |
|
80 | 80 | |
|
81 | 81 | SCM = 'unknown' |
|
82 | 82 | |
|
83 | 83 | def __init__(self, application, config): |
|
84 | 84 | self.application = application |
|
85 | 85 | self.config = config |
|
86 | 86 | # base path of repo locations |
|
87 | 87 | self.basepath = self.config['base_path'] |
|
88 | 88 | # authenticate this VCS request using authfunc |
|
89 | 89 | auth_ret_code_detection = \ |
|
90 | 90 | str2bool(self.config.get('auth_ret_code_detection', False)) |
|
91 | 91 | self.authenticate = BasicAuth('', authenticate, |
|
92 | 92 | config.get('auth_ret_code'), |
|
93 | 93 | auth_ret_code_detection) |
|
94 | 94 | self.ip_addr = '0.0.0.0' |
|
95 | 95 | |
|
96 | 96 | @property |
|
97 | 97 | def scm_app(self): |
|
98 | 98 | custom_implementation = self.config.get('vcs.scm_app_implementation') |
|
99 | 99 | if custom_implementation: |
|
100 | 100 | log.info( |
|
101 | 101 | "Using custom implementation of scm_app: %s", |
|
102 | 102 | custom_implementation) |
|
103 | 103 | scm_app_impl = importlib.import_module(custom_implementation) |
|
104 | 104 | else: |
|
105 | 105 | scm_app_impl = scm_app |
|
106 | 106 | return scm_app_impl |
|
107 | 107 | |
|
108 | 108 | def _get_by_id(self, repo_name): |
|
109 | 109 | """ |
|
110 | 110 | Gets a special pattern _<ID> from clone url and tries to replace it |
|
111 | 111 | with a repository_name for support of _<ID> non changable urls |
|
112 | 112 | |
|
113 | 113 | :param repo_name: |
|
114 | 114 | """ |
|
115 | 115 | |
|
116 | 116 | data = repo_name.split('/') |
|
117 | 117 | if len(data) >= 2: |
|
118 | 118 | from rhodecode.model.repo import RepoModel |
|
119 | 119 | by_id_match = RepoModel().get_repo_by_id(repo_name) |
|
120 | 120 | if by_id_match: |
|
121 | 121 | data[1] = by_id_match.repo_name |
|
122 | 122 | |
|
123 | 123 | return safe_str('/'.join(data)) |
|
124 | 124 | |
|
125 | 125 | def _invalidate_cache(self, repo_name): |
|
126 | 126 | """ |
|
127 | 127 | Set's cache for this repository for invalidation on next access |
|
128 | 128 | |
|
129 | 129 | :param repo_name: full repo name, also a cache key |
|
130 | 130 | """ |
|
131 | 131 | ScmModel().mark_for_invalidation(repo_name) |
|
132 | 132 | |
|
133 | 133 | def is_valid_and_existing_repo(self, repo_name, base_path, scm_type): |
|
134 | 134 | db_repo = Repository.get_by_repo_name(repo_name) |
|
135 | 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 | 138 | return False |
|
138 | 139 | |
|
139 | 140 | if db_repo.repo_type != scm_type: |
|
140 | 141 | log.warning( |
|
141 | 142 | 'Repository `%s` have incorrect scm_type, expected %s got %s', |
|
142 | 143 | repo_name, db_repo.repo_type, scm_type) |
|
143 | 144 | return False |
|
144 | 145 | |
|
145 | 146 | return is_valid_repo(repo_name, base_path, expect_scm=scm_type) |
|
146 | 147 | |
|
147 | 148 | def valid_and_active_user(self, user): |
|
148 | 149 | """ |
|
149 | 150 | Checks if that user is not empty, and if it's actually object it checks |
|
150 | 151 | if he's active. |
|
151 | 152 | |
|
152 | 153 | :param user: user object or None |
|
153 | 154 | :return: boolean |
|
154 | 155 | """ |
|
155 | 156 | if user is None: |
|
156 | 157 | return False |
|
157 | 158 | |
|
158 | 159 | elif user.active: |
|
159 | 160 | return True |
|
160 | 161 | |
|
161 | 162 | return False |
|
162 | 163 | |
|
163 | 164 | def _check_permission(self, action, user, repo_name, ip_addr=None): |
|
164 | 165 | """ |
|
165 | 166 | Checks permissions using action (push/pull) user and repository |
|
166 | 167 | name |
|
167 | 168 | |
|
168 | 169 | :param action: push or pull action |
|
169 | 170 | :param user: user instance |
|
170 | 171 | :param repo_name: repository name |
|
171 | 172 | """ |
|
172 | 173 | # check IP |
|
173 | 174 | inherit = user.inherit_default_permissions |
|
174 | 175 | ip_allowed = AuthUser.check_ip_allowed(user.user_id, ip_addr, |
|
175 | 176 | inherit_from_default=inherit) |
|
176 | 177 | if ip_allowed: |
|
177 | 178 | log.info('Access for IP:%s allowed', ip_addr) |
|
178 | 179 | else: |
|
179 | 180 | return False |
|
180 | 181 | |
|
181 | 182 | if action == 'push': |
|
182 | 183 | if not HasPermissionAnyMiddleware('repository.write', |
|
183 | 184 | 'repository.admin')(user, |
|
184 | 185 | repo_name): |
|
185 | 186 | return False |
|
186 | 187 | |
|
187 | 188 | else: |
|
188 | 189 | # any other action need at least read permission |
|
189 | 190 | if not HasPermissionAnyMiddleware('repository.read', |
|
190 | 191 | 'repository.write', |
|
191 | 192 | 'repository.admin')(user, |
|
192 | 193 | repo_name): |
|
193 | 194 | return False |
|
194 | 195 | |
|
195 | 196 | return True |
|
196 | 197 | |
|
197 | 198 | def _check_ssl(self, environ, start_response): |
|
198 | 199 | """ |
|
199 | 200 | Checks the SSL check flag and returns False if SSL is not present |
|
200 | 201 | and required True otherwise |
|
201 | 202 | """ |
|
202 | 203 | org_proto = environ['wsgi._org_proto'] |
|
203 | 204 | # check if we have SSL required ! if not it's a bad request ! |
|
204 | 205 | require_ssl = str2bool( |
|
205 | 206 | SettingsModel().get_ui_by_key('push_ssl').ui_value) |
|
206 | 207 | if require_ssl and org_proto == 'http': |
|
207 | 208 | log.debug('proto is %s and SSL is required BAD REQUEST !', |
|
208 | 209 | org_proto) |
|
209 | 210 | return False |
|
210 | 211 | return True |
|
211 | 212 | |
|
212 | 213 | def __call__(self, environ, start_response): |
|
213 | 214 | try: |
|
214 | 215 | return self._handle_request(environ, start_response) |
|
215 | 216 | except Exception: |
|
216 | 217 | log.exception("Exception while handling request") |
|
217 | 218 | appenlight.track_exception(environ) |
|
218 | 219 | return HTTPInternalServerError()(environ, start_response) |
|
219 | 220 | finally: |
|
220 | 221 | meta.Session.remove() |
|
221 | 222 | |
|
222 | 223 | def _handle_request(self, environ, start_response): |
|
223 | 224 | |
|
224 | 225 | if not self._check_ssl(environ, start_response): |
|
225 | 226 | reason = ('SSL required, while RhodeCode was unable ' |
|
226 | 227 | 'to detect this as SSL request') |
|
227 | 228 | log.debug('User not allowed to proceed, %s', reason) |
|
228 | 229 | return HTTPNotAcceptable(reason)(environ, start_response) |
|
229 | 230 | |
|
230 | 231 | ip_addr = get_ip_addr(environ) |
|
231 | 232 | username = None |
|
232 | 233 | |
|
233 | 234 | # skip passing error to error controller |
|
234 | 235 | environ['pylons.status_code_redirect'] = True |
|
235 | 236 | |
|
236 | 237 | # ====================================================================== |
|
237 | 238 | # EXTRACT REPOSITORY NAME FROM ENV |
|
238 | 239 | # ====================================================================== |
|
239 | 240 | environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO']) |
|
240 | 241 | repo_name = self._get_repository_name(environ) |
|
241 | 242 | environ['REPO_NAME'] = repo_name |
|
242 | 243 | log.debug('Extracted repo name is %s', repo_name) |
|
243 | 244 | |
|
244 | 245 | # check for type, presence in database and on filesystem |
|
245 | 246 | if not self.is_valid_and_existing_repo( |
|
246 | 247 | repo_name, self.basepath, self.SCM): |
|
247 | 248 | return HTTPNotFound()(environ, start_response) |
|
248 | 249 | |
|
249 | 250 | # ====================================================================== |
|
250 | 251 | # GET ACTION PULL or PUSH |
|
251 | 252 | # ====================================================================== |
|
252 | 253 | action = self._get_action(environ) |
|
253 | 254 | |
|
254 | 255 | # ====================================================================== |
|
255 | 256 | # CHECK ANONYMOUS PERMISSION |
|
256 | 257 | # ====================================================================== |
|
257 | 258 | if action in ['pull', 'push']: |
|
258 | 259 | anonymous_user = User.get_default_user() |
|
259 | 260 | username = anonymous_user.username |
|
260 | 261 | if anonymous_user.active: |
|
261 | 262 | # ONLY check permissions if the user is activated |
|
262 | 263 | anonymous_perm = self._check_permission( |
|
263 | 264 | action, anonymous_user, repo_name, ip_addr) |
|
264 | 265 | else: |
|
265 | 266 | anonymous_perm = False |
|
266 | 267 | |
|
267 | 268 | if not anonymous_user.active or not anonymous_perm: |
|
268 | 269 | if not anonymous_user.active: |
|
269 | 270 | log.debug('Anonymous access is disabled, running ' |
|
270 | 271 | 'authentication') |
|
271 | 272 | |
|
272 | 273 | if not anonymous_perm: |
|
273 | 274 | log.debug('Not enough credentials to access this ' |
|
274 | 275 | 'repository as anonymous user') |
|
275 | 276 | |
|
276 | 277 | username = None |
|
277 | 278 | # ============================================================== |
|
278 | 279 | # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE |
|
279 | 280 | # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS |
|
280 | 281 | # ============================================================== |
|
281 | 282 | |
|
282 | 283 | # try to auth based on environ, container auth methods |
|
283 | 284 | log.debug('Running PRE-AUTH for container based authentication') |
|
284 | 285 | pre_auth = authenticate('', '', environ,VCS_TYPE) |
|
285 | 286 | if pre_auth and pre_auth.get('username'): |
|
286 | 287 | username = pre_auth['username'] |
|
287 | 288 | log.debug('PRE-AUTH got %s as username', username) |
|
288 | 289 | |
|
289 | 290 | # If not authenticated by the container, running basic auth |
|
290 | 291 | if not username: |
|
291 | 292 | self.authenticate.realm = \ |
|
292 | 293 | safe_str(self.config['rhodecode_realm']) |
|
293 | 294 | |
|
294 | 295 | try: |
|
295 | 296 | result = self.authenticate(environ) |
|
296 | 297 | except (UserCreationError, NotAllowedToCreateUserError) as e: |
|
297 | 298 | log.error(e) |
|
298 | 299 | reason = safe_str(e) |
|
299 | 300 | return HTTPNotAcceptable(reason)(environ, start_response) |
|
300 | 301 | |
|
301 | 302 | if isinstance(result, str): |
|
302 | 303 | AUTH_TYPE.update(environ, 'basic') |
|
303 | 304 | REMOTE_USER.update(environ, result) |
|
304 | 305 | username = result |
|
305 | 306 | else: |
|
306 | 307 | return result.wsgi_application(environ, start_response) |
|
307 | 308 | |
|
308 | 309 | # ============================================================== |
|
309 | 310 | # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME |
|
310 | 311 | # ============================================================== |
|
311 | 312 | user = User.get_by_username(username) |
|
312 | 313 | if not self.valid_and_active_user(user): |
|
313 | 314 | return HTTPForbidden()(environ, start_response) |
|
314 | 315 | username = user.username |
|
315 | 316 | user.update_lastactivity() |
|
316 | 317 | meta.Session().commit() |
|
317 | 318 | |
|
318 | 319 | # check user attributes for password change flag |
|
319 | 320 | user_obj = user |
|
320 | 321 | if user_obj and user_obj.username != User.DEFAULT_USER and \ |
|
321 | 322 | user_obj.user_data.get('force_password_change'): |
|
322 | 323 | reason = 'password change required' |
|
323 | 324 | log.debug('User not allowed to authenticate, %s', reason) |
|
324 | 325 | return HTTPNotAcceptable(reason)(environ, start_response) |
|
325 | 326 | |
|
326 | 327 | # check permissions for this repository |
|
327 | 328 | perm = self._check_permission(action, user, repo_name, ip_addr) |
|
328 | 329 | if not perm: |
|
329 | 330 | return HTTPForbidden()(environ, start_response) |
|
330 | 331 | |
|
331 | 332 | # extras are injected into UI object and later available |
|
332 | 333 | # in hooks executed by rhodecode |
|
333 | 334 | check_locking = _should_check_locking(environ.get('QUERY_STRING')) |
|
334 | 335 | extras = vcs_operation_context( |
|
335 | 336 | environ, repo_name=repo_name, username=username, |
|
336 | 337 | action=action, scm=self.SCM, |
|
337 | 338 | check_locking=check_locking) |
|
338 | 339 | |
|
339 | 340 | # ====================================================================== |
|
340 | 341 | # REQUEST HANDLING |
|
341 | 342 | # ====================================================================== |
|
342 | 343 | str_repo_name = safe_str(repo_name) |
|
343 | 344 | repo_path = os.path.join(safe_str(self.basepath), str_repo_name) |
|
344 | 345 | log.debug('Repository path is %s', repo_path) |
|
345 | 346 | |
|
346 | 347 | fix_PATH() |
|
347 | 348 | |
|
348 | 349 | log.info( |
|
349 | 350 | '%s action on %s repo "%s" by "%s" from %s', |
|
350 | 351 | action, self.SCM, str_repo_name, safe_str(username), ip_addr) |
|
351 | 352 | return self._generate_vcs_response( |
|
352 | 353 | environ, start_response, repo_path, repo_name, extras, action) |
|
353 | 354 | |
|
354 | 355 | @initialize_generator |
|
355 | 356 | def _generate_vcs_response( |
|
356 | 357 | self, environ, start_response, repo_path, repo_name, extras, |
|
357 | 358 | action): |
|
358 | 359 | """ |
|
359 | 360 | Returns a generator for the response content. |
|
360 | 361 | |
|
361 | 362 | This method is implemented as a generator, so that it can trigger |
|
362 | 363 | the cache validation after all content sent back to the client. It |
|
363 | 364 | also handles the locking exceptions which will be triggered when |
|
364 | 365 | the first chunk is produced by the underlying WSGI application. |
|
365 | 366 | """ |
|
366 | 367 | callback_daemon, extras = self._prepare_callback_daemon(extras) |
|
367 | 368 | config = self._create_config(extras, repo_name) |
|
368 | 369 | log.debug('HOOKS extras is %s', extras) |
|
369 | 370 | app = self._create_wsgi_app(repo_path, repo_name, config) |
|
370 | 371 | |
|
371 | 372 | try: |
|
372 | 373 | with callback_daemon: |
|
373 | 374 | try: |
|
374 | 375 | response = app(environ, start_response) |
|
375 | 376 | finally: |
|
376 | 377 | # This statement works together with the decorator |
|
377 | 378 | # "initialize_generator" above. The decorator ensures that |
|
378 | 379 | # we hit the first yield statement before the generator is |
|
379 | 380 | # returned back to the WSGI server. This is needed to |
|
380 | 381 | # ensure that the call to "app" above triggers the |
|
381 | 382 | # needed callback to "start_response" before the |
|
382 | 383 | # generator is actually used. |
|
383 | 384 | yield "__init__" |
|
384 | 385 | |
|
385 | 386 | for chunk in response: |
|
386 | 387 | yield chunk |
|
387 | 388 | except Exception as exc: |
|
388 | 389 | # TODO: johbo: Improve "translating" back the exception. |
|
389 | 390 | if getattr(exc, '_vcs_kind', None) == 'repo_locked': |
|
390 | 391 | exc = HTTPLockedRC(*exc.args) |
|
391 | 392 | _code = rhodecode.CONFIG.get('lock_ret_code') |
|
392 | 393 | log.debug('Repository LOCKED ret code %s!', (_code,)) |
|
393 | 394 | elif getattr(exc, '_vcs_kind', None) == 'requirement': |
|
394 | 395 | log.debug( |
|
395 | 396 | 'Repository requires features unknown to this Mercurial') |
|
396 | 397 | exc = HTTPRequirementError(*exc.args) |
|
397 | 398 | else: |
|
398 | 399 | raise |
|
399 | 400 | |
|
400 | 401 | for chunk in exc(environ, start_response): |
|
401 | 402 | yield chunk |
|
402 | 403 | finally: |
|
403 | 404 | # invalidate cache on push |
|
404 | 405 | if action == 'push': |
|
405 | 406 | self._invalidate_cache(repo_name) |
|
406 | 407 | |
|
407 | 408 | def _get_repository_name(self, environ): |
|
408 | 409 | """Get repository name out of the environmnent |
|
409 | 410 | |
|
410 | 411 | :param environ: WSGI environment |
|
411 | 412 | """ |
|
412 | 413 | raise NotImplementedError() |
|
413 | 414 | |
|
414 | 415 | def _get_action(self, environ): |
|
415 | 416 | """Map request commands into a pull or push command. |
|
416 | 417 | |
|
417 | 418 | :param environ: WSGI environment |
|
418 | 419 | """ |
|
419 | 420 | raise NotImplementedError() |
|
420 | 421 | |
|
421 | 422 | def _create_wsgi_app(self, repo_path, repo_name, config): |
|
422 | 423 | """Return the WSGI app that will finally handle the request.""" |
|
423 | 424 | raise NotImplementedError() |
|
424 | 425 | |
|
425 | 426 | def _create_config(self, extras, repo_name): |
|
426 | 427 | """Create a Pyro safe config representation.""" |
|
427 | 428 | raise NotImplementedError() |
|
428 | 429 | |
|
429 | 430 | def _prepare_callback_daemon(self, extras): |
|
430 | 431 | return prepare_callback_daemon( |
|
431 | 432 | extras, protocol=self.config.get('vcs.hooks.protocol'), |
|
432 | 433 | use_direct_calls=self.config.get('vcs.hooks.direct_calls')) |
|
433 | 434 | |
|
434 | 435 | |
|
435 | 436 | def _should_check_locking(query_string): |
|
436 | 437 | # this is kind of hacky, but due to how mercurial handles client-server |
|
437 | 438 | # server see all operation on commit; bookmarks, phases and |
|
438 | 439 | # obsolescence marker in different transaction, we don't want to check |
|
439 | 440 | # locking on those |
|
440 | 441 | return query_string not in ['cmd=listkeys'] |
@@ -1,1128 +1,1130 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | |
|
3 | 3 | # Copyright (C) 2012-2016 RhodeCode GmbH |
|
4 | 4 | # |
|
5 | 5 | # This program is free software: you can redistribute it and/or modify |
|
6 | 6 | # it under the terms of the GNU Affero General Public License, version 3 |
|
7 | 7 | # (only), as published by the Free Software Foundation. |
|
8 | 8 | # |
|
9 | 9 | # This program is distributed in the hope that it will be useful, |
|
10 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 | 12 | # GNU General Public License for more details. |
|
13 | 13 | # |
|
14 | 14 | # You should have received a copy of the GNU Affero General Public License |
|
15 | 15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | 16 | # |
|
17 | 17 | # This program is dual-licensed. If you wish to learn more about the |
|
18 | 18 | # RhodeCode Enterprise Edition, including its added features, Support services, |
|
19 | 19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ |
|
20 | 20 | |
|
21 | 21 | |
|
22 | 22 | """ |
|
23 | 23 | pull request model for RhodeCode |
|
24 | 24 | """ |
|
25 | 25 | |
|
26 | 26 | from collections import namedtuple |
|
27 | 27 | import json |
|
28 | 28 | import logging |
|
29 | 29 | import datetime |
|
30 | 30 | |
|
31 | 31 | from pylons.i18n.translation import _ |
|
32 | 32 | from pylons.i18n.translation import lazy_ugettext |
|
33 | 33 | |
|
34 | 34 | import rhodecode |
|
35 | 35 | from rhodecode.lib import helpers as h, hooks_utils, diffs |
|
36 | 36 | from rhodecode.lib.compat import OrderedDict |
|
37 | 37 | from rhodecode.lib.hooks_daemon import prepare_callback_daemon |
|
38 | 38 | from rhodecode.lib.markup_renderer import ( |
|
39 | 39 | DEFAULT_COMMENTS_RENDERER, RstTemplateRenderer) |
|
40 | 40 | from rhodecode.lib.utils import action_logger |
|
41 | 41 | from rhodecode.lib.utils2 import safe_unicode, safe_str, md5_safe |
|
42 | 42 | from rhodecode.lib.vcs.backends.base import ( |
|
43 | 43 | Reference, MergeResponse, MergeFailureReason) |
|
44 | 44 | from rhodecode.lib.vcs.exceptions import ( |
|
45 | 45 | CommitDoesNotExistError, EmptyRepositoryError) |
|
46 | 46 | from rhodecode.model import BaseModel |
|
47 | 47 | from rhodecode.model.changeset_status import ChangesetStatusModel |
|
48 | 48 | from rhodecode.model.comment import ChangesetCommentsModel |
|
49 | 49 | from rhodecode.model.db import ( |
|
50 | 50 | PullRequest, PullRequestReviewers, Notification, ChangesetStatus, |
|
51 | 51 | PullRequestVersion, ChangesetComment) |
|
52 | 52 | from rhodecode.model.meta import Session |
|
53 | 53 | from rhodecode.model.notification import NotificationModel, \ |
|
54 | 54 | EmailNotificationModel |
|
55 | 55 | from rhodecode.model.scm import ScmModel |
|
56 | 56 | from rhodecode.model.settings import VcsSettingsModel |
|
57 | 57 | |
|
58 | 58 | |
|
59 | 59 | log = logging.getLogger(__name__) |
|
60 | 60 | |
|
61 | 61 | |
|
62 | 62 | class PullRequestModel(BaseModel): |
|
63 | 63 | |
|
64 | 64 | cls = PullRequest |
|
65 | 65 | |
|
66 | 66 | DIFF_CONTEXT = 3 |
|
67 | 67 | |
|
68 | 68 | MERGE_STATUS_MESSAGES = { |
|
69 | 69 | MergeFailureReason.NONE: lazy_ugettext( |
|
70 | 70 | 'This pull request can be automatically merged.'), |
|
71 | 71 | MergeFailureReason.UNKNOWN: lazy_ugettext( |
|
72 | 72 | 'This pull request cannot be merged because of an unhandled' |
|
73 | 73 | ' exception.'), |
|
74 | 74 | MergeFailureReason.MERGE_FAILED: lazy_ugettext( |
|
75 | 75 | 'This pull request cannot be merged because of conflicts.'), |
|
76 | 76 | MergeFailureReason.PUSH_FAILED: lazy_ugettext( |
|
77 | 77 | 'This pull request could not be merged because push to target' |
|
78 | 78 | ' failed.'), |
|
79 | 79 | MergeFailureReason.TARGET_IS_NOT_HEAD: lazy_ugettext( |
|
80 | 80 | 'This pull request cannot be merged because the target is not a' |
|
81 | 81 | ' head.'), |
|
82 | 82 | MergeFailureReason.HG_SOURCE_HAS_MORE_BRANCHES: lazy_ugettext( |
|
83 | 83 | 'This pull request cannot be merged because the source contains' |
|
84 | 84 | ' more branches than the target.'), |
|
85 | 85 | MergeFailureReason.HG_TARGET_HAS_MULTIPLE_HEADS: lazy_ugettext( |
|
86 | 86 | 'This pull request cannot be merged because the target has' |
|
87 | 87 | ' multiple heads.'), |
|
88 | 88 | MergeFailureReason.TARGET_IS_LOCKED: lazy_ugettext( |
|
89 | 89 | 'This pull request cannot be merged because the target repository' |
|
90 | 90 | ' is locked.'), |
|
91 | 91 | MergeFailureReason.MISSING_COMMIT: lazy_ugettext( |
|
92 | 92 | 'This pull request cannot be merged because the target or the ' |
|
93 | 93 | 'source reference is missing.'), |
|
94 | 94 | } |
|
95 | 95 | |
|
96 | 96 | def __get_pull_request(self, pull_request): |
|
97 | 97 | return self._get_instance(PullRequest, pull_request) |
|
98 | 98 | |
|
99 | 99 | def _check_perms(self, perms, pull_request, user, api=False): |
|
100 | 100 | if not api: |
|
101 | 101 | return h.HasRepoPermissionAny(*perms)( |
|
102 | 102 | user=user, repo_name=pull_request.target_repo.repo_name) |
|
103 | 103 | else: |
|
104 | 104 | return h.HasRepoPermissionAnyApi(*perms)( |
|
105 | 105 | user=user, repo_name=pull_request.target_repo.repo_name) |
|
106 | 106 | |
|
107 | 107 | def check_user_read(self, pull_request, user, api=False): |
|
108 | 108 | _perms = ('repository.admin', 'repository.write', 'repository.read',) |
|
109 | 109 | return self._check_perms(_perms, pull_request, user, api) |
|
110 | 110 | |
|
111 | 111 | def check_user_merge(self, pull_request, user, api=False): |
|
112 | 112 | _perms = ('repository.admin', 'repository.write', 'hg.admin',) |
|
113 | 113 | return self._check_perms(_perms, pull_request, user, api) |
|
114 | 114 | |
|
115 | 115 | def check_user_update(self, pull_request, user, api=False): |
|
116 | 116 | owner = user.user_id == pull_request.user_id |
|
117 | 117 | return self.check_user_merge(pull_request, user, api) or owner |
|
118 | 118 | |
|
119 | 119 | def check_user_change_status(self, pull_request, user, api=False): |
|
120 | 120 | reviewer = user.user_id in [x.user_id for x in |
|
121 | 121 | pull_request.reviewers] |
|
122 | 122 | return self.check_user_update(pull_request, user, api) or reviewer |
|
123 | 123 | |
|
124 | 124 | def get(self, pull_request): |
|
125 | 125 | return self.__get_pull_request(pull_request) |
|
126 | 126 | |
|
127 | 127 | def _prepare_get_all_query(self, repo_name, source=False, statuses=None, |
|
128 | 128 | opened_by=None, order_by=None, |
|
129 | 129 | order_dir='desc'): |
|
130 | 130 | repo = self._get_repo(repo_name) |
|
131 | 131 | q = PullRequest.query() |
|
132 | 132 | # source or target |
|
133 | 133 | if source: |
|
134 | 134 | q = q.filter(PullRequest.source_repo == repo) |
|
135 | 135 | else: |
|
136 | 136 | q = q.filter(PullRequest.target_repo == repo) |
|
137 | 137 | |
|
138 | 138 | # closed,opened |
|
139 | 139 | if statuses: |
|
140 | 140 | q = q.filter(PullRequest.status.in_(statuses)) |
|
141 | 141 | |
|
142 | 142 | # opened by filter |
|
143 | 143 | if opened_by: |
|
144 | 144 | q = q.filter(PullRequest.user_id.in_(opened_by)) |
|
145 | 145 | |
|
146 | 146 | if order_by: |
|
147 | 147 | order_map = { |
|
148 | 148 | 'name_raw': PullRequest.pull_request_id, |
|
149 | 149 | 'title': PullRequest.title, |
|
150 | 150 | 'updated_on_raw': PullRequest.updated_on |
|
151 | 151 | } |
|
152 | 152 | if order_dir == 'asc': |
|
153 | 153 | q = q.order_by(order_map[order_by].asc()) |
|
154 | 154 | else: |
|
155 | 155 | q = q.order_by(order_map[order_by].desc()) |
|
156 | 156 | |
|
157 | 157 | return q |
|
158 | 158 | |
|
159 | 159 | def count_all(self, repo_name, source=False, statuses=None, |
|
160 | 160 | opened_by=None): |
|
161 | 161 | """ |
|
162 | 162 | Count the number of pull requests for a specific repository. |
|
163 | 163 | |
|
164 | 164 | :param repo_name: target or source repo |
|
165 | 165 | :param source: boolean flag to specify if repo_name refers to source |
|
166 | 166 | :param statuses: list of pull request statuses |
|
167 | 167 | :param opened_by: author user of the pull request |
|
168 | 168 | :returns: int number of pull requests |
|
169 | 169 | """ |
|
170 | 170 | q = self._prepare_get_all_query( |
|
171 | 171 | repo_name, source=source, statuses=statuses, opened_by=opened_by) |
|
172 | 172 | |
|
173 | 173 | return q.count() |
|
174 | 174 | |
|
175 | 175 | def get_all(self, repo_name, source=False, statuses=None, opened_by=None, |
|
176 | 176 | offset=0, length=None, order_by=None, order_dir='desc'): |
|
177 | 177 | """ |
|
178 | 178 | Get all pull requests for a specific repository. |
|
179 | 179 | |
|
180 | 180 | :param repo_name: target or source repo |
|
181 | 181 | :param source: boolean flag to specify if repo_name refers to source |
|
182 | 182 | :param statuses: list of pull request statuses |
|
183 | 183 | :param opened_by: author user of the pull request |
|
184 | 184 | :param offset: pagination offset |
|
185 | 185 | :param length: length of returned list |
|
186 | 186 | :param order_by: order of the returned list |
|
187 | 187 | :param order_dir: 'asc' or 'desc' ordering direction |
|
188 | 188 | :returns: list of pull requests |
|
189 | 189 | """ |
|
190 | 190 | q = self._prepare_get_all_query( |
|
191 | 191 | repo_name, source=source, statuses=statuses, opened_by=opened_by, |
|
192 | 192 | order_by=order_by, order_dir=order_dir) |
|
193 | 193 | |
|
194 | 194 | if length: |
|
195 | 195 | pull_requests = q.limit(length).offset(offset).all() |
|
196 | 196 | else: |
|
197 | 197 | pull_requests = q.all() |
|
198 | 198 | |
|
199 | 199 | return pull_requests |
|
200 | 200 | |
|
201 | 201 | def count_awaiting_review(self, repo_name, source=False, statuses=None, |
|
202 | 202 | opened_by=None): |
|
203 | 203 | """ |
|
204 | 204 | Count the number of pull requests for a specific repository that are |
|
205 | 205 | awaiting review. |
|
206 | 206 | |
|
207 | 207 | :param repo_name: target or source repo |
|
208 | 208 | :param source: boolean flag to specify if repo_name refers to source |
|
209 | 209 | :param statuses: list of pull request statuses |
|
210 | 210 | :param opened_by: author user of the pull request |
|
211 | 211 | :returns: int number of pull requests |
|
212 | 212 | """ |
|
213 | 213 | pull_requests = self.get_awaiting_review( |
|
214 | 214 | repo_name, source=source, statuses=statuses, opened_by=opened_by) |
|
215 | 215 | |
|
216 | 216 | return len(pull_requests) |
|
217 | 217 | |
|
218 | 218 | def get_awaiting_review(self, repo_name, source=False, statuses=None, |
|
219 | 219 | opened_by=None, offset=0, length=None, |
|
220 | 220 | order_by=None, order_dir='desc'): |
|
221 | 221 | """ |
|
222 | 222 | Get all pull requests for a specific repository that are awaiting |
|
223 | 223 | review. |
|
224 | 224 | |
|
225 | 225 | :param repo_name: target or source repo |
|
226 | 226 | :param source: boolean flag to specify if repo_name refers to source |
|
227 | 227 | :param statuses: list of pull request statuses |
|
228 | 228 | :param opened_by: author user of the pull request |
|
229 | 229 | :param offset: pagination offset |
|
230 | 230 | :param length: length of returned list |
|
231 | 231 | :param order_by: order of the returned list |
|
232 | 232 | :param order_dir: 'asc' or 'desc' ordering direction |
|
233 | 233 | :returns: list of pull requests |
|
234 | 234 | """ |
|
235 | 235 | pull_requests = self.get_all( |
|
236 | 236 | repo_name, source=source, statuses=statuses, opened_by=opened_by, |
|
237 | 237 | order_by=order_by, order_dir=order_dir) |
|
238 | 238 | |
|
239 | 239 | _filtered_pull_requests = [] |
|
240 | 240 | for pr in pull_requests: |
|
241 | 241 | status = pr.calculated_review_status() |
|
242 | 242 | if status in [ChangesetStatus.STATUS_NOT_REVIEWED, |
|
243 | 243 | ChangesetStatus.STATUS_UNDER_REVIEW]: |
|
244 | 244 | _filtered_pull_requests.append(pr) |
|
245 | 245 | if length: |
|
246 | 246 | return _filtered_pull_requests[offset:offset+length] |
|
247 | 247 | else: |
|
248 | 248 | return _filtered_pull_requests |
|
249 | 249 | |
|
250 | 250 | def count_awaiting_my_review(self, repo_name, source=False, statuses=None, |
|
251 | 251 | opened_by=None, user_id=None): |
|
252 | 252 | """ |
|
253 | 253 | Count the number of pull requests for a specific repository that are |
|
254 | 254 | awaiting review from a specific user. |
|
255 | 255 | |
|
256 | 256 | :param repo_name: target or source repo |
|
257 | 257 | :param source: boolean flag to specify if repo_name refers to source |
|
258 | 258 | :param statuses: list of pull request statuses |
|
259 | 259 | :param opened_by: author user of the pull request |
|
260 | 260 | :param user_id: reviewer user of the pull request |
|
261 | 261 | :returns: int number of pull requests |
|
262 | 262 | """ |
|
263 | 263 | pull_requests = self.get_awaiting_my_review( |
|
264 | 264 | repo_name, source=source, statuses=statuses, opened_by=opened_by, |
|
265 | 265 | user_id=user_id) |
|
266 | 266 | |
|
267 | 267 | return len(pull_requests) |
|
268 | 268 | |
|
269 | 269 | def get_awaiting_my_review(self, repo_name, source=False, statuses=None, |
|
270 | 270 | opened_by=None, user_id=None, offset=0, |
|
271 | 271 | length=None, order_by=None, order_dir='desc'): |
|
272 | 272 | """ |
|
273 | 273 | Get all pull requests for a specific repository that are awaiting |
|
274 | 274 | review from a specific user. |
|
275 | 275 | |
|
276 | 276 | :param repo_name: target or source repo |
|
277 | 277 | :param source: boolean flag to specify if repo_name refers to source |
|
278 | 278 | :param statuses: list of pull request statuses |
|
279 | 279 | :param opened_by: author user of the pull request |
|
280 | 280 | :param user_id: reviewer user of the pull request |
|
281 | 281 | :param offset: pagination offset |
|
282 | 282 | :param length: length of returned list |
|
283 | 283 | :param order_by: order of the returned list |
|
284 | 284 | :param order_dir: 'asc' or 'desc' ordering direction |
|
285 | 285 | :returns: list of pull requests |
|
286 | 286 | """ |
|
287 | 287 | pull_requests = self.get_all( |
|
288 | 288 | repo_name, source=source, statuses=statuses, opened_by=opened_by, |
|
289 | 289 | order_by=order_by, order_dir=order_dir) |
|
290 | 290 | |
|
291 | 291 | _my = PullRequestModel().get_not_reviewed(user_id) |
|
292 | 292 | my_participation = [] |
|
293 | 293 | for pr in pull_requests: |
|
294 | 294 | if pr in _my: |
|
295 | 295 | my_participation.append(pr) |
|
296 | 296 | _filtered_pull_requests = my_participation |
|
297 | 297 | if length: |
|
298 | 298 | return _filtered_pull_requests[offset:offset+length] |
|
299 | 299 | else: |
|
300 | 300 | return _filtered_pull_requests |
|
301 | 301 | |
|
302 | 302 | def get_not_reviewed(self, user_id): |
|
303 | 303 | return [ |
|
304 | 304 | x.pull_request for x in PullRequestReviewers.query().filter( |
|
305 | 305 | PullRequestReviewers.user_id == user_id).all() |
|
306 | 306 | ] |
|
307 | 307 | |
|
308 | 308 | def get_versions(self, pull_request): |
|
309 | 309 | """ |
|
310 | 310 | returns version of pull request sorted by ID descending |
|
311 | 311 | """ |
|
312 | 312 | return PullRequestVersion.query()\ |
|
313 | 313 | .filter(PullRequestVersion.pull_request == pull_request)\ |
|
314 | 314 | .order_by(PullRequestVersion.pull_request_version_id.asc())\ |
|
315 | 315 | .all() |
|
316 | 316 | |
|
317 | 317 | def create(self, created_by, source_repo, source_ref, target_repo, |
|
318 | 318 | target_ref, revisions, reviewers, title, description=None): |
|
319 | 319 | created_by_user = self._get_user(created_by) |
|
320 | 320 | source_repo = self._get_repo(source_repo) |
|
321 | 321 | target_repo = self._get_repo(target_repo) |
|
322 | 322 | |
|
323 | 323 | pull_request = PullRequest() |
|
324 | 324 | pull_request.source_repo = source_repo |
|
325 | 325 | pull_request.source_ref = source_ref |
|
326 | 326 | pull_request.target_repo = target_repo |
|
327 | 327 | pull_request.target_ref = target_ref |
|
328 | 328 | pull_request.revisions = revisions |
|
329 | 329 | pull_request.title = title |
|
330 | 330 | pull_request.description = description |
|
331 | 331 | pull_request.author = created_by_user |
|
332 | 332 | |
|
333 | 333 | Session().add(pull_request) |
|
334 | 334 | Session().flush() |
|
335 | 335 | |
|
336 | 336 | # members / reviewers |
|
337 | 337 | for user_id in set(reviewers): |
|
338 | 338 | user = self._get_user(user_id) |
|
339 | 339 | reviewer = PullRequestReviewers(user, pull_request) |
|
340 | 340 | Session().add(reviewer) |
|
341 | 341 | |
|
342 | 342 | # Set approval status to "Under Review" for all commits which are |
|
343 | 343 | # part of this pull request. |
|
344 | 344 | ChangesetStatusModel().set_status( |
|
345 | 345 | repo=target_repo, |
|
346 | 346 | status=ChangesetStatus.STATUS_UNDER_REVIEW, |
|
347 | 347 | user=created_by_user, |
|
348 | 348 | pull_request=pull_request |
|
349 | 349 | ) |
|
350 | 350 | |
|
351 | 351 | self.notify_reviewers(pull_request, reviewers) |
|
352 | 352 | self._trigger_pull_request_hook( |
|
353 | 353 | pull_request, created_by_user, 'create') |
|
354 | 354 | |
|
355 | 355 | return pull_request |
|
356 | 356 | |
|
357 | 357 | def _trigger_pull_request_hook(self, pull_request, user, action): |
|
358 | 358 | pull_request = self.__get_pull_request(pull_request) |
|
359 | 359 | target_scm = pull_request.target_repo.scm_instance() |
|
360 | 360 | if action == 'create': |
|
361 | 361 | trigger_hook = hooks_utils.trigger_log_create_pull_request_hook |
|
362 | 362 | elif action == 'merge': |
|
363 | 363 | trigger_hook = hooks_utils.trigger_log_merge_pull_request_hook |
|
364 | 364 | elif action == 'close': |
|
365 | 365 | trigger_hook = hooks_utils.trigger_log_close_pull_request_hook |
|
366 | 366 | elif action == 'review_status_change': |
|
367 | 367 | trigger_hook = hooks_utils.trigger_log_review_pull_request_hook |
|
368 | 368 | elif action == 'update': |
|
369 | 369 | trigger_hook = hooks_utils.trigger_log_update_pull_request_hook |
|
370 | 370 | else: |
|
371 | 371 | return |
|
372 | 372 | |
|
373 | 373 | trigger_hook( |
|
374 | 374 | username=user.username, |
|
375 | 375 | repo_name=pull_request.target_repo.repo_name, |
|
376 | 376 | repo_alias=target_scm.alias, |
|
377 | 377 | pull_request=pull_request) |
|
378 | 378 | |
|
379 | 379 | def _get_commit_ids(self, pull_request): |
|
380 | 380 | """ |
|
381 | 381 | Return the commit ids of the merged pull request. |
|
382 | 382 | |
|
383 | 383 | This method is not dealing correctly yet with the lack of autoupdates |
|
384 | 384 | nor with the implicit target updates. |
|
385 | 385 | For example: if a commit in the source repo is already in the target it |
|
386 | 386 | will be reported anyways. |
|
387 | 387 | """ |
|
388 | 388 | merge_rev = pull_request.merge_rev |
|
389 | 389 | if merge_rev is None: |
|
390 | 390 | raise ValueError('This pull request was not merged yet') |
|
391 | 391 | |
|
392 | 392 | commit_ids = list(pull_request.revisions) |
|
393 | 393 | if merge_rev not in commit_ids: |
|
394 | 394 | commit_ids.append(merge_rev) |
|
395 | 395 | |
|
396 | 396 | return commit_ids |
|
397 | 397 | |
|
398 | 398 | def merge(self, pull_request, user, extras): |
|
399 | 399 | merge_state = self._merge_pull_request(pull_request, user, extras) |
|
400 | 400 | if merge_state.executed: |
|
401 | 401 | self._comment_and_close_pr(pull_request, user, merge_state) |
|
402 | 402 | self._log_action('user_merged_pull_request', user, pull_request) |
|
403 | 403 | return merge_state |
|
404 | 404 | |
|
405 | 405 | def _merge_pull_request(self, pull_request, user, extras): |
|
406 | 406 | target_vcs = pull_request.target_repo.scm_instance() |
|
407 | 407 | source_vcs = pull_request.source_repo.scm_instance() |
|
408 | 408 | target_ref = self._refresh_reference( |
|
409 | 409 | pull_request.target_ref_parts, target_vcs) |
|
410 | 410 | |
|
411 | 411 | message = _( |
|
412 | 412 | 'Merge pull request #%(pr_id)s from ' |
|
413 | 413 | '%(source_repo)s %(source_ref_name)s\n\n %(pr_title)s') % { |
|
414 | 414 | 'pr_id': pull_request.pull_request_id, |
|
415 | 415 | 'source_repo': source_vcs.name, |
|
416 | 416 | 'source_ref_name': pull_request.source_ref_parts.name, |
|
417 | 417 | 'pr_title': pull_request.title |
|
418 | 418 | } |
|
419 | 419 | |
|
420 | 420 | workspace_id = self._workspace_id(pull_request) |
|
421 | 421 | protocol = rhodecode.CONFIG.get('vcs.hooks.protocol') |
|
422 | 422 | use_direct_calls = rhodecode.CONFIG.get('vcs.hooks.direct_calls') |
|
423 | 423 | |
|
424 | 424 | callback_daemon, extras = prepare_callback_daemon( |
|
425 | 425 | extras, protocol=protocol, use_direct_calls=use_direct_calls) |
|
426 | 426 | |
|
427 | 427 | with callback_daemon: |
|
428 | 428 | # TODO: johbo: Implement a clean way to run a config_override |
|
429 | 429 | # for a single call. |
|
430 | 430 | target_vcs.config.set( |
|
431 | 431 | 'rhodecode', 'RC_SCM_DATA', json.dumps(extras)) |
|
432 | 432 | merge_state = target_vcs.merge( |
|
433 | 433 | target_ref, source_vcs, pull_request.source_ref_parts, |
|
434 | 434 | workspace_id, user_name=user.username, |
|
435 | 435 | user_email=user.email, message=message) |
|
436 | 436 | return merge_state |
|
437 | 437 | |
|
438 | 438 | def _comment_and_close_pr(self, pull_request, user, merge_state): |
|
439 | 439 | pull_request.merge_rev = merge_state.merge_commit_id |
|
440 | 440 | pull_request.updated_on = datetime.datetime.now() |
|
441 | 441 | |
|
442 | 442 | ChangesetCommentsModel().create( |
|
443 | 443 | text=unicode(_('Pull request merged and closed')), |
|
444 | 444 | repo=pull_request.target_repo.repo_id, |
|
445 | 445 | user=user.user_id, |
|
446 | 446 | pull_request=pull_request.pull_request_id, |
|
447 | 447 | f_path=None, |
|
448 | 448 | line_no=None, |
|
449 | 449 | closing_pr=True |
|
450 | 450 | ) |
|
451 | 451 | |
|
452 | 452 | Session().add(pull_request) |
|
453 | 453 | Session().flush() |
|
454 | 454 | # TODO: paris: replace invalidation with less radical solution |
|
455 | 455 | ScmModel().mark_for_invalidation( |
|
456 | 456 | pull_request.target_repo.repo_name) |
|
457 | 457 | self._trigger_pull_request_hook(pull_request, user, 'merge') |
|
458 | 458 | |
|
459 | 459 | def has_valid_update_type(self, pull_request): |
|
460 | 460 | source_ref_type = pull_request.source_ref_parts.type |
|
461 | 461 | return source_ref_type in ['book', 'branch', 'tag'] |
|
462 | 462 | |
|
463 | 463 | def update_commits(self, pull_request): |
|
464 | 464 | """ |
|
465 | 465 | Get the updated list of commits for the pull request |
|
466 | 466 | and return the new pull request version and the list |
|
467 | 467 | of commits processed by this update action |
|
468 | 468 | """ |
|
469 | 469 | |
|
470 | 470 | pull_request = self.__get_pull_request(pull_request) |
|
471 | 471 | source_ref_type = pull_request.source_ref_parts.type |
|
472 | 472 | source_ref_name = pull_request.source_ref_parts.name |
|
473 | 473 | source_ref_id = pull_request.source_ref_parts.commit_id |
|
474 | 474 | |
|
475 | 475 | if not self.has_valid_update_type(pull_request): |
|
476 | 476 | log.debug( |
|
477 | 477 | "Skipping update of pull request %s due to ref type: %s", |
|
478 | 478 | pull_request, source_ref_type) |
|
479 | 479 | return (None, None) |
|
480 | 480 | |
|
481 | 481 | source_repo = pull_request.source_repo.scm_instance() |
|
482 | 482 | source_commit = source_repo.get_commit(commit_id=source_ref_name) |
|
483 | 483 | if source_ref_id == source_commit.raw_id: |
|
484 | 484 | log.debug("Nothing changed in pull request %s", pull_request) |
|
485 | 485 | return (None, None) |
|
486 | 486 | |
|
487 | 487 | # Finally there is a need for an update |
|
488 | 488 | pull_request_version = self._create_version_from_snapshot(pull_request) |
|
489 | 489 | self._link_comments_to_version(pull_request_version) |
|
490 | 490 | |
|
491 | 491 | target_ref_type = pull_request.target_ref_parts.type |
|
492 | 492 | target_ref_name = pull_request.target_ref_parts.name |
|
493 | 493 | target_ref_id = pull_request.target_ref_parts.commit_id |
|
494 | 494 | target_repo = pull_request.target_repo.scm_instance() |
|
495 | 495 | |
|
496 | 496 | if target_ref_type in ('tag', 'branch', 'book'): |
|
497 | 497 | target_commit = target_repo.get_commit(target_ref_name) |
|
498 | 498 | else: |
|
499 | 499 | target_commit = target_repo.get_commit(target_ref_id) |
|
500 | 500 | |
|
501 | 501 | # re-compute commit ids |
|
502 | 502 | old_commit_ids = set(pull_request.revisions) |
|
503 | 503 | pre_load = ["author", "branch", "date", "message"] |
|
504 | 504 | commit_ranges = target_repo.compare( |
|
505 | 505 | target_commit.raw_id, source_commit.raw_id, source_repo, merge=True, |
|
506 | 506 | pre_load=pre_load) |
|
507 | 507 | |
|
508 | 508 | ancestor = target_repo.get_common_ancestor( |
|
509 | 509 | target_commit.raw_id, source_commit.raw_id, source_repo) |
|
510 | 510 | |
|
511 | 511 | pull_request.source_ref = '%s:%s:%s' % ( |
|
512 | 512 | source_ref_type, source_ref_name, source_commit.raw_id) |
|
513 | 513 | pull_request.target_ref = '%s:%s:%s' % ( |
|
514 | 514 | target_ref_type, target_ref_name, ancestor) |
|
515 | 515 | pull_request.revisions = [ |
|
516 | 516 | commit.raw_id for commit in reversed(commit_ranges)] |
|
517 | 517 | pull_request.updated_on = datetime.datetime.now() |
|
518 | 518 | Session().add(pull_request) |
|
519 | 519 | new_commit_ids = set(pull_request.revisions) |
|
520 | 520 | |
|
521 | 521 | changes = self._calculate_commit_id_changes( |
|
522 | 522 | old_commit_ids, new_commit_ids) |
|
523 | 523 | |
|
524 | 524 | old_diff_data, new_diff_data = self._generate_update_diffs( |
|
525 | 525 | pull_request, pull_request_version) |
|
526 | 526 | |
|
527 | 527 | ChangesetCommentsModel().outdate_comments( |
|
528 | 528 | pull_request, old_diff_data=old_diff_data, |
|
529 | 529 | new_diff_data=new_diff_data) |
|
530 | 530 | |
|
531 | 531 | file_changes = self._calculate_file_changes( |
|
532 | 532 | old_diff_data, new_diff_data) |
|
533 | 533 | |
|
534 | 534 | # Add an automatic comment to the pull request |
|
535 | 535 | update_comment = ChangesetCommentsModel().create( |
|
536 | 536 | text=self._render_update_message(changes, file_changes), |
|
537 | 537 | repo=pull_request.target_repo, |
|
538 | 538 | user=pull_request.author, |
|
539 | 539 | pull_request=pull_request, |
|
540 | 540 | send_email=False, renderer=DEFAULT_COMMENTS_RENDERER) |
|
541 | 541 | |
|
542 | 542 | # Update status to "Under Review" for added commits |
|
543 | 543 | for commit_id in changes.added: |
|
544 | 544 | ChangesetStatusModel().set_status( |
|
545 | 545 | repo=pull_request.source_repo, |
|
546 | 546 | status=ChangesetStatus.STATUS_UNDER_REVIEW, |
|
547 | 547 | comment=update_comment, |
|
548 | 548 | user=pull_request.author, |
|
549 | 549 | pull_request=pull_request, |
|
550 | 550 | revision=commit_id) |
|
551 | 551 | |
|
552 | 552 | log.debug( |
|
553 | 553 | 'Updated pull request %s, added_ids: %s, common_ids: %s, ' |
|
554 | 554 | 'removed_ids: %s', pull_request.pull_request_id, |
|
555 | 555 | changes.added, changes.common, changes.removed) |
|
556 | 556 | log.debug('Updated pull request with the following file changes: %s', |
|
557 | 557 | file_changes) |
|
558 | 558 | |
|
559 | 559 | log.info( |
|
560 | 560 | "Updated pull request %s from commit %s to commit %s, " |
|
561 | 561 | "stored new version %s of this pull request.", |
|
562 | 562 | pull_request.pull_request_id, source_ref_id, |
|
563 | 563 | pull_request.source_ref_parts.commit_id, |
|
564 | 564 | pull_request_version.pull_request_version_id) |
|
565 | 565 | Session().commit() |
|
566 | 566 | self._trigger_pull_request_hook(pull_request, pull_request.author, |
|
567 | 567 | 'update') |
|
568 | 568 | return (pull_request_version, changes) |
|
569 | 569 | |
|
570 | 570 | def _create_version_from_snapshot(self, pull_request): |
|
571 | 571 | version = PullRequestVersion() |
|
572 | 572 | version.title = pull_request.title |
|
573 | 573 | version.description = pull_request.description |
|
574 | 574 | version.status = pull_request.status |
|
575 | 575 | version.created_on = pull_request.created_on |
|
576 | 576 | version.updated_on = pull_request.updated_on |
|
577 | 577 | version.user_id = pull_request.user_id |
|
578 | 578 | version.source_repo = pull_request.source_repo |
|
579 | 579 | version.source_ref = pull_request.source_ref |
|
580 | 580 | version.target_repo = pull_request.target_repo |
|
581 | 581 | version.target_ref = pull_request.target_ref |
|
582 | 582 | |
|
583 | 583 | version._last_merge_source_rev = pull_request._last_merge_source_rev |
|
584 | 584 | version._last_merge_target_rev = pull_request._last_merge_target_rev |
|
585 | 585 | version._last_merge_status = pull_request._last_merge_status |
|
586 | 586 | version.merge_rev = pull_request.merge_rev |
|
587 | 587 | |
|
588 | 588 | version.revisions = pull_request.revisions |
|
589 | 589 | version.pull_request = pull_request |
|
590 | 590 | Session().add(version) |
|
591 | 591 | Session().flush() |
|
592 | 592 | |
|
593 | 593 | return version |
|
594 | 594 | |
|
595 | 595 | def _generate_update_diffs(self, pull_request, pull_request_version): |
|
596 | 596 | diff_context = ( |
|
597 | 597 | self.DIFF_CONTEXT + |
|
598 | 598 | ChangesetCommentsModel.needed_extra_diff_context()) |
|
599 | 599 | old_diff = self._get_diff_from_pr_or_version( |
|
600 | 600 | pull_request_version, context=diff_context) |
|
601 | 601 | new_diff = self._get_diff_from_pr_or_version( |
|
602 | 602 | pull_request, context=diff_context) |
|
603 | 603 | |
|
604 | 604 | old_diff_data = diffs.DiffProcessor(old_diff) |
|
605 | 605 | old_diff_data.prepare() |
|
606 | 606 | new_diff_data = diffs.DiffProcessor(new_diff) |
|
607 | 607 | new_diff_data.prepare() |
|
608 | 608 | |
|
609 | 609 | return old_diff_data, new_diff_data |
|
610 | 610 | |
|
611 | 611 | def _link_comments_to_version(self, pull_request_version): |
|
612 | 612 | """ |
|
613 | 613 | Link all unlinked comments of this pull request to the given version. |
|
614 | 614 | |
|
615 | 615 | :param pull_request_version: The `PullRequestVersion` to which |
|
616 | 616 | the comments shall be linked. |
|
617 | 617 | |
|
618 | 618 | """ |
|
619 | 619 | pull_request = pull_request_version.pull_request |
|
620 | 620 | comments = ChangesetComment.query().filter( |
|
621 | 621 | # TODO: johbo: Should we query for the repo at all here? |
|
622 | 622 | # Pending decision on how comments of PRs are to be related |
|
623 | 623 | # to either the source repo, the target repo or no repo at all. |
|
624 | 624 | ChangesetComment.repo_id == pull_request.target_repo.repo_id, |
|
625 | 625 | ChangesetComment.pull_request == pull_request, |
|
626 | 626 | ChangesetComment.pull_request_version == None) |
|
627 | 627 | |
|
628 | 628 | # TODO: johbo: Find out why this breaks if it is done in a bulk |
|
629 | 629 | # operation. |
|
630 | 630 | for comment in comments: |
|
631 | 631 | comment.pull_request_version_id = ( |
|
632 | 632 | pull_request_version.pull_request_version_id) |
|
633 | 633 | Session().add(comment) |
|
634 | 634 | |
|
635 | 635 | def _calculate_commit_id_changes(self, old_ids, new_ids): |
|
636 | 636 | added = new_ids.difference(old_ids) |
|
637 | 637 | common = old_ids.intersection(new_ids) |
|
638 | 638 | removed = old_ids.difference(new_ids) |
|
639 | 639 | return ChangeTuple(added, common, removed) |
|
640 | 640 | |
|
641 | 641 | def _calculate_file_changes(self, old_diff_data, new_diff_data): |
|
642 | 642 | |
|
643 | 643 | old_files = OrderedDict() |
|
644 | 644 | for diff_data in old_diff_data.parsed_diff: |
|
645 | 645 | old_files[diff_data['filename']] = md5_safe(diff_data['raw_diff']) |
|
646 | 646 | |
|
647 | 647 | added_files = [] |
|
648 | 648 | modified_files = [] |
|
649 | 649 | removed_files = [] |
|
650 | 650 | for diff_data in new_diff_data.parsed_diff: |
|
651 | 651 | new_filename = diff_data['filename'] |
|
652 | 652 | new_hash = md5_safe(diff_data['raw_diff']) |
|
653 | 653 | |
|
654 | 654 | old_hash = old_files.get(new_filename) |
|
655 | 655 | if not old_hash: |
|
656 | 656 | # file is not present in old diff, means it's added |
|
657 | 657 | added_files.append(new_filename) |
|
658 | 658 | else: |
|
659 | 659 | if new_hash != old_hash: |
|
660 | 660 | modified_files.append(new_filename) |
|
661 | 661 | # now remove a file from old, since we have seen it already |
|
662 | 662 | del old_files[new_filename] |
|
663 | 663 | |
|
664 | 664 | # removed files is when there are present in old, but not in NEW, |
|
665 | 665 | # since we remove old files that are present in new diff, left-overs |
|
666 | 666 | # if any should be the removed files |
|
667 | 667 | removed_files.extend(old_files.keys()) |
|
668 | 668 | |
|
669 | 669 | return FileChangeTuple(added_files, modified_files, removed_files) |
|
670 | 670 | |
|
671 | 671 | def _render_update_message(self, changes, file_changes): |
|
672 | 672 | """ |
|
673 | 673 | render the message using DEFAULT_COMMENTS_RENDERER (RST renderer), |
|
674 | 674 | so it's always looking the same disregarding on which default |
|
675 | 675 | renderer system is using. |
|
676 | 676 | |
|
677 | 677 | :param changes: changes named tuple |
|
678 | 678 | :param file_changes: file changes named tuple |
|
679 | 679 | |
|
680 | 680 | """ |
|
681 | 681 | new_status = ChangesetStatus.get_status_lbl( |
|
682 | 682 | ChangesetStatus.STATUS_UNDER_REVIEW) |
|
683 | 683 | |
|
684 | 684 | changed_files = ( |
|
685 | 685 | file_changes.added + file_changes.modified + file_changes.removed) |
|
686 | 686 | |
|
687 | 687 | params = { |
|
688 | 688 | 'under_review_label': new_status, |
|
689 | 689 | 'added_commits': changes.added, |
|
690 | 690 | 'removed_commits': changes.removed, |
|
691 | 691 | 'changed_files': changed_files, |
|
692 | 692 | 'added_files': file_changes.added, |
|
693 | 693 | 'modified_files': file_changes.modified, |
|
694 | 694 | 'removed_files': file_changes.removed, |
|
695 | 695 | } |
|
696 | 696 | renderer = RstTemplateRenderer() |
|
697 | 697 | return renderer.render('pull_request_update.mako', **params) |
|
698 | 698 | |
|
699 | 699 | def edit(self, pull_request, title, description): |
|
700 | 700 | pull_request = self.__get_pull_request(pull_request) |
|
701 | 701 | if pull_request.is_closed(): |
|
702 | 702 | raise ValueError('This pull request is closed') |
|
703 | 703 | if title: |
|
704 | 704 | pull_request.title = title |
|
705 | 705 | pull_request.description = description |
|
706 | 706 | pull_request.updated_on = datetime.datetime.now() |
|
707 | 707 | Session().add(pull_request) |
|
708 | 708 | |
|
709 | 709 | def update_reviewers(self, pull_request, reviewers_ids): |
|
710 | 710 | reviewers_ids = set(reviewers_ids) |
|
711 | 711 | pull_request = self.__get_pull_request(pull_request) |
|
712 | 712 | current_reviewers = PullRequestReviewers.query()\ |
|
713 | 713 | .filter(PullRequestReviewers.pull_request == |
|
714 | 714 | pull_request).all() |
|
715 | 715 | current_reviewers_ids = set([x.user.user_id for x in current_reviewers]) |
|
716 | 716 | |
|
717 | 717 | ids_to_add = reviewers_ids.difference(current_reviewers_ids) |
|
718 | 718 | ids_to_remove = current_reviewers_ids.difference(reviewers_ids) |
|
719 | 719 | |
|
720 | 720 | log.debug("Adding %s reviewers", ids_to_add) |
|
721 | 721 | log.debug("Removing %s reviewers", ids_to_remove) |
|
722 | 722 | changed = False |
|
723 | 723 | for uid in ids_to_add: |
|
724 | 724 | changed = True |
|
725 | 725 | _usr = self._get_user(uid) |
|
726 | 726 | reviewer = PullRequestReviewers(_usr, pull_request) |
|
727 | 727 | Session().add(reviewer) |
|
728 | 728 | |
|
729 | 729 | self.notify_reviewers(pull_request, ids_to_add) |
|
730 | 730 | |
|
731 | 731 | for uid in ids_to_remove: |
|
732 | 732 | changed = True |
|
733 | 733 | reviewer = PullRequestReviewers.query()\ |
|
734 | 734 | .filter(PullRequestReviewers.user_id == uid, |
|
735 | 735 | PullRequestReviewers.pull_request == pull_request)\ |
|
736 | 736 | .scalar() |
|
737 | 737 | if reviewer: |
|
738 | 738 | Session().delete(reviewer) |
|
739 | 739 | if changed: |
|
740 | 740 | pull_request.updated_on = datetime.datetime.now() |
|
741 | 741 | Session().add(pull_request) |
|
742 | 742 | |
|
743 | 743 | return ids_to_add, ids_to_remove |
|
744 | 744 | |
|
745 | 745 | def notify_reviewers(self, pull_request, reviewers_ids): |
|
746 | 746 | # notification to reviewers |
|
747 | 747 | if not reviewers_ids: |
|
748 | 748 | return |
|
749 | 749 | |
|
750 | 750 | pull_request_obj = pull_request |
|
751 | 751 | # get the current participants of this pull request |
|
752 | 752 | recipients = reviewers_ids |
|
753 | 753 | notification_type = EmailNotificationModel.TYPE_PULL_REQUEST |
|
754 | 754 | |
|
755 | 755 | pr_source_repo = pull_request_obj.source_repo |
|
756 | 756 | pr_target_repo = pull_request_obj.target_repo |
|
757 | 757 | |
|
758 | 758 | pr_url = h.url( |
|
759 | 759 | 'pullrequest_show', |
|
760 | 760 | repo_name=pr_target_repo.repo_name, |
|
761 | 761 | pull_request_id=pull_request_obj.pull_request_id, |
|
762 | 762 | qualified=True,) |
|
763 | 763 | |
|
764 | 764 | # set some variables for email notification |
|
765 | 765 | pr_target_repo_url = h.url( |
|
766 | 766 | 'summary_home', |
|
767 | 767 | repo_name=pr_target_repo.repo_name, |
|
768 | 768 | qualified=True) |
|
769 | 769 | |
|
770 | 770 | pr_source_repo_url = h.url( |
|
771 | 771 | 'summary_home', |
|
772 | 772 | repo_name=pr_source_repo.repo_name, |
|
773 | 773 | qualified=True) |
|
774 | 774 | |
|
775 | 775 | # pull request specifics |
|
776 | 776 | pull_request_commits = [ |
|
777 | 777 | (x.raw_id, x.message) |
|
778 | 778 | for x in map(pr_source_repo.get_commit, pull_request.revisions)] |
|
779 | 779 | |
|
780 | 780 | kwargs = { |
|
781 | 781 | 'user': pull_request.author, |
|
782 | 782 | 'pull_request': pull_request_obj, |
|
783 | 783 | 'pull_request_commits': pull_request_commits, |
|
784 | 784 | |
|
785 | 785 | 'pull_request_target_repo': pr_target_repo, |
|
786 | 786 | 'pull_request_target_repo_url': pr_target_repo_url, |
|
787 | 787 | |
|
788 | 788 | 'pull_request_source_repo': pr_source_repo, |
|
789 | 789 | 'pull_request_source_repo_url': pr_source_repo_url, |
|
790 | 790 | |
|
791 | 791 | 'pull_request_url': pr_url, |
|
792 | 792 | } |
|
793 | 793 | |
|
794 | 794 | # pre-generate the subject for notification itself |
|
795 | 795 | (subject, |
|
796 | 796 | _h, _e, # we don't care about those |
|
797 | 797 | body_plaintext) = EmailNotificationModel().render_email( |
|
798 | 798 | notification_type, **kwargs) |
|
799 | 799 | |
|
800 | 800 | # create notification objects, and emails |
|
801 | 801 | NotificationModel().create( |
|
802 | 802 | created_by=pull_request.author, |
|
803 | 803 | notification_subject=subject, |
|
804 | 804 | notification_body=body_plaintext, |
|
805 | 805 | notification_type=notification_type, |
|
806 | 806 | recipients=recipients, |
|
807 | 807 | email_kwargs=kwargs, |
|
808 | 808 | ) |
|
809 | 809 | |
|
810 | 810 | def delete(self, pull_request): |
|
811 | 811 | pull_request = self.__get_pull_request(pull_request) |
|
812 | 812 | self._cleanup_merge_workspace(pull_request) |
|
813 | 813 | Session().delete(pull_request) |
|
814 | 814 | |
|
815 | 815 | def close_pull_request(self, pull_request, user): |
|
816 | 816 | pull_request = self.__get_pull_request(pull_request) |
|
817 | 817 | self._cleanup_merge_workspace(pull_request) |
|
818 | 818 | pull_request.status = PullRequest.STATUS_CLOSED |
|
819 | 819 | pull_request.updated_on = datetime.datetime.now() |
|
820 | 820 | Session().add(pull_request) |
|
821 | 821 | self._trigger_pull_request_hook( |
|
822 | 822 | pull_request, pull_request.author, 'close') |
|
823 | 823 | self._log_action('user_closed_pull_request', user, pull_request) |
|
824 | 824 | |
|
825 | 825 | def close_pull_request_with_comment(self, pull_request, user, repo, |
|
826 | 826 | message=None): |
|
827 | 827 | status = ChangesetStatus.STATUS_REJECTED |
|
828 | 828 | |
|
829 | 829 | if not message: |
|
830 | 830 | message = ( |
|
831 | 831 | _('Status change %(transition_icon)s %(status)s') % { |
|
832 | 832 | 'transition_icon': '>', |
|
833 | 833 | 'status': ChangesetStatus.get_status_lbl(status)}) |
|
834 | 834 | |
|
835 | 835 | internal_message = _('Closing with') + ' ' + message |
|
836 | 836 | |
|
837 | 837 | comm = ChangesetCommentsModel().create( |
|
838 | 838 | text=internal_message, |
|
839 | 839 | repo=repo.repo_id, |
|
840 | 840 | user=user.user_id, |
|
841 | 841 | pull_request=pull_request.pull_request_id, |
|
842 | 842 | f_path=None, |
|
843 | 843 | line_no=None, |
|
844 | 844 | status_change=ChangesetStatus.get_status_lbl(status), |
|
845 | 845 | closing_pr=True |
|
846 | 846 | ) |
|
847 | 847 | |
|
848 | 848 | ChangesetStatusModel().set_status( |
|
849 | 849 | repo.repo_id, |
|
850 | 850 | status, |
|
851 | 851 | user.user_id, |
|
852 | 852 | comm, |
|
853 | 853 | pull_request=pull_request.pull_request_id |
|
854 | 854 | ) |
|
855 | 855 | Session().flush() |
|
856 | 856 | |
|
857 | 857 | PullRequestModel().close_pull_request( |
|
858 | 858 | pull_request.pull_request_id, user) |
|
859 | 859 | |
|
860 | 860 | def merge_status(self, pull_request): |
|
861 | 861 | if not self._is_merge_enabled(pull_request): |
|
862 | 862 | return False, _('Server-side pull request merging is disabled.') |
|
863 | 863 | if pull_request.is_closed(): |
|
864 | 864 | return False, _('This pull request is closed.') |
|
865 | 865 | merge_possible, msg = self._check_repo_requirements( |
|
866 | 866 | target=pull_request.target_repo, source=pull_request.source_repo) |
|
867 | 867 | if not merge_possible: |
|
868 | 868 | return merge_possible, msg |
|
869 | 869 | |
|
870 | 870 | try: |
|
871 | 871 | resp = self._try_merge(pull_request) |
|
872 | 872 | status = resp.possible, self.merge_status_message( |
|
873 | 873 | resp.failure_reason) |
|
874 | 874 | except NotImplementedError: |
|
875 | 875 | status = False, _('Pull request merging is not supported.') |
|
876 | 876 | |
|
877 | 877 | return status |
|
878 | 878 | |
|
879 | 879 | def _check_repo_requirements(self, target, source): |
|
880 | 880 | """ |
|
881 | 881 | Check if `target` and `source` have compatible requirements. |
|
882 | 882 | |
|
883 | 883 | Currently this is just checking for largefiles. |
|
884 | 884 | """ |
|
885 | 885 | target_has_largefiles = self._has_largefiles(target) |
|
886 | 886 | source_has_largefiles = self._has_largefiles(source) |
|
887 | 887 | merge_possible = True |
|
888 | 888 | message = u'' |
|
889 | 889 | |
|
890 | 890 | if target_has_largefiles != source_has_largefiles: |
|
891 | 891 | merge_possible = False |
|
892 | 892 | if source_has_largefiles: |
|
893 | 893 | message = _( |
|
894 | 894 | 'Target repository large files support is disabled.') |
|
895 | 895 | else: |
|
896 | 896 | message = _( |
|
897 | 897 | 'Source repository large files support is disabled.') |
|
898 | 898 | |
|
899 | 899 | return merge_possible, message |
|
900 | 900 | |
|
901 | 901 | def _has_largefiles(self, repo): |
|
902 | 902 | largefiles_ui = VcsSettingsModel(repo=repo).get_ui_settings( |
|
903 | 903 | 'extensions', 'largefiles') |
|
904 | 904 | return largefiles_ui and largefiles_ui[0].active |
|
905 | 905 | |
|
906 | 906 | def _try_merge(self, pull_request): |
|
907 | 907 | """ |
|
908 | 908 | Try to merge the pull request and return the merge status. |
|
909 | 909 | """ |
|
910 | 910 | target_vcs = pull_request.target_repo.scm_instance() |
|
911 | 911 | target_ref = self._refresh_reference( |
|
912 | 912 | pull_request.target_ref_parts, target_vcs) |
|
913 | 913 | |
|
914 | 914 | target_locked = pull_request.target_repo.locked |
|
915 | 915 | if target_locked and target_locked[0]: |
|
916 | 916 | merge_state = MergeResponse( |
|
917 | 917 | False, False, None, MergeFailureReason.TARGET_IS_LOCKED) |
|
918 | 918 | elif self._needs_merge_state_refresh(pull_request, target_ref): |
|
919 | 919 | merge_state = self._refresh_merge_state( |
|
920 | 920 | pull_request, target_vcs, target_ref) |
|
921 | 921 | else: |
|
922 | 922 | possible = pull_request.\ |
|
923 | 923 | _last_merge_status == MergeFailureReason.NONE |
|
924 | 924 | merge_state = MergeResponse( |
|
925 | 925 | possible, False, None, pull_request._last_merge_status) |
|
926 | 926 | return merge_state |
|
927 | 927 | |
|
928 | 928 | def _refresh_reference(self, reference, vcs_repository): |
|
929 | 929 | if reference.type in ('branch', 'book'): |
|
930 | 930 | name_or_id = reference.name |
|
931 | 931 | else: |
|
932 | 932 | name_or_id = reference.commit_id |
|
933 | 933 | refreshed_commit = vcs_repository.get_commit(name_or_id) |
|
934 | 934 | refreshed_reference = Reference( |
|
935 | 935 | reference.type, reference.name, refreshed_commit.raw_id) |
|
936 | 936 | return refreshed_reference |
|
937 | 937 | |
|
938 | 938 | def _needs_merge_state_refresh(self, pull_request, target_reference): |
|
939 | 939 | return not( |
|
940 | 940 | pull_request.revisions and |
|
941 | 941 | pull_request.revisions[0] == pull_request._last_merge_source_rev and |
|
942 | 942 | target_reference.commit_id == pull_request._last_merge_target_rev) |
|
943 | 943 | |
|
944 | 944 | def _refresh_merge_state(self, pull_request, target_vcs, target_reference): |
|
945 | 945 | workspace_id = self._workspace_id(pull_request) |
|
946 | 946 | source_vcs = pull_request.source_repo.scm_instance() |
|
947 | 947 | merge_state = target_vcs.merge( |
|
948 | 948 | target_reference, source_vcs, pull_request.source_ref_parts, |
|
949 | 949 | workspace_id, dry_run=True) |
|
950 | 950 | |
|
951 | 951 | # Do not store the response if there was an unknown error. |
|
952 | 952 | if merge_state.failure_reason != MergeFailureReason.UNKNOWN: |
|
953 | 953 | pull_request._last_merge_source_rev = pull_request.\ |
|
954 | 954 | source_ref_parts.commit_id |
|
955 | 955 | pull_request._last_merge_target_rev = target_reference.commit_id |
|
956 | 956 | pull_request._last_merge_status = ( |
|
957 | 957 | merge_state.failure_reason) |
|
958 | 958 | Session().add(pull_request) |
|
959 | 959 | Session().flush() |
|
960 | 960 | |
|
961 | 961 | return merge_state |
|
962 | 962 | |
|
963 | 963 | def _workspace_id(self, pull_request): |
|
964 | 964 | workspace_id = 'pr-%s' % pull_request.pull_request_id |
|
965 | 965 | return workspace_id |
|
966 | 966 | |
|
967 | 967 | def merge_status_message(self, status_code): |
|
968 | 968 | """ |
|
969 | 969 | Return a human friendly error message for the given merge status code. |
|
970 | 970 | """ |
|
971 | 971 | return self.MERGE_STATUS_MESSAGES[status_code] |
|
972 | 972 | |
|
973 | 973 | def generate_repo_data(self, repo, commit_id=None, branch=None, |
|
974 | 974 | bookmark=None): |
|
975 | 975 | all_refs, selected_ref = \ |
|
976 | 976 | self._get_repo_pullrequest_sources( |
|
977 | 977 | repo.scm_instance(), commit_id=commit_id, |
|
978 | 978 | branch=branch, bookmark=bookmark) |
|
979 | 979 | |
|
980 | 980 | refs_select2 = [] |
|
981 | 981 | for element in all_refs: |
|
982 | 982 | children = [{'id': x[0], 'text': x[1]} for x in element[0]] |
|
983 | 983 | refs_select2.append({'text': element[1], 'children': children}) |
|
984 | 984 | |
|
985 | 985 | return { |
|
986 | 986 | 'user': { |
|
987 | 987 | 'user_id': repo.user.user_id, |
|
988 | 988 | 'username': repo.user.username, |
|
989 | 989 | 'firstname': repo.user.firstname, |
|
990 | 990 | 'lastname': repo.user.lastname, |
|
991 | 991 | 'gravatar_link': h.gravatar_url(repo.user.email, 14), |
|
992 | 992 | }, |
|
993 | 993 | 'description': h.chop_at_smart(repo.description, '\n'), |
|
994 | 994 | 'refs': { |
|
995 | 995 | 'all_refs': all_refs, |
|
996 | 996 | 'selected_ref': selected_ref, |
|
997 | 997 | 'select2_refs': refs_select2 |
|
998 | 998 | } |
|
999 | 999 | } |
|
1000 | 1000 | |
|
1001 | 1001 | def generate_pullrequest_title(self, source, source_ref, target): |
|
1002 | 1002 | return '{source}#{at_ref} to {target}'.format( |
|
1003 | 1003 | source=source, |
|
1004 | 1004 | at_ref=source_ref, |
|
1005 | 1005 | target=target, |
|
1006 | 1006 | ) |
|
1007 | 1007 | |
|
1008 | 1008 | def _cleanup_merge_workspace(self, pull_request): |
|
1009 | 1009 | # Merging related cleanup |
|
1010 | 1010 | target_scm = pull_request.target_repo.scm_instance() |
|
1011 | 1011 | workspace_id = 'pr-%s' % pull_request.pull_request_id |
|
1012 | 1012 | |
|
1013 | 1013 | try: |
|
1014 | 1014 | target_scm.cleanup_merge_workspace(workspace_id) |
|
1015 | 1015 | except NotImplementedError: |
|
1016 | 1016 | pass |
|
1017 | 1017 | |
|
1018 | 1018 | def _get_repo_pullrequest_sources( |
|
1019 | 1019 | self, repo, commit_id=None, branch=None, bookmark=None): |
|
1020 | 1020 | """ |
|
1021 | 1021 | Return a structure with repo's interesting commits, suitable for |
|
1022 | 1022 | the selectors in pullrequest controller |
|
1023 | 1023 | |
|
1024 | 1024 | :param commit_id: a commit that must be in the list somehow |
|
1025 | 1025 | and selected by default |
|
1026 | 1026 | :param branch: a branch that must be in the list and selected |
|
1027 | 1027 | by default - even if closed |
|
1028 | 1028 | :param bookmark: a bookmark that must be in the list and selected |
|
1029 | 1029 | """ |
|
1030 | 1030 | |
|
1031 | 1031 | commit_id = safe_str(commit_id) if commit_id else None |
|
1032 | 1032 | branch = safe_str(branch) if branch else None |
|
1033 | 1033 | bookmark = safe_str(bookmark) if bookmark else None |
|
1034 | 1034 | |
|
1035 | 1035 | selected = None |
|
1036 | 1036 | |
|
1037 | 1037 | # order matters: first source that has commit_id in it will be selected |
|
1038 | 1038 | sources = [] |
|
1039 | 1039 | sources.append(('book', repo.bookmarks.items(), _('Bookmarks'), bookmark)) |
|
1040 | 1040 | sources.append(('branch', repo.branches.items(), _('Branches'), branch)) |
|
1041 | 1041 | |
|
1042 | 1042 | if commit_id: |
|
1043 | 1043 | ref_commit = (h.short_id(commit_id), commit_id) |
|
1044 | 1044 | sources.append(('rev', [ref_commit], _('Commit IDs'), commit_id)) |
|
1045 | 1045 | |
|
1046 | 1046 | sources.append( |
|
1047 | 1047 | ('branch', repo.branches_closed.items(), _('Closed Branches'), branch), |
|
1048 | 1048 | ) |
|
1049 | 1049 | |
|
1050 | 1050 | groups = [] |
|
1051 | 1051 | for group_key, ref_list, group_name, match in sources: |
|
1052 | 1052 | group_refs = [] |
|
1053 | 1053 | for ref_name, ref_id in ref_list: |
|
1054 | 1054 | ref_key = '%s:%s:%s' % (group_key, ref_name, ref_id) |
|
1055 | 1055 | group_refs.append((ref_key, ref_name)) |
|
1056 | 1056 | |
|
1057 |
if not selected |
|
|
1057 | if not selected: | |
|
1058 | if set([commit_id, match]) & set([ref_id, ref_name]): | |
|
1058 | 1059 | selected = ref_key |
|
1060 | ||
|
1059 | 1061 | if group_refs: |
|
1060 | 1062 | groups.append((group_refs, group_name)) |
|
1061 | 1063 | |
|
1062 | 1064 | if not selected: |
|
1063 | 1065 | ref = commit_id or branch or bookmark |
|
1064 | 1066 | if ref: |
|
1065 | 1067 | raise CommitDoesNotExistError( |
|
1066 | 1068 | 'No commit refs could be found matching: %s' % ref) |
|
1067 | 1069 | elif repo.DEFAULT_BRANCH_NAME in repo.branches: |
|
1068 | 1070 | selected = 'branch:%s:%s' % ( |
|
1069 | 1071 | repo.DEFAULT_BRANCH_NAME, |
|
1070 | 1072 | repo.branches[repo.DEFAULT_BRANCH_NAME] |
|
1071 | 1073 | ) |
|
1072 | 1074 | elif repo.commit_ids: |
|
1073 | 1075 | rev = repo.commit_ids[0] |
|
1074 | 1076 | selected = 'rev:%s:%s' % (rev, rev) |
|
1075 | 1077 | else: |
|
1076 | 1078 | raise EmptyRepositoryError() |
|
1077 | 1079 | return groups, selected |
|
1078 | 1080 | |
|
1079 | 1081 | def get_diff(self, pull_request, context=DIFF_CONTEXT): |
|
1080 | 1082 | pull_request = self.__get_pull_request(pull_request) |
|
1081 | 1083 | return self._get_diff_from_pr_or_version(pull_request, context=context) |
|
1082 | 1084 | |
|
1083 | 1085 | def _get_diff_from_pr_or_version(self, pr_or_version, context): |
|
1084 | 1086 | source_repo = pr_or_version.source_repo |
|
1085 | 1087 | |
|
1086 | 1088 | # we swap org/other ref since we run a simple diff on one repo |
|
1087 | 1089 | target_ref_id = pr_or_version.target_ref_parts.commit_id |
|
1088 | 1090 | source_ref_id = pr_or_version.source_ref_parts.commit_id |
|
1089 | 1091 | target_commit = source_repo.get_commit( |
|
1090 | 1092 | commit_id=safe_str(target_ref_id)) |
|
1091 | 1093 | source_commit = source_repo.get_commit(commit_id=safe_str(source_ref_id)) |
|
1092 | 1094 | vcs_repo = source_repo.scm_instance() |
|
1093 | 1095 | |
|
1094 | 1096 | # TODO: johbo: In the context of an update, we cannot reach |
|
1095 | 1097 | # the old commit anymore with our normal mechanisms. It needs |
|
1096 | 1098 | # some sort of special support in the vcs layer to avoid this |
|
1097 | 1099 | # workaround. |
|
1098 | 1100 | if (source_commit.raw_id == vcs_repo.EMPTY_COMMIT_ID and |
|
1099 | 1101 | vcs_repo.alias == 'git'): |
|
1100 | 1102 | source_commit.raw_id = safe_str(source_ref_id) |
|
1101 | 1103 | |
|
1102 | 1104 | log.debug('calculating diff between ' |
|
1103 | 1105 | 'source_ref:%s and target_ref:%s for repo `%s`', |
|
1104 | 1106 | target_ref_id, source_ref_id, |
|
1105 | 1107 | safe_unicode(vcs_repo.path)) |
|
1106 | 1108 | |
|
1107 | 1109 | vcs_diff = vcs_repo.get_diff( |
|
1108 | 1110 | commit1=target_commit, commit2=source_commit, context=context) |
|
1109 | 1111 | return vcs_diff |
|
1110 | 1112 | |
|
1111 | 1113 | def _is_merge_enabled(self, pull_request): |
|
1112 | 1114 | settings_model = VcsSettingsModel(repo=pull_request.target_repo) |
|
1113 | 1115 | settings = settings_model.get_general_settings() |
|
1114 | 1116 | return settings.get('rhodecode_pr_merge_enabled', False) |
|
1115 | 1117 | |
|
1116 | 1118 | def _log_action(self, action, user, pull_request): |
|
1117 | 1119 | action_logger( |
|
1118 | 1120 | user, |
|
1119 | 1121 | '{action}:{pr_id}'.format( |
|
1120 | 1122 | action=action, pr_id=pull_request.pull_request_id), |
|
1121 | 1123 | pull_request.target_repo) |
|
1122 | 1124 | |
|
1123 | 1125 | |
|
1124 | 1126 | ChangeTuple = namedtuple('ChangeTuple', |
|
1125 | 1127 | ['added', 'common', 'removed']) |
|
1126 | 1128 | |
|
1127 | 1129 | FileChangeTuple = namedtuple('FileChangeTuple', |
|
1128 | 1130 | ['added', 'modified', 'removed']) |
@@ -1,600 +1,605 b'' | |||
|
1 | 1 | // Default styles |
|
2 | 2 | |
|
3 | 3 | .diff-collapse { |
|
4 | 4 | margin: @padding 0; |
|
5 | 5 | text-align: right; |
|
6 | 6 | } |
|
7 | 7 | |
|
8 | 8 | .diff-container { |
|
9 | 9 | margin-bottom: @space; |
|
10 | 10 | |
|
11 | 11 | .diffblock { |
|
12 | 12 | margin-bottom: @space; |
|
13 | 13 | } |
|
14 | 14 | |
|
15 | 15 | &.hidden { |
|
16 | 16 | display: none; |
|
17 | 17 | overflow: hidden; |
|
18 | 18 | } |
|
19 | 19 | } |
|
20 | 20 | |
|
21 | 21 | .compare_view_files { |
|
22 | 22 | |
|
23 | 23 | .diff-container { |
|
24 | 24 | |
|
25 | 25 | .diffblock { |
|
26 | 26 | margin-bottom: 0; |
|
27 | 27 | } |
|
28 | 28 | } |
|
29 | 29 | } |
|
30 | 30 | |
|
31 | 31 | div.diffblock .sidebyside { |
|
32 | 32 | background: #ffffff; |
|
33 | 33 | } |
|
34 | 34 | |
|
35 | 35 | div.diffblock { |
|
36 | 36 | overflow-x: auto; |
|
37 | 37 | overflow-y: hidden; |
|
38 | 38 | clear: both; |
|
39 | 39 | padding: 0px; |
|
40 | 40 | background: @grey6; |
|
41 | 41 | border: @border-thickness solid @grey5; |
|
42 | 42 | -webkit-border-radius: @border-radius @border-radius 0px 0px; |
|
43 | 43 | border-radius: @border-radius @border-radius 0px 0px; |
|
44 | 44 | |
|
45 | 45 | |
|
46 | 46 | .comments-number { |
|
47 | 47 | float: right; |
|
48 | 48 | } |
|
49 | 49 | |
|
50 | 50 | // BEGIN CODE-HEADER STYLES |
|
51 | 51 | |
|
52 | 52 | .code-header { |
|
53 | 53 | background: @grey6; |
|
54 | 54 | padding: 10px 0 10px 0; |
|
55 | 55 | height: auto; |
|
56 | 56 | width: 100%; |
|
57 | 57 | |
|
58 | 58 | .hash { |
|
59 | 59 | float: left; |
|
60 | 60 | padding: 2px 0 0 2px; |
|
61 | 61 | } |
|
62 | 62 | |
|
63 | 63 | .date { |
|
64 | 64 | float: left; |
|
65 | 65 | text-transform: uppercase; |
|
66 | 66 | padding: 4px 0px 0px 2px; |
|
67 | 67 | } |
|
68 | 68 | |
|
69 | 69 | div { |
|
70 | 70 | margin-left: 4px; |
|
71 | 71 | } |
|
72 | 72 | |
|
73 | 73 | div.compare_header { |
|
74 | 74 | min-height: 40px; |
|
75 | 75 | margin: 0; |
|
76 | 76 | padding: 0 @padding; |
|
77 | 77 | |
|
78 | 78 | .drop-menu { |
|
79 | 79 | float:left; |
|
80 | 80 | display: block; |
|
81 | 81 | margin:0 0 @padding 0; |
|
82 | 82 | } |
|
83 | 83 | |
|
84 | 84 | .compare-label { |
|
85 | 85 | float: left; |
|
86 | 86 | clear: both; |
|
87 | 87 | display: inline-block; |
|
88 | 88 | min-width: 5em; |
|
89 | 89 | margin: 0; |
|
90 | 90 | padding: @button-padding @button-padding @button-padding 0; |
|
91 | 91 | font-family: @text-semibold; |
|
92 | 92 | } |
|
93 | 93 | |
|
94 | 94 | .compare-buttons { |
|
95 | 95 | float: left; |
|
96 | 96 | margin: 0; |
|
97 | 97 | padding: 0 0 @padding; |
|
98 | 98 | |
|
99 | 99 | .btn { |
|
100 | 100 | margin: 0 @padding 0 0; |
|
101 | 101 | } |
|
102 | 102 | } |
|
103 | 103 | } |
|
104 | 104 | |
|
105 | 105 | } |
|
106 | 106 | |
|
107 | 107 | .parents { |
|
108 | 108 | float: left; |
|
109 | 109 | width: 100px; |
|
110 | 110 | font-weight: 400; |
|
111 | 111 | vertical-align: middle; |
|
112 | 112 | padding: 0px 2px 0px 2px; |
|
113 | 113 | background-color: @grey6; |
|
114 | 114 | |
|
115 | 115 | #parent_link { |
|
116 | 116 | margin: 00px 2px; |
|
117 | 117 | |
|
118 | 118 | &.double { |
|
119 | 119 | margin: 0px 2px; |
|
120 | 120 | } |
|
121 | 121 | |
|
122 | 122 | &.disabled{ |
|
123 | 123 | margin-right: @padding; |
|
124 | 124 | } |
|
125 | 125 | } |
|
126 | 126 | } |
|
127 | 127 | |
|
128 | 128 | .children { |
|
129 | 129 | float: right; |
|
130 | 130 | width: 100px; |
|
131 | 131 | font-weight: 400; |
|
132 | 132 | vertical-align: middle; |
|
133 | 133 | text-align: right; |
|
134 | 134 | padding: 0px 2px 0px 2px; |
|
135 | 135 | background-color: @grey6; |
|
136 | 136 | |
|
137 | 137 | #child_link { |
|
138 | 138 | margin: 0px 2px; |
|
139 | 139 | |
|
140 | 140 | &.double { |
|
141 | 141 | margin: 0px 2px; |
|
142 | 142 | } |
|
143 | 143 | |
|
144 | 144 | &.disabled{ |
|
145 | 145 | margin-right: @padding; |
|
146 | 146 | } |
|
147 | 147 | } |
|
148 | 148 | } |
|
149 | 149 | |
|
150 | 150 | .changeset_header { |
|
151 | 151 | height: 16px; |
|
152 | 152 | |
|
153 | 153 | & > div{ |
|
154 | 154 | margin-right: @padding; |
|
155 | 155 | } |
|
156 | 156 | } |
|
157 | 157 | |
|
158 | 158 | .changeset_file { |
|
159 | 159 | text-align: left; |
|
160 | 160 | float: left; |
|
161 | 161 | padding: 0; |
|
162 | 162 | |
|
163 | 163 | a{ |
|
164 | 164 | display: inline-block; |
|
165 | 165 | margin-right: 0.5em; |
|
166 | 166 | } |
|
167 | 167 | |
|
168 | 168 | #selected_mode{ |
|
169 | 169 | margin-left: 0; |
|
170 | 170 | } |
|
171 | 171 | } |
|
172 | 172 | |
|
173 | 173 | .diff-menu-wrapper { |
|
174 | 174 | float: left; |
|
175 | 175 | } |
|
176 | 176 | |
|
177 | 177 | .diff-menu { |
|
178 | 178 | position: absolute; |
|
179 | 179 | background: none repeat scroll 0 0 #FFFFFF; |
|
180 | 180 | border-color: #003367 @grey3 @grey3; |
|
181 | 181 | border-right: 1px solid @grey3; |
|
182 | 182 | border-style: solid solid solid; |
|
183 | 183 | border-width: @border-thickness; |
|
184 | 184 | box-shadow: 2px 8px 4px rgba(0, 0, 0, 0.2); |
|
185 | 185 | margin-top: 5px; |
|
186 | 186 | margin-left: 1px; |
|
187 | 187 | } |
|
188 | 188 | |
|
189 | 189 | .diff-actions, .editor-actions { |
|
190 | 190 | float: left; |
|
191 | 191 | |
|
192 | 192 | input{ |
|
193 | 193 | margin: 0 0.5em 0 0; |
|
194 | 194 | } |
|
195 | 195 | } |
|
196 | 196 | |
|
197 | 197 | // END CODE-HEADER STYLES |
|
198 | 198 | |
|
199 | 199 | // BEGIN CODE-BODY STYLES |
|
200 | 200 | |
|
201 | 201 | .code-body { |
|
202 | 202 | background: white; |
|
203 | 203 | padding: 0; |
|
204 | 204 | background-color: #ffffff; |
|
205 | 205 | position: relative; |
|
206 | 206 | max-width: none; |
|
207 | 207 | box-sizing: border-box; |
|
208 | 208 | // TODO: johbo: Parent has overflow: auto, this forces the child here |
|
209 | 209 | // to have the intended size and to scroll. Should be simplified. |
|
210 | 210 | width: 100%; |
|
211 | 211 | overflow-x: auto; |
|
212 | 212 | } |
|
213 | 213 | |
|
214 | 214 | pre.raw { |
|
215 | 215 | background: white; |
|
216 | 216 | color: @grey1; |
|
217 | 217 | } |
|
218 | 218 | // END CODE-BODY STYLES |
|
219 | 219 | |
|
220 | 220 | } |
|
221 | 221 | |
|
222 | 222 | |
|
223 | 223 | table.code-difftable { |
|
224 | 224 | border-collapse: collapse; |
|
225 | 225 | width: 99%; |
|
226 | 226 | border-radius: 0px !important; |
|
227 | 227 | |
|
228 | 228 | td { |
|
229 | 229 | padding: 0 !important; |
|
230 | 230 | background: none !important; |
|
231 | 231 | border: 0 !important; |
|
232 | 232 | } |
|
233 | 233 | |
|
234 | 234 | .context { |
|
235 | 235 | background: none repeat scroll 0 0 #DDE7EF; |
|
236 | 236 | } |
|
237 | 237 | |
|
238 | 238 | .add { |
|
239 | 239 | background: none repeat scroll 0 0 #DDFFDD; |
|
240 | 240 | |
|
241 | 241 | ins { |
|
242 | 242 | background: none repeat scroll 0 0 #AAFFAA; |
|
243 | 243 | text-decoration: none; |
|
244 | 244 | } |
|
245 | 245 | } |
|
246 | 246 | |
|
247 | 247 | .del { |
|
248 | 248 | background: none repeat scroll 0 0 #FFDDDD; |
|
249 | 249 | |
|
250 | 250 | del { |
|
251 | 251 | background: none repeat scroll 0 0 #FFAAAA; |
|
252 | 252 | text-decoration: none; |
|
253 | 253 | } |
|
254 | 254 | } |
|
255 | 255 | |
|
256 | 256 | /** LINE NUMBERS **/ |
|
257 | 257 | .lineno { |
|
258 | 258 | padding-left: 2px; |
|
259 | 259 | padding-right: 2px; |
|
260 | 260 | text-align: right; |
|
261 | 261 | width: 32px; |
|
262 | 262 | -moz-user-select: none; |
|
263 | 263 | -webkit-user-select: none; |
|
264 | 264 | border-right: @border-thickness solid @grey5 !important; |
|
265 | 265 | border-left: 0px solid #CCC !important; |
|
266 | 266 | border-top: 0px solid #CCC !important; |
|
267 | 267 | border-bottom: none !important; |
|
268 | 268 | |
|
269 | 269 | a { |
|
270 | 270 | &:extend(pre); |
|
271 | 271 | text-align: right; |
|
272 | 272 | padding-right: 2px; |
|
273 | 273 | cursor: pointer; |
|
274 | 274 | display: block; |
|
275 | 275 | width: 32px; |
|
276 | 276 | } |
|
277 | 277 | } |
|
278 | 278 | |
|
279 | 279 | .context { |
|
280 | 280 | cursor: auto; |
|
281 | 281 | &:extend(pre); |
|
282 | 282 | } |
|
283 | 283 | |
|
284 | 284 | .lineno-inline { |
|
285 | 285 | background: none repeat scroll 0 0 #FFF !important; |
|
286 | 286 | padding-left: 2px; |
|
287 | 287 | padding-right: 2px; |
|
288 | 288 | text-align: right; |
|
289 | 289 | width: 30px; |
|
290 | 290 | -moz-user-select: none; |
|
291 | 291 | -webkit-user-select: none; |
|
292 | 292 | } |
|
293 | 293 | |
|
294 | 294 | /** CODE **/ |
|
295 | 295 | .code { |
|
296 | 296 | display: block; |
|
297 | 297 | width: 100%; |
|
298 | 298 | |
|
299 | 299 | td { |
|
300 | 300 | margin: 0; |
|
301 | 301 | padding: 0; |
|
302 | 302 | } |
|
303 | 303 | |
|
304 | 304 | pre { |
|
305 | 305 | margin: 0; |
|
306 | 306 | padding: 0; |
|
307 | 307 | margin-left: .5em; |
|
308 | 308 | } |
|
309 | 309 | } |
|
310 | 310 | } |
|
311 | 311 | |
|
312 | 312 | |
|
313 | 313 | // Comments |
|
314 | 314 | |
|
315 | 315 | div.comment:target { |
|
316 | 316 | border-left: 6px solid @comment-highlight-color; |
|
317 | 317 | padding-left: 3px; |
|
318 | 318 | margin-left: -9px; |
|
319 | 319 | } |
|
320 | 320 | |
|
321 | 321 | //TODO: anderson: can't get an absolute number out of anything, so had to put the |
|
322 | 322 | //current values that might change. But to make it clear I put as a calculation |
|
323 | 323 | @comment-max-width: 1065px; |
|
324 | 324 | @pr-extra-margin: 34px; |
|
325 | 325 | @pr-border-spacing: 4px; |
|
326 | 326 | @pr-comment-width: @comment-max-width - @pr-extra-margin - @pr-border-spacing; |
|
327 | 327 | |
|
328 | 328 | // Pull Request |
|
329 | 329 | .cs_files .code-difftable { |
|
330 | 330 | border: @border-thickness solid @grey5; //borders only on PRs |
|
331 | 331 | |
|
332 | 332 | .comment-inline-form, |
|
333 | 333 | div.comment { |
|
334 | 334 | width: @pr-comment-width; |
|
335 | 335 | } |
|
336 | 336 | } |
|
337 | 337 | |
|
338 | 338 | // Changeset |
|
339 | 339 | .code-difftable { |
|
340 | 340 | .comment-inline-form, |
|
341 | 341 | div.comment { |
|
342 | 342 | width: @comment-max-width; |
|
343 | 343 | } |
|
344 | 344 | } |
|
345 | 345 | |
|
346 | 346 | //Style page |
|
347 | 347 | @style-extra-margin: @sidebar-width + (@sidebarpadding * 3) + @padding; |
|
348 | 348 | #style-page .code-difftable{ |
|
349 | 349 | .comment-inline-form, |
|
350 | 350 | div.comment { |
|
351 | 351 | width: @comment-max-width - @style-extra-margin; |
|
352 | 352 | } |
|
353 | 353 | } |
|
354 | 354 | |
|
355 | 355 | #context-bar > h2 { |
|
356 | 356 | font-size: 20px; |
|
357 | 357 | } |
|
358 | 358 | |
|
359 | 359 | #context-bar > h2> a { |
|
360 | 360 | font-size: 20px; |
|
361 | 361 | } |
|
362 | 362 | // end of defaults |
|
363 | 363 | |
|
364 | 364 | .file_diff_buttons { |
|
365 | 365 | padding: 0 0 @padding; |
|
366 | 366 | |
|
367 | 367 | .drop-menu { |
|
368 | 368 | float: left; |
|
369 | 369 | margin: 0 @padding 0 0; |
|
370 | 370 | } |
|
371 | 371 | .btn { |
|
372 | 372 | margin: 0 @padding 0 0; |
|
373 | 373 | } |
|
374 | 374 | } |
|
375 | 375 | |
|
376 | 376 | .code-body.textarea.editor { |
|
377 | 377 | max-width: none; |
|
378 | 378 | padding: 15px; |
|
379 | 379 | } |
|
380 | 380 | |
|
381 | 381 | td.injected_diff{ |
|
382 | 382 | max-width: 1178px; |
|
383 | 383 | overflow-x: auto; |
|
384 | 384 | overflow-y: hidden; |
|
385 | 385 | |
|
386 | 386 | div.diff-container, |
|
387 | 387 | div.diffblock{ |
|
388 | 388 | max-width: 100%; |
|
389 | 389 | } |
|
390 | 390 | |
|
391 | 391 | div.code-body { |
|
392 | 392 | max-width: 1124px; |
|
393 | 393 | overflow-x: auto; |
|
394 | 394 | padding: 0; |
|
395 | 395 | } |
|
396 | 396 | div.diffblock { |
|
397 | 397 | border: none; |
|
398 | 398 | } |
|
399 | 399 | |
|
400 | 400 | &.inline-form { |
|
401 | 401 | width: 99% |
|
402 | 402 | } |
|
403 | 403 | } |
|
404 | 404 | |
|
405 | 405 | |
|
406 | 406 | table.code-difftable { |
|
407 | 407 | width: 100%; |
|
408 | 408 | } |
|
409 | 409 | |
|
410 | 410 | /** PYGMENTS COLORING **/ |
|
411 | 411 | div.codeblock { |
|
412 | 412 | |
|
413 | 413 | // TODO: johbo: Added interim to get rid of the margin around |
|
414 | 414 | // Select2 widgets. This needs further cleanup. |
|
415 | 415 | margin-top: @padding; |
|
416 | 416 | |
|
417 | 417 | overflow: auto; |
|
418 | 418 | padding: 0px; |
|
419 | 419 | border: @border-thickness solid @grey5; |
|
420 | 420 | background: @grey6; |
|
421 | 421 | .border-radius(@border-radius); |
|
422 | 422 | |
|
423 | 423 | #remove_gist { |
|
424 | 424 | float: right; |
|
425 | 425 | } |
|
426 | 426 | |
|
427 | 427 | .author { |
|
428 | 428 | clear: both; |
|
429 | 429 | vertical-align: middle; |
|
430 | 430 | font-family: @text-bold; |
|
431 | 431 | } |
|
432 | 432 | |
|
433 | 433 | .btn-mini { |
|
434 | 434 | float: left; |
|
435 | 435 | margin: 0 5px 0 0; |
|
436 | 436 | } |
|
437 | 437 | |
|
438 | 438 | .code-header { |
|
439 | 439 | padding: @padding; |
|
440 | 440 | border-bottom: @border-thickness solid @grey5; |
|
441 | 441 | |
|
442 | .rc-user { | |
|
443 | min-width: 0; | |
|
444 | margin-right: .5em; | |
|
445 | } | |
|
446 | ||
|
442 | 447 | .stats { |
|
443 | 448 | clear: both; |
|
444 | 449 | margin: 0 0 @padding 0; |
|
445 | 450 | padding: 0; |
|
446 | 451 | .left { |
|
447 | 452 | float: left; |
|
448 | 453 | clear: left; |
|
449 | 454 | max-width: 75%; |
|
450 | 455 | margin: 0 0 @padding 0; |
|
451 | 456 | |
|
452 | 457 | &.item { |
|
453 | 458 | margin-right: @padding; |
|
454 | 459 | &.last { border-right: none; } |
|
455 | 460 | } |
|
456 | 461 | } |
|
457 | 462 | .buttons { float: right; } |
|
458 | 463 | .author { |
|
459 | 464 | height: 25px; margin-left: 15px; font-weight: bold; |
|
460 | 465 | } |
|
461 | 466 | } |
|
462 | 467 | |
|
463 | 468 | .commit { |
|
464 | 469 | margin: 5px 0 0 26px; |
|
465 | 470 | font-weight: normal; |
|
466 | 471 | white-space: pre-wrap; |
|
467 | 472 | } |
|
468 | 473 | } |
|
469 | 474 | |
|
470 | 475 | .message { |
|
471 | 476 | position: relative; |
|
472 | 477 | margin: @padding; |
|
473 | 478 | |
|
474 | 479 | .codeblock-label { |
|
475 | 480 | margin: 0 0 1em 0; |
|
476 | 481 | } |
|
477 | 482 | } |
|
478 | 483 | |
|
479 | 484 | .code-body { |
|
480 | 485 | padding: @padding; |
|
481 | 486 | background-color: #ffffff; |
|
482 | 487 | min-width: 100%; |
|
483 | 488 | box-sizing: border-box; |
|
484 | 489 | // TODO: johbo: Parent has overflow: auto, this forces the child here |
|
485 | 490 | // to have the intended size and to scroll. Should be simplified. |
|
486 | 491 | width: 100%; |
|
487 | 492 | overflow-x: auto; |
|
488 | 493 | } |
|
489 | 494 | } |
|
490 | 495 | |
|
491 | 496 | .code-highlighttable, |
|
492 | 497 | div.codeblock .code-body table { |
|
493 | 498 | width: 0 !important; |
|
494 | 499 | border: 0px !important; |
|
495 | 500 | margin: 0; |
|
496 | 501 | letter-spacing: normal; |
|
497 | 502 | |
|
498 | 503 | |
|
499 | 504 | td { |
|
500 | 505 | border: 0px !important; |
|
501 | 506 | vertical-align: top; |
|
502 | 507 | } |
|
503 | 508 | } |
|
504 | 509 | |
|
505 | 510 | div.codeblock .code-header .search-path { padding: 0 0 0 10px; } |
|
506 | 511 | div.search-code-body { |
|
507 | 512 | background-color: #ffffff; padding: 5px 0 5px 10px; |
|
508 | 513 | pre { |
|
509 | 514 | .match { background-color: #faffa6;} |
|
510 | 515 | .break { display: block; width: 100%; background-color: #DDE7EF; color: #747474; } |
|
511 | 516 | } |
|
512 | 517 | } |
|
513 | 518 | |
|
514 | 519 | div.annotatediv { margin-left: 2px; margin-right: 4px; } |
|
515 | 520 | .code-highlight { |
|
516 | 521 | margin: 0; padding: 0; border-left: @border-thickness solid @grey5; |
|
517 | 522 | pre, .linenodiv pre { padding: 0 5px; margin: 0; } |
|
518 | 523 | pre div:target {background-color: @comment-highlight-color !important;} |
|
519 | 524 | } |
|
520 | 525 | |
|
521 | 526 | .linenos a { text-decoration: none; } |
|
522 | 527 | |
|
523 | 528 | .CodeMirror-selected { background: @rchighlightblue; } |
|
524 | 529 | .CodeMirror-focused .CodeMirror-selected { background: @rchighlightblue; } |
|
525 | 530 | .CodeMirror ::selection { background: @rchighlightblue; } |
|
526 | 531 | .CodeMirror ::-moz-selection { background: @rchighlightblue; } |
|
527 | 532 | |
|
528 | 533 | .code { display: block; border:0px !important; } |
|
529 | 534 | .code-highlight, |
|
530 | 535 | .codehilite { |
|
531 | 536 | .hll { background-color: #ffffcc } |
|
532 | 537 | .c { color: #408080; font-style: italic } /* Comment */ |
|
533 | 538 | .err, .codehilite .err { border: @border-thickness solid #FF0000 } /* Error */ |
|
534 | 539 | .k { color: #008000; font-weight: bold } /* Keyword */ |
|
535 | 540 | .o { color: #666666 } /* Operator */ |
|
536 | 541 | .cm { color: #408080; font-style: italic } /* Comment.Multiline */ |
|
537 | 542 | .cp { color: #BC7A00 } /* Comment.Preproc */ |
|
538 | 543 | .c1 { color: #408080; font-style: italic } /* Comment.Single */ |
|
539 | 544 | .cs { color: #408080; font-style: italic } /* Comment.Special */ |
|
540 | 545 | .gd { color: #A00000 } /* Generic.Deleted */ |
|
541 | 546 | .ge { font-style: italic } /* Generic.Emph */ |
|
542 | 547 | .gr { color: #FF0000 } /* Generic.Error */ |
|
543 | 548 | .gh { color: #000080; font-weight: bold } /* Generic.Heading */ |
|
544 | 549 | .gi { color: #00A000 } /* Generic.Inserted */ |
|
545 | 550 | .go { color: #808080 } /* Generic.Output */ |
|
546 | 551 | .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ |
|
547 | 552 | .gs { font-weight: bold } /* Generic.Strong */ |
|
548 | 553 | .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ |
|
549 | 554 | .gt { color: #0040D0 } /* Generic.Traceback */ |
|
550 | 555 | .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ |
|
551 | 556 | .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ |
|
552 | 557 | .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ |
|
553 | 558 | .kp { color: #008000 } /* Keyword.Pseudo */ |
|
554 | 559 | .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ |
|
555 | 560 | .kt { color: #B00040 } /* Keyword.Type */ |
|
556 | 561 | .m { color: #666666 } /* Literal.Number */ |
|
557 | 562 | .s { color: #BA2121 } /* Literal.String */ |
|
558 | 563 | .na { color: #7D9029 } /* Name.Attribute */ |
|
559 | 564 | .nb { color: #008000 } /* Name.Builtin */ |
|
560 | 565 | .nc { color: #0000FF; font-weight: bold } /* Name.Class */ |
|
561 | 566 | .no { color: #880000 } /* Name.Constant */ |
|
562 | 567 | .nd { color: #AA22FF } /* Name.Decorator */ |
|
563 | 568 | .ni { color: #999999; font-weight: bold } /* Name.Entity */ |
|
564 | 569 | .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ |
|
565 | 570 | .nf { color: #0000FF } /* Name.Function */ |
|
566 | 571 | .nl { color: #A0A000 } /* Name.Label */ |
|
567 | 572 | .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ |
|
568 | 573 | .nt { color: #008000; font-weight: bold } /* Name.Tag */ |
|
569 | 574 | .nv { color: #19177C } /* Name.Variable */ |
|
570 | 575 | .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ |
|
571 | 576 | .w { color: #bbbbbb } /* Text.Whitespace */ |
|
572 | 577 | .mf { color: #666666 } /* Literal.Number.Float */ |
|
573 | 578 | .mh { color: #666666 } /* Literal.Number.Hex */ |
|
574 | 579 | .mi { color: #666666 } /* Literal.Number.Integer */ |
|
575 | 580 | .mo { color: #666666 } /* Literal.Number.Oct */ |
|
576 | 581 | .sb { color: #BA2121 } /* Literal.String.Backtick */ |
|
577 | 582 | .sc { color: #BA2121 } /* Literal.String.Char */ |
|
578 | 583 | .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ |
|
579 | 584 | .s2 { color: #BA2121 } /* Literal.String.Double */ |
|
580 | 585 | .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ |
|
581 | 586 | .sh { color: #BA2121 } /* Literal.String.Heredoc */ |
|
582 | 587 | .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ |
|
583 | 588 | .sx { color: #008000 } /* Literal.String.Other */ |
|
584 | 589 | .sr { color: #BB6688 } /* Literal.String.Regex */ |
|
585 | 590 | .s1 { color: #BA2121 } /* Literal.String.Single */ |
|
586 | 591 | .ss { color: #19177C } /* Literal.String.Symbol */ |
|
587 | 592 | .bp { color: #008000 } /* Name.Builtin.Pseudo */ |
|
588 | 593 | .vc { color: #19177C } /* Name.Variable.Class */ |
|
589 | 594 | .vg { color: #19177C } /* Name.Variable.Global */ |
|
590 | 595 | .vi { color: #19177C } /* Name.Variable.Instance */ |
|
591 | 596 | .il { color: #666666 } /* Literal.Number.Integer.Long */ |
|
592 | 597 | } |
|
593 | 598 | |
|
594 | 599 | /* customized pre blocks for markdown/rst */ |
|
595 | 600 | pre.literal-block, .codehilite pre{ |
|
596 | 601 | padding: @padding; |
|
597 | 602 | border: 1px solid @grey6; |
|
598 | 603 | .border-radius(@border-radius); |
|
599 | 604 | background-color: @grey7; |
|
600 | 605 | } |
@@ -1,356 +1,361 b'' | |||
|
1 | 1 | // comments.less |
|
2 | 2 | // For use in RhodeCode applications; |
|
3 | 3 | // see style guide documentation for guidelines. |
|
4 | 4 | |
|
5 | 5 | |
|
6 | 6 | // Comments |
|
7 | 7 | .comments { |
|
8 | 8 | width: 100%; |
|
9 | 9 | } |
|
10 | 10 | |
|
11 | 11 | tr.inline-comments div { |
|
12 | 12 | max-width: 100%; |
|
13 | 13 | |
|
14 | 14 | p { |
|
15 | 15 | white-space: normal; |
|
16 | 16 | } |
|
17 | 17 | |
|
18 | 18 | code, pre, .code, dd { |
|
19 | 19 | overflow-x: auto; |
|
20 | 20 | width: 1062px; |
|
21 | 21 | } |
|
22 | 22 | |
|
23 | 23 | dd { |
|
24 | 24 | width: auto; |
|
25 | 25 | } |
|
26 | 26 | } |
|
27 | 27 | |
|
28 | 28 | #injected_page_comments { |
|
29 | 29 | .comment-previous-link, |
|
30 | 30 | .comment-next-link, |
|
31 | 31 | .comment-links-divider { |
|
32 | 32 | display: none; |
|
33 | 33 | } |
|
34 | 34 | } |
|
35 | 35 | |
|
36 | 36 | .add-comment { |
|
37 | 37 | margin-bottom: 10px; |
|
38 | 38 | } |
|
39 | 39 | .hide-comment-button .add-comment { |
|
40 | 40 | display: none; |
|
41 | 41 | } |
|
42 | 42 | |
|
43 | 43 | .comment-bubble { |
|
44 | 44 | color: @grey4; |
|
45 | 45 | margin-top: 4px; |
|
46 | 46 | margin-right: 30px; |
|
47 | 47 | visibility: hidden; |
|
48 | 48 | } |
|
49 | 49 | |
|
50 | 50 | .comment { |
|
51 | 51 | margin: @padding 0; |
|
52 | 52 | padding: 4px 0 0 0; |
|
53 | 53 | line-height: 1em; |
|
54 | 54 | |
|
55 | .rc-user { | |
|
56 | min-width: 0; | |
|
57 | margin: -2px .5em 0 0; | |
|
58 | } | |
|
59 | ||
|
55 | 60 | .meta { |
|
56 | 61 | position: relative; |
|
57 | 62 | width: 100%; |
|
58 | 63 | margin: 0 0 .5em 0; |
|
59 | 64 | |
|
60 | 65 | &:hover .permalink { |
|
61 | 66 | visibility: visible; |
|
62 | 67 | color: @rcblue; |
|
63 | 68 | } |
|
64 | 69 | } |
|
65 | 70 | |
|
66 | 71 | .author, |
|
67 | 72 | .date { |
|
68 | 73 | display: inline; |
|
69 | 74 | margin: 0 .5 0 0; |
|
70 | 75 | padding: 0 .5 0 0; |
|
71 | 76 | |
|
72 | 77 | &:after { |
|
73 | 78 | content: ' | '; |
|
74 | 79 | color: @grey5; |
|
75 | 80 | } |
|
76 | 81 | } |
|
77 | 82 | |
|
78 | 83 | .status-change, |
|
79 | 84 | .permalink, |
|
80 | 85 | .changeset-status-lbl { |
|
81 | 86 | display: inline; |
|
82 | 87 | } |
|
83 | 88 | |
|
84 | 89 | .permalink { |
|
85 | 90 | visibility: hidden; |
|
86 | 91 | } |
|
87 | 92 | |
|
88 | 93 | .comment-links-divider { |
|
89 | 94 | display: inline; |
|
90 | 95 | } |
|
91 | 96 | |
|
92 | 97 | .comment-links-block { |
|
93 | 98 | float:right; |
|
94 | 99 | text-align: right; |
|
95 | 100 | min-width: 85px; |
|
96 | 101 | |
|
97 | 102 | [class^="icon-"]:before, |
|
98 | 103 | [class*=" icon-"]:before { |
|
99 | 104 | margin-left: 0; |
|
100 | 105 | margin-right: 0; |
|
101 | 106 | } |
|
102 | 107 | } |
|
103 | 108 | |
|
104 | 109 | .comment-previous-link { |
|
105 | 110 | display: inline-block; |
|
106 | 111 | |
|
107 | 112 | .arrow_comment_link{ |
|
108 | 113 | cursor: pointer; |
|
109 | 114 | i { |
|
110 | 115 | font-size:10px; |
|
111 | 116 | } |
|
112 | 117 | } |
|
113 | 118 | .arrow_comment_link.disabled { |
|
114 | 119 | cursor: default; |
|
115 | 120 | color: @grey5; |
|
116 | 121 | } |
|
117 | 122 | } |
|
118 | 123 | |
|
119 | 124 | .comment-next-link { |
|
120 | 125 | display: inline-block; |
|
121 | 126 | |
|
122 | 127 | .arrow_comment_link{ |
|
123 | 128 | cursor: pointer; |
|
124 | 129 | i { |
|
125 | 130 | font-size:10px; |
|
126 | 131 | } |
|
127 | 132 | } |
|
128 | 133 | .arrow_comment_link.disabled { |
|
129 | 134 | cursor: default; |
|
130 | 135 | color: @grey5; |
|
131 | 136 | } |
|
132 | 137 | } |
|
133 | 138 | |
|
134 | 139 | .flag_status { |
|
135 | 140 | display: inline-block; |
|
136 | 141 | margin: -2px .5em 0 .25em |
|
137 | 142 | } |
|
138 | 143 | |
|
139 | 144 | .delete-comment { |
|
140 | 145 | display: inline-block; |
|
141 | 146 | color: @rcblue; |
|
142 | 147 | |
|
143 | 148 | &:hover { |
|
144 | 149 | cursor: pointer; |
|
145 | 150 | } |
|
146 | 151 | } |
|
147 | 152 | |
|
148 | 153 | |
|
149 | 154 | .text { |
|
150 | 155 | clear: both; |
|
151 | 156 | border: @border-thickness solid @grey5; |
|
152 | 157 | .border-radius(@border-radius); |
|
153 | 158 | .box-sizing(border-box); |
|
154 | 159 | |
|
155 | 160 | .markdown-block p, |
|
156 | 161 | .rst-block p { |
|
157 | 162 | margin: .5em 0 !important; |
|
158 | 163 | // TODO: lisa: This is needed because of other rst !important rules :[ |
|
159 | 164 | } |
|
160 | 165 | } |
|
161 | 166 | } |
|
162 | 167 | |
|
163 | 168 | .show-outdated-comments { |
|
164 | 169 | display: inline; |
|
165 | 170 | color: @rcblue; |
|
166 | 171 | } |
|
167 | 172 | |
|
168 | 173 | .outdated { |
|
169 | 174 | display: none; |
|
170 | 175 | opacity: 0.6; |
|
171 | 176 | |
|
172 | 177 | .comment { |
|
173 | 178 | margin: 0 0 @padding; |
|
174 | 179 | |
|
175 | 180 | .date:after { |
|
176 | 181 | content: none; |
|
177 | 182 | } |
|
178 | 183 | } |
|
179 | 184 | .outdated_comment_block { |
|
180 | 185 | padding: 0 0 @space 0; |
|
181 | 186 | } |
|
182 | 187 | } |
|
183 | 188 | |
|
184 | 189 | // Comment Form |
|
185 | 190 | div.comment-form { |
|
186 | 191 | margin-top: 20px; |
|
187 | 192 | } |
|
188 | 193 | |
|
189 | 194 | .comment-form strong { |
|
190 | 195 | display: block; |
|
191 | 196 | margin-bottom: 15px; |
|
192 | 197 | } |
|
193 | 198 | |
|
194 | 199 | .comment-form textarea { |
|
195 | 200 | width: 100%; |
|
196 | 201 | height: 100px; |
|
197 | 202 | font-family: 'Monaco', 'Courier', 'Courier New', monospace; |
|
198 | 203 | } |
|
199 | 204 | |
|
200 | 205 | form.comment-form { |
|
201 | 206 | margin-top: 10px; |
|
202 | 207 | margin-left: 10px; |
|
203 | 208 | } |
|
204 | 209 | |
|
205 | 210 | .comment-inline-form .comment-block-ta, |
|
206 | 211 | .comment-form .comment-block-ta, |
|
207 | 212 | .comment-form .preview-box { |
|
208 | 213 | border: @border-thickness solid @grey5; |
|
209 | 214 | .border-radius(@border-radius); |
|
210 | 215 | .box-sizing(border-box); |
|
211 | 216 | background-color: white; |
|
212 | 217 | } |
|
213 | 218 | |
|
214 | 219 | .comment-form-submit { |
|
215 | 220 | margin-top: 5px; |
|
216 | 221 | margin-left: 525px; |
|
217 | 222 | } |
|
218 | 223 | |
|
219 | 224 | .file-comments { |
|
220 | 225 | display: none; |
|
221 | 226 | } |
|
222 | 227 | |
|
223 | 228 | .comment-form .preview-box.unloaded, |
|
224 | 229 | .comment-inline-form .preview-box.unloaded { |
|
225 | 230 | height: 50px; |
|
226 | 231 | text-align: center; |
|
227 | 232 | padding: 20px; |
|
228 | 233 | background-color: white; |
|
229 | 234 | } |
|
230 | 235 | |
|
231 | 236 | .comment-footer { |
|
232 | 237 | position: relative; |
|
233 | 238 | width: 100%; |
|
234 | 239 | min-height: 42px; |
|
235 | 240 | |
|
236 | 241 | .status_box, |
|
237 | 242 | .cancel-button { |
|
238 | 243 | float: left; |
|
239 | 244 | display: inline-block; |
|
240 | 245 | } |
|
241 | 246 | |
|
242 | 247 | .action-buttons { |
|
243 | 248 | float: right; |
|
244 | 249 | display: inline-block; |
|
245 | 250 | } |
|
246 | 251 | } |
|
247 | 252 | |
|
248 | 253 | .comment-form { |
|
249 | 254 | |
|
250 | 255 | .comment { |
|
251 | 256 | margin-left: 10px; |
|
252 | 257 | } |
|
253 | 258 | |
|
254 | 259 | .comment-help { |
|
255 | 260 | color: @grey4; |
|
256 | 261 | padding: 5px 0 5px 0; |
|
257 | 262 | } |
|
258 | 263 | |
|
259 | 264 | .comment-title { |
|
260 | 265 | padding: 5px 0 5px 0; |
|
261 | 266 | } |
|
262 | 267 | |
|
263 | 268 | .comment-button { |
|
264 | 269 | display: inline-block; |
|
265 | 270 | } |
|
266 | 271 | |
|
267 | 272 | .comment-button .comment-button-input { |
|
268 | 273 | margin-right: 0; |
|
269 | 274 | } |
|
270 | 275 | |
|
271 | 276 | .comment-footer { |
|
272 | 277 | margin-bottom: 110px; |
|
273 | 278 | } |
|
274 | 279 | } |
|
275 | 280 | |
|
276 | 281 | |
|
277 | 282 | .comment-form-login { |
|
278 | 283 | .comment-help { |
|
279 | 284 | padding: 0.9em; //same as the button |
|
280 | 285 | } |
|
281 | 286 | |
|
282 | 287 | div.clearfix { |
|
283 | 288 | clear: both; |
|
284 | 289 | width: 100%; |
|
285 | 290 | display: block; |
|
286 | 291 | } |
|
287 | 292 | } |
|
288 | 293 | |
|
289 | 294 | .preview-box { |
|
290 | 295 | padding: 10px; |
|
291 | 296 | min-height: 100px; |
|
292 | 297 | margin-bottom: 15px; |
|
293 | 298 | background-color: white; |
|
294 | 299 | border: @border-thickness solid #ccc; |
|
295 | 300 | .border-radius(@border-radius); |
|
296 | 301 | .box-sizing(border-box); |
|
297 | 302 | } |
|
298 | 303 | |
|
299 | 304 | .add-another-button { |
|
300 | 305 | margin-left: 10px; |
|
301 | 306 | margin-top: 10px; |
|
302 | 307 | margin-bottom: 10px; |
|
303 | 308 | } |
|
304 | 309 | |
|
305 | 310 | .comment .buttons { |
|
306 | 311 | float: right; |
|
307 | 312 | margin: -1px 0px 0px 0px; |
|
308 | 313 | } |
|
309 | 314 | |
|
310 | 315 | // Inline Comment Form |
|
311 | 316 | .injected_diff .comment-inline-form, |
|
312 | 317 | .comment-inline-form { |
|
313 | 318 | background-color: @grey6; |
|
314 | 319 | margin-top: 10px; |
|
315 | 320 | margin-bottom: 20px; |
|
316 | 321 | } |
|
317 | 322 | |
|
318 | 323 | .inline-form { |
|
319 | 324 | padding: 10px 7px; |
|
320 | 325 | } |
|
321 | 326 | |
|
322 | 327 | .inline-form div { |
|
323 | 328 | max-width: 100%; |
|
324 | 329 | } |
|
325 | 330 | |
|
326 | 331 | .overlay { |
|
327 | 332 | display: none; |
|
328 | 333 | position: absolute; |
|
329 | 334 | width: 100%; |
|
330 | 335 | text-align: center; |
|
331 | 336 | vertical-align: middle; |
|
332 | 337 | font-size: 16px; |
|
333 | 338 | background: none repeat scroll 0 0 white; |
|
334 | 339 | |
|
335 | 340 | &.submitting { |
|
336 | 341 | display: block; |
|
337 | 342 | opacity: 0.5; |
|
338 | 343 | z-index: 100; |
|
339 | 344 | } |
|
340 | 345 | } |
|
341 | 346 | .comment-inline-form .overlay.submitting .overlay-text { |
|
342 | 347 | margin-top: 5%; |
|
343 | 348 | } |
|
344 | 349 | |
|
345 | 350 | .comment-inline-form .clearfix, |
|
346 | 351 | .comment-form .clearfix { |
|
347 | 352 | .border-radius(@border-radius); |
|
348 | 353 | margin: 0px; |
|
349 | 354 | } |
|
350 | 355 | |
|
351 | 356 | .hide-inline-form-button { |
|
352 | 357 | margin-left: 5px; |
|
353 | 358 | } |
|
354 | 359 | .comment-button .hide-inline-form { |
|
355 | 360 | background: white; |
|
356 | 361 | } |
@@ -1,2076 +1,2087 b'' | |||
|
1 | 1 | //Primary CSS |
|
2 | 2 | |
|
3 | 3 | //--- IMPORTS ------------------// |
|
4 | 4 | |
|
5 | 5 | @import 'helpers'; |
|
6 | 6 | @import 'mixins'; |
|
7 | 7 | @import 'rcicons'; |
|
8 | 8 | @import 'fonts'; |
|
9 | 9 | @import 'variables'; |
|
10 | 10 | @import 'bootstrap-variables'; |
|
11 | 11 | @import 'form-bootstrap'; |
|
12 | 12 | @import 'codemirror'; |
|
13 | 13 | @import 'legacy_code_styles'; |
|
14 | 14 | @import 'progress-bar'; |
|
15 | 15 | |
|
16 | 16 | @import 'type'; |
|
17 | 17 | @import 'alerts'; |
|
18 | 18 | @import 'buttons'; |
|
19 | 19 | @import 'tags'; |
|
20 | 20 | @import 'code-block'; |
|
21 | 21 | @import 'examples'; |
|
22 | 22 | @import 'login'; |
|
23 | 23 | @import 'main-content'; |
|
24 | 24 | @import 'select2'; |
|
25 | 25 | @import 'comments'; |
|
26 | 26 | @import 'panels-bootstrap'; |
|
27 | 27 | @import 'panels'; |
|
28 | 28 | |
|
29 | 29 | |
|
30 | 30 | //--- BASE ------------------// |
|
31 | 31 | .noscript-error { |
|
32 | 32 | top: 0; |
|
33 | 33 | left: 0; |
|
34 | 34 | width: 100%; |
|
35 | 35 | z-index: 101; |
|
36 | 36 | text-align: center; |
|
37 | 37 | font-family: @text-semibold; |
|
38 | 38 | font-size: 120%; |
|
39 | 39 | color: white; |
|
40 | 40 | background-color: @alert2; |
|
41 | 41 | padding: 5px 0 5px 0; |
|
42 | 42 | } |
|
43 | 43 | |
|
44 | 44 | html { |
|
45 | 45 | display: table; |
|
46 | 46 | height: 100%; |
|
47 | 47 | width: 100%; |
|
48 | 48 | } |
|
49 | 49 | |
|
50 | 50 | body { |
|
51 | 51 | display: table-cell; |
|
52 | 52 | width: 100%; |
|
53 | 53 | } |
|
54 | 54 | |
|
55 | 55 | //--- LAYOUT ------------------// |
|
56 | 56 | |
|
57 | 57 | .hidden{ |
|
58 | 58 | display: none !important; |
|
59 | 59 | } |
|
60 | 60 | |
|
61 | 61 | .box{ |
|
62 | 62 | float: left; |
|
63 | 63 | width: 100%; |
|
64 | 64 | } |
|
65 | 65 | |
|
66 | 66 | .browser-header { |
|
67 | 67 | clear: both; |
|
68 | 68 | } |
|
69 | 69 | .main { |
|
70 | 70 | clear: both; |
|
71 | 71 | padding:0 0 @pagepadding; |
|
72 | 72 | height: auto; |
|
73 | 73 | |
|
74 | 74 | &:after { //clearfix |
|
75 | 75 | content:""; |
|
76 | 76 | clear:both; |
|
77 | 77 | width:100%; |
|
78 | 78 | display:block; |
|
79 | 79 | } |
|
80 | 80 | } |
|
81 | 81 | |
|
82 | 82 | .action-link{ |
|
83 | 83 | margin-left: @padding; |
|
84 | 84 | padding-left: @padding; |
|
85 | 85 | border-left: @border-thickness solid @border-default-color; |
|
86 | 86 | } |
|
87 | 87 | |
|
88 | 88 | input + .action-link, .action-link.first{ |
|
89 | 89 | border-left: none; |
|
90 | 90 | } |
|
91 | 91 | |
|
92 | 92 | .action-link.last{ |
|
93 | 93 | margin-right: @padding; |
|
94 | 94 | padding-right: @padding; |
|
95 | 95 | } |
|
96 | 96 | |
|
97 | 97 | .action-link.active, |
|
98 | 98 | .action-link.active a{ |
|
99 | 99 | color: @grey4; |
|
100 | 100 | } |
|
101 | 101 | |
|
102 | 102 | ul.simple-list{ |
|
103 | 103 | list-style: none; |
|
104 | 104 | margin: 0; |
|
105 | 105 | padding: 0; |
|
106 | 106 | } |
|
107 | 107 | |
|
108 | 108 | .main-content { |
|
109 | 109 | padding-bottom: @pagepadding; |
|
110 | 110 | } |
|
111 | 111 | |
|
112 | 112 | .wrapper { |
|
113 | 113 | position: relative; |
|
114 | 114 | max-width: @wrapper-maxwidth; |
|
115 | 115 | margin: 0 auto; |
|
116 | 116 | } |
|
117 | 117 | |
|
118 | 118 | #content { |
|
119 | 119 | clear: both; |
|
120 | 120 | padding: 0 @contentpadding; |
|
121 | 121 | } |
|
122 | 122 | |
|
123 | 123 | .advanced-settings-fields{ |
|
124 | 124 | input{ |
|
125 | 125 | margin-left: @textmargin; |
|
126 | 126 | margin-right: @padding/2; |
|
127 | 127 | } |
|
128 | 128 | } |
|
129 | 129 | |
|
130 | 130 | .cs_files_title { |
|
131 | 131 | margin: @pagepadding 0 0; |
|
132 | 132 | } |
|
133 | 133 | |
|
134 | 134 | input.inline[type="file"] { |
|
135 | 135 | display: inline; |
|
136 | 136 | } |
|
137 | 137 | |
|
138 | 138 | .error_page { |
|
139 | 139 | margin: 10% auto; |
|
140 | 140 | |
|
141 | 141 | h1 { |
|
142 | 142 | color: @grey2; |
|
143 | 143 | } |
|
144 | 144 | |
|
145 | 145 | .error-branding { |
|
146 | 146 | font-family: @text-semibold; |
|
147 | 147 | color: @grey4; |
|
148 | 148 | } |
|
149 | 149 | |
|
150 | 150 | .error_message { |
|
151 | 151 | font-family: @text-regular; |
|
152 | 152 | } |
|
153 | 153 | |
|
154 | 154 | .sidebar { |
|
155 | 155 | min-height: 275px; |
|
156 | 156 | margin: 0; |
|
157 | 157 | padding: 0 0 @sidebarpadding @sidebarpadding; |
|
158 | 158 | border: none; |
|
159 | 159 | } |
|
160 | 160 | |
|
161 | 161 | .main-content { |
|
162 | 162 | position: relative; |
|
163 | 163 | margin: 0 @sidebarpadding @sidebarpadding; |
|
164 | 164 | padding: 0 0 0 @sidebarpadding; |
|
165 | 165 | border-left: @border-thickness solid @grey5; |
|
166 | 166 | |
|
167 | 167 | @media (max-width:767px) { |
|
168 | 168 | clear: both; |
|
169 | 169 | width: 100%; |
|
170 | 170 | margin: 0; |
|
171 | 171 | border: none; |
|
172 | 172 | } |
|
173 | 173 | } |
|
174 | 174 | |
|
175 | 175 | .inner-column { |
|
176 | 176 | float: left; |
|
177 | 177 | width: 29.75%; |
|
178 | 178 | min-height: 150px; |
|
179 | 179 | margin: @sidebarpadding 2% 0 0; |
|
180 | 180 | padding: 0 2% 0 0; |
|
181 | 181 | border-right: @border-thickness solid @grey5; |
|
182 | 182 | |
|
183 | 183 | @media (max-width:767px) { |
|
184 | 184 | clear: both; |
|
185 | 185 | width: 100%; |
|
186 | 186 | border: none; |
|
187 | 187 | } |
|
188 | 188 | |
|
189 | 189 | ul { |
|
190 | 190 | padding-left: 1.25em; |
|
191 | 191 | } |
|
192 | 192 | |
|
193 | 193 | &:last-child { |
|
194 | 194 | margin: @sidebarpadding 0 0; |
|
195 | 195 | border: none; |
|
196 | 196 | } |
|
197 | 197 | |
|
198 | 198 | h4 { |
|
199 | 199 | margin: 0 0 @padding; |
|
200 | 200 | font-family: @text-semibold; |
|
201 | 201 | } |
|
202 | 202 | } |
|
203 | 203 | } |
|
204 | 204 | .error-page-logo { |
|
205 | 205 | width: 130px; |
|
206 | 206 | height: 160px; |
|
207 | 207 | } |
|
208 | 208 | |
|
209 | 209 | // HEADER |
|
210 | 210 | .header { |
|
211 | 211 | |
|
212 | 212 | // TODO: johbo: Fix login pages, so that they work without a min-height |
|
213 | 213 | // for the header and then remove the min-height. I chose a smaller value |
|
214 | 214 | // intentionally here to avoid rendering issues in the main navigation. |
|
215 | 215 | min-height: 49px; |
|
216 | 216 | |
|
217 | 217 | position: relative; |
|
218 | 218 | vertical-align: bottom; |
|
219 | 219 | padding: 0 @header-padding; |
|
220 | 220 | background-color: @grey2; |
|
221 | 221 | color: @grey5; |
|
222 | 222 | |
|
223 | 223 | .title { |
|
224 | 224 | overflow: visible; |
|
225 | 225 | } |
|
226 | 226 | |
|
227 | 227 | &:before, |
|
228 | 228 | &:after { |
|
229 | 229 | content: ""; |
|
230 | 230 | clear: both; |
|
231 | 231 | width: 100%; |
|
232 | 232 | } |
|
233 | 233 | |
|
234 | 234 | // TODO: johbo: Avoids breaking "Repositories" chooser |
|
235 | 235 | .select2-container .select2-choice .select2-arrow { |
|
236 | 236 | display: none; |
|
237 | 237 | } |
|
238 | 238 | } |
|
239 | 239 | |
|
240 | 240 | #header-inner { |
|
241 | 241 | &.title { |
|
242 | 242 | margin: 0; |
|
243 | 243 | } |
|
244 | 244 | &:before, |
|
245 | 245 | &:after { |
|
246 | 246 | content: ""; |
|
247 | 247 | clear: both; |
|
248 | 248 | } |
|
249 | 249 | } |
|
250 | 250 | |
|
251 | 251 | // Gists |
|
252 | 252 | #files_data { |
|
253 | 253 | clear: both; //for firefox |
|
254 | 254 | } |
|
255 | 255 | #gistid { |
|
256 | 256 | margin-right: @padding; |
|
257 | 257 | } |
|
258 | 258 | |
|
259 | 259 | // Global Settings Editor |
|
260 | 260 | .textarea.editor { |
|
261 | 261 | float: left; |
|
262 | 262 | position: relative; |
|
263 | 263 | max-width: @texteditor-width; |
|
264 | 264 | |
|
265 | 265 | select { |
|
266 | 266 | position: absolute; |
|
267 | 267 | top:10px; |
|
268 | 268 | right:0; |
|
269 | 269 | } |
|
270 | 270 | |
|
271 | 271 | .CodeMirror { |
|
272 | 272 | margin: 0; |
|
273 | 273 | } |
|
274 | 274 | |
|
275 | 275 | .help-block { |
|
276 | 276 | margin: 0 0 @padding; |
|
277 | 277 | padding:.5em; |
|
278 | 278 | background-color: @grey6; |
|
279 | 279 | } |
|
280 | 280 | } |
|
281 | 281 | |
|
282 | 282 | ul.auth_plugins { |
|
283 | 283 | margin: @padding 0 @padding @legend-width; |
|
284 | 284 | padding: 0; |
|
285 | 285 | |
|
286 | 286 | li { |
|
287 | 287 | margin-bottom: @padding; |
|
288 | 288 | line-height: 1em; |
|
289 | 289 | list-style-type: none; |
|
290 | 290 | |
|
291 | 291 | .auth_buttons .btn { |
|
292 | 292 | margin-right: @padding; |
|
293 | 293 | } |
|
294 | 294 | |
|
295 | 295 | &:before { content: none; } |
|
296 | 296 | } |
|
297 | 297 | } |
|
298 | 298 | |
|
299 | 299 | // Pull Requests |
|
300 | 300 | |
|
301 | 301 | .pullrequestlist { |
|
302 | 302 | max-width: @pullrequest-width; |
|
303 | 303 | margin-bottom: @space; |
|
304 | 304 | |
|
305 | 305 | // Tweaks for "My Account" / "Pull requests" |
|
306 | 306 | .prwrapper { |
|
307 | 307 | clear: left; |
|
308 | 308 | |
|
309 | 309 | .pr { |
|
310 | 310 | margin: 0; |
|
311 | 311 | padding: 0; |
|
312 | 312 | border-bottom: none; |
|
313 | 313 | } |
|
314 | 314 | |
|
315 | 315 | // TODO: johbo: Replace with something that makes up an inline form or |
|
316 | 316 | // similar. |
|
317 | 317 | .repolist_actions { |
|
318 | 318 | display: inline-block; |
|
319 | 319 | } |
|
320 | 320 | } |
|
321 | 321 | |
|
322 | 322 | } |
|
323 | 323 | |
|
324 | 324 | .pullrequests_section_head { |
|
325 | 325 | display: block; |
|
326 | 326 | clear: both; |
|
327 | 327 | margin: @padding 0; |
|
328 | 328 | font-family: @text-bold; |
|
329 | 329 | } |
|
330 | 330 | |
|
331 | 331 | .pr-origininfo, .pr-targetinfo { |
|
332 | 332 | position: relative; |
|
333 | 333 | |
|
334 | 334 | .tag { |
|
335 | 335 | display: inline-block; |
|
336 | 336 | margin: 0 1em .5em 0; |
|
337 | 337 | } |
|
338 | 338 | |
|
339 | 339 | .clone-url { |
|
340 | 340 | display: inline-block; |
|
341 | 341 | margin: 0 0 .5em 0; |
|
342 | 342 | padding: 0; |
|
343 | 343 | line-height: 1.2em; |
|
344 | 344 | } |
|
345 | 345 | } |
|
346 | 346 | |
|
347 | 347 | .pr-pullinfo { |
|
348 | 348 | clear: both; |
|
349 | 349 | margin: .5em 0; |
|
350 | 350 | } |
|
351 | 351 | |
|
352 | 352 | #pr-title-input { |
|
353 | 353 | width: 72%; |
|
354 | 354 | font-size: 1em; |
|
355 | 355 | font-family: @text-bold; |
|
356 | 356 | margin: 0; |
|
357 | 357 | padding: 0 0 0 @padding/4; |
|
358 | 358 | line-height: 1.7em; |
|
359 | 359 | color: @text-color; |
|
360 | 360 | letter-spacing: .02em; |
|
361 | 361 | } |
|
362 | 362 | |
|
363 | 363 | #pullrequest_title { |
|
364 | 364 | width: 100%; |
|
365 | 365 | box-sizing: border-box; |
|
366 | 366 | } |
|
367 | 367 | |
|
368 | 368 | #pr_open_message { |
|
369 | 369 | border: @border-thickness solid #fff; |
|
370 | 370 | border-radius: @border-radius; |
|
371 | 371 | padding: @padding-large-vertical @padding-large-vertical @padding-large-vertical 0; |
|
372 | 372 | text-align: right; |
|
373 | 373 | overflow: hidden; |
|
374 | 374 | } |
|
375 | 375 | |
|
376 | 376 | .pr-submit-button { |
|
377 | 377 | float: right; |
|
378 | 378 | margin: 0 0 0 5px; |
|
379 | 379 | } |
|
380 | 380 | |
|
381 | 381 | .pr-spacing-container { |
|
382 | 382 | padding: 20px; |
|
383 | 383 | clear: both |
|
384 | 384 | } |
|
385 | 385 | |
|
386 | 386 | #pr-description-input { |
|
387 | 387 | margin-bottom: 0; |
|
388 | 388 | } |
|
389 | 389 | |
|
390 | 390 | .pr-description-label { |
|
391 | 391 | vertical-align: top; |
|
392 | 392 | } |
|
393 | 393 | |
|
394 | 394 | .perms_section_head { |
|
395 | 395 | min-width: 625px; |
|
396 | 396 | |
|
397 | 397 | h2 { |
|
398 | 398 | margin-bottom: 0; |
|
399 | 399 | } |
|
400 | 400 | |
|
401 | 401 | .label-checkbox { |
|
402 | 402 | float: left; |
|
403 | 403 | } |
|
404 | 404 | |
|
405 | 405 | &.field { |
|
406 | 406 | margin: @space 0 @padding; |
|
407 | 407 | } |
|
408 | 408 | |
|
409 | 409 | &:first-child.field { |
|
410 | 410 | margin-top: 0; |
|
411 | 411 | |
|
412 | 412 | .label { |
|
413 | 413 | margin-top: 0; |
|
414 | 414 | padding-top: 0; |
|
415 | 415 | } |
|
416 | 416 | |
|
417 | 417 | .radios { |
|
418 | 418 | padding-top: 0; |
|
419 | 419 | } |
|
420 | 420 | } |
|
421 | 421 | |
|
422 | 422 | .radios { |
|
423 | 423 | float: right; |
|
424 | 424 | position: relative; |
|
425 | 425 | width: 405px; |
|
426 | 426 | } |
|
427 | 427 | } |
|
428 | 428 | |
|
429 | 429 | //--- MODULES ------------------// |
|
430 | 430 | |
|
431 | 431 | |
|
432 | 432 | // Fixed Sidebar Column |
|
433 | 433 | .sidebar-col-wrapper { |
|
434 | 434 | padding-left: @sidebar-all-width; |
|
435 | 435 | |
|
436 | 436 | .sidebar { |
|
437 | 437 | width: @sidebar-width; |
|
438 | 438 | margin-left: -@sidebar-all-width; |
|
439 | 439 | } |
|
440 | 440 | } |
|
441 | 441 | |
|
442 | 442 | .sidebar-col-wrapper.scw-small { |
|
443 | 443 | padding-left: @sidebar-small-all-width; |
|
444 | 444 | |
|
445 | 445 | .sidebar { |
|
446 | 446 | width: @sidebar-small-width; |
|
447 | 447 | margin-left: -@sidebar-small-all-width; |
|
448 | 448 | } |
|
449 | 449 | } |
|
450 | 450 | |
|
451 | 451 | |
|
452 | 452 | // FOOTER |
|
453 | 453 | #footer { |
|
454 | 454 | padding: 0; |
|
455 | 455 | text-align: center; |
|
456 | 456 | vertical-align: middle; |
|
457 | 457 | color: @grey2; |
|
458 | 458 | background-color: @grey6; |
|
459 | 459 | |
|
460 | 460 | p { |
|
461 | 461 | margin: 0; |
|
462 | 462 | padding: 1em; |
|
463 | 463 | line-height: 1em; |
|
464 | 464 | } |
|
465 | 465 | |
|
466 | 466 | .server-instance { //server instance |
|
467 | 467 | display: none; |
|
468 | 468 | } |
|
469 | 469 | |
|
470 | 470 | .title { |
|
471 | 471 | float: none; |
|
472 | 472 | margin: 0 auto; |
|
473 | 473 | } |
|
474 | 474 | } |
|
475 | 475 | |
|
476 | 476 | button.close { |
|
477 | 477 | padding: 0; |
|
478 | 478 | cursor: pointer; |
|
479 | 479 | background: transparent; |
|
480 | 480 | border: 0; |
|
481 | 481 | .box-shadow(none); |
|
482 | 482 | -webkit-appearance: none; |
|
483 | 483 | } |
|
484 | 484 | |
|
485 | 485 | .close { |
|
486 | 486 | float: right; |
|
487 | 487 | font-size: 21px; |
|
488 | 488 | font-family: @text-bootstrap; |
|
489 | 489 | line-height: 1em; |
|
490 | 490 | font-weight: bold; |
|
491 | 491 | color: @grey2; |
|
492 | 492 | |
|
493 | 493 | &:hover, |
|
494 | 494 | &:focus { |
|
495 | 495 | color: @grey1; |
|
496 | 496 | text-decoration: none; |
|
497 | 497 | cursor: pointer; |
|
498 | 498 | } |
|
499 | 499 | } |
|
500 | 500 | |
|
501 | 501 | // GRID |
|
502 | 502 | .sorting, |
|
503 | 503 | .sorting_desc, |
|
504 | 504 | .sorting_asc { |
|
505 | 505 | cursor: pointer; |
|
506 | 506 | } |
|
507 | 507 | .sorting_desc:after { |
|
508 | 508 | content: "\00A0\25B2"; |
|
509 | 509 | font-size: .75em; |
|
510 | 510 | } |
|
511 | 511 | .sorting_asc:after { |
|
512 | 512 | content: "\00A0\25BC"; |
|
513 | 513 | font-size: .68em; |
|
514 | 514 | } |
|
515 | 515 | |
|
516 | 516 | |
|
517 | 517 | .user_auth_tokens { |
|
518 | 518 | |
|
519 | 519 | &.truncate { |
|
520 | 520 | white-space: nowrap; |
|
521 | 521 | overflow: hidden; |
|
522 | 522 | text-overflow: ellipsis; |
|
523 | 523 | } |
|
524 | 524 | |
|
525 | 525 | .fields .field .input { |
|
526 | 526 | margin: 0; |
|
527 | 527 | } |
|
528 | 528 | |
|
529 | 529 | input#description { |
|
530 | 530 | width: 100px; |
|
531 | 531 | margin: 0; |
|
532 | 532 | } |
|
533 | 533 | |
|
534 | 534 | .drop-menu { |
|
535 | 535 | // TODO: johbo: Remove this, should work out of the box when |
|
536 | 536 | // having multiple inputs inline |
|
537 | 537 | margin: 0 0 0 5px; |
|
538 | 538 | } |
|
539 | 539 | } |
|
540 | 540 | #user_list_table { |
|
541 | 541 | .closed { |
|
542 | 542 | background-color: @grey6; |
|
543 | 543 | } |
|
544 | 544 | } |
|
545 | 545 | |
|
546 | 546 | |
|
547 | 547 | input { |
|
548 | 548 | &.disabled { |
|
549 | 549 | opacity: .5; |
|
550 | 550 | } |
|
551 | 551 | } |
|
552 | 552 | |
|
553 | 553 | // remove extra padding in firefox |
|
554 | 554 | input::-moz-focus-inner { border:0; padding:0 } |
|
555 | 555 | |
|
556 | 556 | .adjacent input { |
|
557 | 557 | margin-bottom: @padding; |
|
558 | 558 | } |
|
559 | 559 | |
|
560 | 560 | .permissions_boxes { |
|
561 | 561 | display: block; |
|
562 | 562 | } |
|
563 | 563 | |
|
564 | 564 | //TODO: lisa: this should be in tables |
|
565 | 565 | .show_more_col { |
|
566 | 566 | width: 20px; |
|
567 | 567 | } |
|
568 | 568 | |
|
569 | 569 | //FORMS |
|
570 | 570 | |
|
571 | 571 | .medium-inline, |
|
572 | 572 | input#description.medium-inline { |
|
573 | 573 | display: inline; |
|
574 | 574 | width: @medium-inline-input-width; |
|
575 | 575 | min-width: 100px; |
|
576 | 576 | } |
|
577 | 577 | |
|
578 | 578 | select { |
|
579 | 579 | //reset |
|
580 | 580 | -webkit-appearance: none; |
|
581 | 581 | -moz-appearance: none; |
|
582 | 582 | |
|
583 | 583 | display: inline-block; |
|
584 | 584 | height: 28px; |
|
585 | 585 | width: auto; |
|
586 | 586 | margin: 0 @padding @padding 0; |
|
587 | 587 | padding: 0 18px 0 8px; |
|
588 | 588 | line-height:1em; |
|
589 | 589 | font-size: @basefontsize; |
|
590 | 590 | border: @border-thickness solid @rcblue; |
|
591 | 591 | background:white url("../images/dt-arrow-dn.png") no-repeat 100% 50%; |
|
592 | 592 | color: @rcblue; |
|
593 | 593 | |
|
594 | 594 | &:after { |
|
595 | 595 | content: "\00A0\25BE"; |
|
596 | 596 | } |
|
597 | 597 | |
|
598 | 598 | &:focus { |
|
599 | 599 | outline: none; |
|
600 | 600 | } |
|
601 | 601 | } |
|
602 | 602 | |
|
603 | 603 | option { |
|
604 | 604 | &:focus { |
|
605 | 605 | outline: none; |
|
606 | 606 | } |
|
607 | 607 | } |
|
608 | 608 | |
|
609 | 609 | input, |
|
610 | 610 | textarea { |
|
611 | 611 | padding: @input-padding; |
|
612 | 612 | border: @input-border-thickness solid @border-highlight-color; |
|
613 | 613 | .border-radius (@border-radius); |
|
614 | 614 | font-family: @text-light; |
|
615 | 615 | font-size: @basefontsize; |
|
616 | 616 | |
|
617 | 617 | &.input-sm { |
|
618 | 618 | padding: 5px; |
|
619 | 619 | } |
|
620 | 620 | |
|
621 | 621 | &#description { |
|
622 | 622 | min-width: @input-description-minwidth; |
|
623 | 623 | min-height: 1em; |
|
624 | 624 | padding: 10px; |
|
625 | 625 | } |
|
626 | 626 | } |
|
627 | 627 | |
|
628 | 628 | .field-sm { |
|
629 | 629 | input, |
|
630 | 630 | textarea { |
|
631 | 631 | padding: 5px; |
|
632 | 632 | } |
|
633 | 633 | } |
|
634 | 634 | |
|
635 | 635 | textarea { |
|
636 | 636 | display: block; |
|
637 | 637 | clear: both; |
|
638 | 638 | width: 100%; |
|
639 | 639 | min-height: 100px; |
|
640 | 640 | margin-bottom: @padding; |
|
641 | 641 | .box-sizing(border-box); |
|
642 | 642 | overflow: auto; |
|
643 | 643 | } |
|
644 | 644 | |
|
645 | 645 | label { |
|
646 | 646 | font-family: @text-light; |
|
647 | 647 | } |
|
648 | 648 | |
|
649 | 649 | // GRAVATARS |
|
650 | 650 | // centers gravatar on username to the right |
|
651 | 651 | |
|
652 | 652 | .gravatar { |
|
653 | 653 | display: inline; |
|
654 | 654 | min-width: 16px; |
|
655 | 655 | min-height: 16px; |
|
656 | 656 | margin: -5px 0; |
|
657 | 657 | padding: 0; |
|
658 | 658 | line-height: 1em; |
|
659 | 659 | border: 1px solid @grey4; |
|
660 | 660 | |
|
661 | 661 | &.gravatar-large { |
|
662 | 662 | margin: -0.5em .25em -0.5em 0; |
|
663 | 663 | } |
|
664 | 664 | |
|
665 | 665 | & + .user { |
|
666 | 666 | display: inline; |
|
667 | 667 | margin: 0; |
|
668 | 668 | padding: 0 0 0 .17em; |
|
669 | 669 | line-height: 1em; |
|
670 | 670 | } |
|
671 | 671 | } |
|
672 | 672 | |
|
673 | 673 | .rc-user { // gravatar + user wrapper |
|
674 | float: left; | |
|
674 | 675 |
|
|
675 | 676 | min-width: 100px; |
|
676 | 677 | max-width: 200px; |
|
677 | 678 | min-height: (@gravatar-size + @border-thickness * 2); // account for border |
|
678 | 679 | display: block; |
|
679 | 680 | padding: 0 0 0 (@gravatar-size + @basefontsize/2 + @border-thickness * 2); |
|
680 | 681 | |
|
681 | 682 | |
|
682 | 683 | .gravatar { |
|
683 | 684 | display: block; |
|
684 | 685 | position: absolute; |
|
685 | 686 | top: 0; |
|
686 | 687 | left: 0; |
|
687 | 688 | min-width: @gravatar-size; |
|
688 | 689 | min-height: @gravatar-size; |
|
689 | 690 | margin: 0; |
|
690 | 691 | } |
|
691 | 692 | |
|
692 | 693 | .user { |
|
693 | 694 | display: block; |
|
694 | 695 | max-width: 175px; |
|
695 | 696 | padding-top: 2px; |
|
696 | 697 | overflow: hidden; |
|
697 | 698 | text-overflow: ellipsis; |
|
698 | 699 | } |
|
699 | 700 | } |
|
700 | 701 | |
|
701 | 702 | .gist-gravatar, |
|
702 | 703 | .journal_container { |
|
703 | 704 | .gravatar-large { |
|
704 | 705 | margin: 0 .5em -10px 0; |
|
705 | 706 | } |
|
706 | 707 | } |
|
707 | 708 | |
|
708 | 709 | |
|
709 | 710 | // ADMIN SETTINGS |
|
710 | 711 | |
|
711 | 712 | // Tag Patterns |
|
712 | 713 | .tag_patterns { |
|
713 | 714 | .tag_input { |
|
714 | 715 | margin-bottom: @padding; |
|
715 | 716 | } |
|
716 | 717 | } |
|
717 | 718 | |
|
718 | 719 | .locked_input { |
|
719 | 720 | position: relative; |
|
720 | 721 | |
|
721 | 722 | input { |
|
722 | 723 | display: inline; |
|
723 | 724 | margin-top: 3px; |
|
724 | 725 | } |
|
725 | 726 | |
|
726 | 727 | br { |
|
727 | 728 | display: none; |
|
728 | 729 | } |
|
729 | 730 | |
|
730 | 731 | .error-message { |
|
731 | 732 | float: left; |
|
732 | 733 | width: 100%; |
|
733 | 734 | } |
|
734 | 735 | |
|
735 | 736 | .lock_input_button { |
|
736 | 737 | display: inline; |
|
737 | 738 | } |
|
738 | 739 | |
|
739 | 740 | .help-block { |
|
740 | 741 | clear: both; |
|
741 | 742 | } |
|
742 | 743 | } |
|
743 | 744 | |
|
744 | 745 | // Notifications |
|
745 | 746 | |
|
746 | 747 | .notifications_buttons { |
|
747 | 748 | margin: 0 0 @space 0; |
|
748 | 749 | padding: 0; |
|
749 | 750 | |
|
750 | 751 | .btn { |
|
751 | 752 | display: inline-block; |
|
752 | 753 | } |
|
753 | 754 | } |
|
754 | 755 | |
|
755 | 756 | .notification-list { |
|
756 | 757 | |
|
757 | 758 | div { |
|
758 | 759 | display: inline-block; |
|
759 | 760 | vertical-align: middle; |
|
760 | 761 | } |
|
761 | 762 | |
|
762 | 763 | .container { |
|
763 | 764 | display: block; |
|
764 | 765 | margin: 0 0 @padding 0; |
|
765 | 766 | } |
|
766 | 767 | |
|
767 | 768 | .delete-notifications { |
|
768 | 769 | margin-left: @padding; |
|
769 | 770 | text-align: right; |
|
770 | 771 | cursor: pointer; |
|
771 | 772 | } |
|
772 | 773 | |
|
773 | 774 | .read-notifications { |
|
774 | 775 | margin-left: @padding/2; |
|
775 | 776 | text-align: right; |
|
776 | 777 | width: 35px; |
|
777 | 778 | cursor: pointer; |
|
778 | 779 | } |
|
779 | 780 | |
|
780 | 781 | .icon-minus-sign { |
|
781 | 782 | color: @alert2; |
|
782 | 783 | } |
|
783 | 784 | |
|
784 | 785 | .icon-ok-sign { |
|
785 | 786 | color: @alert1; |
|
786 | 787 | } |
|
787 | 788 | } |
|
788 | 789 | |
|
789 | 790 | .user_settings { |
|
790 | 791 | float: left; |
|
791 | 792 | clear: both; |
|
792 | 793 | display: block; |
|
793 | 794 | width: 100%; |
|
794 | 795 | |
|
795 | 796 | .gravatar_box { |
|
796 | 797 | margin-bottom: @padding; |
|
797 | 798 | |
|
798 | 799 | &:after { |
|
799 | 800 | content: " "; |
|
800 | 801 | clear: both; |
|
801 | 802 | width: 100%; |
|
802 | 803 | } |
|
803 | 804 | } |
|
804 | 805 | |
|
805 | 806 | .fields .field { |
|
806 | 807 | clear: both; |
|
807 | 808 | } |
|
808 | 809 | } |
|
809 | 810 | |
|
810 | 811 | .advanced_settings { |
|
811 | 812 | margin-bottom: @space; |
|
812 | 813 | |
|
813 | 814 | .help-block { |
|
814 | 815 | margin-left: 0; |
|
815 | 816 | } |
|
816 | 817 | |
|
817 | 818 | button + .help-block { |
|
818 | 819 | margin-top: @padding; |
|
819 | 820 | } |
|
820 | 821 | } |
|
821 | 822 | |
|
822 | 823 | // admin settings radio buttons and labels |
|
823 | 824 | .label-2 { |
|
824 | 825 | float: left; |
|
825 | 826 | width: @label2-width; |
|
826 | 827 | |
|
827 | 828 | label { |
|
828 | 829 | color: @grey1; |
|
829 | 830 | } |
|
830 | 831 | } |
|
831 | 832 | .checkboxes { |
|
832 | 833 | float: left; |
|
833 | 834 | width: @checkboxes-width; |
|
834 | 835 | margin-bottom: @padding; |
|
835 | 836 | |
|
836 | 837 | .checkbox { |
|
837 | 838 | width: 100%; |
|
838 | 839 | |
|
839 | 840 | label { |
|
840 | 841 | margin: 0; |
|
841 | 842 | padding: 0; |
|
842 | 843 | } |
|
843 | 844 | } |
|
844 | 845 | |
|
845 | 846 | .checkbox + .checkbox { |
|
846 | 847 | display: inline-block; |
|
847 | 848 | } |
|
848 | 849 | |
|
849 | 850 | label { |
|
850 | 851 | margin-right: 1em; |
|
851 | 852 | } |
|
852 | 853 | } |
|
853 | 854 | |
|
854 | 855 | // CHANGELOG |
|
855 | 856 | .container_header { |
|
856 | 857 | float: left; |
|
857 | 858 | display: block; |
|
858 | 859 | width: 100%; |
|
859 | 860 | margin: @padding 0 @padding; |
|
860 | 861 | |
|
861 | 862 | #filter_changelog { |
|
862 | 863 | float: left; |
|
863 | 864 | margin-right: @padding; |
|
864 | 865 | } |
|
865 | 866 | |
|
866 | 867 | .breadcrumbs_light { |
|
867 | 868 | display: inline-block; |
|
868 | 869 | } |
|
869 | 870 | } |
|
870 | 871 | |
|
871 | 872 | .info_box { |
|
872 | 873 | float: right; |
|
873 | 874 | } |
|
874 | 875 | |
|
875 | 876 | |
|
876 | 877 | #graph_nodes { |
|
877 | 878 | padding-top: 43px; |
|
878 | 879 | } |
|
879 | 880 | |
|
880 | 881 | #graph_content{ |
|
881 | 882 | |
|
882 | 883 | // adjust for table headers so that graph renders properly |
|
883 | 884 | // #graph_nodes padding - table cell padding |
|
884 | 885 | padding-top: (@space - (@basefontsize * 2.4)); |
|
885 | 886 | |
|
886 | 887 | &.graph_full_width { |
|
887 | 888 | width: 100%; |
|
888 | 889 | max-width: 100%; |
|
889 | 890 | } |
|
890 | 891 | } |
|
891 | 892 | |
|
892 | 893 | #graph { |
|
893 | 894 | .flag_status { |
|
894 | 895 | margin: 0; |
|
895 | 896 | } |
|
896 | 897 | |
|
897 | 898 | .pagination-left { |
|
898 | 899 | float: left; |
|
899 | 900 | clear: both; |
|
900 | 901 | } |
|
901 | 902 | |
|
902 | 903 | .log-container { |
|
903 | 904 | max-width: 345px; |
|
904 | 905 | |
|
905 | 906 | .message{ |
|
906 | 907 | max-width: 340px; |
|
907 | 908 | } |
|
908 | 909 | } |
|
909 | 910 | |
|
910 | 911 | .graph-col-wrapper { |
|
911 | 912 | padding-left: 110px; |
|
912 | 913 | |
|
913 | 914 | #graph_nodes { |
|
914 | 915 | width: 100px; |
|
915 | 916 | margin-left: -110px; |
|
916 | 917 | float: left; |
|
917 | 918 | clear: left; |
|
918 | 919 | } |
|
919 | 920 | } |
|
920 | 921 | } |
|
921 | 922 | |
|
922 | 923 | #filter_changelog { |
|
923 | 924 | float: left; |
|
924 | 925 | } |
|
925 | 926 | |
|
926 | 927 | |
|
927 | 928 | //--- THEME ------------------// |
|
928 | 929 | |
|
929 | 930 | #logo { |
|
930 | 931 | float: left; |
|
931 | 932 | margin: 9px 0 0 0; |
|
932 | 933 | |
|
933 | 934 | .header { |
|
934 | 935 | background-color: transparent; |
|
935 | 936 | } |
|
936 | 937 | |
|
937 | 938 | a { |
|
938 | 939 | display: inline-block; |
|
939 | 940 | } |
|
940 | 941 | |
|
941 | 942 | img { |
|
942 | 943 | height:30px; |
|
943 | 944 | } |
|
944 | 945 | } |
|
945 | 946 | |
|
946 | 947 | .logo-wrapper { |
|
947 | 948 | float:left; |
|
948 | 949 | } |
|
949 | 950 | |
|
950 | 951 | .branding{ |
|
951 | 952 | float: left; |
|
952 | 953 | padding: 9px 2px; |
|
953 | 954 | line-height: 1em; |
|
954 | 955 | font-size: @navigation-fontsize; |
|
955 | 956 | } |
|
956 | 957 | |
|
957 | 958 | img { |
|
958 | 959 | border: none; |
|
959 | 960 | outline: none; |
|
960 | 961 | } |
|
961 | 962 | user-profile-header |
|
962 | 963 | label { |
|
963 | 964 | |
|
964 | 965 | input[type="checkbox"] { |
|
965 | 966 | margin-right: 1em; |
|
966 | 967 | } |
|
967 | 968 | input[type="radio"] { |
|
968 | 969 | margin-right: 1em; |
|
969 | 970 | } |
|
970 | 971 | } |
|
971 | 972 | |
|
972 | 973 | .flag_status { |
|
973 | 974 | margin: 2px 8px 6px 2px; |
|
974 | 975 | &.under_review { |
|
975 | 976 | .circle(5px, @alert3); |
|
976 | 977 | } |
|
977 | 978 | &.approved { |
|
978 | 979 | .circle(5px, @alert1); |
|
979 | 980 | } |
|
980 | 981 | &.rejected, |
|
981 | 982 | &.forced_closed{ |
|
982 | 983 | .circle(5px, @alert2); |
|
983 | 984 | } |
|
984 | 985 | &.not_reviewed { |
|
985 | 986 | .circle(5px, @grey5); |
|
986 | 987 | } |
|
987 | 988 | } |
|
988 | 989 | |
|
989 | 990 | .flag_status_comment_box { |
|
990 | 991 | margin: 5px 6px 0px 2px; |
|
991 | 992 | } |
|
992 | 993 | .test_pattern_preview { |
|
993 | 994 | margin: @space 0; |
|
994 | 995 | |
|
995 | 996 | p { |
|
996 | 997 | margin-bottom: 0; |
|
997 | 998 | border-bottom: @border-thickness solid @border-default-color; |
|
998 | 999 | color: @grey3; |
|
999 | 1000 | } |
|
1000 | 1001 | |
|
1001 | 1002 | .btn { |
|
1002 | 1003 | margin-bottom: @padding; |
|
1003 | 1004 | } |
|
1004 | 1005 | } |
|
1005 | 1006 | #test_pattern_result { |
|
1006 | 1007 | display: none; |
|
1007 | 1008 | &:extend(pre); |
|
1008 | 1009 | padding: .9em; |
|
1009 | 1010 | color: @grey3; |
|
1010 | 1011 | background-color: @grey7; |
|
1011 | 1012 | border-right: @border-thickness solid @border-default-color; |
|
1012 | 1013 | border-bottom: @border-thickness solid @border-default-color; |
|
1013 | 1014 | border-left: @border-thickness solid @border-default-color; |
|
1014 | 1015 | } |
|
1015 | 1016 | |
|
1016 | 1017 | #repo_vcs_settings { |
|
1017 | 1018 | #inherit_overlay_vcs_default { |
|
1018 | 1019 | display: none; |
|
1019 | 1020 | } |
|
1020 | 1021 | #inherit_overlay_vcs_custom { |
|
1021 | 1022 | display: custom; |
|
1022 | 1023 | } |
|
1023 | 1024 | &.inherited { |
|
1024 | 1025 | #inherit_overlay_vcs_default { |
|
1025 | 1026 | display: block; |
|
1026 | 1027 | } |
|
1027 | 1028 | #inherit_overlay_vcs_custom { |
|
1028 | 1029 | display: none; |
|
1029 | 1030 | } |
|
1030 | 1031 | } |
|
1031 | 1032 | } |
|
1032 | 1033 | |
|
1033 | 1034 | .issue-tracker-link { |
|
1034 | 1035 | color: @rcblue; |
|
1035 | 1036 | } |
|
1036 | 1037 | |
|
1037 | 1038 | // Issue Tracker Table Show/Hide |
|
1038 | 1039 | #repo_issue_tracker { |
|
1039 | 1040 | #inherit_overlay { |
|
1040 | 1041 | display: none; |
|
1041 | 1042 | } |
|
1042 | 1043 | #custom_overlay { |
|
1043 | 1044 | display: custom; |
|
1044 | 1045 | } |
|
1045 | 1046 | &.inherited { |
|
1046 | 1047 | #inherit_overlay { |
|
1047 | 1048 | display: block; |
|
1048 | 1049 | } |
|
1049 | 1050 | #custom_overlay { |
|
1050 | 1051 | display: none; |
|
1051 | 1052 | } |
|
1052 | 1053 | } |
|
1053 | 1054 | } |
|
1054 | 1055 | table.issuetracker { |
|
1055 | 1056 | &.readonly { |
|
1056 | 1057 | tr, td { |
|
1057 | 1058 | color: @grey3; |
|
1058 | 1059 | } |
|
1059 | 1060 | } |
|
1060 | 1061 | .edit { |
|
1061 | 1062 | display: none; |
|
1062 | 1063 | } |
|
1063 | 1064 | .editopen { |
|
1064 | 1065 | .edit { |
|
1065 | 1066 | display: inline; |
|
1066 | 1067 | } |
|
1067 | 1068 | .entry { |
|
1068 | 1069 | display: none; |
|
1069 | 1070 | } |
|
1070 | 1071 | } |
|
1071 | 1072 | tr td.td-action { |
|
1072 | 1073 | min-width: 117px; |
|
1073 | 1074 | } |
|
1074 | 1075 | td input { |
|
1075 | 1076 | max-width: none; |
|
1076 | 1077 | min-width: 30px; |
|
1077 | 1078 | width: 80%; |
|
1078 | 1079 | } |
|
1079 | 1080 | .issuetracker_pref input { |
|
1080 | 1081 | width: 40%; |
|
1081 | 1082 | } |
|
1082 | 1083 | input.edit_issuetracker_update { |
|
1083 | 1084 | margin-right: 0; |
|
1084 | 1085 | width: auto; |
|
1085 | 1086 | } |
|
1086 | 1087 | } |
|
1087 | 1088 | |
|
1088 | 1089 | //Permissions Settings |
|
1089 | 1090 | #add_perm { |
|
1090 | 1091 | margin: 0 0 @padding; |
|
1091 | 1092 | cursor: pointer; |
|
1092 | 1093 | } |
|
1093 | 1094 | |
|
1094 | 1095 | .perm_ac { |
|
1095 | 1096 | input { |
|
1096 | 1097 | width: 95%; |
|
1097 | 1098 | } |
|
1098 | 1099 | } |
|
1099 | 1100 | |
|
1100 | 1101 | .autocomplete-suggestions { |
|
1101 | 1102 | width: auto !important; // overrides autocomplete.js |
|
1102 | 1103 | margin: 0; |
|
1103 | 1104 | border: @border-thickness solid @rcblue; |
|
1104 | 1105 | border-radius: @border-radius; |
|
1105 | 1106 | color: @rcblue; |
|
1106 | 1107 | background-color: white; |
|
1107 | 1108 | } |
|
1108 | 1109 | .autocomplete-selected { |
|
1109 | 1110 | background: #F0F0F0; |
|
1110 | 1111 | } |
|
1111 | 1112 | .ac-container-wrap { |
|
1112 | 1113 | margin: 0; |
|
1113 | 1114 | padding: 8px; |
|
1114 | 1115 | border-bottom: @border-thickness solid @rclightblue; |
|
1115 | 1116 | list-style-type: none; |
|
1116 | 1117 | cursor: pointer; |
|
1117 | 1118 | |
|
1118 | 1119 | &:hover { |
|
1119 | 1120 | background-color: @rclightblue; |
|
1120 | 1121 | } |
|
1121 | 1122 | |
|
1122 | 1123 | img { |
|
1123 | 1124 | margin-right: 1em; |
|
1124 | 1125 | } |
|
1125 | 1126 | |
|
1126 | 1127 | strong { |
|
1127 | 1128 | font-weight: normal; |
|
1128 | 1129 | } |
|
1129 | 1130 | } |
|
1130 | 1131 | |
|
1131 | 1132 | // Settings Dropdown |
|
1132 | 1133 | .user-menu .container { |
|
1133 | 1134 | padding: 0 4px; |
|
1134 | 1135 | margin: 0; |
|
1135 | 1136 | } |
|
1136 | 1137 | |
|
1137 | 1138 | .user-menu .gravatar { |
|
1138 | 1139 | cursor: pointer; |
|
1139 | 1140 | } |
|
1140 | 1141 | |
|
1141 | 1142 | .codeblock { |
|
1142 | 1143 | margin-bottom: @padding; |
|
1143 | 1144 | clear: both; |
|
1144 | 1145 | |
|
1145 | 1146 | .stats{ |
|
1146 | 1147 | overflow: hidden; |
|
1147 | 1148 | } |
|
1148 | 1149 | |
|
1149 | 1150 | .message{ |
|
1150 | 1151 | textarea{ |
|
1151 | 1152 | margin: 0; |
|
1152 | 1153 | } |
|
1153 | 1154 | } |
|
1154 | 1155 | |
|
1155 | 1156 | .code-header { |
|
1156 | 1157 | .stats { |
|
1157 | 1158 | line-height: 2em; |
|
1158 | 1159 | |
|
1159 | 1160 | .revision_id { |
|
1160 | 1161 | margin-left: 0; |
|
1161 | 1162 | } |
|
1162 | 1163 | .buttons { |
|
1163 | 1164 | padding-right: 0; |
|
1164 | 1165 | } |
|
1165 | 1166 | } |
|
1166 | 1167 | |
|
1167 | 1168 | .item{ |
|
1168 | 1169 | margin-right: 0.5em; |
|
1169 | 1170 | } |
|
1170 | 1171 | } |
|
1171 | 1172 | |
|
1172 | 1173 | #editor_container{ |
|
1173 | 1174 | position: relative; |
|
1174 | 1175 | margin: @padding; |
|
1175 | 1176 | } |
|
1176 | 1177 | } |
|
1177 | 1178 | |
|
1178 | 1179 | #file_history_container { |
|
1179 | 1180 | display: none; |
|
1180 | 1181 | } |
|
1181 | 1182 | |
|
1182 | 1183 | .file-history-inner { |
|
1183 | 1184 | margin-bottom: 10px; |
|
1184 | 1185 | } |
|
1185 | 1186 | |
|
1186 | 1187 | // Pull Requests |
|
1187 | 1188 | .summary-details { |
|
1188 | 1189 | width: 72%; |
|
1189 | 1190 | } |
|
1190 | 1191 | .pr-summary { |
|
1191 | 1192 | border-bottom: @border-thickness solid @grey5; |
|
1192 | 1193 | margin-bottom: @space; |
|
1193 | 1194 | } |
|
1194 | 1195 | .reviewers-title { |
|
1195 | 1196 | width: 25%; |
|
1196 | 1197 | min-width: 200px; |
|
1197 | 1198 | } |
|
1198 | 1199 | .reviewers { |
|
1199 | 1200 | width: 25%; |
|
1200 | 1201 | min-width: 200px; |
|
1201 | 1202 | } |
|
1202 | 1203 | .reviewers ul li { |
|
1203 | 1204 | position: relative; |
|
1204 | 1205 | width: 100%; |
|
1205 | 1206 | margin-bottom: 8px; |
|
1206 | 1207 | } |
|
1207 | 1208 | .reviewers_member { |
|
1208 | 1209 | width: 100%; |
|
1209 | 1210 | overflow: auto; |
|
1210 | 1211 | } |
|
1211 | 1212 | .reviewer_status { |
|
1212 | 1213 | display: inline-block; |
|
1213 | 1214 | vertical-align: top; |
|
1214 | 1215 | width: 7%; |
|
1215 | 1216 | min-width: 20px; |
|
1216 | 1217 | height: 1.2em; |
|
1217 | 1218 | margin-top: 3px; |
|
1218 | 1219 | line-height: 1em; |
|
1219 | 1220 | } |
|
1220 | 1221 | |
|
1221 | 1222 | .reviewer_name { |
|
1222 | 1223 | display: inline-block; |
|
1223 | 1224 | max-width: 83%; |
|
1224 | 1225 | padding-right: 20px; |
|
1225 | 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 | 1239 | .reviewer_member_remove { |
|
1229 | 1240 | position: absolute; |
|
1230 | 1241 | right: 0; |
|
1231 | 1242 | top: 0; |
|
1232 | 1243 | width: 16px; |
|
1233 | 1244 | margin-bottom: 10px; |
|
1234 | 1245 | padding: 0; |
|
1235 | 1246 | color: black; |
|
1236 | 1247 | } |
|
1237 | 1248 | .reviewer_member_status { |
|
1238 | 1249 | margin-top: 5px; |
|
1239 | 1250 | } |
|
1240 | 1251 | .pr-summary #summary{ |
|
1241 | 1252 | width: 100%; |
|
1242 | 1253 | } |
|
1243 | 1254 | .pr-summary .action_button:hover { |
|
1244 | 1255 | border: 0; |
|
1245 | 1256 | cursor: pointer; |
|
1246 | 1257 | } |
|
1247 | 1258 | .pr-details-title { |
|
1248 | 1259 | padding-bottom: 8px; |
|
1249 | 1260 | border-bottom: @border-thickness solid @grey5; |
|
1250 | 1261 | .action_button { |
|
1251 | 1262 | color: @rcblue; |
|
1252 | 1263 | } |
|
1253 | 1264 | } |
|
1254 | 1265 | .pr-details-content { |
|
1255 | 1266 | margin-top: @textmargin; |
|
1256 |
margin-bottom: @textmargin |
|
|
1267 | margin-bottom: @textmargin; | |
|
1257 | 1268 | } |
|
1258 | 1269 | .pr-description { |
|
1259 | 1270 | white-space:pre-wrap; |
|
1260 | 1271 | } |
|
1261 | 1272 | .group_members { |
|
1262 | 1273 | margin-top: 0; |
|
1263 | 1274 | padding: 0; |
|
1264 | 1275 | list-style: outside none none; |
|
1265 | 1276 | } |
|
1266 | 1277 | .reviewer_ac .ac-input { |
|
1267 | 1278 | width: 92%; |
|
1268 | 1279 | margin-bottom: 1em; |
|
1269 | 1280 | } |
|
1270 | 1281 | #update_commits { |
|
1271 | 1282 | float: right; |
|
1272 | 1283 | } |
|
1273 | 1284 | .compare_view_commits tr{ |
|
1274 | 1285 | height: 20px; |
|
1275 | 1286 | } |
|
1276 | 1287 | .compare_view_commits td { |
|
1277 | 1288 | vertical-align: top; |
|
1278 | 1289 | padding-top: 10px; |
|
1279 | 1290 | } |
|
1280 | 1291 | .compare_view_commits .author { |
|
1281 | 1292 | margin-left: 5px; |
|
1282 | 1293 | } |
|
1283 | 1294 | |
|
1284 | 1295 | .compare_view_files { |
|
1285 | 1296 | width: 100%; |
|
1286 | 1297 | |
|
1287 | 1298 | td { |
|
1288 | 1299 | vertical-align: middle; |
|
1289 | 1300 | } |
|
1290 | 1301 | } |
|
1291 | 1302 | |
|
1292 | 1303 | .compare_view_filepath { |
|
1293 | 1304 | color: @grey1; |
|
1294 | 1305 | } |
|
1295 | 1306 | |
|
1296 | 1307 | .show_more { |
|
1297 | 1308 | display: inline-block; |
|
1298 | 1309 | position: relative; |
|
1299 | 1310 | vertical-align: middle; |
|
1300 | 1311 | width: 4px; |
|
1301 | 1312 | height: @basefontsize; |
|
1302 | 1313 | |
|
1303 | 1314 | &:after { |
|
1304 | 1315 | content: "\00A0\25BE"; |
|
1305 | 1316 | display: inline-block; |
|
1306 | 1317 | width:10px; |
|
1307 | 1318 | line-height: 5px; |
|
1308 | 1319 | font-size: 12px; |
|
1309 | 1320 | cursor: pointer; |
|
1310 | 1321 | } |
|
1311 | 1322 | } |
|
1312 | 1323 | |
|
1313 | 1324 | .journal_more .show_more { |
|
1314 | 1325 | display: inline; |
|
1315 | 1326 | |
|
1316 | 1327 | &:after { |
|
1317 | 1328 | content: none; |
|
1318 | 1329 | } |
|
1319 | 1330 | } |
|
1320 | 1331 | |
|
1321 | 1332 | .open .show_more:after, |
|
1322 | 1333 | .select2-dropdown-open .show_more:after { |
|
1323 | 1334 | .rotate(180deg); |
|
1324 | 1335 | margin-left: 4px; |
|
1325 | 1336 | } |
|
1326 | 1337 | |
|
1327 | 1338 | |
|
1328 | 1339 | .compare_view_commits .collapse_commit:after { |
|
1329 | 1340 | cursor: pointer; |
|
1330 | 1341 | content: "\00A0\25B4"; |
|
1331 | 1342 | margin-left: -3px; |
|
1332 | 1343 | font-size: 17px; |
|
1333 | 1344 | color: @grey4; |
|
1334 | 1345 | } |
|
1335 | 1346 | |
|
1336 | 1347 | .diff_links { |
|
1337 | 1348 | margin-left: 8px; |
|
1338 | 1349 | } |
|
1339 | 1350 | |
|
1340 | 1351 | p.ancestor { |
|
1341 | 1352 | margin: @padding 0; |
|
1342 | 1353 | } |
|
1343 | 1354 | |
|
1344 | 1355 | .cs_icon_td input[type="checkbox"] { |
|
1345 | 1356 | display: none; |
|
1346 | 1357 | } |
|
1347 | 1358 | |
|
1348 | 1359 | .cs_icon_td .expand_file_icon:after { |
|
1349 | 1360 | cursor: pointer; |
|
1350 | 1361 | content: "\00A0\25B6"; |
|
1351 | 1362 | font-size: 12px; |
|
1352 | 1363 | color: @grey4; |
|
1353 | 1364 | } |
|
1354 | 1365 | |
|
1355 | 1366 | .cs_icon_td .collapse_file_icon:after { |
|
1356 | 1367 | cursor: pointer; |
|
1357 | 1368 | content: "\00A0\25BC"; |
|
1358 | 1369 | font-size: 12px; |
|
1359 | 1370 | color: @grey4; |
|
1360 | 1371 | } |
|
1361 | 1372 | |
|
1362 | 1373 | /*new binary |
|
1363 | 1374 | NEW_FILENODE = 1 |
|
1364 | 1375 | DEL_FILENODE = 2 |
|
1365 | 1376 | MOD_FILENODE = 3 |
|
1366 | 1377 | RENAMED_FILENODE = 4 |
|
1367 | 1378 | COPIED_FILENODE = 5 |
|
1368 | 1379 | CHMOD_FILENODE = 6 |
|
1369 | 1380 | BIN_FILENODE = 7 |
|
1370 | 1381 | */ |
|
1371 | 1382 | .cs_files_expand { |
|
1372 | 1383 | font-size: @basefontsize + 5px; |
|
1373 | 1384 | line-height: 1.8em; |
|
1374 | 1385 | float: right; |
|
1375 | 1386 | } |
|
1376 | 1387 | |
|
1377 | 1388 | .cs_files_expand span{ |
|
1378 | 1389 | color: @rcblue; |
|
1379 | 1390 | cursor: pointer; |
|
1380 | 1391 | } |
|
1381 | 1392 | .cs_files { |
|
1382 | 1393 | clear: both; |
|
1383 | 1394 | padding-bottom: @padding; |
|
1384 | 1395 | |
|
1385 | 1396 | .cur_cs { |
|
1386 | 1397 | margin: 10px 2px; |
|
1387 | 1398 | font-weight: bold; |
|
1388 | 1399 | } |
|
1389 | 1400 | |
|
1390 | 1401 | .node { |
|
1391 | 1402 | float: left; |
|
1392 | 1403 | } |
|
1393 | 1404 | |
|
1394 | 1405 | .changes { |
|
1395 | 1406 | float: right; |
|
1396 | 1407 | color: white; |
|
1397 | 1408 | font-size: @basefontsize - 4px; |
|
1398 | 1409 | margin-top: 4px; |
|
1399 | 1410 | opacity: 0.6; |
|
1400 | 1411 | filter: Alpha(opacity=60); /* IE8 and earlier */ |
|
1401 | 1412 | |
|
1402 | 1413 | .added { |
|
1403 | 1414 | background-color: @alert1; |
|
1404 | 1415 | float: left; |
|
1405 | 1416 | text-align: center; |
|
1406 | 1417 | } |
|
1407 | 1418 | |
|
1408 | 1419 | .deleted { |
|
1409 | 1420 | background-color: @alert2; |
|
1410 | 1421 | float: left; |
|
1411 | 1422 | text-align: center; |
|
1412 | 1423 | } |
|
1413 | 1424 | |
|
1414 | 1425 | .bin { |
|
1415 | 1426 | background-color: @alert1; |
|
1416 | 1427 | text-align: center; |
|
1417 | 1428 | } |
|
1418 | 1429 | |
|
1419 | 1430 | /*new binary*/ |
|
1420 | 1431 | .bin.bin1 { |
|
1421 | 1432 | background-color: @alert1; |
|
1422 | 1433 | text-align: center; |
|
1423 | 1434 | } |
|
1424 | 1435 | |
|
1425 | 1436 | /*deleted binary*/ |
|
1426 | 1437 | .bin.bin2 { |
|
1427 | 1438 | background-color: @alert2; |
|
1428 | 1439 | text-align: center; |
|
1429 | 1440 | } |
|
1430 | 1441 | |
|
1431 | 1442 | /*mod binary*/ |
|
1432 | 1443 | .bin.bin3 { |
|
1433 | 1444 | background-color: @grey2; |
|
1434 | 1445 | text-align: center; |
|
1435 | 1446 | } |
|
1436 | 1447 | |
|
1437 | 1448 | /*rename file*/ |
|
1438 | 1449 | .bin.bin4 { |
|
1439 | 1450 | background-color: @alert4; |
|
1440 | 1451 | text-align: center; |
|
1441 | 1452 | } |
|
1442 | 1453 | |
|
1443 | 1454 | /*copied file*/ |
|
1444 | 1455 | .bin.bin5 { |
|
1445 | 1456 | background-color: @alert4; |
|
1446 | 1457 | text-align: center; |
|
1447 | 1458 | } |
|
1448 | 1459 | |
|
1449 | 1460 | /*chmod file*/ |
|
1450 | 1461 | .bin.bin6 { |
|
1451 | 1462 | background-color: @grey2; |
|
1452 | 1463 | text-align: center; |
|
1453 | 1464 | } |
|
1454 | 1465 | } |
|
1455 | 1466 | } |
|
1456 | 1467 | |
|
1457 | 1468 | .cs_files .cs_added, .cs_files .cs_A, |
|
1458 | 1469 | .cs_files .cs_added, .cs_files .cs_M, |
|
1459 | 1470 | .cs_files .cs_added, .cs_files .cs_D { |
|
1460 | 1471 | height: 16px; |
|
1461 | 1472 | padding-right: 10px; |
|
1462 | 1473 | margin-top: 7px; |
|
1463 | 1474 | text-align: left; |
|
1464 | 1475 | } |
|
1465 | 1476 | |
|
1466 | 1477 | .cs_icon_td { |
|
1467 | 1478 | min-width: 16px; |
|
1468 | 1479 | width: 16px; |
|
1469 | 1480 | } |
|
1470 | 1481 | |
|
1471 | 1482 | .pull-request-merge { |
|
1472 | 1483 | padding: 10px 0; |
|
1473 | 1484 | margin-top: 10px; |
|
1474 | 1485 | margin-bottom: 20px; |
|
1475 | 1486 | } |
|
1476 | 1487 | |
|
1477 | 1488 | .pull-request-merge .pull-request-wrap { |
|
1478 | 1489 | height: 25px; |
|
1479 | 1490 | padding: 5px 0; |
|
1480 | 1491 | } |
|
1481 | 1492 | |
|
1482 | 1493 | .pull-request-merge span { |
|
1483 | 1494 | margin-right: 10px; |
|
1484 | 1495 | } |
|
1485 | 1496 | #close_pull_request { |
|
1486 | 1497 | margin-right: 0px; |
|
1487 | 1498 | } |
|
1488 | 1499 | |
|
1489 | 1500 | .empty_data { |
|
1490 | 1501 | color: @grey4; |
|
1491 | 1502 | } |
|
1492 | 1503 | |
|
1493 | 1504 | #changeset_compare_view_content { |
|
1494 | 1505 | margin-bottom: @space; |
|
1495 | 1506 | clear: both; |
|
1496 | 1507 | width: 100%; |
|
1497 | 1508 | box-sizing: border-box; |
|
1498 | 1509 | .border-radius(@border-radius); |
|
1499 | 1510 | |
|
1500 | 1511 | .help-block { |
|
1501 | 1512 | margin: @padding 0; |
|
1502 | 1513 | color: @text-color; |
|
1503 | 1514 | } |
|
1504 | 1515 | |
|
1505 | 1516 | .empty_data { |
|
1506 | 1517 | margin: @padding 0; |
|
1507 | 1518 | } |
|
1508 | 1519 | |
|
1509 | 1520 | .alert { |
|
1510 | 1521 | margin-bottom: @space; |
|
1511 | 1522 | } |
|
1512 | 1523 | } |
|
1513 | 1524 | |
|
1514 | 1525 | .table_disp { |
|
1515 | 1526 | .status { |
|
1516 | 1527 | width: auto; |
|
1517 | 1528 | |
|
1518 | 1529 | .flag_status { |
|
1519 | 1530 | float: left; |
|
1520 | 1531 | } |
|
1521 | 1532 | } |
|
1522 | 1533 | } |
|
1523 | 1534 | |
|
1524 | 1535 | .status_box_menu { |
|
1525 | 1536 | margin: 0; |
|
1526 | 1537 | } |
|
1527 | 1538 | |
|
1528 | 1539 | .notification-table{ |
|
1529 | 1540 | margin-bottom: @space; |
|
1530 | 1541 | display: table; |
|
1531 | 1542 | width: 100%; |
|
1532 | 1543 | |
|
1533 | 1544 | .container{ |
|
1534 | 1545 | display: table-row; |
|
1535 | 1546 | |
|
1536 | 1547 | .notification-header{ |
|
1537 | 1548 | border-bottom: @border-thickness solid @border-default-color; |
|
1538 | 1549 | } |
|
1539 | 1550 | |
|
1540 | 1551 | .notification-subject{ |
|
1541 | 1552 | display: table-cell; |
|
1542 | 1553 | } |
|
1543 | 1554 | } |
|
1544 | 1555 | } |
|
1545 | 1556 | |
|
1546 | 1557 | // Notifications |
|
1547 | 1558 | .notification-header{ |
|
1548 | 1559 | display: table; |
|
1549 | 1560 | width: 100%; |
|
1550 | 1561 | padding: floor(@basefontsize/2) 0; |
|
1551 | 1562 | line-height: 1em; |
|
1552 | 1563 | |
|
1553 | 1564 | .desc, .delete-notifications, .read-notifications{ |
|
1554 | 1565 | display: table-cell; |
|
1555 | 1566 | text-align: left; |
|
1556 | 1567 | } |
|
1557 | 1568 | |
|
1558 | 1569 | .desc{ |
|
1559 | 1570 | width: 1163px; |
|
1560 | 1571 | } |
|
1561 | 1572 | |
|
1562 | 1573 | .delete-notifications, .read-notifications{ |
|
1563 | 1574 | width: 35px; |
|
1564 | 1575 | min-width: 35px; //fixes when only one button is displayed |
|
1565 | 1576 | } |
|
1566 | 1577 | } |
|
1567 | 1578 | |
|
1568 | 1579 | .notification-body { |
|
1569 | 1580 | .markdown-block, |
|
1570 | 1581 | .rst-block { |
|
1571 | 1582 | padding: @padding 0; |
|
1572 | 1583 | } |
|
1573 | 1584 | |
|
1574 | 1585 | .notification-subject { |
|
1575 | 1586 | padding: @textmargin 0; |
|
1576 | 1587 | border-bottom: @border-thickness solid @border-default-color; |
|
1577 | 1588 | } |
|
1578 | 1589 | } |
|
1579 | 1590 | |
|
1580 | 1591 | |
|
1581 | 1592 | .notifications_buttons{ |
|
1582 | 1593 | float: right; |
|
1583 | 1594 | } |
|
1584 | 1595 | |
|
1585 | 1596 | // Repositories |
|
1586 | 1597 | |
|
1587 | 1598 | #summary.fields{ |
|
1588 | 1599 | display: table; |
|
1589 | 1600 | |
|
1590 | 1601 | .field{ |
|
1591 | 1602 | display: table-row; |
|
1592 | 1603 | |
|
1593 | 1604 | .label-summary{ |
|
1594 | 1605 | display: table-cell; |
|
1595 | 1606 | min-width: @label-summary-minwidth; |
|
1596 | 1607 | padding-top: @padding/2; |
|
1597 | 1608 | padding-bottom: @padding/2; |
|
1598 | 1609 | padding-right: @padding/2; |
|
1599 | 1610 | } |
|
1600 | 1611 | |
|
1601 | 1612 | .input{ |
|
1602 | 1613 | display: table-cell; |
|
1603 | 1614 | padding: @padding/2; |
|
1604 | 1615 | |
|
1605 | 1616 | input{ |
|
1606 | 1617 | min-width: 29em; |
|
1607 | 1618 | padding: @padding/4; |
|
1608 | 1619 | } |
|
1609 | 1620 | } |
|
1610 | 1621 | .statistics, .downloads{ |
|
1611 | 1622 | .disabled{ |
|
1612 | 1623 | color: @grey4; |
|
1613 | 1624 | } |
|
1614 | 1625 | } |
|
1615 | 1626 | } |
|
1616 | 1627 | } |
|
1617 | 1628 | |
|
1618 | 1629 | #summary{ |
|
1619 | 1630 | width: 70%; |
|
1620 | 1631 | } |
|
1621 | 1632 | |
|
1622 | 1633 | |
|
1623 | 1634 | // Journal |
|
1624 | 1635 | .journal.title { |
|
1625 | 1636 | h5 { |
|
1626 | 1637 | float: left; |
|
1627 | 1638 | margin: 0; |
|
1628 | 1639 | width: 70%; |
|
1629 | 1640 | } |
|
1630 | 1641 | |
|
1631 | 1642 | ul { |
|
1632 | 1643 | float: right; |
|
1633 | 1644 | display: inline-block; |
|
1634 | 1645 | margin: 0; |
|
1635 | 1646 | width: 30%; |
|
1636 | 1647 | text-align: right; |
|
1637 | 1648 | |
|
1638 | 1649 | li { |
|
1639 | 1650 | display: inline; |
|
1640 | 1651 | font-size: @journal-fontsize; |
|
1641 | 1652 | line-height: 1em; |
|
1642 | 1653 | |
|
1643 | 1654 | &:before { content: none; } |
|
1644 | 1655 | } |
|
1645 | 1656 | } |
|
1646 | 1657 | } |
|
1647 | 1658 | |
|
1648 | 1659 | .filterexample { |
|
1649 | 1660 | position: absolute; |
|
1650 | 1661 | top: 95px; |
|
1651 | 1662 | left: @contentpadding; |
|
1652 | 1663 | color: @rcblue; |
|
1653 | 1664 | font-size: 11px; |
|
1654 | 1665 | font-family: @text-regular; |
|
1655 | 1666 | cursor: help; |
|
1656 | 1667 | |
|
1657 | 1668 | &:hover { |
|
1658 | 1669 | color: @rcdarkblue; |
|
1659 | 1670 | } |
|
1660 | 1671 | |
|
1661 | 1672 | @media (max-width:768px) { |
|
1662 | 1673 | position: relative; |
|
1663 | 1674 | top: auto; |
|
1664 | 1675 | left: auto; |
|
1665 | 1676 | display: block; |
|
1666 | 1677 | } |
|
1667 | 1678 | } |
|
1668 | 1679 | |
|
1669 | 1680 | |
|
1670 | 1681 | #journal{ |
|
1671 | 1682 | margin-bottom: @space; |
|
1672 | 1683 | |
|
1673 | 1684 | .journal_day{ |
|
1674 | 1685 | margin-bottom: @textmargin/2; |
|
1675 | 1686 | padding-bottom: @textmargin/2; |
|
1676 | 1687 | font-size: @journal-fontsize; |
|
1677 | 1688 | border-bottom: @border-thickness solid @border-default-color; |
|
1678 | 1689 | } |
|
1679 | 1690 | |
|
1680 | 1691 | .journal_container{ |
|
1681 | 1692 | margin-bottom: @space; |
|
1682 | 1693 | |
|
1683 | 1694 | .journal_user{ |
|
1684 | 1695 | display: inline-block; |
|
1685 | 1696 | } |
|
1686 | 1697 | .journal_action_container{ |
|
1687 | 1698 | display: block; |
|
1688 | 1699 | margin-top: @textmargin; |
|
1689 | 1700 | |
|
1690 | 1701 | div{ |
|
1691 | 1702 | display: inline; |
|
1692 | 1703 | } |
|
1693 | 1704 | |
|
1694 | 1705 | div.journal_action_params{ |
|
1695 | 1706 | display: block; |
|
1696 | 1707 | } |
|
1697 | 1708 | |
|
1698 | 1709 | div.journal_repo:after{ |
|
1699 | 1710 | content: "\A"; |
|
1700 | 1711 | white-space: pre; |
|
1701 | 1712 | } |
|
1702 | 1713 | |
|
1703 | 1714 | div.date{ |
|
1704 | 1715 | display: block; |
|
1705 | 1716 | margin-bottom: @textmargin; |
|
1706 | 1717 | } |
|
1707 | 1718 | } |
|
1708 | 1719 | } |
|
1709 | 1720 | } |
|
1710 | 1721 | |
|
1711 | 1722 | // Files |
|
1712 | 1723 | .edit-file-title { |
|
1713 | 1724 | border-bottom: @border-thickness solid @border-default-color; |
|
1714 | 1725 | |
|
1715 | 1726 | .breadcrumbs { |
|
1716 | 1727 | margin-bottom: 0; |
|
1717 | 1728 | } |
|
1718 | 1729 | } |
|
1719 | 1730 | |
|
1720 | 1731 | .edit-file-fieldset { |
|
1721 | 1732 | margin-top: @sidebarpadding; |
|
1722 | 1733 | |
|
1723 | 1734 | .fieldset { |
|
1724 | 1735 | .left-label { |
|
1725 | 1736 | width: 13%; |
|
1726 | 1737 | } |
|
1727 | 1738 | .right-content { |
|
1728 | 1739 | width: 87%; |
|
1729 | 1740 | max-width: 100%; |
|
1730 | 1741 | } |
|
1731 | 1742 | .filename-label { |
|
1732 | 1743 | margin-top: 13px; |
|
1733 | 1744 | } |
|
1734 | 1745 | .commit-message-label { |
|
1735 | 1746 | margin-top: 4px; |
|
1736 | 1747 | } |
|
1737 | 1748 | .file-upload-input { |
|
1738 | 1749 | input { |
|
1739 | 1750 | display: none; |
|
1740 | 1751 | } |
|
1741 | 1752 | } |
|
1742 | 1753 | p { |
|
1743 | 1754 | margin-top: 5px; |
|
1744 | 1755 | } |
|
1745 | 1756 | |
|
1746 | 1757 | } |
|
1747 | 1758 | .custom-path-link { |
|
1748 | 1759 | margin-left: 5px; |
|
1749 | 1760 | } |
|
1750 | 1761 | #commit { |
|
1751 | 1762 | resize: vertical; |
|
1752 | 1763 | } |
|
1753 | 1764 | } |
|
1754 | 1765 | |
|
1755 | 1766 | .delete-file-preview { |
|
1756 | 1767 | max-height: 250px; |
|
1757 | 1768 | } |
|
1758 | 1769 | |
|
1759 | 1770 | .new-file, |
|
1760 | 1771 | #filter_activate, |
|
1761 | 1772 | #filter_deactivate { |
|
1762 | 1773 | float: left; |
|
1763 | 1774 | margin: 0 0 0 15px; |
|
1764 | 1775 | } |
|
1765 | 1776 | |
|
1766 | 1777 | h3.files_location{ |
|
1767 | 1778 | line-height: 2.4em; |
|
1768 | 1779 | } |
|
1769 | 1780 | |
|
1770 | 1781 | .browser-nav { |
|
1771 | 1782 | display: table; |
|
1772 | 1783 | margin-bottom: @space; |
|
1773 | 1784 | |
|
1774 | 1785 | |
|
1775 | 1786 | .info_box { |
|
1776 | 1787 | display: inline-table; |
|
1777 | 1788 | height: 2.5em; |
|
1778 | 1789 | |
|
1779 | 1790 | .browser-cur-rev, .info_box_elem { |
|
1780 | 1791 | display: table-cell; |
|
1781 | 1792 | vertical-align: middle; |
|
1782 | 1793 | } |
|
1783 | 1794 | |
|
1784 | 1795 | .info_box_elem { |
|
1785 | 1796 | border-top: @border-thickness solid @rcblue; |
|
1786 | 1797 | border-bottom: @border-thickness solid @rcblue; |
|
1787 | 1798 | |
|
1788 | 1799 | #at_rev, a { |
|
1789 | 1800 | padding: 0.6em 0.9em; |
|
1790 | 1801 | margin: 0; |
|
1791 | 1802 | .box-shadow(none); |
|
1792 | 1803 | border: 0; |
|
1793 | 1804 | height: 12px; |
|
1794 | 1805 | } |
|
1795 | 1806 | |
|
1796 | 1807 | input#at_rev { |
|
1797 | 1808 | max-width: 50px; |
|
1798 | 1809 | text-align: right; |
|
1799 | 1810 | } |
|
1800 | 1811 | |
|
1801 | 1812 | &.previous { |
|
1802 | 1813 | border: @border-thickness solid @rcblue; |
|
1803 | 1814 | .disabled { |
|
1804 | 1815 | color: @grey4; |
|
1805 | 1816 | cursor: not-allowed; |
|
1806 | 1817 | } |
|
1807 | 1818 | } |
|
1808 | 1819 | |
|
1809 | 1820 | &.next { |
|
1810 | 1821 | border: @border-thickness solid @rcblue; |
|
1811 | 1822 | .disabled { |
|
1812 | 1823 | color: @grey4; |
|
1813 | 1824 | cursor: not-allowed; |
|
1814 | 1825 | } |
|
1815 | 1826 | } |
|
1816 | 1827 | } |
|
1817 | 1828 | |
|
1818 | 1829 | .browser-cur-rev { |
|
1819 | 1830 | |
|
1820 | 1831 | span{ |
|
1821 | 1832 | margin: 0; |
|
1822 | 1833 | color: @rcblue; |
|
1823 | 1834 | height: 12px; |
|
1824 | 1835 | display: inline-block; |
|
1825 | 1836 | padding: 0.7em 1em ; |
|
1826 | 1837 | border: @border-thickness solid @rcblue; |
|
1827 | 1838 | margin-right: @padding; |
|
1828 | 1839 | } |
|
1829 | 1840 | } |
|
1830 | 1841 | } |
|
1831 | 1842 | |
|
1832 | 1843 | .search_activate { |
|
1833 | 1844 | display: table-cell; |
|
1834 | 1845 | vertical-align: middle; |
|
1835 | 1846 | |
|
1836 | 1847 | input, label{ |
|
1837 | 1848 | margin: 0; |
|
1838 | 1849 | padding: 0; |
|
1839 | 1850 | } |
|
1840 | 1851 | |
|
1841 | 1852 | input{ |
|
1842 | 1853 | margin-left: @textmargin; |
|
1843 | 1854 | } |
|
1844 | 1855 | |
|
1845 | 1856 | } |
|
1846 | 1857 | } |
|
1847 | 1858 | |
|
1848 | 1859 | .file_author{ |
|
1849 | 1860 | margin-bottom: @padding; |
|
1850 | 1861 | |
|
1851 | 1862 | div{ |
|
1852 | 1863 | display: inline-block; |
|
1853 | 1864 | margin-right: 0.5em; |
|
1854 | 1865 | } |
|
1855 | 1866 | } |
|
1856 | 1867 | |
|
1857 | 1868 | .browser-cur-rev{ |
|
1858 | 1869 | margin-bottom: @textmargin; |
|
1859 | 1870 | } |
|
1860 | 1871 | |
|
1861 | 1872 | #node_filter_box_loading{ |
|
1862 | 1873 | .info_text; |
|
1863 | 1874 | } |
|
1864 | 1875 | |
|
1865 | 1876 | .browser-search { |
|
1866 | 1877 | margin: -25px 0px 5px 0px; |
|
1867 | 1878 | } |
|
1868 | 1879 | |
|
1869 | 1880 | .node-filter { |
|
1870 | 1881 | font-size: @repo-title-fontsize; |
|
1871 | 1882 | padding: 4px 0px 0px 0px; |
|
1872 | 1883 | |
|
1873 | 1884 | .node-filter-path { |
|
1874 | 1885 | float: left; |
|
1875 | 1886 | color: @grey4; |
|
1876 | 1887 | } |
|
1877 | 1888 | .node-filter-input { |
|
1878 | 1889 | float: left; |
|
1879 | 1890 | margin: -2px 0px 0px 2px; |
|
1880 | 1891 | input { |
|
1881 | 1892 | padding: 2px; |
|
1882 | 1893 | border: none; |
|
1883 | 1894 | font-size: @repo-title-fontsize; |
|
1884 | 1895 | } |
|
1885 | 1896 | } |
|
1886 | 1897 | } |
|
1887 | 1898 | |
|
1888 | 1899 | |
|
1889 | 1900 | .browser-result{ |
|
1890 | 1901 | td a{ |
|
1891 | 1902 | margin-left: 0.5em; |
|
1892 | 1903 | display: inline-block; |
|
1893 | 1904 | |
|
1894 | 1905 | em{ |
|
1895 | 1906 | font-family: @text-bold; |
|
1896 | 1907 | } |
|
1897 | 1908 | } |
|
1898 | 1909 | } |
|
1899 | 1910 | |
|
1900 | 1911 | .browser-highlight{ |
|
1901 | 1912 | background-color: @grey5-alpha; |
|
1902 | 1913 | } |
|
1903 | 1914 | |
|
1904 | 1915 | |
|
1905 | 1916 | // Search |
|
1906 | 1917 | |
|
1907 | 1918 | .search-form{ |
|
1908 | 1919 | #q { |
|
1909 | 1920 | width: @search-form-width; |
|
1910 | 1921 | } |
|
1911 | 1922 | .fields{ |
|
1912 | 1923 | margin: 0 0 @space; |
|
1913 | 1924 | } |
|
1914 | 1925 | |
|
1915 | 1926 | label{ |
|
1916 | 1927 | display: inline-block; |
|
1917 | 1928 | margin-right: @textmargin; |
|
1918 | 1929 | padding-top: 0.25em; |
|
1919 | 1930 | } |
|
1920 | 1931 | |
|
1921 | 1932 | |
|
1922 | 1933 | .results{ |
|
1923 | 1934 | clear: both; |
|
1924 | 1935 | margin: 0 0 @padding; |
|
1925 | 1936 | } |
|
1926 | 1937 | } |
|
1927 | 1938 | |
|
1928 | 1939 | div.search-feedback-items { |
|
1929 | 1940 | display: inline-block; |
|
1930 | 1941 | padding:0px 0px 0px 96px; |
|
1931 | 1942 | } |
|
1932 | 1943 | |
|
1933 | 1944 | div.search-code-body { |
|
1934 | 1945 | background-color: #ffffff; padding: 5px 0 5px 10px; |
|
1935 | 1946 | pre { |
|
1936 | 1947 | .match { background-color: #faffa6;} |
|
1937 | 1948 | .break { display: block; width: 100%; background-color: #DDE7EF; color: #747474; } |
|
1938 | 1949 | } |
|
1939 | 1950 | } |
|
1940 | 1951 | |
|
1941 | 1952 | .expand_commit.search { |
|
1942 | 1953 | .show_more.open { |
|
1943 | 1954 | height: auto; |
|
1944 | 1955 | max-height: none; |
|
1945 | 1956 | } |
|
1946 | 1957 | } |
|
1947 | 1958 | |
|
1948 | 1959 | .search-results { |
|
1949 | 1960 | |
|
1950 | 1961 | h2 { |
|
1951 | 1962 | margin-bottom: 0; |
|
1952 | 1963 | } |
|
1953 | 1964 | .codeblock { |
|
1954 | 1965 | border: none; |
|
1955 | 1966 | background: transparent; |
|
1956 | 1967 | } |
|
1957 | 1968 | |
|
1958 | 1969 | .codeblock-header { |
|
1959 | 1970 | border: none; |
|
1960 | 1971 | background: transparent; |
|
1961 | 1972 | } |
|
1962 | 1973 | |
|
1963 | 1974 | .code-body { |
|
1964 | 1975 | border: @border-thickness solid @border-default-color; |
|
1965 | 1976 | .border-radius(@border-radius); |
|
1966 | 1977 | } |
|
1967 | 1978 | |
|
1968 | 1979 | .td-commit { |
|
1969 | 1980 | &:extend(pre); |
|
1970 | 1981 | border-bottom: @border-thickness solid @border-default-color; |
|
1971 | 1982 | } |
|
1972 | 1983 | |
|
1973 | 1984 | .message { |
|
1974 | 1985 | height: auto; |
|
1975 | 1986 | max-width: 350px; |
|
1976 | 1987 | white-space: normal; |
|
1977 | 1988 | text-overflow: initial; |
|
1978 | 1989 | overflow: visible; |
|
1979 | 1990 | |
|
1980 | 1991 | .match { background-color: #faffa6;} |
|
1981 | 1992 | .break { background-color: #DDE7EF; width: 100%; color: #747474; display: block; } |
|
1982 | 1993 | } |
|
1983 | 1994 | |
|
1984 | 1995 | } |
|
1985 | 1996 | |
|
1986 | 1997 | table.rctable td.td-search-results div { |
|
1987 | 1998 | max-width: 100%; |
|
1988 | 1999 | } |
|
1989 | 2000 | |
|
1990 | 2001 | #tip-box, .tip-box{ |
|
1991 | 2002 | padding: @menupadding/2; |
|
1992 | 2003 | display: block; |
|
1993 | 2004 | border: @border-thickness solid @border-highlight-color; |
|
1994 | 2005 | .border-radius(@border-radius); |
|
1995 | 2006 | background-color: white; |
|
1996 | 2007 | z-index: 99; |
|
1997 | 2008 | white-space: pre-wrap; |
|
1998 | 2009 | } |
|
1999 | 2010 | |
|
2000 | 2011 | #linktt { |
|
2001 | 2012 | width: 79px; |
|
2002 | 2013 | } |
|
2003 | 2014 | |
|
2004 | 2015 | #help_kb .modal-content{ |
|
2005 | 2016 | max-width: 750px; |
|
2006 | 2017 | margin: 10% auto; |
|
2007 | 2018 | |
|
2008 | 2019 | table{ |
|
2009 | 2020 | td,th{ |
|
2010 | 2021 | border-bottom: none; |
|
2011 | 2022 | line-height: 2.5em; |
|
2012 | 2023 | } |
|
2013 | 2024 | th{ |
|
2014 | 2025 | padding-bottom: @textmargin/2; |
|
2015 | 2026 | } |
|
2016 | 2027 | td.keys{ |
|
2017 | 2028 | text-align: center; |
|
2018 | 2029 | } |
|
2019 | 2030 | } |
|
2020 | 2031 | |
|
2021 | 2032 | .block-left{ |
|
2022 | 2033 | width: 45%; |
|
2023 | 2034 | margin-right: 5%; |
|
2024 | 2035 | } |
|
2025 | 2036 | .modal-footer{ |
|
2026 | 2037 | clear: both; |
|
2027 | 2038 | } |
|
2028 | 2039 | .key.tag{ |
|
2029 | 2040 | padding: 0.5em; |
|
2030 | 2041 | background-color: @rcblue; |
|
2031 | 2042 | color: white; |
|
2032 | 2043 | border-color: @rcblue; |
|
2033 | 2044 | .box-shadow(none); |
|
2034 | 2045 | } |
|
2035 | 2046 | } |
|
2036 | 2047 | |
|
2037 | 2048 | |
|
2038 | 2049 | |
|
2039 | 2050 | //--- IMPORTS FOR REFACTORED STYLES ------------------// |
|
2040 | 2051 | |
|
2041 | 2052 | @import 'statistics-graph'; |
|
2042 | 2053 | @import 'tables'; |
|
2043 | 2054 | @import 'forms'; |
|
2044 | 2055 | @import 'diff'; |
|
2045 | 2056 | @import 'summary'; |
|
2046 | 2057 | @import 'navigation'; |
|
2047 | 2058 | |
|
2048 | 2059 | //--- SHOW/HIDE SECTIONS --// |
|
2049 | 2060 | |
|
2050 | 2061 | .btn-collapse { |
|
2051 | 2062 | float: right; |
|
2052 | 2063 | text-align: right; |
|
2053 | 2064 | font-family: @text-light; |
|
2054 | 2065 | font-size: @basefontsize; |
|
2055 | 2066 | cursor: pointer; |
|
2056 | 2067 | border: none; |
|
2057 | 2068 | color: @rcblue; |
|
2058 | 2069 | } |
|
2059 | 2070 | |
|
2060 | 2071 | table.rctable, |
|
2061 | 2072 | table.dataTable { |
|
2062 | 2073 | .btn-collapse { |
|
2063 | 2074 | float: right; |
|
2064 | 2075 | text-align: right; |
|
2065 | 2076 | } |
|
2066 | 2077 | } |
|
2067 | 2078 | |
|
2068 | 2079 | |
|
2069 | 2080 | // TODO: johbo: Fix for IE10, this avoids that we see a border |
|
2070 | 2081 | // and padding around checkboxes and radio boxes. Move to the right place, |
|
2071 | 2082 | // or better: Remove this once we did the form refactoring. |
|
2072 | 2083 | input[type=checkbox], |
|
2073 | 2084 | input[type=radio] { |
|
2074 | 2085 | padding: 0; |
|
2075 | 2086 | border: none; |
|
2076 | 2087 | } |
@@ -1,47 +1,49 b'' | |||
|
1 | 1 | ## -*- coding: utf-8 -*- |
|
2 | 2 | <%namespace name="base" file="/base/base.html"/> |
|
3 | 3 | |
|
4 | 4 | % if c.forks_pager: |
|
5 | 5 | <table class="rctable fork_summary"> |
|
6 | 6 | <tr> |
|
7 | <th>${_('Owner')}</th> | |
|
7 | 8 | <th>${_('Fork')}</th> |
|
8 | 9 | <th>${_('Description')}</th> |
|
9 | 10 | <th>${_('Forked')}</th> |
|
10 | 11 | <th></th> |
|
11 | 12 | </tr> |
|
12 | 13 | % for f in c.forks_pager: |
|
13 | 14 | <tr> |
|
14 | 15 | <td class="td-user fork_user"> |
|
15 | 16 | ${base.gravatar_with_user(f.user.email, 16)} |
|
16 |
|
|
|
17 | </td> | |
|
18 | <td class="td-componentname"> | |
|
17 | 19 | ${h.link_to(f.repo_name,h.url('summary_home',repo_name=f.repo_name))} |
|
18 | 20 | </td> |
|
19 | 21 | <td class="td-description"> |
|
20 | 22 | <div class="truncate">${f.description}</div> |
|
21 | 23 | </td> |
|
22 | 24 | <td class="td-time follower_date"> |
|
23 | 25 | ${h.age_component(f.created_on)} |
|
24 | 26 | </td> |
|
25 | 27 | <td class="td-compare"> |
|
26 | 28 | <a title="${_('Compare fork with %s' % c.repo_name)}" |
|
27 | 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 | 30 | class="btn-link"><i class="icon-loop"></i> ${_('Compare fork')}</a> |
|
29 | 31 | </td> |
|
30 | 32 | </tr> |
|
31 | 33 | % endfor |
|
32 | 34 | </table> |
|
33 | 35 | <div class="pagination-wh pagination-left"> |
|
34 | 36 | <script type="text/javascript"> |
|
35 | 37 | $(document).pjax('#forks .pager_link','#forks'); |
|
36 | 38 | $(document).on('pjax:success',function(){ |
|
37 | 39 | show_more_event(); |
|
38 | 40 | timeagoActivate(); |
|
39 | 41 | tooltip_activate(); |
|
40 | 42 | show_changeset_tooltip(); |
|
41 | 43 | }); |
|
42 | 44 | </script> |
|
43 | 45 | ${c.forks_pager.pager('$link_previous ~2~ $link_next')} |
|
44 | 46 | </div> |
|
45 | 47 | % else: |
|
46 | 48 | ${_('There are no forks yet')} |
|
47 | 49 | % endif |
@@ -1,568 +1,568 b'' | |||
|
1 | 1 | <%inherit file="/base/base.html"/> |
|
2 | 2 | |
|
3 | 3 | <%def name="title()"> |
|
4 | 4 | ${_('%s Pull Request #%s') % (c.repo_name, c.pull_request.pull_request_id)} |
|
5 | 5 | %if c.rhodecode_name: |
|
6 | 6 | · ${h.branding(c.rhodecode_name)} |
|
7 | 7 | %endif |
|
8 | 8 | </%def> |
|
9 | 9 | |
|
10 | 10 | <%def name="breadcrumbs_links()"> |
|
11 | 11 | <span id="pr-title"> |
|
12 | 12 | ${c.pull_request.title} |
|
13 | 13 | %if c.pull_request.is_closed(): |
|
14 | 14 | (${_('Closed')}) |
|
15 | 15 | %endif |
|
16 | 16 | </span> |
|
17 | 17 | <div id="pr-title-edit" class="input" style="display: none;"> |
|
18 | 18 | ${h.text('pullrequest_title', id_="pr-title-input", class_="large", value=c.pull_request.title)} |
|
19 | 19 | </div> |
|
20 | 20 | </%def> |
|
21 | 21 | |
|
22 | 22 | <%def name="menu_bar_nav()"> |
|
23 | 23 | ${self.menu_items(active='repositories')} |
|
24 | 24 | </%def> |
|
25 | 25 | |
|
26 | 26 | <%def name="menu_bar_subnav()"> |
|
27 | 27 | ${self.repo_menu(active='showpullrequest')} |
|
28 | 28 | </%def> |
|
29 | 29 | |
|
30 | 30 | <%def name="main()"> |
|
31 | 31 | <script type="text/javascript"> |
|
32 | 32 | // TODO: marcink switch this to pyroutes |
|
33 | 33 | AJAX_COMMENT_DELETE_URL = "${url('pullrequest_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}"; |
|
34 | 34 | templateContext.pull_request_data.pull_request_id = ${c.pull_request.pull_request_id}; |
|
35 | 35 | </script> |
|
36 | 36 | <div class="box"> |
|
37 | 37 | <div class="title"> |
|
38 | 38 | ${self.repo_page_title(c.rhodecode_db_repo)} |
|
39 | 39 | </div> |
|
40 | 40 | |
|
41 | 41 | ${self.breadcrumbs()} |
|
42 | 42 | |
|
43 | 43 | |
|
44 | 44 | <div class="box pr-summary"> |
|
45 | 45 | <div class="summary-details block-left"> |
|
46 | 46 | <%summary = lambda n:{False:'summary-short'}.get(n)%> |
|
47 | 47 | <div class="pr-details-title"> |
|
48 | 48 | ${_('Pull request #%s') % c.pull_request.pull_request_id} ${_('From')} ${h.format_date(c.pull_request.created_on)} |
|
49 | 49 | %if c.allowed_to_update: |
|
50 | 50 | <span id="open_edit_pullrequest" class="block-right action_button">${_('Edit')}</span> |
|
51 | 51 | <span id="close_edit_pullrequest" class="block-right action_button" style="display: none;">${_('Close')}</span> |
|
52 | 52 | %endif |
|
53 | 53 | </div> |
|
54 | 54 | |
|
55 | 55 | <div id="summary" class="fields pr-details-content"> |
|
56 | 56 | <div class="field"> |
|
57 | 57 | <div class="label-summary"> |
|
58 | 58 | <label>${_('Origin')}:</label> |
|
59 | 59 | </div> |
|
60 | 60 | <div class="input"> |
|
61 | 61 | <div class="pr-origininfo"> |
|
62 | 62 | ## branch link is only valid if it is a branch |
|
63 | 63 | <span class="tag"> |
|
64 | 64 | %if c.pull_request.source_ref_parts.type == 'branch': |
|
65 | 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 | 66 | %else: |
|
67 | 67 | ${c.pull_request.source_ref_parts.type}: ${c.pull_request.source_ref_parts.name} |
|
68 | 68 | %endif |
|
69 | 69 | </span> |
|
70 | 70 | <span class="clone-url"> |
|
71 | 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 | 72 | </span> |
|
73 | 73 | </div> |
|
74 | 74 | <div class="pr-pullinfo"> |
|
75 | 75 | %if h.is_hg(c.pull_request.source_repo): |
|
76 | 76 | <input type="text" value="hg pull -r ${h.short_id(c.source_ref)} ${c.pull_request.source_repo.clone_url()}" readonly="readonly"> |
|
77 | 77 | %elif h.is_git(c.pull_request.source_repo): |
|
78 | 78 | <input type="text" value="git pull ${c.pull_request.source_repo.clone_url()} ${c.pull_request.source_ref_parts.name}" readonly="readonly"> |
|
79 | 79 | %endif |
|
80 | 80 | </div> |
|
81 | 81 | </div> |
|
82 | 82 | </div> |
|
83 | 83 | <div class="field"> |
|
84 | 84 | <div class="label-summary"> |
|
85 | 85 | <label>${_('Target')}:</label> |
|
86 | 86 | </div> |
|
87 | 87 | <div class="input"> |
|
88 | 88 | <div class="pr-targetinfo"> |
|
89 | 89 | ## branch link is only valid if it is a branch |
|
90 | 90 | <span class="tag"> |
|
91 | 91 | %if c.pull_request.target_ref_parts.type == 'branch': |
|
92 | 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 | 93 | %else: |
|
94 | 94 | ${c.pull_request.target_ref_parts.type}: ${c.pull_request.target_ref_parts.name} |
|
95 | 95 | %endif |
|
96 | 96 | </span> |
|
97 | 97 | <span class="clone-url"> |
|
98 | 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 | 99 | </span> |
|
100 | 100 | </div> |
|
101 | 101 | </div> |
|
102 | 102 | </div> |
|
103 | 103 | <div class="field"> |
|
104 | 104 | <div class="label-summary"> |
|
105 | 105 | <label>${_('Review')}:</label> |
|
106 | 106 | </div> |
|
107 | 107 | <div class="input"> |
|
108 | 108 | %if c.pull_request_review_status: |
|
109 | 109 | <div class="${'flag_status %s' % c.pull_request_review_status} tooltip pull-left"></div> |
|
110 | 110 | <span class="changeset-status-lbl tooltip"> |
|
111 | 111 | %if c.pull_request.is_closed(): |
|
112 | 112 | ${_('Closed')}, |
|
113 | 113 | %endif |
|
114 | 114 | ${h.commit_status_lbl(c.pull_request_review_status)} |
|
115 | 115 | </span> |
|
116 | 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 | 117 | %endif |
|
118 | 118 | </div> |
|
119 | 119 | </div> |
|
120 | 120 | <div class="field"> |
|
121 | 121 | <div class="pr-description-label label-summary"> |
|
122 | 122 | <label>${_('Description')}:</label> |
|
123 | 123 | </div> |
|
124 | 124 | <div id="pr-desc" class="input"> |
|
125 | 125 | <div class="pr-description">${h.urlify_commit_message(c.pull_request.description, c.repo_name)}</div> |
|
126 | 126 | </div> |
|
127 | 127 | <div id="pr-desc-edit" class="input textarea editor" style="display: none;"> |
|
128 | 128 | <textarea id="pr-description-input" size="30">${c.pull_request.description}</textarea> |
|
129 | 129 | </div> |
|
130 | 130 | </div> |
|
131 | 131 | <div class="field"> |
|
132 | 132 | <div class="label-summary"> |
|
133 | 133 | <label>${_('Comments')}:</label> |
|
134 | 134 | </div> |
|
135 | 135 | <div class="input"> |
|
136 | 136 | <div> |
|
137 | 137 | <div class="comments-number"> |
|
138 | 138 | %if c.comments: |
|
139 | 139 | <a href="#comments">${ungettext("%d Pull request comment", "%d Pull request comments", len(c.comments)) % len(c.comments)}</a>, |
|
140 | 140 | %else: |
|
141 | 141 | ${ungettext("%d Pull request comment", "%d Pull request comments", len(c.comments)) % len(c.comments)} |
|
142 | 142 | %endif |
|
143 | 143 | %if c.inline_cnt: |
|
144 | 144 | ## this is replaced with a proper link to first comment via JS linkifyComments() func |
|
145 | 145 | <a href="#inline-comments" id="inline-comments-counter">${ungettext("%d Inline Comment", "%d Inline Comments", c.inline_cnt) % c.inline_cnt}</a> |
|
146 | 146 | %else: |
|
147 | 147 | ${ungettext("%d Inline Comment", "%d Inline Comments", c.inline_cnt) % c.inline_cnt} |
|
148 | 148 | %endif |
|
149 | 149 | |
|
150 | 150 | % if c.outdated_cnt: |
|
151 | 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 | 152 | % endif |
|
153 | 153 | </div> |
|
154 | 154 | </div> |
|
155 | 155 | </div> |
|
156 | 156 | </div> |
|
157 | 157 | <div id="pr-save" class="field" style="display: none;"> |
|
158 | 158 | <div class="label-summary"></div> |
|
159 | 159 | <div class="input"> |
|
160 | 160 | <span id="edit_pull_request" class="btn btn-small">${_('Save Changes')}</span> |
|
161 | 161 | </div> |
|
162 | 162 | </div> |
|
163 | 163 | </div> |
|
164 | 164 | </div> |
|
165 | 165 | <div> |
|
166 | 166 | ## AUTHOR |
|
167 | 167 | <div class="reviewers-title block-right"> |
|
168 | 168 | <div class="pr-details-title"> |
|
169 | 169 | ${_('Author')} |
|
170 | 170 | </div> |
|
171 | 171 | </div> |
|
172 | 172 | <div class="block-right pr-details-content reviewers"> |
|
173 | 173 | <ul class="group_members"> |
|
174 | 174 | <li> |
|
175 | 175 | ${self.gravatar_with_user(c.pull_request.author.email, 16)} |
|
176 | 176 | </li> |
|
177 | 177 | </ul> |
|
178 | 178 | </div> |
|
179 | 179 | ## REVIEWERS |
|
180 | 180 | <div class="reviewers-title block-right"> |
|
181 | 181 | <div class="pr-details-title"> |
|
182 | 182 | ${_('Pull request reviewers')} |
|
183 | 183 | %if c.allowed_to_update: |
|
184 | 184 | <span id="open_edit_reviewers" class="block-right action_button">${_('Edit')}</span> |
|
185 | 185 | <span id="close_edit_reviewers" class="block-right action_button" style="display: none;">${_('Close')}</span> |
|
186 | 186 | %endif |
|
187 | 187 | </div> |
|
188 | 188 | </div> |
|
189 | 189 | <div id="reviewers" class="block-right pr-details-content reviewers"> |
|
190 | 190 | ## members goes here ! |
|
191 | 191 | <ul id="review_members" class="group_members"> |
|
192 | 192 | %for member,status in c.pull_request_reviewers: |
|
193 | 193 | <li id="reviewer_${member.user_id}"> |
|
194 | 194 | <div class="reviewers_member"> |
|
195 | 195 | <div class="reviewer_status tooltip" title="${h.tooltip(h.commit_status_lbl(status[0][1].status if status else 'not_reviewed'))}"> |
|
196 | 196 | <div class="${'flag_status %s' % (status[0][1].status if status else 'not_reviewed')} pull-left reviewer_member_status"></div> |
|
197 | 197 | </div> |
|
198 |
< |
|
|
199 | ${self.gravatar_with_user(member.email, 16)} | |
|
200 | (${_('owner') if c.pull_request.user_id == member.user_id else _('reviewer')})</span> | |
|
198 | <div id="reviewer_${member.user_id}_name" class="reviewer_name"> | |
|
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 | </div> | |
|
201 | 201 | <input id="reviewer_${member.user_id}_input" type="hidden" value="${member.user_id}" name="review_members" /> |
|
202 | 202 | %if c.allowed_to_update: |
|
203 | 203 | <div class="reviewer_member_remove action_button" onclick="removeReviewMember(${member.user_id}, true)" style="visibility: hidden;"> |
|
204 | 204 | <i class="icon-remove-sign" ></i> |
|
205 | 205 | </div> |
|
206 | 206 | %endif |
|
207 | 207 | </div> |
|
208 | 208 | </li> |
|
209 | 209 | %endfor |
|
210 | 210 | </ul> |
|
211 | 211 | %if not c.pull_request.is_closed(): |
|
212 | 212 | <div id="add_reviewer_input" class='ac' style="display: none;"> |
|
213 | 213 | %if c.allowed_to_update: |
|
214 | 214 | <div class="reviewer_ac"> |
|
215 | 215 | ${h.text('user', class_='ac-input', placeholder=_('Add reviewer'))} |
|
216 | 216 | <div id="reviewers_container"></div> |
|
217 | 217 | </div> |
|
218 | 218 | <div> |
|
219 | 219 | <span id="update_pull_request" class="btn btn-small">${_('Save Changes')}</span> |
|
220 | 220 | </div> |
|
221 | 221 | %endif |
|
222 | 222 | </div> |
|
223 | 223 | %endif |
|
224 | 224 | </div> |
|
225 | 225 | </div> |
|
226 | 226 | </div> |
|
227 | 227 | <div class="box"> |
|
228 | 228 | ##DIFF |
|
229 | 229 | <div class="table" > |
|
230 | 230 | <div id="changeset_compare_view_content"> |
|
231 | 231 | ##CS |
|
232 | 232 | % if c.missing_requirements: |
|
233 | 233 | <div class="box"> |
|
234 | 234 | <div class="alert alert-warning"> |
|
235 | 235 | <div> |
|
236 | 236 | <strong>${_('Missing requirements:')}</strong> |
|
237 | 237 | ${_('These commits cannot be displayed, because this repository uses the Mercurial largefiles extension, which was not enabled.')} |
|
238 | 238 | </div> |
|
239 | 239 | </div> |
|
240 | 240 | </div> |
|
241 | 241 | % elif c.missing_commits: |
|
242 | 242 | <div class="box"> |
|
243 | 243 | <div class="alert alert-warning"> |
|
244 | 244 | <div> |
|
245 | 245 | <strong>${_('Missing commits')}:</strong> |
|
246 | 246 | ${_('This pull request cannot be displayed, because one or more commits no longer exist in the source repository.')} |
|
247 | 247 | ${_('Please update this pull request, push the commits back into the source repository, or consider closing this pull request.')} |
|
248 | 248 | </div> |
|
249 | 249 | </div> |
|
250 | 250 | </div> |
|
251 | 251 | % endif |
|
252 | 252 | <div class="compare_view_commits_title"> |
|
253 | 253 | % if c.allowed_to_update and not c.pull_request.is_closed(): |
|
254 | 254 | <button id="update_commits" class="btn btn-small">${_('Update commits')}</button> |
|
255 | 255 | % endif |
|
256 | 256 | % if len(c.commit_ranges): |
|
257 | 257 | <h2>${ungettext('Compare View: %s commit','Compare View: %s commits', len(c.commit_ranges)) % len(c.commit_ranges)}</h2> |
|
258 | 258 | % endif |
|
259 | 259 | </div> |
|
260 | 260 | % if not c.missing_commits: |
|
261 | 261 | <%include file="/compare/compare_commits.html" /> |
|
262 | 262 | ## FILES |
|
263 | 263 | <div class="cs_files_title"> |
|
264 | 264 | <span class="cs_files_expand"> |
|
265 | 265 | <span id="expand_all_files">${_('Expand All')}</span> | <span id="collapse_all_files">${_('Collapse All')}</span> |
|
266 | 266 | </span> |
|
267 | 267 | <h2> |
|
268 | 268 | ${diff_block.diff_summary_text(len(c.files), c.lines_added, c.lines_deleted, c.limited_diff)} |
|
269 | 269 | </h2> |
|
270 | 270 | </div> |
|
271 | 271 | % endif |
|
272 | 272 | <div class="cs_files"> |
|
273 | 273 | %if not c.files and not c.missing_commits: |
|
274 | 274 | <span class="empty_data">${_('No files')}</span> |
|
275 | 275 | %endif |
|
276 | 276 | <table class="compare_view_files"> |
|
277 | 277 | <%namespace name="diff_block" file="/changeset/diff_block.html"/> |
|
278 | 278 | %for FID, change, path, stats in c.files: |
|
279 | 279 | <tr class="cs_${change} collapse_file" fid="${FID}"> |
|
280 | 280 | <td class="cs_icon_td"> |
|
281 | 281 | <span class="collapse_file_icon" fid="${FID}"></span> |
|
282 | 282 | </td> |
|
283 | 283 | <td class="cs_icon_td"> |
|
284 | 284 | <div class="flag_status not_reviewed hidden"></div> |
|
285 | 285 | </td> |
|
286 | 286 | <td class="cs_${change}" id="a_${FID}"> |
|
287 | 287 | <div class="node"> |
|
288 | 288 | <a href="#a_${FID}"> |
|
289 | 289 | <i class="icon-file-${change.lower()}"></i> |
|
290 | 290 | ${h.safe_unicode(path)} |
|
291 | 291 | </a> |
|
292 | 292 | </div> |
|
293 | 293 | </td> |
|
294 | 294 | <td> |
|
295 | 295 | <div class="changes pull-right">${h.fancy_file_stats(stats)}</div> |
|
296 | 296 | <div class="comment-bubble pull-right" data-path="${path}"> |
|
297 | 297 | <i class="icon-comment"></i> |
|
298 | 298 | </div> |
|
299 | 299 | </td> |
|
300 | 300 | </tr> |
|
301 | 301 | <tr fid="${FID}" id="diff_${FID}" class="diff_links"> |
|
302 | 302 | <td></td> |
|
303 | 303 | <td></td> |
|
304 | 304 | <td class="cs_${change}"> |
|
305 | 305 | %if c.target_repo.repo_name == c.repo_name: |
|
306 | 306 | ${diff_block.diff_menu(c.repo_name, h.safe_unicode(path), c.target_ref, c.source_ref, change)} |
|
307 | 307 | %else: |
|
308 | 308 | ## this is slightly different case later, since the other repo can have this |
|
309 | 309 | ## file in other state than the origin repo |
|
310 | 310 | ${diff_block.diff_menu(c.target_repo.repo_name, h.safe_unicode(path), c.target_ref, c.source_ref, change)} |
|
311 | 311 | %endif |
|
312 | 312 | </td> |
|
313 | 313 | <td class="td-actions rc-form"> |
|
314 | 314 | </td> |
|
315 | 315 | </tr> |
|
316 | 316 | <tr id="tr_${FID}"> |
|
317 | 317 | <td></td> |
|
318 | 318 | <td></td> |
|
319 | 319 | <td class="injected_diff" colspan="2"> |
|
320 | 320 | ${diff_block.diff_block_simple([c.changes[FID]])} |
|
321 | 321 | </td> |
|
322 | 322 | </tr> |
|
323 | 323 | |
|
324 | 324 | ## Loop through inline comments |
|
325 | 325 | % if c.outdated_comments.get(path,False): |
|
326 | 326 | <tr class="outdated"> |
|
327 | 327 | <td></td> |
|
328 | 328 | <td></td> |
|
329 | 329 | <td colspan="2"> |
|
330 | 330 | <p>${_('Outdated Inline Comments')}:</p> |
|
331 | 331 | </td> |
|
332 | 332 | </tr> |
|
333 | 333 | <tr class="outdated"> |
|
334 | 334 | <td></td> |
|
335 | 335 | <td></td> |
|
336 | 336 | <td colspan="2" class="outdated_comment_block"> |
|
337 | 337 | % for line, comments in c.outdated_comments[path].iteritems(): |
|
338 | 338 | <div class="inline-comment-placeholder" path="${path}" target_id="${h.safeid(h.safe_unicode(path))}"> |
|
339 | 339 | % for co in comments: |
|
340 | 340 | ${comment.comment_block_outdated(co)} |
|
341 | 341 | % endfor |
|
342 | 342 | </div> |
|
343 | 343 | % endfor |
|
344 | 344 | </td> |
|
345 | 345 | </tr> |
|
346 | 346 | % endif |
|
347 | 347 | %endfor |
|
348 | 348 | ## Loop through inline comments for deleted files |
|
349 | 349 | %for path in c.deleted_files: |
|
350 | 350 | <tr class="outdated deleted"> |
|
351 | 351 | <td></td> |
|
352 | 352 | <td></td> |
|
353 | 353 | <td>${path}</td> |
|
354 | 354 | </tr> |
|
355 | 355 | <tr class="outdated deleted"> |
|
356 | 356 | <td></td> |
|
357 | 357 | <td></td> |
|
358 | 358 | <td>(${_('Removed')})</td> |
|
359 | 359 | </tr> |
|
360 | 360 | % if path in c.outdated_comments: |
|
361 | 361 | <tr class="outdated deleted"> |
|
362 | 362 | <td></td> |
|
363 | 363 | <td></td> |
|
364 | 364 | <td colspan="2"> |
|
365 | 365 | <p>${_('Outdated Inline Comments')}:</p> |
|
366 | 366 | </td> |
|
367 | 367 | </tr> |
|
368 | 368 | <tr class="outdated"> |
|
369 | 369 | <td></td> |
|
370 | 370 | <td></td> |
|
371 | 371 | <td colspan="2" class="outdated_comment_block"> |
|
372 | 372 | % for line, comments in c.outdated_comments[path].iteritems(): |
|
373 | 373 | <div class="inline-comment-placeholder" path="${path}" target_id="${h.safeid(h.safe_unicode(path))}"> |
|
374 | 374 | % for co in comments: |
|
375 | 375 | ${comment.comment_block_outdated(co)} |
|
376 | 376 | % endfor |
|
377 | 377 | </div> |
|
378 | 378 | % endfor |
|
379 | 379 | </td> |
|
380 | 380 | </tr> |
|
381 | 381 | % endif |
|
382 | 382 | %endfor |
|
383 | 383 | </table> |
|
384 | 384 | </div> |
|
385 | 385 | % if c.limited_diff: |
|
386 | 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 | 387 | % endif |
|
388 | 388 | </div> |
|
389 | 389 | </div> |
|
390 | 390 | |
|
391 | 391 | % if c.limited_diff: |
|
392 | 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 | 393 | % endif |
|
394 | 394 | |
|
395 | 395 | ## template for inline comment form |
|
396 | 396 | <%namespace name="comment" file="/changeset/changeset_file_comment.html"/> |
|
397 | 397 | ${comment.comment_inline_form()} |
|
398 | 398 | |
|
399 | 399 | ## render comments and inlines |
|
400 | 400 | ${comment.generate_comments(include_pull_request=True, is_pull_request=True)} |
|
401 | 401 | |
|
402 | 402 | % if not c.pull_request.is_closed(): |
|
403 | 403 | ## main comment form and it status |
|
404 | 404 | ${comment.comments(h.url('pullrequest_comment', repo_name=c.repo_name, |
|
405 | 405 | pull_request_id=c.pull_request.pull_request_id), |
|
406 | 406 | c.pull_request_review_status, |
|
407 | 407 | is_pull_request=True, change_status=c.allowed_to_change_status)} |
|
408 | 408 | %endif |
|
409 | 409 | |
|
410 | 410 | <script type="text/javascript"> |
|
411 | 411 | if (location.href.indexOf('#') != -1) { |
|
412 | 412 | var id = '#'+location.href.substring(location.href.indexOf('#') + 1).split('#'); |
|
413 | 413 | var line = $('html').find(id); |
|
414 | 414 | offsetScroll(line, 70); |
|
415 | 415 | } |
|
416 | 416 | $(function(){ |
|
417 | 417 | ReviewerAutoComplete('user'); |
|
418 | 418 | // custom code mirror |
|
419 | 419 | var codeMirrorInstance = initPullRequestsCodeMirror('#pr-description-input'); |
|
420 | 420 | |
|
421 | 421 | var PRDetails = { |
|
422 | 422 | editButton: $('#open_edit_pullrequest'), |
|
423 | 423 | closeButton: $('#close_edit_pullrequest'), |
|
424 | 424 | viewFields: $('#pr-desc, #pr-title'), |
|
425 | 425 | editFields: $('#pr-desc-edit, #pr-title-edit, #pr-save'), |
|
426 | 426 | |
|
427 | 427 | init: function() { |
|
428 | 428 | var that = this; |
|
429 | 429 | this.editButton.on('click', function(e) { that.edit(); }); |
|
430 | 430 | this.closeButton.on('click', function(e) { that.view(); }); |
|
431 | 431 | }, |
|
432 | 432 | |
|
433 | 433 | edit: function(event) { |
|
434 | 434 | this.viewFields.hide(); |
|
435 | 435 | this.editButton.hide(); |
|
436 | 436 | this.editFields.show(); |
|
437 | 437 | codeMirrorInstance.refresh(); |
|
438 | 438 | }, |
|
439 | 439 | |
|
440 | 440 | view: function(event) { |
|
441 | 441 | this.editFields.hide(); |
|
442 | 442 | this.closeButton.hide(); |
|
443 | 443 | this.viewFields.show(); |
|
444 | 444 | } |
|
445 | 445 | }; |
|
446 | 446 | |
|
447 | 447 | var ReviewersPanel = { |
|
448 | 448 | editButton: $('#open_edit_reviewers'), |
|
449 | 449 | closeButton: $('#close_edit_reviewers'), |
|
450 | 450 | addButton: $('#add_reviewer_input'), |
|
451 | 451 | removeButtons: $('.reviewer_member_remove'), |
|
452 | 452 | |
|
453 | 453 | init: function() { |
|
454 | 454 | var that = this; |
|
455 | 455 | this.editButton.on('click', function(e) { that.edit(); }); |
|
456 | 456 | this.closeButton.on('click', function(e) { that.close(); }); |
|
457 | 457 | }, |
|
458 | 458 | |
|
459 | 459 | edit: function(event) { |
|
460 | 460 | this.editButton.hide(); |
|
461 | 461 | this.closeButton.show(); |
|
462 | 462 | this.addButton.show(); |
|
463 | 463 | this.removeButtons.css('visibility', 'visible'); |
|
464 | 464 | }, |
|
465 | 465 | |
|
466 | 466 | close: function(event) { |
|
467 | 467 | this.editButton.show(); |
|
468 | 468 | this.closeButton.hide(); |
|
469 | 469 | this.addButton.hide(); |
|
470 | 470 | this.removeButtons.css('visibility', 'hidden'); |
|
471 | 471 | } |
|
472 | 472 | }; |
|
473 | 473 | |
|
474 | 474 | PRDetails.init(); |
|
475 | 475 | ReviewersPanel.init(); |
|
476 | 476 | |
|
477 | 477 | $('#show-outdated-comments').on('click', function(e){ |
|
478 | 478 | var button = $(this); |
|
479 | 479 | var outdated = $('.outdated'); |
|
480 | 480 | if (button.html() === "(Show)") { |
|
481 | 481 | button.html("(Hide)"); |
|
482 | 482 | outdated.show(); |
|
483 | 483 | } else { |
|
484 | 484 | button.html("(Show)"); |
|
485 | 485 | outdated.hide(); |
|
486 | 486 | } |
|
487 | 487 | }); |
|
488 | 488 | |
|
489 | 489 | $('.show-inline-comments').on('change', function(e){ |
|
490 | 490 | var show = 'none'; |
|
491 | 491 | var target = e.currentTarget; |
|
492 | 492 | if(target.checked){ |
|
493 | 493 | show = '' |
|
494 | 494 | } |
|
495 | 495 | var boxid = $(target).attr('id_for'); |
|
496 | 496 | var comments = $('#{0} .inline-comments'.format(boxid)); |
|
497 | 497 | var fn_display = function(idx){ |
|
498 | 498 | $(this).css('display', show); |
|
499 | 499 | }; |
|
500 | 500 | $(comments).each(fn_display); |
|
501 | 501 | var btns = $('#{0} .inline-comments-button'.format(boxid)); |
|
502 | 502 | $(btns).each(fn_display); |
|
503 | 503 | }); |
|
504 | 504 | |
|
505 | 505 | // inject comments into their proper positions |
|
506 | 506 | var file_comments = $('.inline-comment-placeholder'); |
|
507 | 507 | %if c.pull_request.is_closed(): |
|
508 | 508 | renderInlineComments(file_comments, false); |
|
509 | 509 | %else: |
|
510 | 510 | renderInlineComments(file_comments, true); |
|
511 | 511 | %endif |
|
512 | 512 | var commentTotals = {}; |
|
513 | 513 | $.each(file_comments, function(i, comment) { |
|
514 | 514 | var path = $(comment).attr('path'); |
|
515 | 515 | var comms = $(comment).children().length; |
|
516 | 516 | if (path in commentTotals) { |
|
517 | 517 | commentTotals[path] += comms; |
|
518 | 518 | } else { |
|
519 | 519 | commentTotals[path] = comms; |
|
520 | 520 | } |
|
521 | 521 | }); |
|
522 | 522 | $.each(commentTotals, function(path, total) { |
|
523 | 523 | var elem = $('.comment-bubble[data-path="'+ path +'"]'); |
|
524 | 524 | elem.css('visibility', 'visible'); |
|
525 | 525 | elem.html(elem.html() + ' ' + total ); |
|
526 | 526 | }); |
|
527 | 527 | |
|
528 | 528 | $('#merge_pull_request_form').submit(function() { |
|
529 | 529 | if (!$('#merge_pull_request').attr('disabled')) { |
|
530 | 530 | $('#merge_pull_request').attr('disabled', 'disabled'); |
|
531 | 531 | } |
|
532 | 532 | return true; |
|
533 | 533 | }); |
|
534 | 534 | |
|
535 | 535 | $('#edit_pull_request').on('click', function(e){ |
|
536 | 536 | var title = $('#pr-title-input').val(); |
|
537 | 537 | var description = codeMirrorInstance.getValue(); |
|
538 | 538 | editPullRequest( |
|
539 | 539 | "${c.repo_name}", "${c.pull_request.pull_request_id}", |
|
540 | 540 | title, description); |
|
541 | 541 | }); |
|
542 | 542 | |
|
543 | 543 | $('#update_pull_request').on('click', function(e){ |
|
544 | 544 | updateReviewers(undefined, "${c.repo_name}", "${c.pull_request.pull_request_id}"); |
|
545 | 545 | }); |
|
546 | 546 | |
|
547 | 547 | $('#update_commits').on('click', function(e){ |
|
548 | 548 | var isDisabled = !$(e.currentTarget).attr('disabled'); |
|
549 | 549 | $(e.currentTarget).text(_TM['Updating...']); |
|
550 | 550 | $(e.currentTarget).attr('disabled', 'disabled'); |
|
551 | 551 | if(isDisabled){ |
|
552 | 552 | updateCommits("${c.repo_name}", "${c.pull_request.pull_request_id}"); |
|
553 | 553 | } |
|
554 | 554 | |
|
555 | 555 | }); |
|
556 | 556 | // fixing issue with caches on firefox |
|
557 | 557 | $('#update_commits').removeAttr("disabled"); |
|
558 | 558 | |
|
559 | 559 | $('#close_pull_request').on('click', function(e){ |
|
560 | 560 | closePullRequest("${c.repo_name}", "${c.pull_request.pull_request_id}"); |
|
561 | 561 | }); |
|
562 | 562 | }) |
|
563 | 563 | </script> |
|
564 | 564 | |
|
565 | 565 | </div> |
|
566 | 566 | </div> |
|
567 | 567 | |
|
568 | 568 | </%def> |
@@ -1,241 +1,243 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | |
|
3 | 3 | # Import early to make sure things are patched up properly |
|
4 | 4 | from setuptools import setup, find_packages |
|
5 | 5 | |
|
6 | 6 | import os |
|
7 | 7 | import sys |
|
8 | 8 | import platform |
|
9 | 9 | |
|
10 | 10 | if sys.version_info < (2, 7): |
|
11 | 11 | raise Exception('RhodeCode requires Python 2.7 or later') |
|
12 | 12 | |
|
13 | 13 | |
|
14 | 14 | here = os.path.abspath(os.path.dirname(__file__)) |
|
15 | 15 | |
|
16 | 16 | |
|
17 | 17 | def _get_meta_var(name, data, callback_handler=None): |
|
18 | 18 | import re |
|
19 | 19 | matches = re.compile(r'(?:%s)\s*=\s*(.*)' % name).search(data) |
|
20 | 20 | if matches: |
|
21 | 21 | if not callable(callback_handler): |
|
22 | 22 | callback_handler = lambda v: v |
|
23 | 23 | |
|
24 | 24 | return callback_handler(eval(matches.groups()[0])) |
|
25 | 25 | |
|
26 | 26 | _meta = open(os.path.join(here, 'rhodecode', '__init__.py'), 'rb') |
|
27 | 27 | _metadata = _meta.read() |
|
28 | 28 | _meta.close() |
|
29 | 29 | |
|
30 | 30 | callback = lambda V: ('.'.join(map(str, V[:3])) + '.'.join(V[3:])) |
|
31 | 31 | __version__ = open(os.path.join('rhodecode', 'VERSION')).read().strip() |
|
32 | 32 | __license__ = _get_meta_var('__license__', _metadata) |
|
33 | 33 | __author__ = _get_meta_var('__author__', _metadata) |
|
34 | 34 | __url__ = _get_meta_var('__url__', _metadata) |
|
35 | 35 | # defines current platform |
|
36 | 36 | __platform__ = platform.system() |
|
37 | 37 | |
|
38 | 38 | # Cygwin has different platform identifiers, but they all contain the |
|
39 | 39 | # term "CYGWIN" |
|
40 | 40 | is_windows = __platform__ == 'Windows' or 'CYGWIN' in __platform__ |
|
41 | 41 | |
|
42 | 42 | requirements = [ |
|
43 | 43 | 'Babel', |
|
44 | 44 | 'Beaker', |
|
45 | 45 | 'FormEncode', |
|
46 | 46 | 'Mako', |
|
47 | 47 | 'Markdown', |
|
48 | 48 | 'MarkupSafe', |
|
49 | 49 | 'MySQL-python', |
|
50 | 50 | 'Paste', |
|
51 | 51 | 'PasteDeploy', |
|
52 | 52 | 'PasteScript', |
|
53 | 53 | 'Pygments', |
|
54 | 54 | 'Pylons', |
|
55 | 55 | 'Pyro4', |
|
56 | 56 | 'Routes', |
|
57 | 57 | 'SQLAlchemy', |
|
58 | 58 | 'Tempita', |
|
59 | 59 | 'URLObject', |
|
60 | 60 | 'WebError', |
|
61 | 61 | 'WebHelpers', |
|
62 | 62 | 'WebHelpers2', |
|
63 | 63 | 'WebOb', |
|
64 | 64 | 'WebTest', |
|
65 | 65 | 'Whoosh', |
|
66 | 66 | 'alembic', |
|
67 | 67 | 'amqplib', |
|
68 | 68 | 'anyjson', |
|
69 | 69 | 'appenlight-client', |
|
70 | 70 | 'authomatic', |
|
71 | 71 | 'backport_ipaddress', |
|
72 | 72 | 'celery', |
|
73 | 73 | 'colander', |
|
74 | 74 | 'decorator', |
|
75 | 75 | 'docutils', |
|
76 | 'gunicorn', | |
|
76 | 77 | 'infrae.cache', |
|
77 | 78 | 'ipython', |
|
78 | 79 | 'iso8601', |
|
79 | 80 | 'kombu', |
|
80 | 81 | 'msgpack-python', |
|
81 | 82 | 'packaging', |
|
82 | 83 | 'psycopg2', |
|
83 | 84 | 'pycrypto', |
|
84 | 85 | 'pycurl', |
|
85 | 86 | 'pyparsing', |
|
86 | 87 | 'pyramid', |
|
87 | 88 | 'pyramid-debugtoolbar', |
|
88 | 89 | 'pyramid-mako', |
|
89 | 90 | 'pyramid-beaker', |
|
90 | 91 | 'pysqlite', |
|
91 | 92 | 'python-dateutil', |
|
92 | 93 | 'python-ldap', |
|
93 | 94 | 'python-memcached', |
|
94 | 95 | 'python-pam', |
|
95 | 96 | 'recaptcha-client', |
|
96 | 97 | 'repoze.lru', |
|
97 | 98 | 'requests', |
|
98 | 99 | 'simplejson', |
|
99 | 100 | 'waitress', |
|
100 | 101 | 'zope.cachedescriptors', |
|
101 | 102 | ] |
|
102 | 103 | |
|
103 | 104 | if is_windows: |
|
104 | 105 | pass |
|
105 | 106 | else: |
|
106 | 107 | requirements.append('psutil') |
|
107 | 108 | requirements.append('py-bcrypt') |
|
108 | 109 | |
|
109 | 110 | test_requirements = [ |
|
110 | 111 | 'WebTest', |
|
111 | 112 | 'configobj', |
|
112 | 113 | 'cssselect', |
|
113 | 114 | 'flake8', |
|
114 | 115 | 'lxml', |
|
115 | 116 | 'mock', |
|
116 | 117 | 'pytest', |
|
118 | 'pytest-cov', | |
|
117 | 119 | 'pytest-runner', |
|
118 | 120 | ] |
|
119 | 121 | |
|
120 | 122 | setup_requirements = [ |
|
121 | 123 | 'PasteScript', |
|
122 | 124 | 'pytest-runner', |
|
123 | 125 | ] |
|
124 | 126 | |
|
125 | 127 | dependency_links = [ |
|
126 | 128 | ] |
|
127 | 129 | |
|
128 | 130 | classifiers = [ |
|
129 | 131 | 'Development Status :: 6 - Mature', |
|
130 | 132 | 'Environment :: Web Environment', |
|
131 | 133 | 'Framework :: Pylons', |
|
132 | 134 | 'Intended Audience :: Developers', |
|
133 | 135 | 'Operating System :: OS Independent', |
|
134 | 136 | 'Programming Language :: Python', |
|
135 | 137 | 'Programming Language :: Python :: 2.7', |
|
136 | 138 | ] |
|
137 | 139 | |
|
138 | 140 | |
|
139 | 141 | # additional files from project that goes somewhere in the filesystem |
|
140 | 142 | # relative to sys.prefix |
|
141 | 143 | data_files = [] |
|
142 | 144 | |
|
143 | 145 | # additional files that goes into package itself |
|
144 | 146 | package_data = {'rhodecode': ['i18n/*/LC_MESSAGES/*.mo', ], } |
|
145 | 147 | |
|
146 | 148 | description = ('RhodeCode is a fast and powerful management tool ' |
|
147 | 149 | 'for Mercurial and GIT with a built in push/pull server, ' |
|
148 | 150 | 'full text search and code-review.') |
|
149 | 151 | |
|
150 | 152 | keywords = ' '.join([ |
|
151 | 153 | 'rhodecode', 'rhodiumcode', 'mercurial', 'git', 'code review', |
|
152 | 154 | 'repo groups', 'ldap', 'repository management', 'hgweb replacement', |
|
153 | 155 | 'hgwebdir', 'gitweb replacement', 'serving hgweb', |
|
154 | 156 | ]) |
|
155 | 157 | |
|
156 | 158 | # long description |
|
157 | 159 | README_FILE = 'README.rst' |
|
158 | 160 | CHANGELOG_FILE = 'CHANGES.rst' |
|
159 | 161 | try: |
|
160 | 162 | long_description = open(README_FILE).read() + '\n\n' + \ |
|
161 | 163 | open(CHANGELOG_FILE).read() |
|
162 | 164 | |
|
163 | 165 | except IOError, err: |
|
164 | 166 | sys.stderr.write( |
|
165 | 167 | '[WARNING] Cannot find file specified as long_description (%s)\n or ' |
|
166 | 168 | 'changelog (%s) skipping that file' % (README_FILE, CHANGELOG_FILE) |
|
167 | 169 | ) |
|
168 | 170 | long_description = description |
|
169 | 171 | |
|
170 | 172 | # packages |
|
171 | 173 | packages = find_packages() |
|
172 | 174 | |
|
173 | 175 | paster_commands = [ |
|
174 | 176 | 'make-config=rhodecode.lib.paster_commands.make_config:Command', |
|
175 | 177 | 'setup-rhodecode=rhodecode.lib.paster_commands.setup_rhodecode:Command', |
|
176 | 178 | 'update-repoinfo=rhodecode.lib.paster_commands.update_repoinfo:Command', |
|
177 | 179 | 'cache-keys=rhodecode.lib.paster_commands.cache_keys:Command', |
|
178 | 180 | 'ishell=rhodecode.lib.paster_commands.ishell:Command', |
|
179 | 181 | 'upgrade-db=rhodecode.lib.dbmigrate:UpgradeDb', |
|
180 | 182 | 'celeryd=rhodecode.lib.celerypylons.commands:CeleryDaemonCommand', |
|
181 | 183 | ] |
|
182 | 184 | |
|
183 | 185 | setup( |
|
184 | 186 | name='rhodecode-enterprise-ce', |
|
185 | 187 | version=__version__, |
|
186 | 188 | description=description, |
|
187 | 189 | long_description=long_description, |
|
188 | 190 | keywords=keywords, |
|
189 | 191 | license=__license__, |
|
190 | 192 | author=__author__, |
|
191 | 193 | author_email='marcin@rhodecode.com', |
|
192 | 194 | dependency_links=dependency_links, |
|
193 | 195 | url=__url__, |
|
194 | 196 | install_requires=requirements, |
|
195 | 197 | tests_require=test_requirements, |
|
196 | 198 | classifiers=classifiers, |
|
197 | 199 | setup_requires=setup_requirements, |
|
198 | 200 | data_files=data_files, |
|
199 | 201 | packages=packages, |
|
200 | 202 | include_package_data=True, |
|
201 | 203 | package_data=package_data, |
|
202 | 204 | message_extractors={ |
|
203 | 205 | 'rhodecode': [ |
|
204 | 206 | ('**.py', 'python', None), |
|
205 | 207 | ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}), |
|
206 | 208 | ('templates/**.html', 'mako', {'input_encoding': 'utf-8'}), |
|
207 | 209 | ('public/**', 'ignore', None), |
|
208 | 210 | ] |
|
209 | 211 | }, |
|
210 | 212 | zip_safe=False, |
|
211 | 213 | paster_plugins=['PasteScript', 'Pylons'], |
|
212 | 214 | entry_points={ |
|
213 | 215 | 'enterprise.plugins1': [ |
|
214 | 216 | 'crowd=rhodecode.authentication.plugins.auth_crowd:plugin_factory', |
|
215 | 217 | 'jasig_cas=rhodecode.authentication.plugins.auth_jasig_cas:plugin_factory', |
|
216 | 218 | 'ldap=rhodecode.authentication.plugins.auth_ldap:plugin_factory', |
|
217 | 219 | 'pam=rhodecode.authentication.plugins.auth_pam:plugin_factory', |
|
218 | 220 | 'rhodecode=rhodecode.authentication.plugins.auth_rhodecode:plugin_factory', |
|
219 | 221 | ], |
|
220 | 222 | 'paste.app_factory': [ |
|
221 | 223 | 'main=rhodecode.config.middleware:make_pyramid_app', |
|
222 | 224 | 'pylons=rhodecode.config.middleware:make_app', |
|
223 | 225 | ], |
|
224 | 226 | 'paste.app_install': [ |
|
225 | 227 | 'main=pylons.util:PylonsInstaller', |
|
226 | 228 | 'pylons=pylons.util:PylonsInstaller', |
|
227 | 229 | ], |
|
228 | 230 | 'paste.global_paster_command': paster_commands, |
|
229 | 231 | 'pytest11': [ |
|
230 | 232 | 'pylons=rhodecode.tests.pylons_plugin', |
|
231 | 233 | 'enterprise=rhodecode.tests.plugin', |
|
232 | 234 | ], |
|
233 | 235 | 'console_scripts': [ |
|
234 | 236 | 'rcserver=rhodecode.rcserver:main', |
|
235 | 237 | ], |
|
236 | 238 | 'beaker.backends': [ |
|
237 | 239 | 'memorylru_base=rhodecode.lib.memory_lru_debug:MemoryLRUNamespaceManagerBase', |
|
238 | 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