Show More
@@ -0,0 +1,63 b'' | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | """ | |||
|
3 | rhodecode.model.users_group | |||
|
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |||
|
5 | ||||
|
6 | repository permission model for RhodeCode | |||
|
7 | ||||
|
8 | :created_on: Oct 1, 2011 | |||
|
9 | :author: nvinot | |||
|
10 | :copyright: (C) 2011-2011 Nicolas Vinot <aeris@imirhil.fr> | |||
|
11 | :license: GPLv3, see COPYING for more details. | |||
|
12 | """ | |||
|
13 | # This program is free software: you can redistribute it and/or modify | |||
|
14 | # it under the terms of the GNU General Public License as published by | |||
|
15 | # the Free Software Foundation, either version 3 of the License, or | |||
|
16 | # (at your option) any later version. | |||
|
17 | # | |||
|
18 | # This program is distributed in the hope that it will be useful, | |||
|
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
21 | # GNU General Public License for more details. | |||
|
22 | # | |||
|
23 | # You should have received a copy of the GNU General Public License | |||
|
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
|
25 | ||||
|
26 | import logging | |||
|
27 | from rhodecode.model.db import BaseModel, RepoToPerm, Permission | |||
|
28 | from rhodecode.model.meta import Session | |||
|
29 | ||||
|
30 | log = logging.getLogger(__name__) | |||
|
31 | ||||
|
32 | class RepositoryPermissionModel(BaseModel): | |||
|
33 | def get_user_permission(self, repository, user): | |||
|
34 | return RepoToPerm.query() \ | |||
|
35 | .filter(RepoToPerm.user == user) \ | |||
|
36 | .filter(RepoToPerm.repository == repository) \ | |||
|
37 | .scalar() | |||
|
38 | ||||
|
39 | def update_user_permission(self, repository, user, permission): | |||
|
40 | permission = Permission.get_by_key(permission) | |||
|
41 | current = self.get_user_permission(repository, user) | |||
|
42 | if current: | |||
|
43 | if not current.permission is permission: | |||
|
44 | current.permission = permission | |||
|
45 | else: | |||
|
46 | p = RepoToPerm() | |||
|
47 | p.user = user | |||
|
48 | p.repository = repository | |||
|
49 | p.permission = permission | |||
|
50 | Session.add(p) | |||
|
51 | Session.commit() | |||
|
52 | ||||
|
53 | def delete_user_permission(self, repository, user): | |||
|
54 | current = self.get_user_permission(repository, user) | |||
|
55 | if current: | |||
|
56 | Session.delete(current) | |||
|
57 | Session.commit() | |||
|
58 | ||||
|
59 | def update_or_delete_user_permission(self, repository, user, permission): | |||
|
60 | if permission: | |||
|
61 | self.update_user_permission(repository, user, permission) | |||
|
62 | else: | |||
|
63 | self.delete_user_permission(repository, user) |
@@ -0,0 +1,89 b'' | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | """ | |||
|
3 | rhodecode.model.users_group | |||
|
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |||
|
5 | ||||
|
6 | users group model for RhodeCode | |||
|
7 | ||||
|
8 | :created_on: Oct 1, 2011 | |||
|
9 | :author: nvinot | |||
|
10 | :copyright: (C) 2011-2011 Nicolas Vinot <aeris@imirhil.fr> | |||
|
11 | :license: GPLv3, see COPYING for more details. | |||
|
12 | """ | |||
|
13 | # This program is free software: you can redistribute it and/or modify | |||
|
14 | # it under the terms of the GNU General Public License as published by | |||
|
15 | # the Free Software Foundation, either version 3 of the License, or | |||
|
16 | # (at your option) any later version. | |||
|
17 | # | |||
|
18 | # This program is distributed in the hope that it will be useful, | |||
|
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
21 | # GNU General Public License for more details. | |||
|
22 | # | |||
|
23 | # You should have received a copy of the GNU General Public License | |||
|
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
|
25 | ||||
|
26 | import logging | |||
|
27 | import traceback | |||
|
28 | ||||
|
29 | from rhodecode.model import BaseModel | |||
|
30 | from rhodecode.model.caching_query import FromCache | |||
|
31 | from rhodecode.model.db import UsersGroupMember, UsersGroup | |||
|
32 | ||||
|
33 | log = logging.getLogger(__name__) | |||
|
34 | ||||
|
35 | class UsersGroupModel(BaseModel): | |||
|
36 | ||||
|
37 | def get(self, users_group_id, cache = False): | |||
|
38 | users_group = UsersGroup.query() | |||
|
39 | if cache: | |||
|
40 | users_group = users_group.options(FromCache("sql_cache_short", | |||
|
41 | "get_users_group_%s" % users_group_id)) | |||
|
42 | return users_group.get(users_group_id) | |||
|
43 | ||||
|
44 | def get_by_name(self, name, cache = False, case_insensitive = False): | |||
|
45 | users_group = UsersGroup.query() | |||
|
46 | if case_insensitive: | |||
|
47 | users_group = users_group.filter(UsersGroup.users_group_name.ilike(name)) | |||
|
48 | else: | |||
|
49 | users_group = users_group.filter(UsersGroup.users_group_name == name) | |||
|
50 | if cache: | |||
|
51 | users_group = users_group.options(FromCache("sql_cache_short", | |||
|
52 | "get_users_group_%s" % name)) | |||
|
53 | return users_group.scalar() | |||
|
54 | ||||
|
55 | def create(self, form_data): | |||
|
56 | try: | |||
|
57 | new_users_group = UsersGroup() | |||
|
58 | for k, v in form_data.items(): | |||
|
59 | setattr(new_users_group, k, v) | |||
|
60 | ||||
|
61 | self.sa.add(new_users_group) | |||
|
62 | self.sa.commit() | |||
|
63 | return new_users_group | |||
|
64 | except: | |||
|
65 | log.error(traceback.format_exc()) | |||
|
66 | self.sa.rollback() | |||
|
67 | raise | |||
|
68 | ||||
|
69 | def add_user_to_group(self, users_group, user): | |||
|
70 | for m in users_group.members: | |||
|
71 | u = m.user | |||
|
72 | if u.user_id == user.user_id: | |||
|
73 | return m | |||
|
74 | ||||
|
75 | try: | |||
|
76 | users_group_member = UsersGroupMember() | |||
|
77 | users_group_member.user = user | |||
|
78 | users_group_member.users_group = users_group | |||
|
79 | ||||
|
80 | users_group.members.append(users_group_member) | |||
|
81 | user.group_member.append(users_group_member) | |||
|
82 | ||||
|
83 | self.sa.add(users_group_member) | |||
|
84 | self.sa.commit() | |||
|
85 | return users_group_member | |||
|
86 | except: | |||
|
87 | log.error(traceback.format_exc()) | |||
|
88 | self.sa.rollback() | |||
|
89 | raise |
@@ -1,233 +1,235 b'' | |||||
1 | ################################################################################ |
|
1 | ################################################################################ | |
2 | ################################################################################ |
|
2 | ################################################################################ | |
3 | # RhodeCode - Pylons environment configuration # |
|
3 | # RhodeCode - Pylons environment configuration # | |
4 | # # |
|
4 | # # | |
5 | # The %(here)s variable will be replaced with the parent directory of this file# |
|
5 | # The %(here)s variable will be replaced with the parent directory of this file# | |
6 | ################################################################################ |
|
6 | ################################################################################ | |
7 |
|
7 | |||
8 | [DEFAULT] |
|
8 | [DEFAULT] | |
9 | debug = true |
|
9 | debug = true | |
10 | pdebug = false |
|
10 | pdebug = false | |
11 | ################################################################################ |
|
11 | ################################################################################ | |
12 | ## Uncomment and replace with the address which should receive ## |
|
12 | ## Uncomment and replace with the address which should receive ## | |
13 | ## any error reports after application crash ## |
|
13 | ## any error reports after application crash ## | |
14 | ## Additionally those settings will be used by RhodeCode mailing system ## |
|
14 | ## Additionally those settings will be used by RhodeCode mailing system ## | |
15 | ################################################################################ |
|
15 | ################################################################################ | |
16 | #email_to = admin@localhost |
|
16 | #email_to = admin@localhost | |
17 | #error_email_from = paste_error@localhost |
|
17 | #error_email_from = paste_error@localhost | |
18 | #app_email_from = rhodecode-noreply@localhost |
|
18 | #app_email_from = rhodecode-noreply@localhost | |
19 | #error_message = |
|
19 | #error_message = | |
20 |
|
20 | |||
21 | #smtp_server = mail.server.com |
|
21 | #smtp_server = mail.server.com | |
22 | #smtp_username = |
|
22 | #smtp_username = | |
23 | #smtp_password = |
|
23 | #smtp_password = | |
24 | #smtp_port = |
|
24 | #smtp_port = | |
25 | #smtp_use_tls = false |
|
25 | #smtp_use_tls = false | |
26 | #smtp_use_ssl = true |
|
26 | #smtp_use_ssl = true | |
|
27 | # Specify available auth parameters here (e.g. LOGIN PLAIN CRAM-MD5, etc.) | |||
|
28 | #smtp_auth = | |||
27 |
|
29 | |||
28 | [server:main] |
|
30 | [server:main] | |
29 | ##nr of threads to spawn |
|
31 | ##nr of threads to spawn | |
30 | threadpool_workers = 5 |
|
32 | threadpool_workers = 5 | |
31 |
|
33 | |||
32 | ##max request before thread respawn |
|
34 | ##max request before thread respawn | |
33 | threadpool_max_requests = 6 |
|
35 | threadpool_max_requests = 6 | |
34 |
|
36 | |||
35 | ##option to use threads of process |
|
37 | ##option to use threads of process | |
36 | use_threadpool = true |
|
38 | use_threadpool = true | |
37 |
|
39 | |||
38 | use = egg:Paste#http |
|
40 | use = egg:Paste#http | |
39 | host = 0.0.0.0 |
|
41 | host = 0.0.0.0 | |
40 | port = 5000 |
|
42 | port = 5000 | |
41 |
|
43 | |||
42 | [app:main] |
|
44 | [app:main] | |
43 | use = egg:rhodecode |
|
45 | use = egg:rhodecode | |
44 | full_stack = true |
|
46 | full_stack = true | |
45 | static_files = true |
|
47 | static_files = true | |
46 | lang=en |
|
48 | lang=en | |
47 | cache_dir = %(here)s/data |
|
49 | cache_dir = %(here)s/data | |
48 | index_dir = %(here)s/data/index |
|
50 | index_dir = %(here)s/data/index | |
49 | app_instance_uuid = develop |
|
51 | app_instance_uuid = develop | |
50 | cut_off_limit = 256000 |
|
52 | cut_off_limit = 256000 | |
51 | force_https = false |
|
53 | force_https = false | |
52 | commit_parse_limit = 25 |
|
54 | commit_parse_limit = 25 | |
53 | use_gravatar = true |
|
55 | use_gravatar = true | |
54 |
|
56 | |||
55 | #################################### |
|
57 | #################################### | |
56 | ### CELERY CONFIG #### |
|
58 | ### CELERY CONFIG #### | |
57 | #################################### |
|
59 | #################################### | |
58 | use_celery = false |
|
60 | use_celery = false | |
59 | broker.host = localhost |
|
61 | broker.host = localhost | |
60 | broker.vhost = rabbitmqhost |
|
62 | broker.vhost = rabbitmqhost | |
61 | broker.port = 5672 |
|
63 | broker.port = 5672 | |
62 | broker.user = rabbitmq |
|
64 | broker.user = rabbitmq | |
63 | broker.password = qweqwe |
|
65 | broker.password = qweqwe | |
64 |
|
66 | |||
65 | celery.imports = rhodecode.lib.celerylib.tasks |
|
67 | celery.imports = rhodecode.lib.celerylib.tasks | |
66 |
|
68 | |||
67 | celery.result.backend = amqp |
|
69 | celery.result.backend = amqp | |
68 | celery.result.dburi = amqp:// |
|
70 | celery.result.dburi = amqp:// | |
69 | celery.result.serialier = json |
|
71 | celery.result.serialier = json | |
70 |
|
72 | |||
71 | #celery.send.task.error.emails = true |
|
73 | #celery.send.task.error.emails = true | |
72 | #celery.amqp.task.result.expires = 18000 |
|
74 | #celery.amqp.task.result.expires = 18000 | |
73 |
|
75 | |||
74 | celeryd.concurrency = 2 |
|
76 | celeryd.concurrency = 2 | |
75 | #celeryd.log.file = celeryd.log |
|
77 | #celeryd.log.file = celeryd.log | |
76 | celeryd.log.level = debug |
|
78 | celeryd.log.level = debug | |
77 | celeryd.max.tasks.per.child = 1 |
|
79 | celeryd.max.tasks.per.child = 1 | |
78 |
|
80 | |||
79 | #tasks will never be sent to the queue, but executed locally instead. |
|
81 | #tasks will never be sent to the queue, but executed locally instead. | |
80 | celery.always.eager = false |
|
82 | celery.always.eager = false | |
81 |
|
83 | |||
82 | #################################### |
|
84 | #################################### | |
83 | ### BEAKER CACHE #### |
|
85 | ### BEAKER CACHE #### | |
84 | #################################### |
|
86 | #################################### | |
85 | beaker.cache.data_dir=%(here)s/data/cache/data |
|
87 | beaker.cache.data_dir=%(here)s/data/cache/data | |
86 | beaker.cache.lock_dir=%(here)s/data/cache/lock |
|
88 | beaker.cache.lock_dir=%(here)s/data/cache/lock | |
87 |
|
89 | |||
88 | beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long |
|
90 | beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long | |
89 |
|
91 | |||
90 | beaker.cache.super_short_term.type=memory |
|
92 | beaker.cache.super_short_term.type=memory | |
91 | beaker.cache.super_short_term.expire=10 |
|
93 | beaker.cache.super_short_term.expire=10 | |
92 |
|
94 | |||
93 | beaker.cache.short_term.type=memory |
|
95 | beaker.cache.short_term.type=memory | |
94 | beaker.cache.short_term.expire=60 |
|
96 | beaker.cache.short_term.expire=60 | |
95 |
|
97 | |||
96 | beaker.cache.long_term.type=memory |
|
98 | beaker.cache.long_term.type=memory | |
97 | beaker.cache.long_term.expire=36000 |
|
99 | beaker.cache.long_term.expire=36000 | |
98 |
|
100 | |||
99 | beaker.cache.sql_cache_short.type=memory |
|
101 | beaker.cache.sql_cache_short.type=memory | |
100 | beaker.cache.sql_cache_short.expire=10 |
|
102 | beaker.cache.sql_cache_short.expire=10 | |
101 |
|
103 | |||
102 | beaker.cache.sql_cache_med.type=memory |
|
104 | beaker.cache.sql_cache_med.type=memory | |
103 | beaker.cache.sql_cache_med.expire=360 |
|
105 | beaker.cache.sql_cache_med.expire=360 | |
104 |
|
106 | |||
105 | beaker.cache.sql_cache_long.type=file |
|
107 | beaker.cache.sql_cache_long.type=file | |
106 | beaker.cache.sql_cache_long.expire=3600 |
|
108 | beaker.cache.sql_cache_long.expire=3600 | |
107 |
|
109 | |||
108 | #################################### |
|
110 | #################################### | |
109 | ### BEAKER SESSION #### |
|
111 | ### BEAKER SESSION #### | |
110 | #################################### |
|
112 | #################################### | |
111 | ## Type of storage used for the session, current types are |
|
113 | ## Type of storage used for the session, current types are | |
112 | ## dbm, file, memcached, database, and memory. |
|
114 | ## dbm, file, memcached, database, and memory. | |
113 | ## The storage uses the Container API |
|
115 | ## The storage uses the Container API | |
114 | ##that is also used by the cache system. |
|
116 | ##that is also used by the cache system. | |
115 | beaker.session.type = file |
|
117 | beaker.session.type = file | |
116 |
|
118 | |||
117 | beaker.session.key = rhodecode |
|
119 | beaker.session.key = rhodecode | |
118 | beaker.session.secret = g654dcno0-9873jhgfreyu |
|
120 | beaker.session.secret = g654dcno0-9873jhgfreyu | |
119 | beaker.session.timeout = 36000 |
|
121 | beaker.session.timeout = 36000 | |
120 |
|
122 | |||
121 | ##auto save the session to not to use .save() |
|
123 | ##auto save the session to not to use .save() | |
122 | beaker.session.auto = False |
|
124 | beaker.session.auto = False | |
123 |
|
125 | |||
124 | ##true exire at browser close |
|
126 | ##true exire at browser close | |
125 | #beaker.session.cookie_expires = 3600 |
|
127 | #beaker.session.cookie_expires = 3600 | |
126 |
|
128 | |||
127 |
|
129 | |||
128 | ################################################################################ |
|
130 | ################################################################################ | |
129 | ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ## |
|
131 | ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ## | |
130 | ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ## |
|
132 | ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ## | |
131 | ## execute malicious code after an exception is raised. ## |
|
133 | ## execute malicious code after an exception is raised. ## | |
132 | ################################################################################ |
|
134 | ################################################################################ | |
133 | #set debug = false |
|
135 | #set debug = false | |
134 |
|
136 | |||
135 | ################################## |
|
137 | ################################## | |
136 | ### LOGVIEW CONFIG ### |
|
138 | ### LOGVIEW CONFIG ### | |
137 | ################################## |
|
139 | ################################## | |
138 | logview.sqlalchemy = #faa |
|
140 | logview.sqlalchemy = #faa | |
139 | logview.pylons.templating = #bfb |
|
141 | logview.pylons.templating = #bfb | |
140 | logview.pylons.util = #eee |
|
142 | logview.pylons.util = #eee | |
141 |
|
143 | |||
142 | ######################################################### |
|
144 | ######################################################### | |
143 | ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ### |
|
145 | ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ### | |
144 | ######################################################### |
|
146 | ######################################################### | |
145 | #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db |
|
147 | #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db | |
146 | sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode |
|
148 | sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode | |
147 | sqlalchemy.db1.echo = false |
|
149 | sqlalchemy.db1.echo = false | |
148 | sqlalchemy.db1.pool_recycle = 3600 |
|
150 | sqlalchemy.db1.pool_recycle = 3600 | |
149 | sqlalchemy.convert_unicode = true |
|
151 | sqlalchemy.convert_unicode = true | |
150 |
|
152 | |||
151 | ################################ |
|
153 | ################################ | |
152 | ### LOGGING CONFIGURATION #### |
|
154 | ### LOGGING CONFIGURATION #### | |
153 | ################################ |
|
155 | ################################ | |
154 | [loggers] |
|
156 | [loggers] | |
155 | keys = root, routes, rhodecode, sqlalchemy, beaker, templates |
|
157 | keys = root, routes, rhodecode, sqlalchemy, beaker, templates | |
156 |
|
158 | |||
157 | [handlers] |
|
159 | [handlers] | |
158 | keys = console, console_sql |
|
160 | keys = console, console_sql | |
159 |
|
161 | |||
160 | [formatters] |
|
162 | [formatters] | |
161 | keys = generic, color_formatter, color_formatter_sql |
|
163 | keys = generic, color_formatter, color_formatter_sql | |
162 |
|
164 | |||
163 | ############# |
|
165 | ############# | |
164 | ## LOGGERS ## |
|
166 | ## LOGGERS ## | |
165 | ############# |
|
167 | ############# | |
166 | [logger_root] |
|
168 | [logger_root] | |
167 | level = NOTSET |
|
169 | level = NOTSET | |
168 | handlers = console |
|
170 | handlers = console | |
169 |
|
171 | |||
170 | [logger_routes] |
|
172 | [logger_routes] | |
171 | level = DEBUG |
|
173 | level = DEBUG | |
172 | handlers = |
|
174 | handlers = | |
173 | qualname = routes.middleware |
|
175 | qualname = routes.middleware | |
174 | # "level = DEBUG" logs the route matched and routing variables. |
|
176 | # "level = DEBUG" logs the route matched and routing variables. | |
175 | propagate = 1 |
|
177 | propagate = 1 | |
176 |
|
178 | |||
177 | [logger_beaker] |
|
179 | [logger_beaker] | |
178 | level = DEBUG |
|
180 | level = DEBUG | |
179 | handlers = |
|
181 | handlers = | |
180 | qualname = beaker.container |
|
182 | qualname = beaker.container | |
181 | propagate = 1 |
|
183 | propagate = 1 | |
182 |
|
184 | |||
183 | [logger_templates] |
|
185 | [logger_templates] | |
184 | level = INFO |
|
186 | level = INFO | |
185 | handlers = |
|
187 | handlers = | |
186 | qualname = pylons.templating |
|
188 | qualname = pylons.templating | |
187 | propagate = 1 |
|
189 | propagate = 1 | |
188 |
|
190 | |||
189 | [logger_rhodecode] |
|
191 | [logger_rhodecode] | |
190 | level = DEBUG |
|
192 | level = DEBUG | |
191 | handlers = |
|
193 | handlers = | |
192 | qualname = rhodecode |
|
194 | qualname = rhodecode | |
193 | propagate = 1 |
|
195 | propagate = 1 | |
194 |
|
196 | |||
195 | [logger_sqlalchemy] |
|
197 | [logger_sqlalchemy] | |
196 | level = INFO |
|
198 | level = INFO | |
197 | handlers = console_sql |
|
199 | handlers = console_sql | |
198 | qualname = sqlalchemy.engine |
|
200 | qualname = sqlalchemy.engine | |
199 | propagate = 0 |
|
201 | propagate = 0 | |
200 |
|
202 | |||
201 | ############## |
|
203 | ############## | |
202 | ## HANDLERS ## |
|
204 | ## HANDLERS ## | |
203 | ############## |
|
205 | ############## | |
204 |
|
206 | |||
205 | [handler_console] |
|
207 | [handler_console] | |
206 | class = StreamHandler |
|
208 | class = StreamHandler | |
207 | args = (sys.stderr,) |
|
209 | args = (sys.stderr,) | |
208 | level = DEBUG |
|
210 | level = DEBUG | |
209 | formatter = color_formatter |
|
211 | formatter = color_formatter | |
210 |
|
212 | |||
211 | [handler_console_sql] |
|
213 | [handler_console_sql] | |
212 | class = StreamHandler |
|
214 | class = StreamHandler | |
213 | args = (sys.stderr,) |
|
215 | args = (sys.stderr,) | |
214 | level = DEBUG |
|
216 | level = DEBUG | |
215 | formatter = color_formatter_sql |
|
217 | formatter = color_formatter_sql | |
216 |
|
218 | |||
217 | ################ |
|
219 | ################ | |
218 | ## FORMATTERS ## |
|
220 | ## FORMATTERS ## | |
219 | ################ |
|
221 | ################ | |
220 |
|
222 | |||
221 | [formatter_generic] |
|
223 | [formatter_generic] | |
222 | format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s |
|
224 | format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s | |
223 | datefmt = %Y-%m-%d %H:%M:%S |
|
225 | datefmt = %Y-%m-%d %H:%M:%S | |
224 |
|
226 | |||
225 | [formatter_color_formatter] |
|
227 | [formatter_color_formatter] | |
226 | class=rhodecode.lib.colored_formatter.ColorFormatter |
|
228 | class=rhodecode.lib.colored_formatter.ColorFormatter | |
227 | format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s |
|
229 | format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s | |
228 | datefmt = %Y-%m-%d %H:%M:%S |
|
230 | datefmt = %Y-%m-%d %H:%M:%S | |
229 |
|
231 | |||
230 | [formatter_color_formatter_sql] |
|
232 | [formatter_color_formatter_sql] | |
231 | class=rhodecode.lib.colored_formatter.ColorFormatterSql |
|
233 | class=rhodecode.lib.colored_formatter.ColorFormatterSql | |
232 | format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s |
|
234 | format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s | |
233 | datefmt = %Y-%m-%d %H:%M:%S |
|
235 | datefmt = %Y-%m-%d %H:%M:%S |
@@ -1,111 +1,362 b'' | |||||
1 | .. _api: |
|
1 | .. _api: | |
2 |
|
2 | |||
3 |
|
3 | |||
4 | API |
|
4 | API | |
5 | === |
|
5 | === | |
6 |
|
6 | |||
7 |
|
7 | |||
8 | Starting from RhodeCode version 1.2 a simple API was implemented. |
|
8 | Starting from RhodeCode version 1.2 a simple API was implemented. | |
9 | There's a single schema for calling all api methods. API is implemented |
|
9 | There's a single schema for calling all api methods. API is implemented | |
10 |
with JSON protocol both ways. An url to send API request in RhodeCode is |
|
10 | with JSON protocol both ways. An url to send API request in RhodeCode is | |
11 | <your_server>/_admin/api |
|
11 | <your_server>/_admin/api | |
12 |
|
12 | |||
13 |
|
13 | |||
14 | All clients need to send JSON data in such format:: |
|
14 | All clients need to send JSON data in such format:: | |
15 |
|
15 | |||
16 | { |
|
16 | { | |
17 | "api_key":"<api_key>", |
|
17 | "api_key":"<api_key>", | |
18 | "method":"<method_name>", |
|
18 | "method":"<method_name>", | |
19 | "args":{"<arg_key>":"<arg_val>"} |
|
19 | "args":{"<arg_key>":"<arg_val>"} | |
20 | } |
|
20 | } | |
21 |
|
21 | |||
22 | Example call for autopulling remotes repos using curl:: |
|
22 | Example call for autopulling remotes repos using curl:: | |
23 | curl https://server.com/_admin/api -X POST -H 'content-type:text/plain' --data-binary '{"api_key":"xe7cdb2v278e4evbdf5vs04v832v0efvcbcve4a3","method":"pull","args":{"repo":"CPython"}}' |
|
23 | curl https://server.com/_admin/api -X POST -H 'content-type:text/plain' --data-binary '{"api_key":"xe7cdb2v278e4evbdf5vs04v832v0efvcbcve4a3","method":"pull","args":{"repo":"CPython"}}' | |
24 |
|
24 | |||
25 |
Simply provide |
|
25 | Simply provide | |
26 | - *api_key* for access and permission validation. |
|
26 | - *api_key* for access and permission validation. | |
27 | - *method* is name of method to call |
|
27 | - *method* is name of method to call | |
28 | - *args* is an key:value list of arguments to pass to method |
|
28 | - *args* is an key:value list of arguments to pass to method | |
29 |
|
29 | |||
30 | .. note:: |
|
30 | .. note:: | |
31 |
|
31 | |||
32 |
api_key can be found in your user account page |
|
32 | api_key can be found in your user account page | |
33 |
|
33 | |||
34 |
|
34 | |||
35 | RhodeCode API will return always a JSON formatted answer:: |
|
35 | RhodeCode API will return always a JSON formatted answer:: | |
36 |
|
36 | |||
37 | { |
|
37 | { | |
38 |
"result": "<result>", |
|
38 | "result": "<result>", | |
39 | "error": null |
|
39 | "error": null | |
40 | } |
|
40 | } | |
41 |
|
41 | |||
42 | All responses from API will be `HTTP/1.0 200 OK`, if there's an error while |
|
42 | All responses from API will be `HTTP/1.0 200 OK`, if there's an error while | |
43 |
calling api *error* key from response will contain failure description |
|
43 | calling api *error* key from response will contain failure description | |
44 | and result will be null. |
|
44 | and result will be null. | |
45 |
|
45 | |||
46 | API METHODS |
|
46 | API METHODS | |
47 | +++++++++++ |
|
47 | +++++++++++ | |
48 |
|
48 | |||
49 |
|
49 | |||
50 | pull |
|
50 | pull | |
51 | ---- |
|
51 | ---- | |
52 |
|
52 | |||
53 |
Pulls given repo from remote location. Can be used to automatically keep |
|
53 | Pulls given repo from remote location. Can be used to automatically keep | |
54 |
remote repos up to date. This command can be executed only using api_key |
|
54 | remote repos up to date. This command can be executed only using api_key | |
55 | belonging to user with admin rights |
|
|||
56 |
|
||||
57 | INPUT:: |
|
|||
58 |
|
||||
59 | api_key:"<api_key>" |
|
|||
60 | method: "pull" |
|
|||
61 | args: {"repo":<repo_name>} |
|
|||
62 |
|
||||
63 | OUTPUT:: |
|
|||
64 |
|
||||
65 | result:"Pulled from <repo_name>" |
|
|||
66 | error:null |
|
|||
67 |
|
||||
68 |
|
||||
69 | create_user |
|
|||
70 | ----------- |
|
|||
71 |
|
||||
72 | Creates new user in RhodeCode. This command can be executed only using api_key |
|
|||
73 | belonging to user with admin rights |
|
55 | belonging to user with admin rights | |
74 |
|
56 | |||
75 | INPUT:: |
|
57 | INPUT:: | |
76 |
|
58 | |||
77 | api_key:"<api_key>" |
|
59 | api_key : "<api_key>" | |
78 |
method |
|
60 | method : "pull" | |
79 | args: {"username": "<username>", |
|
61 | args : { | |
80 |
|
|
62 | "repo" : "<repo_name>" | |
81 | "active": "<bool>", |
|
63 | } | |
82 | "admin": "<bool>", |
|
64 | ||
83 | "name": "<firstname>", |
|
65 | OUTPUT:: | |
84 | "lastname": "<lastname>", |
|
66 | ||
85 | "email": "<useremail>"} |
|
67 | result : "Pulled from <repo_name>" | |
|
68 | error : null | |||
|
69 | ||||
|
70 | ||||
|
71 | get_users | |||
|
72 | --------- | |||
|
73 | ||||
|
74 | Lists all existing users. This command can be executed only using api_key | |||
|
75 | belonging to user with admin rights. | |||
|
76 | ||||
|
77 | INPUT:: | |||
|
78 | ||||
|
79 | api_key : "<api_key>" | |||
|
80 | method : "get_users" | |||
|
81 | args : { } | |||
|
82 | ||||
|
83 | OUTPUT:: | |||
|
84 | ||||
|
85 | result: [ | |||
|
86 | { | |||
|
87 | "id" : "<id>", | |||
|
88 | "username" : "<username>", | |||
|
89 | "firstname": "<firstname>", | |||
|
90 | "lastname" : "<lastname>", | |||
|
91 | "email" : "<email>", | |||
|
92 | "active" : "<bool>", | |||
|
93 | "admin" :Â "<bool>", | |||
|
94 | "ldap" : "<ldap_dn>" | |||
|
95 | }, | |||
|
96 | … | |||
|
97 | ] | |||
|
98 | error: null | |||
|
99 | ||||
|
100 | create_user | |||
|
101 | ----------- | |||
|
102 | ||||
|
103 | Creates new user in RhodeCode. This command can be executed only using api_key | |||
|
104 | belonging to user with admin rights. | |||
|
105 | ||||
|
106 | INPUT:: | |||
|
107 | ||||
|
108 | api_key : "<api_key>" | |||
|
109 | method : "create_user" | |||
|
110 | args : { | |||
|
111 | "username" : "<username>", | |||
|
112 | "password" : "<password>", | |||
|
113 | "firstname" : "<firstname>", | |||
|
114 | "lastname" : "<lastname>", | |||
|
115 | "email" : "<useremail>" | |||
|
116 | "active" : "<bool> = True", | |||
|
117 | "admin" : "<bool> = False", | |||
|
118 | "ldap_dn" : "<ldap_dn> = None" | |||
|
119 | } | |||
86 |
|
120 | |||
87 | OUTPUT:: |
|
121 | OUTPUT:: | |
88 |
|
122 | |||
89 | result:{"id": <newuserid>, |
|
123 | result: { | |
90 |
"msg":"created new user <username>" |
|
124 | "msg" : "created new user <username>" | |
91 | error:null |
|
125 | } | |
92 |
|
126 | error: null | ||
93 |
|
127 | |||
|
128 | get_users_groups | |||
|
129 | ---------------- | |||
|
130 | ||||
|
131 | Lists all existing users groups. This command can be executed only using api_key | |||
|
132 | belonging to user with admin rights. | |||
|
133 | ||||
|
134 | INPUT:: | |||
|
135 | ||||
|
136 | api_key : "<api_key>" | |||
|
137 | method : "get_users_groups" | |||
|
138 | args : { } | |||
|
139 | ||||
|
140 | OUTPUT:: | |||
|
141 | ||||
|
142 | result : [ | |||
|
143 | { | |||
|
144 | "id" : "<id>", | |||
|
145 | "name" : "<name>", | |||
|
146 | "active": "<bool>", | |||
|
147 | "members" : [ | |||
|
148 | { | |||
|
149 | "id" : "<userid>", | |||
|
150 | "username" : "<username>", | |||
|
151 | "firstname": "<firstname>", | |||
|
152 | "lastname" : "<lastname>", | |||
|
153 | "email" : "<email>", | |||
|
154 | "active" : "<bool>", | |||
|
155 | "admin" :Â "<bool>", | |||
|
156 | "ldap" : "<ldap_dn>" | |||
|
157 | }, | |||
|
158 | … | |||
|
159 | ] | |||
|
160 | } | |||
|
161 | ] | |||
|
162 | error : null | |||
|
163 | ||||
|
164 | get_users_group | |||
|
165 | --------------- | |||
|
166 | ||||
|
167 | Gets an existing users group. This command can be executed only using api_key | |||
|
168 | belonging to user with admin rights. | |||
|
169 | ||||
|
170 | INPUT:: | |||
|
171 | ||||
|
172 | api_key : "<api_key>" | |||
|
173 | method : "get_users_group" | |||
|
174 | args : { | |||
|
175 | "group_name" : "<name>" | |||
|
176 | } | |||
|
177 | ||||
|
178 | OUTPUT:: | |||
|
179 | ||||
|
180 | result : None if group not exist | |||
|
181 | { | |||
|
182 | "id" : "<id>", | |||
|
183 | "name" : "<name>", | |||
|
184 | "active": "<bool>", | |||
|
185 | "members" : [ | |||
|
186 | { "id" : "<userid>", | |||
|
187 | "username" : "<username>", | |||
|
188 | "firstname": "<firstname>", | |||
|
189 | "lastname" : "<lastname>", | |||
|
190 | "email" : "<email>", | |||
|
191 | "active" : "<bool>", | |||
|
192 | "admin" :Â "<bool>", | |||
|
193 | "ldap" : "<ldap_dn>" | |||
|
194 | }, | |||
|
195 | … | |||
|
196 | ] | |||
|
197 | } | |||
|
198 | error : null | |||
|
199 | ||||
94 | create_users_group |
|
200 | create_users_group | |
95 | ------------------ |
|
201 | ------------------ | |
96 |
|
202 | |||
97 |
|
|
203 | Creates new users group. This command can be executed only using api_key | |
98 | belonging to user with admin rights |
|
204 | belonging to user with admin rights | |
99 |
|
205 | |||
100 | INPUT:: |
|
206 | INPUT:: | |
101 |
|
207 | |||
102 | api_key:"<api_key>" |
|
208 | api_key : "<api_key>" | |
103 | method: "create_user" |
|
209 | method : "create_users_group" | |
104 | args: {"name": "<groupname>", |
|
210 | args: { | |
105 |
|
|
211 | "name": "<name>", | |
|
212 | "active":"<bool> = True" | |||
|
213 | } | |||
|
214 | ||||
|
215 | OUTPUT:: | |||
|
216 | ||||
|
217 | result: { | |||
|
218 | "id": "<newusersgroupid>", | |||
|
219 | "msg": "created new users group <name>" | |||
|
220 | } | |||
|
221 | error: null | |||
|
222 | ||||
|
223 | add_user_to_users_groups | |||
|
224 | ------------------------ | |||
|
225 | ||||
|
226 | Adds a user to a users group. This command can be executed only using api_key | |||
|
227 | belonging to user with admin rights | |||
|
228 | ||||
|
229 | INPUT:: | |||
|
230 | ||||
|
231 | api_key : "<api_key>" | |||
|
232 | method : "add_user_users_group" | |||
|
233 | args: { | |||
|
234 | "group_name" : "<groupname>", | |||
|
235 | "user_name" : "<username>" | |||
|
236 | } | |||
|
237 | ||||
|
238 | OUTPUT:: | |||
|
239 | ||||
|
240 | result: { | |||
|
241 | "id": "<newusersgroupmemberid>", | |||
|
242 | "msg": "created new users group member" | |||
|
243 | } | |||
|
244 | error: null | |||
|
245 | ||||
|
246 | get_repos | |||
|
247 | --------- | |||
|
248 | ||||
|
249 | Lists all existing repositories. This command can be executed only using api_key | |||
|
250 | belonging to user with admin rights | |||
|
251 | ||||
|
252 | INPUT:: | |||
|
253 | ||||
|
254 | api_key : "<api_key>" | |||
|
255 | method : "get_repos" | |||
|
256 | args: { } | |||
106 |
|
257 | |||
107 | OUTPUT:: |
|
258 | OUTPUT:: | |
108 |
|
259 | |||
109 | result:{"id": <newusersgroupid>, |
|
260 | result: [ | |
110 | "msg":"created new users group <groupname>"} |
|
261 | { | |
111 | error:null |
|
262 | "id" : "<id>", | |
|
263 | "name" : "<name>" | |||
|
264 | "type" : "<type>", | |||
|
265 | "description" : "<description>" | |||
|
266 | }, | |||
|
267 | … | |||
|
268 | ] | |||
|
269 | error: null | |||
|
270 | ||||
|
271 | get_repo | |||
|
272 | -------- | |||
|
273 | ||||
|
274 | Gets an existing repository. This command can be executed only using api_key | |||
|
275 | belonging to user with admin rights | |||
|
276 | ||||
|
277 | INPUT:: | |||
|
278 | ||||
|
279 | api_key : "<api_key>" | |||
|
280 | method : "get_repo" | |||
|
281 | args: { | |||
|
282 | "name" : "<name>" | |||
|
283 | } | |||
|
284 | ||||
|
285 | OUTPUT:: | |||
|
286 | ||||
|
287 | result: None if repository not exist | |||
|
288 | { | |||
|
289 | "id" : "<id>", | |||
|
290 | "name" : "<name>" | |||
|
291 | "type" : "<type>", | |||
|
292 | "description" : "<description>", | |||
|
293 | "members" : [ | |||
|
294 | { "id" : "<userid>", | |||
|
295 | "username" : "<username>", | |||
|
296 | "firstname": "<firstname>", | |||
|
297 | "lastname" : "<lastname>", | |||
|
298 | "email" : "<email>", | |||
|
299 | "active" : "<bool>", | |||
|
300 | "admin" :Â "<bool>", | |||
|
301 | "ldap" : "<ldap_dn>", | |||
|
302 | "permission" : "repository_(read|write|admin)" | |||
|
303 | }, | |||
|
304 | … | |||
|
305 | { | |||
|
306 | "id" : "<usersgroupid>", | |||
|
307 | "name" : "<usersgroupname>", | |||
|
308 | "active": "<bool>", | |||
|
309 | "permission" : "repository_(read|write|admin)" | |||
|
310 | }, | |||
|
311 | … | |||
|
312 | ] | |||
|
313 | } | |||
|
314 | error: null | |||
|
315 | ||||
|
316 | create_repo | |||
|
317 | ----------- | |||
|
318 | ||||
|
319 | Creates a repository. This command can be executed only using api_key | |||
|
320 | belonging to user with admin rights. | |||
|
321 | If repository name contains "/", all needed repository groups will be created. | |||
|
322 | For example "foo/bar/baz" will create groups "foo", "bar" (with "foo" as parent), | |||
|
323 | and create "baz" repository with "bar" as group. | |||
|
324 | ||||
|
325 | INPUT:: | |||
|
326 | ||||
|
327 | api_key : "<api_key>" | |||
|
328 | method : "create_repo" | |||
|
329 | args: { | |||
|
330 | "name" : "<name>", | |||
|
331 | "owner_name" : "<ownername>", | |||
|
332 | "description" : "<description> = ''", | |||
|
333 | "repo_type" : "<type> = 'hg'", | |||
|
334 | "private" : "<bool> = False" | |||
|
335 | } | |||
|
336 | ||||
|
337 | OUTPUT:: | |||
|
338 | ||||
|
339 | result: None | |||
|
340 | error: null | |||
|
341 | ||||
|
342 | add_user_to_repo | |||
|
343 | ---------------- | |||
|
344 | ||||
|
345 | Add a user to a repository. This command can be executed only using api_key | |||
|
346 | belonging to user with admin rights. | |||
|
347 | If "perm" is None, user will be removed from the repository. | |||
|
348 | ||||
|
349 | INPUT:: | |||
|
350 | ||||
|
351 | api_key : "<api_key>" | |||
|
352 | method : "add_user_to_repo" | |||
|
353 | args: { | |||
|
354 | "repo_name" : "<reponame>", | |||
|
355 | "user_name" : "<username>", | |||
|
356 | "perm" : "(None|repository_(read|write|admin))", | |||
|
357 | } | |||
|
358 | ||||
|
359 | OUTPUT:: | |||
|
360 | ||||
|
361 | result: None | |||
|
362 | error: null |
@@ -1,233 +1,235 b'' | |||||
1 | ################################################################################ |
|
1 | ################################################################################ | |
2 | ################################################################################ |
|
2 | ################################################################################ | |
3 | # RhodeCode - Pylons environment configuration # |
|
3 | # RhodeCode - Pylons environment configuration # | |
4 | # # |
|
4 | # # | |
5 | # The %(here)s variable will be replaced with the parent directory of this file# |
|
5 | # The %(here)s variable will be replaced with the parent directory of this file# | |
6 | ################################################################################ |
|
6 | ################################################################################ | |
7 |
|
7 | |||
8 | [DEFAULT] |
|
8 | [DEFAULT] | |
9 | debug = true |
|
9 | debug = true | |
10 | pdebug = false |
|
10 | pdebug = false | |
11 | ################################################################################ |
|
11 | ################################################################################ | |
12 | ## Uncomment and replace with the address which should receive ## |
|
12 | ## Uncomment and replace with the address which should receive ## | |
13 | ## any error reports after application crash ## |
|
13 | ## any error reports after application crash ## | |
14 | ## Additionally those settings will be used by RhodeCode mailing system ## |
|
14 | ## Additionally those settings will be used by RhodeCode mailing system ## | |
15 | ################################################################################ |
|
15 | ################################################################################ | |
16 | #email_to = admin@localhost |
|
16 | #email_to = admin@localhost | |
17 | #error_email_from = paste_error@localhost |
|
17 | #error_email_from = paste_error@localhost | |
18 | #app_email_from = rhodecode-noreply@localhost |
|
18 | #app_email_from = rhodecode-noreply@localhost | |
19 | #error_message = |
|
19 | #error_message = | |
20 |
|
20 | |||
21 | #smtp_server = mail.server.com |
|
21 | #smtp_server = mail.server.com | |
22 | #smtp_username = |
|
22 | #smtp_username = | |
23 | #smtp_password = |
|
23 | #smtp_password = | |
24 | #smtp_port = |
|
24 | #smtp_port = | |
25 | #smtp_use_tls = false |
|
25 | #smtp_use_tls = false | |
26 | #smtp_use_ssl = true |
|
26 | #smtp_use_ssl = true | |
|
27 | # Specify available auth parameters here (e.g. LOGIN PLAIN CRAM-MD5, etc.) | |||
|
28 | #smtp_auth = | |||
27 |
|
29 | |||
28 | [server:main] |
|
30 | [server:main] | |
29 | ##nr of threads to spawn |
|
31 | ##nr of threads to spawn | |
30 | threadpool_workers = 5 |
|
32 | threadpool_workers = 5 | |
31 |
|
33 | |||
32 | ##max request before thread respawn |
|
34 | ##max request before thread respawn | |
33 | threadpool_max_requests = 10 |
|
35 | threadpool_max_requests = 10 | |
34 |
|
36 | |||
35 | ##option to use threads of process |
|
37 | ##option to use threads of process | |
36 | use_threadpool = true |
|
38 | use_threadpool = true | |
37 |
|
39 | |||
38 | use = egg:Paste#http |
|
40 | use = egg:Paste#http | |
39 | host = 127.0.0.1 |
|
41 | host = 127.0.0.1 | |
40 | port = 8001 |
|
42 | port = 8001 | |
41 |
|
43 | |||
42 | [app:main] |
|
44 | [app:main] | |
43 | use = egg:rhodecode |
|
45 | use = egg:rhodecode | |
44 | full_stack = true |
|
46 | full_stack = true | |
45 | static_files = true |
|
47 | static_files = true | |
46 | lang=en |
|
48 | lang=en | |
47 | cache_dir = %(here)s/data |
|
49 | cache_dir = %(here)s/data | |
48 | index_dir = %(here)s/data/index |
|
50 | index_dir = %(here)s/data/index | |
49 | app_instance_uuid = prod1234 |
|
51 | app_instance_uuid = prod1234 | |
50 | cut_off_limit = 256000 |
|
52 | cut_off_limit = 256000 | |
51 | force_https = false |
|
53 | force_https = false | |
52 | commit_parse_limit = 50 |
|
54 | commit_parse_limit = 50 | |
53 | use_gravatar = true |
|
55 | use_gravatar = true | |
54 |
|
56 | |||
55 | #################################### |
|
57 | #################################### | |
56 | ### CELERY CONFIG #### |
|
58 | ### CELERY CONFIG #### | |
57 | #################################### |
|
59 | #################################### | |
58 | use_celery = false |
|
60 | use_celery = false | |
59 | broker.host = localhost |
|
61 | broker.host = localhost | |
60 | broker.vhost = rabbitmqhost |
|
62 | broker.vhost = rabbitmqhost | |
61 | broker.port = 5672 |
|
63 | broker.port = 5672 | |
62 | broker.user = rabbitmq |
|
64 | broker.user = rabbitmq | |
63 | broker.password = qweqwe |
|
65 | broker.password = qweqwe | |
64 |
|
66 | |||
65 | celery.imports = rhodecode.lib.celerylib.tasks |
|
67 | celery.imports = rhodecode.lib.celerylib.tasks | |
66 |
|
68 | |||
67 | celery.result.backend = amqp |
|
69 | celery.result.backend = amqp | |
68 | celery.result.dburi = amqp:// |
|
70 | celery.result.dburi = amqp:// | |
69 | celery.result.serialier = json |
|
71 | celery.result.serialier = json | |
70 |
|
72 | |||
71 | #celery.send.task.error.emails = true |
|
73 | #celery.send.task.error.emails = true | |
72 | #celery.amqp.task.result.expires = 18000 |
|
74 | #celery.amqp.task.result.expires = 18000 | |
73 |
|
75 | |||
74 | celeryd.concurrency = 2 |
|
76 | celeryd.concurrency = 2 | |
75 | #celeryd.log.file = celeryd.log |
|
77 | #celeryd.log.file = celeryd.log | |
76 | celeryd.log.level = debug |
|
78 | celeryd.log.level = debug | |
77 | celeryd.max.tasks.per.child = 1 |
|
79 | celeryd.max.tasks.per.child = 1 | |
78 |
|
80 | |||
79 | #tasks will never be sent to the queue, but executed locally instead. |
|
81 | #tasks will never be sent to the queue, but executed locally instead. | |
80 | celery.always.eager = false |
|
82 | celery.always.eager = false | |
81 |
|
83 | |||
82 | #################################### |
|
84 | #################################### | |
83 | ### BEAKER CACHE #### |
|
85 | ### BEAKER CACHE #### | |
84 | #################################### |
|
86 | #################################### | |
85 | beaker.cache.data_dir=%(here)s/data/cache/data |
|
87 | beaker.cache.data_dir=%(here)s/data/cache/data | |
86 | beaker.cache.lock_dir=%(here)s/data/cache/lock |
|
88 | beaker.cache.lock_dir=%(here)s/data/cache/lock | |
87 |
|
89 | |||
88 | beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long |
|
90 | beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long | |
89 |
|
91 | |||
90 | beaker.cache.super_short_term.type=memory |
|
92 | beaker.cache.super_short_term.type=memory | |
91 | beaker.cache.super_short_term.expire=10 |
|
93 | beaker.cache.super_short_term.expire=10 | |
92 |
|
94 | |||
93 | beaker.cache.short_term.type=memory |
|
95 | beaker.cache.short_term.type=memory | |
94 | beaker.cache.short_term.expire=60 |
|
96 | beaker.cache.short_term.expire=60 | |
95 |
|
97 | |||
96 | beaker.cache.long_term.type=memory |
|
98 | beaker.cache.long_term.type=memory | |
97 | beaker.cache.long_term.expire=36000 |
|
99 | beaker.cache.long_term.expire=36000 | |
98 |
|
100 | |||
99 | beaker.cache.sql_cache_short.type=memory |
|
101 | beaker.cache.sql_cache_short.type=memory | |
100 | beaker.cache.sql_cache_short.expire=10 |
|
102 | beaker.cache.sql_cache_short.expire=10 | |
101 |
|
103 | |||
102 | beaker.cache.sql_cache_med.type=memory |
|
104 | beaker.cache.sql_cache_med.type=memory | |
103 | beaker.cache.sql_cache_med.expire=360 |
|
105 | beaker.cache.sql_cache_med.expire=360 | |
104 |
|
106 | |||
105 | beaker.cache.sql_cache_long.type=file |
|
107 | beaker.cache.sql_cache_long.type=file | |
106 | beaker.cache.sql_cache_long.expire=3600 |
|
108 | beaker.cache.sql_cache_long.expire=3600 | |
107 |
|
109 | |||
108 | #################################### |
|
110 | #################################### | |
109 | ### BEAKER SESSION #### |
|
111 | ### BEAKER SESSION #### | |
110 | #################################### |
|
112 | #################################### | |
111 | ## Type of storage used for the session, current types are |
|
113 | ## Type of storage used for the session, current types are | |
112 | ## dbm, file, memcached, database, and memory. |
|
114 | ## dbm, file, memcached, database, and memory. | |
113 | ## The storage uses the Container API |
|
115 | ## The storage uses the Container API | |
114 | ##that is also used by the cache system. |
|
116 | ##that is also used by the cache system. | |
115 | beaker.session.type = file |
|
117 | beaker.session.type = file | |
116 |
|
118 | |||
117 | beaker.session.key = rhodecode |
|
119 | beaker.session.key = rhodecode | |
118 | beaker.session.secret = g654dcno0-9873jhgfreyu |
|
120 | beaker.session.secret = g654dcno0-9873jhgfreyu | |
119 | beaker.session.timeout = 36000 |
|
121 | beaker.session.timeout = 36000 | |
120 |
|
122 | |||
121 | ##auto save the session to not to use .save() |
|
123 | ##auto save the session to not to use .save() | |
122 | beaker.session.auto = False |
|
124 | beaker.session.auto = False | |
123 |
|
125 | |||
124 | ##true exire at browser close |
|
126 | ##true exire at browser close | |
125 | #beaker.session.cookie_expires = 3600 |
|
127 | #beaker.session.cookie_expires = 3600 | |
126 |
|
128 | |||
127 |
|
129 | |||
128 | ################################################################################ |
|
130 | ################################################################################ | |
129 | ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ## |
|
131 | ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ## | |
130 | ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ## |
|
132 | ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ## | |
131 | ## execute malicious code after an exception is raised. ## |
|
133 | ## execute malicious code after an exception is raised. ## | |
132 | ################################################################################ |
|
134 | ################################################################################ | |
133 | set debug = false |
|
135 | set debug = false | |
134 |
|
136 | |||
135 | ################################## |
|
137 | ################################## | |
136 | ### LOGVIEW CONFIG ### |
|
138 | ### LOGVIEW CONFIG ### | |
137 | ################################## |
|
139 | ################################## | |
138 | logview.sqlalchemy = #faa |
|
140 | logview.sqlalchemy = #faa | |
139 | logview.pylons.templating = #bfb |
|
141 | logview.pylons.templating = #bfb | |
140 | logview.pylons.util = #eee |
|
142 | logview.pylons.util = #eee | |
141 |
|
143 | |||
142 | ######################################################### |
|
144 | ######################################################### | |
143 | ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ### |
|
145 | ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ### | |
144 | ######################################################### |
|
146 | ######################################################### | |
145 | #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db |
|
147 | #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db | |
146 | sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode |
|
148 | sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode | |
147 | sqlalchemy.db1.echo = false |
|
149 | sqlalchemy.db1.echo = false | |
148 | sqlalchemy.db1.pool_recycle = 3600 |
|
150 | sqlalchemy.db1.pool_recycle = 3600 | |
149 | sqlalchemy.convert_unicode = true |
|
151 | sqlalchemy.convert_unicode = true | |
150 |
|
152 | |||
151 | ################################ |
|
153 | ################################ | |
152 | ### LOGGING CONFIGURATION #### |
|
154 | ### LOGGING CONFIGURATION #### | |
153 | ################################ |
|
155 | ################################ | |
154 | [loggers] |
|
156 | [loggers] | |
155 | keys = root, routes, rhodecode, sqlalchemy, beaker, templates |
|
157 | keys = root, routes, rhodecode, sqlalchemy, beaker, templates | |
156 |
|
158 | |||
157 | [handlers] |
|
159 | [handlers] | |
158 | keys = console, console_sql |
|
160 | keys = console, console_sql | |
159 |
|
161 | |||
160 | [formatters] |
|
162 | [formatters] | |
161 | keys = generic, color_formatter, color_formatter_sql |
|
163 | keys = generic, color_formatter, color_formatter_sql | |
162 |
|
164 | |||
163 | ############# |
|
165 | ############# | |
164 | ## LOGGERS ## |
|
166 | ## LOGGERS ## | |
165 | ############# |
|
167 | ############# | |
166 | [logger_root] |
|
168 | [logger_root] | |
167 | level = NOTSET |
|
169 | level = NOTSET | |
168 | handlers = console |
|
170 | handlers = console | |
169 |
|
171 | |||
170 | [logger_routes] |
|
172 | [logger_routes] | |
171 | level = DEBUG |
|
173 | level = DEBUG | |
172 | handlers = |
|
174 | handlers = | |
173 | qualname = routes.middleware |
|
175 | qualname = routes.middleware | |
174 | # "level = DEBUG" logs the route matched and routing variables. |
|
176 | # "level = DEBUG" logs the route matched and routing variables. | |
175 | propagate = 1 |
|
177 | propagate = 1 | |
176 |
|
178 | |||
177 | [logger_beaker] |
|
179 | [logger_beaker] | |
178 | level = DEBUG |
|
180 | level = DEBUG | |
179 | handlers = |
|
181 | handlers = | |
180 | qualname = beaker.container |
|
182 | qualname = beaker.container | |
181 | propagate = 1 |
|
183 | propagate = 1 | |
182 |
|
184 | |||
183 | [logger_templates] |
|
185 | [logger_templates] | |
184 | level = INFO |
|
186 | level = INFO | |
185 | handlers = |
|
187 | handlers = | |
186 | qualname = pylons.templating |
|
188 | qualname = pylons.templating | |
187 | propagate = 1 |
|
189 | propagate = 1 | |
188 |
|
190 | |||
189 | [logger_rhodecode] |
|
191 | [logger_rhodecode] | |
190 | level = DEBUG |
|
192 | level = DEBUG | |
191 | handlers = |
|
193 | handlers = | |
192 | qualname = rhodecode |
|
194 | qualname = rhodecode | |
193 | propagate = 1 |
|
195 | propagate = 1 | |
194 |
|
196 | |||
195 | [logger_sqlalchemy] |
|
197 | [logger_sqlalchemy] | |
196 | level = INFO |
|
198 | level = INFO | |
197 | handlers = console_sql |
|
199 | handlers = console_sql | |
198 | qualname = sqlalchemy.engine |
|
200 | qualname = sqlalchemy.engine | |
199 | propagate = 0 |
|
201 | propagate = 0 | |
200 |
|
202 | |||
201 | ############## |
|
203 | ############## | |
202 | ## HANDLERS ## |
|
204 | ## HANDLERS ## | |
203 | ############## |
|
205 | ############## | |
204 |
|
206 | |||
205 | [handler_console] |
|
207 | [handler_console] | |
206 | class = StreamHandler |
|
208 | class = StreamHandler | |
207 | args = (sys.stderr,) |
|
209 | args = (sys.stderr,) | |
208 | level = INFO |
|
210 | level = INFO | |
209 | formatter = generic |
|
211 | formatter = generic | |
210 |
|
212 | |||
211 | [handler_console_sql] |
|
213 | [handler_console_sql] | |
212 | class = StreamHandler |
|
214 | class = StreamHandler | |
213 | args = (sys.stderr,) |
|
215 | args = (sys.stderr,) | |
214 | level = WARN |
|
216 | level = WARN | |
215 | formatter = generic |
|
217 | formatter = generic | |
216 |
|
218 | |||
217 | ################ |
|
219 | ################ | |
218 | ## FORMATTERS ## |
|
220 | ## FORMATTERS ## | |
219 | ################ |
|
221 | ################ | |
220 |
|
222 | |||
221 | [formatter_generic] |
|
223 | [formatter_generic] | |
222 | format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s |
|
224 | format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s | |
223 | datefmt = %Y-%m-%d %H:%M:%S |
|
225 | datefmt = %Y-%m-%d %H:%M:%S | |
224 |
|
226 | |||
225 | [formatter_color_formatter] |
|
227 | [formatter_color_formatter] | |
226 | class=rhodecode.lib.colored_formatter.ColorFormatter |
|
228 | class=rhodecode.lib.colored_formatter.ColorFormatter | |
227 | format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s |
|
229 | format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s | |
228 | datefmt = %Y-%m-%d %H:%M:%S |
|
230 | datefmt = %Y-%m-%d %H:%M:%S | |
229 |
|
231 | |||
230 | [formatter_color_formatter_sql] |
|
232 | [formatter_color_formatter_sql] | |
231 | class=rhodecode.lib.colored_formatter.ColorFormatterSql |
|
233 | class=rhodecode.lib.colored_formatter.ColorFormatterSql | |
232 | format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s |
|
234 | format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s | |
233 | datefmt = %Y-%m-%d %H:%M:%S No newline at end of file |
|
235 | datefmt = %Y-%m-%d %H:%M:%S |
@@ -1,242 +1,244 b'' | |||||
1 | ################################################################################ |
|
1 | ################################################################################ | |
2 | ################################################################################ |
|
2 | ################################################################################ | |
3 | # RhodeCode - Pylons environment configuration # |
|
3 | # RhodeCode - Pylons environment configuration # | |
4 | # # |
|
4 | # # | |
5 | # The %(here)s variable will be replaced with the parent directory of this file# |
|
5 | # The %(here)s variable will be replaced with the parent directory of this file# | |
6 | ################################################################################ |
|
6 | ################################################################################ | |
7 |
|
7 | |||
8 | [DEFAULT] |
|
8 | [DEFAULT] | |
9 | debug = true |
|
9 | debug = true | |
10 | pdebug = false |
|
10 | pdebug = false | |
11 | ################################################################################ |
|
11 | ################################################################################ | |
12 | ## Uncomment and replace with the address which should receive ## |
|
12 | ## Uncomment and replace with the address which should receive ## | |
13 | ## any error reports after application crash ## |
|
13 | ## any error reports after application crash ## | |
14 | ## Additionally those settings will be used by RhodeCode mailing system ## |
|
14 | ## Additionally those settings will be used by RhodeCode mailing system ## | |
15 | ################################################################################ |
|
15 | ################################################################################ | |
16 | #email_to = admin@localhost |
|
16 | #email_to = admin@localhost | |
17 | #error_email_from = paste_error@localhost |
|
17 | #error_email_from = paste_error@localhost | |
18 | #app_email_from = rhodecode-noreply@localhost |
|
18 | #app_email_from = rhodecode-noreply@localhost | |
19 | #error_message = |
|
19 | #error_message = | |
20 |
|
20 | |||
21 | #smtp_server = mail.server.com |
|
21 | #smtp_server = mail.server.com | |
22 | #smtp_username = |
|
22 | #smtp_username = | |
23 | #smtp_password = |
|
23 | #smtp_password = | |
24 | #smtp_port = |
|
24 | #smtp_port = | |
25 | #smtp_use_tls = false |
|
25 | #smtp_use_tls = false | |
26 | #smtp_use_ssl = true |
|
26 | #smtp_use_ssl = true | |
|
27 | # Specify available auth parameters here (e.g. LOGIN PLAIN CRAM-MD5, etc.) | |||
|
28 | #smtp_auth = | |||
27 |
|
29 | |||
28 | [server:main] |
|
30 | [server:main] | |
29 | ##nr of threads to spawn |
|
31 | ##nr of threads to spawn | |
30 | threadpool_workers = 5 |
|
32 | threadpool_workers = 5 | |
31 |
|
33 | |||
32 | ##max request before thread respawn |
|
34 | ##max request before thread respawn | |
33 | threadpool_max_requests = 10 |
|
35 | threadpool_max_requests = 10 | |
34 |
|
36 | |||
35 | ##option to use threads of process |
|
37 | ##option to use threads of process | |
36 | use_threadpool = true |
|
38 | use_threadpool = true | |
37 |
|
39 | |||
38 | use = egg:Paste#http |
|
40 | use = egg:Paste#http | |
39 | host = 127.0.0.1 |
|
41 | host = 127.0.0.1 | |
40 | port = 5000 |
|
42 | port = 5000 | |
41 |
|
43 | |||
42 | [app:main] |
|
44 | [app:main] | |
43 | use = egg:rhodecode |
|
45 | use = egg:rhodecode | |
44 | full_stack = true |
|
46 | full_stack = true | |
45 | static_files = true |
|
47 | static_files = true | |
46 | lang=en |
|
48 | lang=en | |
47 | cache_dir = %(here)s/data |
|
49 | cache_dir = %(here)s/data | |
48 | index_dir = %(here)s/data/index |
|
50 | index_dir = %(here)s/data/index | |
49 | app_instance_uuid = ${app_instance_uuid} |
|
51 | app_instance_uuid = ${app_instance_uuid} | |
50 | cut_off_limit = 256000 |
|
52 | cut_off_limit = 256000 | |
51 | force_https = false |
|
53 | force_https = false | |
52 | commit_parse_limit = 50 |
|
54 | commit_parse_limit = 50 | |
53 | use_gravatar = true |
|
55 | use_gravatar = true | |
54 |
|
56 | |||
55 | #################################### |
|
57 | #################################### | |
56 | ### CELERY CONFIG #### |
|
58 | ### CELERY CONFIG #### | |
57 | #################################### |
|
59 | #################################### | |
58 | use_celery = false |
|
60 | use_celery = false | |
59 | broker.host = localhost |
|
61 | broker.host = localhost | |
60 | broker.vhost = rabbitmqhost |
|
62 | broker.vhost = rabbitmqhost | |
61 | broker.port = 5672 |
|
63 | broker.port = 5672 | |
62 | broker.user = rabbitmq |
|
64 | broker.user = rabbitmq | |
63 | broker.password = qweqwe |
|
65 | broker.password = qweqwe | |
64 |
|
66 | |||
65 | celery.imports = rhodecode.lib.celerylib.tasks |
|
67 | celery.imports = rhodecode.lib.celerylib.tasks | |
66 |
|
68 | |||
67 | celery.result.backend = amqp |
|
69 | celery.result.backend = amqp | |
68 | celery.result.dburi = amqp:// |
|
70 | celery.result.dburi = amqp:// | |
69 | celery.result.serialier = json |
|
71 | celery.result.serialier = json | |
70 |
|
72 | |||
71 | #celery.send.task.error.emails = true |
|
73 | #celery.send.task.error.emails = true | |
72 | #celery.amqp.task.result.expires = 18000 |
|
74 | #celery.amqp.task.result.expires = 18000 | |
73 |
|
75 | |||
74 | celeryd.concurrency = 2 |
|
76 | celeryd.concurrency = 2 | |
75 | #celeryd.log.file = celeryd.log |
|
77 | #celeryd.log.file = celeryd.log | |
76 | celeryd.log.level = debug |
|
78 | celeryd.log.level = debug | |
77 | celeryd.max.tasks.per.child = 1 |
|
79 | celeryd.max.tasks.per.child = 1 | |
78 |
|
80 | |||
79 | #tasks will never be sent to the queue, but executed locally instead. |
|
81 | #tasks will never be sent to the queue, but executed locally instead. | |
80 | celery.always.eager = false |
|
82 | celery.always.eager = false | |
81 |
|
83 | |||
82 | #################################### |
|
84 | #################################### | |
83 | ### BEAKER CACHE #### |
|
85 | ### BEAKER CACHE #### | |
84 | #################################### |
|
86 | #################################### | |
85 | beaker.cache.data_dir=%(here)s/data/cache/data |
|
87 | beaker.cache.data_dir=%(here)s/data/cache/data | |
86 | beaker.cache.lock_dir=%(here)s/data/cache/lock |
|
88 | beaker.cache.lock_dir=%(here)s/data/cache/lock | |
87 |
|
89 | |||
88 | beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long |
|
90 | beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long | |
89 |
|
91 | |||
90 | beaker.cache.super_short_term.type=memory |
|
92 | beaker.cache.super_short_term.type=memory | |
91 | beaker.cache.super_short_term.expire=10 |
|
93 | beaker.cache.super_short_term.expire=10 | |
92 |
|
94 | |||
93 | beaker.cache.short_term.type=memory |
|
95 | beaker.cache.short_term.type=memory | |
94 | beaker.cache.short_term.expire=60 |
|
96 | beaker.cache.short_term.expire=60 | |
95 |
|
97 | |||
96 | beaker.cache.long_term.type=memory |
|
98 | beaker.cache.long_term.type=memory | |
97 | beaker.cache.long_term.expire=36000 |
|
99 | beaker.cache.long_term.expire=36000 | |
98 |
|
100 | |||
99 | beaker.cache.sql_cache_short.type=memory |
|
101 | beaker.cache.sql_cache_short.type=memory | |
100 | beaker.cache.sql_cache_short.expire=10 |
|
102 | beaker.cache.sql_cache_short.expire=10 | |
101 |
|
103 | |||
102 | beaker.cache.sql_cache_med.type=memory |
|
104 | beaker.cache.sql_cache_med.type=memory | |
103 | beaker.cache.sql_cache_med.expire=360 |
|
105 | beaker.cache.sql_cache_med.expire=360 | |
104 |
|
106 | |||
105 | beaker.cache.sql_cache_long.type=file |
|
107 | beaker.cache.sql_cache_long.type=file | |
106 | beaker.cache.sql_cache_long.expire=3600 |
|
108 | beaker.cache.sql_cache_long.expire=3600 | |
107 |
|
109 | |||
108 | #################################### |
|
110 | #################################### | |
109 | ### BEAKER SESSION #### |
|
111 | ### BEAKER SESSION #### | |
110 | #################################### |
|
112 | #################################### | |
111 | ## Type of storage used for the session, current types are |
|
113 | ## Type of storage used for the session, current types are | |
112 | ## dbm, file, memcached, database, and memory. |
|
114 | ## dbm, file, memcached, database, and memory. | |
113 | ## The storage uses the Container API |
|
115 | ## The storage uses the Container API | |
114 | ##that is also used by the cache system. |
|
116 | ##that is also used by the cache system. | |
115 | beaker.session.type = file |
|
117 | beaker.session.type = file | |
116 |
|
118 | |||
117 | beaker.session.key = rhodecode |
|
119 | beaker.session.key = rhodecode | |
118 | beaker.session.secret = ${app_instance_secret} |
|
120 | beaker.session.secret = ${app_instance_secret} | |
119 | beaker.session.timeout = 36000 |
|
121 | beaker.session.timeout = 36000 | |
120 |
|
122 | |||
121 | ##auto save the session to not to use .save() |
|
123 | ##auto save the session to not to use .save() | |
122 | beaker.session.auto = False |
|
124 | beaker.session.auto = False | |
123 |
|
125 | |||
124 | ##true exire at browser close |
|
126 | ##true exire at browser close | |
125 | #beaker.session.cookie_expires = 3600 |
|
127 | #beaker.session.cookie_expires = 3600 | |
126 |
|
128 | |||
127 |
|
129 | |||
128 | ################################################################################ |
|
130 | ################################################################################ | |
129 | ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ## |
|
131 | ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ## | |
130 | ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ## |
|
132 | ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ## | |
131 | ## execute malicious code after an exception is raised. ## |
|
133 | ## execute malicious code after an exception is raised. ## | |
132 | ################################################################################ |
|
134 | ################################################################################ | |
133 | set debug = false |
|
135 | set debug = false | |
134 |
|
136 | |||
135 | ################################## |
|
137 | ################################## | |
136 | ### LOGVIEW CONFIG ### |
|
138 | ### LOGVIEW CONFIG ### | |
137 | ################################## |
|
139 | ################################## | |
138 | logview.sqlalchemy = #faa |
|
140 | logview.sqlalchemy = #faa | |
139 | logview.pylons.templating = #bfb |
|
141 | logview.pylons.templating = #bfb | |
140 | logview.pylons.util = #eee |
|
142 | logview.pylons.util = #eee | |
141 |
|
143 | |||
142 | ######################################################### |
|
144 | ######################################################### | |
143 | ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ### |
|
145 | ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ### | |
144 | ######################################################### |
|
146 | ######################################################### | |
145 |
|
147 | |||
146 | # SQLITE [default] |
|
148 | # SQLITE [default] | |
147 | sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db |
|
149 | sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db | |
148 |
|
150 | |||
149 | # POSTGRES |
|
151 | # POSTGRESQL | |
150 | # sqlalchemy.db1.url = postgresql://user:pass@localhost/rhodecode |
|
152 | # sqlalchemy.db1.url = postgresql://user:pass@localhost/rhodecode | |
151 |
|
153 | |||
152 | # MySQL |
|
154 | # MySQL | |
153 | # sqlalchemy.db1.url = mysql://user:pass@localhost/rhodecode |
|
155 | # sqlalchemy.db1.url = mysql://user:pass@localhost/rhodecode | |
154 |
|
156 | |||
155 |
|
157 | |||
156 | sqlalchemy.db1.echo = false |
|
158 | sqlalchemy.db1.echo = false | |
157 | sqlalchemy.db1.pool_recycle = 3600 |
|
159 | sqlalchemy.db1.pool_recycle = 3600 | |
158 | sqlalchemy.convert_unicode = true |
|
160 | sqlalchemy.convert_unicode = true | |
159 |
|
161 | |||
160 | ################################ |
|
162 | ################################ | |
161 | ### LOGGING CONFIGURATION #### |
|
163 | ### LOGGING CONFIGURATION #### | |
162 | ################################ |
|
164 | ################################ | |
163 | [loggers] |
|
165 | [loggers] | |
164 | keys = root, routes, rhodecode, sqlalchemy, beaker, templates |
|
166 | keys = root, routes, rhodecode, sqlalchemy, beaker, templates | |
165 |
|
167 | |||
166 | [handlers] |
|
168 | [handlers] | |
167 | keys = console, console_sql |
|
169 | keys = console, console_sql | |
168 |
|
170 | |||
169 | [formatters] |
|
171 | [formatters] | |
170 | keys = generic, color_formatter, color_formatter_sql |
|
172 | keys = generic, color_formatter, color_formatter_sql | |
171 |
|
173 | |||
172 | ############# |
|
174 | ############# | |
173 | ## LOGGERS ## |
|
175 | ## LOGGERS ## | |
174 | ############# |
|
176 | ############# | |
175 | [logger_root] |
|
177 | [logger_root] | |
176 | level = NOTSET |
|
178 | level = NOTSET | |
177 | handlers = console |
|
179 | handlers = console | |
178 |
|
180 | |||
179 | [logger_routes] |
|
181 | [logger_routes] | |
180 | level = DEBUG |
|
182 | level = DEBUG | |
181 | handlers = |
|
183 | handlers = | |
182 | qualname = routes.middleware |
|
184 | qualname = routes.middleware | |
183 | # "level = DEBUG" logs the route matched and routing variables. |
|
185 | # "level = DEBUG" logs the route matched and routing variables. | |
184 | propagate = 1 |
|
186 | propagate = 1 | |
185 |
|
187 | |||
186 | [logger_beaker] |
|
188 | [logger_beaker] | |
187 | level = DEBUG |
|
189 | level = DEBUG | |
188 | handlers = |
|
190 | handlers = | |
189 | qualname = beaker.container |
|
191 | qualname = beaker.container | |
190 | propagate = 1 |
|
192 | propagate = 1 | |
191 |
|
193 | |||
192 | [logger_templates] |
|
194 | [logger_templates] | |
193 | level = INFO |
|
195 | level = INFO | |
194 | handlers = |
|
196 | handlers = | |
195 | qualname = pylons.templating |
|
197 | qualname = pylons.templating | |
196 | propagate = 1 |
|
198 | propagate = 1 | |
197 |
|
199 | |||
198 | [logger_rhodecode] |
|
200 | [logger_rhodecode] | |
199 | level = DEBUG |
|
201 | level = DEBUG | |
200 | handlers = |
|
202 | handlers = | |
201 | qualname = rhodecode |
|
203 | qualname = rhodecode | |
202 | propagate = 1 |
|
204 | propagate = 1 | |
203 |
|
205 | |||
204 | [logger_sqlalchemy] |
|
206 | [logger_sqlalchemy] | |
205 | level = INFO |
|
207 | level = INFO | |
206 | handlers = console_sql |
|
208 | handlers = console_sql | |
207 | qualname = sqlalchemy.engine |
|
209 | qualname = sqlalchemy.engine | |
208 | propagate = 0 |
|
210 | propagate = 0 | |
209 |
|
211 | |||
210 | ############## |
|
212 | ############## | |
211 | ## HANDLERS ## |
|
213 | ## HANDLERS ## | |
212 | ############## |
|
214 | ############## | |
213 |
|
215 | |||
214 | [handler_console] |
|
216 | [handler_console] | |
215 | class = StreamHandler |
|
217 | class = StreamHandler | |
216 | args = (sys.stderr,) |
|
218 | args = (sys.stderr,) | |
217 | level = INFO |
|
219 | level = INFO | |
218 | formatter = color_formatter |
|
220 | formatter = color_formatter | |
219 |
|
221 | |||
220 | [handler_console_sql] |
|
222 | [handler_console_sql] | |
221 | class = StreamHandler |
|
223 | class = StreamHandler | |
222 | args = (sys.stderr,) |
|
224 | args = (sys.stderr,) | |
223 | level = WARN |
|
225 | level = WARN | |
224 | formatter = color_formatter_sql |
|
226 | formatter = color_formatter_sql | |
225 |
|
227 | |||
226 | ################ |
|
228 | ################ | |
227 | ## FORMATTERS ## |
|
229 | ## FORMATTERS ## | |
228 | ################ |
|
230 | ################ | |
229 |
|
231 | |||
230 | [formatter_generic] |
|
232 | [formatter_generic] | |
231 | format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s |
|
233 | format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s | |
232 | datefmt = %Y-%m-%d %H:%M:%S |
|
234 | datefmt = %Y-%m-%d %H:%M:%S | |
233 |
|
235 | |||
234 | [formatter_color_formatter] |
|
236 | [formatter_color_formatter] | |
235 | class=rhodecode.lib.colored_formatter.ColorFormatter |
|
237 | class=rhodecode.lib.colored_formatter.ColorFormatter | |
236 | format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s |
|
238 | format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s | |
237 | datefmt = %Y-%m-%d %H:%M:%S |
|
239 | datefmt = %Y-%m-%d %H:%M:%S | |
238 |
|
240 | |||
239 | [formatter_color_formatter_sql] |
|
241 | [formatter_color_formatter_sql] | |
240 | class=rhodecode.lib.colored_formatter.ColorFormatterSql |
|
242 | class=rhodecode.lib.colored_formatter.ColorFormatterSql | |
241 | format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s |
|
243 | format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s | |
242 | datefmt = %Y-%m-%d %H:%M:%S No newline at end of file |
|
244 | datefmt = %Y-%m-%d %H:%M:%S |
@@ -1,421 +1,397 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """ |
|
2 | """ | |
3 | rhodecode.controllers.admin.repos |
|
3 | rhodecode.controllers.admin.repos | |
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
5 |
|
5 | |||
6 | Admin controller for RhodeCode |
|
6 | Admin controller for RhodeCode | |
7 |
|
7 | |||
8 | :created_on: Apr 7, 2010 |
|
8 | :created_on: Apr 7, 2010 | |
9 | :author: marcink |
|
9 | :author: marcink | |
10 | :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> |
|
10 | :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> | |
11 | :license: GPLv3, see COPYING for more details. |
|
11 | :license: GPLv3, see COPYING for more details. | |
12 | """ |
|
12 | """ | |
13 | # This program is free software: you can redistribute it and/or modify |
|
13 | # This program is free software: you can redistribute it and/or modify | |
14 | # it under the terms of the GNU General Public License as published by |
|
14 | # it under the terms of the GNU General Public License as published by | |
15 | # the Free Software Foundation, either version 3 of the License, or |
|
15 | # the Free Software Foundation, either version 3 of the License, or | |
16 | # (at your option) any later version. |
|
16 | # (at your option) any later version. | |
17 | # |
|
17 | # | |
18 | # This program is distributed in the hope that it will be useful, |
|
18 | # This program is distributed in the hope that it will be useful, | |
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 | # GNU General Public License for more details. |
|
21 | # GNU General Public License for more details. | |
22 | # |
|
22 | # | |
23 | # You should have received a copy of the GNU General Public License |
|
23 | # You should have received a copy of the GNU General Public License | |
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
25 |
|
25 | |||
26 | import logging |
|
26 | import logging | |
27 | import traceback |
|
27 | import traceback | |
28 | import formencode |
|
28 | import formencode | |
29 | from operator import itemgetter |
|
|||
30 | from formencode import htmlfill |
|
29 | from formencode import htmlfill | |
31 |
|
30 | |||
32 | from paste.httpexceptions import HTTPInternalServerError |
|
31 | from paste.httpexceptions import HTTPInternalServerError | |
33 | from pylons import request, response, session, tmpl_context as c, url |
|
32 | from pylons import request, response, session, tmpl_context as c, url | |
34 | from pylons.controllers.util import abort, redirect |
|
33 | from pylons.controllers.util import abort, redirect | |
35 | from pylons.i18n.translation import _ |
|
34 | from pylons.i18n.translation import _ | |
36 |
|
35 | |||
37 | from rhodecode.lib import helpers as h |
|
36 | from rhodecode.lib import helpers as h | |
38 | from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \ |
|
37 | from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \ | |
39 | HasPermissionAnyDecorator |
|
38 | HasPermissionAnyDecorator | |
40 | from rhodecode.lib.base import BaseController, render |
|
39 | from rhodecode.lib.base import BaseController, render | |
41 | from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug |
|
40 | from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug | |
42 | from rhodecode.lib.helpers import get_token |
|
41 | from rhodecode.lib.helpers import get_token | |
43 | from rhodecode.model.db import User, Repository, UserFollowing, Group |
|
42 | from rhodecode.model.db import User, Repository, UserFollowing, Group | |
44 | from rhodecode.model.forms import RepoForm |
|
43 | from rhodecode.model.forms import RepoForm | |
45 | from rhodecode.model.scm import ScmModel |
|
44 | from rhodecode.model.scm import ScmModel | |
46 | from rhodecode.model.repo import RepoModel |
|
45 | from rhodecode.model.repo import RepoModel | |
47 | from sqlalchemy.exc import IntegrityError |
|
46 | from sqlalchemy.exc import IntegrityError | |
48 |
|
47 | |||
49 | log = logging.getLogger(__name__) |
|
48 | log = logging.getLogger(__name__) | |
50 |
|
49 | |||
51 |
|
50 | |||
52 | class ReposController(BaseController): |
|
51 | class ReposController(BaseController): | |
53 | """ |
|
52 | """ | |
54 | REST Controller styled on the Atom Publishing Protocol""" |
|
53 | REST Controller styled on the Atom Publishing Protocol""" | |
55 | # To properly map this controller, ensure your config/routing.py |
|
54 | # To properly map this controller, ensure your config/routing.py | |
56 | # file has a resource setup: |
|
55 | # file has a resource setup: | |
57 | # map.resource('repo', 'repos') |
|
56 | # map.resource('repo', 'repos') | |
58 |
|
57 | |||
59 | @LoginRequired() |
|
58 | @LoginRequired() | |
60 | @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository') |
|
59 | @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository') | |
61 | def __before__(self): |
|
60 | def __before__(self): | |
62 | c.admin_user = session.get('admin_user') |
|
61 | c.admin_user = session.get('admin_user') | |
63 | c.admin_username = session.get('admin_username') |
|
62 | c.admin_username = session.get('admin_username') | |
64 | super(ReposController, self).__before__() |
|
63 | super(ReposController, self).__before__() | |
65 |
|
64 | |||
66 | def __load_defaults(self): |
|
65 | def __load_defaults(self): | |
67 | c.repo_groups = Group.groups_choices() |
|
66 | c.repo_groups = Group.groups_choices() | |
68 | c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups) |
|
67 | c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups) | |
69 |
|
68 | |||
70 | repo_model = RepoModel() |
|
69 | repo_model = RepoModel() | |
71 | c.users_array = repo_model.get_users_js() |
|
70 | c.users_array = repo_model.get_users_js() | |
72 | c.users_groups_array = repo_model.get_users_groups_js() |
|
71 | c.users_groups_array = repo_model.get_users_groups_js() | |
73 |
|
72 | |||
74 | def __load_data(self, repo_name=None): |
|
73 | def __load_data(self, repo_name=None): | |
75 | """ |
|
74 | """ | |
76 | Load defaults settings for edit, and update |
|
75 | Load defaults settings for edit, and update | |
77 |
|
76 | |||
78 | :param repo_name: |
|
77 | :param repo_name: | |
79 | """ |
|
78 | """ | |
80 | self.__load_defaults() |
|
79 | self.__load_defaults() | |
81 |
|
80 | |||
82 | c.repo_info = db_repo = Repository.get_by_repo_name(repo_name) |
|
81 | c.repo_info = db_repo = Repository.get_by_repo_name(repo_name) | |
83 | repo = db_repo.scm_instance |
|
82 | repo = db_repo.scm_instance | |
84 |
|
83 | |||
85 | if c.repo_info is None: |
|
84 | if c.repo_info is None: | |
86 | h.flash(_('%s repository is not mapped to db perhaps' |
|
85 | h.flash(_('%s repository is not mapped to db perhaps' | |
87 | ' it was created or renamed from the filesystem' |
|
86 | ' it was created or renamed from the filesystem' | |
88 | ' please run the application again' |
|
87 | ' please run the application again' | |
89 | ' in order to rescan repositories') % repo_name, |
|
88 | ' in order to rescan repositories') % repo_name, | |
90 | category='error') |
|
89 | category='error') | |
91 |
|
90 | |||
92 | return redirect(url('repos')) |
|
91 | return redirect(url('repos')) | |
93 |
|
92 | |||
94 | c.default_user_id = User.get_by_username('default').user_id |
|
93 | c.default_user_id = User.get_by_username('default').user_id | |
95 |
c.in_public_journal = |
|
94 | c.in_public_journal = UserFollowing.query()\ | |
96 | .filter(UserFollowing.user_id == c.default_user_id)\ |
|
95 | .filter(UserFollowing.user_id == c.default_user_id)\ | |
97 | .filter(UserFollowing.follows_repository == c.repo_info).scalar() |
|
96 | .filter(UserFollowing.follows_repository == c.repo_info).scalar() | |
98 |
|
97 | |||
99 | if c.repo_info.stats: |
|
98 | if c.repo_info.stats: | |
100 | last_rev = c.repo_info.stats.stat_on_revision |
|
99 | last_rev = c.repo_info.stats.stat_on_revision | |
101 | else: |
|
100 | else: | |
102 | last_rev = 0 |
|
101 | last_rev = 0 | |
103 | c.stats_revision = last_rev |
|
102 | c.stats_revision = last_rev | |
104 |
|
103 | |||
105 | c.repo_last_rev = repo.count() - 1 if repo.revisions else 0 |
|
104 | c.repo_last_rev = repo.count() - 1 if repo.revisions else 0 | |
106 |
|
105 | |||
107 | if last_rev == 0 or c.repo_last_rev == 0: |
|
106 | if last_rev == 0 or c.repo_last_rev == 0: | |
108 | c.stats_percentage = 0 |
|
107 | c.stats_percentage = 0 | |
109 | else: |
|
108 | else: | |
110 | c.stats_percentage = '%.2f' % ((float((last_rev)) / |
|
109 | c.stats_percentage = '%.2f' % ((float((last_rev)) / | |
111 | c.repo_last_rev) * 100) |
|
110 | c.repo_last_rev) * 100) | |
112 |
|
111 | |||
113 | defaults = c.repo_info.get_dict() |
|
112 | defaults = RepoModel()._get_defaults(repo_name) | |
114 | group, repo_name = c.repo_info.groups_and_repo |
|
|||
115 | defaults['repo_name'] = repo_name |
|
|||
116 | defaults['repo_group'] = getattr(group[-1] if group else None, |
|
|||
117 | 'group_id', None) |
|
|||
118 |
|
||||
119 | #fill owner |
|
|||
120 | if c.repo_info.user: |
|
|||
121 | defaults.update({'user': c.repo_info.user.username}) |
|
|||
122 | else: |
|
|||
123 | replacement_user = self.sa.query(User)\ |
|
|||
124 | .filter(User.admin == True).first().username |
|
|||
125 | defaults.update({'user': replacement_user}) |
|
|||
126 |
|
||||
127 | #fill repository users |
|
|||
128 | for p in c.repo_info.repo_to_perm: |
|
|||
129 | defaults.update({'u_perm_%s' % p.user.username: |
|
|||
130 | p.permission.permission_name}) |
|
|||
131 |
|
||||
132 | #fill repository groups |
|
|||
133 | for p in c.repo_info.users_group_to_perm: |
|
|||
134 | defaults.update({'g_perm_%s' % p.users_group.users_group_name: |
|
|||
135 | p.permission.permission_name}) |
|
|||
136 |
|
||||
137 | return defaults |
|
113 | return defaults | |
138 |
|
114 | |||
139 | @HasPermissionAllDecorator('hg.admin') |
|
115 | @HasPermissionAllDecorator('hg.admin') | |
140 | def index(self, format='html'): |
|
116 | def index(self, format='html'): | |
141 | """GET /repos: All items in the collection""" |
|
117 | """GET /repos: All items in the collection""" | |
142 | # url('repos') |
|
118 | # url('repos') | |
143 |
|
119 | |||
144 | c.repos_list = ScmModel().get_repos(Repository.query() |
|
120 | c.repos_list = ScmModel().get_repos(Repository.query() | |
145 | .order_by(Repository.repo_name) |
|
121 | .order_by(Repository.repo_name) | |
146 | .all(), sort_key='name_sort') |
|
122 | .all(), sort_key='name_sort') | |
147 | return render('admin/repos/repos.html') |
|
123 | return render('admin/repos/repos.html') | |
148 |
|
124 | |||
149 | @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository') |
|
125 | @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository') | |
150 | def create(self): |
|
126 | def create(self): | |
151 | """ |
|
127 | """ | |
152 | POST /repos: Create a new item""" |
|
128 | POST /repos: Create a new item""" | |
153 | # url('repos') |
|
129 | # url('repos') | |
154 | repo_model = RepoModel() |
|
130 | repo_model = RepoModel() | |
155 | self.__load_defaults() |
|
131 | self.__load_defaults() | |
156 | form_result = {} |
|
132 | form_result = {} | |
157 | try: |
|
133 | try: | |
158 | form_result = RepoForm(repo_groups=c.repo_groups_choices)()\ |
|
134 | form_result = RepoForm(repo_groups=c.repo_groups_choices)()\ | |
159 | .to_python(dict(request.POST)) |
|
135 | .to_python(dict(request.POST)) | |
160 | repo_model.create(form_result, self.rhodecode_user) |
|
136 | repo_model.create(form_result, self.rhodecode_user) | |
161 | if form_result['clone_uri']: |
|
137 | if form_result['clone_uri']: | |
162 | h.flash(_('created repository %s from %s') \ |
|
138 | h.flash(_('created repository %s from %s') \ | |
163 | % (form_result['repo_name'], form_result['clone_uri']), |
|
139 | % (form_result['repo_name'], form_result['clone_uri']), | |
164 | category='success') |
|
140 | category='success') | |
165 | else: |
|
141 | else: | |
166 | h.flash(_('created repository %s') % form_result['repo_name'], |
|
142 | h.flash(_('created repository %s') % form_result['repo_name'], | |
167 | category='success') |
|
143 | category='success') | |
168 |
|
144 | |||
169 | if request.POST.get('user_created'): |
|
145 | if request.POST.get('user_created'): | |
170 | #created by regular non admin user |
|
146 | #created by regular non admin user | |
171 | action_logger(self.rhodecode_user, 'user_created_repo', |
|
147 | action_logger(self.rhodecode_user, 'user_created_repo', | |
172 | form_result['repo_name_full'], '', self.sa) |
|
148 | form_result['repo_name_full'], '', self.sa) | |
173 | else: |
|
149 | else: | |
174 | action_logger(self.rhodecode_user, 'admin_created_repo', |
|
150 | action_logger(self.rhodecode_user, 'admin_created_repo', | |
175 | form_result['repo_name_full'], '', self.sa) |
|
151 | form_result['repo_name_full'], '', self.sa) | |
176 |
|
152 | |||
177 | except formencode.Invalid, errors: |
|
153 | except formencode.Invalid, errors: | |
178 |
|
154 | |||
179 | c.new_repo = errors.value['repo_name'] |
|
155 | c.new_repo = errors.value['repo_name'] | |
180 |
|
156 | |||
181 | if request.POST.get('user_created'): |
|
157 | if request.POST.get('user_created'): | |
182 | r = render('admin/repos/repo_add_create_repository.html') |
|
158 | r = render('admin/repos/repo_add_create_repository.html') | |
183 | else: |
|
159 | else: | |
184 | r = render('admin/repos/repo_add.html') |
|
160 | r = render('admin/repos/repo_add.html') | |
185 |
|
161 | |||
186 | return htmlfill.render( |
|
162 | return htmlfill.render( | |
187 | r, |
|
163 | r, | |
188 | defaults=errors.value, |
|
164 | defaults=errors.value, | |
189 | errors=errors.error_dict or {}, |
|
165 | errors=errors.error_dict or {}, | |
190 | prefix_error=False, |
|
166 | prefix_error=False, | |
191 | encoding="UTF-8") |
|
167 | encoding="UTF-8") | |
192 |
|
168 | |||
193 | except Exception: |
|
169 | except Exception: | |
194 | log.error(traceback.format_exc()) |
|
170 | log.error(traceback.format_exc()) | |
195 | msg = _('error occurred during creation of repository %s') \ |
|
171 | msg = _('error occurred during creation of repository %s') \ | |
196 | % form_result.get('repo_name') |
|
172 | % form_result.get('repo_name') | |
197 | h.flash(msg, category='error') |
|
173 | h.flash(msg, category='error') | |
198 | if request.POST.get('user_created'): |
|
174 | if request.POST.get('user_created'): | |
199 | return redirect(url('home')) |
|
175 | return redirect(url('home')) | |
200 | return redirect(url('repos')) |
|
176 | return redirect(url('repos')) | |
201 |
|
177 | |||
202 | @HasPermissionAllDecorator('hg.admin') |
|
178 | @HasPermissionAllDecorator('hg.admin') | |
203 | def new(self, format='html'): |
|
179 | def new(self, format='html'): | |
204 | """GET /repos/new: Form to create a new item""" |
|
180 | """GET /repos/new: Form to create a new item""" | |
205 | new_repo = request.GET.get('repo', '') |
|
181 | new_repo = request.GET.get('repo', '') | |
206 | c.new_repo = repo_name_slug(new_repo) |
|
182 | c.new_repo = repo_name_slug(new_repo) | |
207 | self.__load_defaults() |
|
183 | self.__load_defaults() | |
208 | return render('admin/repos/repo_add.html') |
|
184 | return render('admin/repos/repo_add.html') | |
209 |
|
185 | |||
210 | @HasPermissionAllDecorator('hg.admin') |
|
186 | @HasPermissionAllDecorator('hg.admin') | |
211 | def update(self, repo_name): |
|
187 | def update(self, repo_name): | |
212 | """ |
|
188 | """ | |
213 | PUT /repos/repo_name: Update an existing item""" |
|
189 | PUT /repos/repo_name: Update an existing item""" | |
214 | # Forms posted to this method should contain a hidden field: |
|
190 | # Forms posted to this method should contain a hidden field: | |
215 | # <input type="hidden" name="_method" value="PUT" /> |
|
191 | # <input type="hidden" name="_method" value="PUT" /> | |
216 | # Or using helpers: |
|
192 | # Or using helpers: | |
217 | # h.form(url('repo', repo_name=ID), |
|
193 | # h.form(url('repo', repo_name=ID), | |
218 | # method='put') |
|
194 | # method='put') | |
219 | # url('repo', repo_name=ID) |
|
195 | # url('repo', repo_name=ID) | |
220 | self.__load_defaults() |
|
196 | self.__load_defaults() | |
221 | repo_model = RepoModel() |
|
197 | repo_model = RepoModel() | |
222 | changed_name = repo_name |
|
198 | changed_name = repo_name | |
223 | _form = RepoForm(edit=True, old_data={'repo_name': repo_name}, |
|
199 | _form = RepoForm(edit=True, old_data={'repo_name': repo_name}, | |
224 | repo_groups=c.repo_groups_choices)() |
|
200 | repo_groups=c.repo_groups_choices)() | |
225 | try: |
|
201 | try: | |
226 | form_result = _form.to_python(dict(request.POST)) |
|
202 | form_result = _form.to_python(dict(request.POST)) | |
227 | repo = repo_model.update(repo_name, form_result) |
|
203 | repo = repo_model.update(repo_name, form_result) | |
228 | invalidate_cache('get_repo_cached_%s' % repo_name) |
|
204 | invalidate_cache('get_repo_cached_%s' % repo_name) | |
229 | h.flash(_('Repository %s updated successfully' % repo_name), |
|
205 | h.flash(_('Repository %s updated successfully' % repo_name), | |
230 | category='success') |
|
206 | category='success') | |
231 | changed_name = repo.repo_name |
|
207 | changed_name = repo.repo_name | |
232 | action_logger(self.rhodecode_user, 'admin_updated_repo', |
|
208 | action_logger(self.rhodecode_user, 'admin_updated_repo', | |
233 | changed_name, '', self.sa) |
|
209 | changed_name, '', self.sa) | |
234 |
|
210 | |||
235 | except formencode.Invalid, errors: |
|
211 | except formencode.Invalid, errors: | |
236 | defaults = self.__load_data(repo_name) |
|
212 | defaults = self.__load_data(repo_name) | |
237 | defaults.update(errors.value) |
|
213 | defaults.update(errors.value) | |
238 | return htmlfill.render( |
|
214 | return htmlfill.render( | |
239 | render('admin/repos/repo_edit.html'), |
|
215 | render('admin/repos/repo_edit.html'), | |
240 | defaults=defaults, |
|
216 | defaults=defaults, | |
241 | errors=errors.error_dict or {}, |
|
217 | errors=errors.error_dict or {}, | |
242 | prefix_error=False, |
|
218 | prefix_error=False, | |
243 | encoding="UTF-8") |
|
219 | encoding="UTF-8") | |
244 |
|
220 | |||
245 | except Exception: |
|
221 | except Exception: | |
246 | log.error(traceback.format_exc()) |
|
222 | log.error(traceback.format_exc()) | |
247 | h.flash(_('error occurred during update of repository %s') \ |
|
223 | h.flash(_('error occurred during update of repository %s') \ | |
248 | % repo_name, category='error') |
|
224 | % repo_name, category='error') | |
249 | return redirect(url('edit_repo', repo_name=changed_name)) |
|
225 | return redirect(url('edit_repo', repo_name=changed_name)) | |
250 |
|
226 | |||
251 | @HasPermissionAllDecorator('hg.admin') |
|
227 | @HasPermissionAllDecorator('hg.admin') | |
252 | def delete(self, repo_name): |
|
228 | def delete(self, repo_name): | |
253 | """ |
|
229 | """ | |
254 | DELETE /repos/repo_name: Delete an existing item""" |
|
230 | DELETE /repos/repo_name: Delete an existing item""" | |
255 | # Forms posted to this method should contain a hidden field: |
|
231 | # Forms posted to this method should contain a hidden field: | |
256 | # <input type="hidden" name="_method" value="DELETE" /> |
|
232 | # <input type="hidden" name="_method" value="DELETE" /> | |
257 | # Or using helpers: |
|
233 | # Or using helpers: | |
258 | # h.form(url('repo', repo_name=ID), |
|
234 | # h.form(url('repo', repo_name=ID), | |
259 | # method='delete') |
|
235 | # method='delete') | |
260 | # url('repo', repo_name=ID) |
|
236 | # url('repo', repo_name=ID) | |
261 |
|
237 | |||
262 | repo_model = RepoModel() |
|
238 | repo_model = RepoModel() | |
263 | repo = repo_model.get_by_repo_name(repo_name) |
|
239 | repo = repo_model.get_by_repo_name(repo_name) | |
264 | if not repo: |
|
240 | if not repo: | |
265 | h.flash(_('%s repository is not mapped to db perhaps' |
|
241 | h.flash(_('%s repository is not mapped to db perhaps' | |
266 | ' it was moved or renamed from the filesystem' |
|
242 | ' it was moved or renamed from the filesystem' | |
267 | ' please run the application again' |
|
243 | ' please run the application again' | |
268 | ' in order to rescan repositories') % repo_name, |
|
244 | ' in order to rescan repositories') % repo_name, | |
269 | category='error') |
|
245 | category='error') | |
270 |
|
246 | |||
271 | return redirect(url('repos')) |
|
247 | return redirect(url('repos')) | |
272 | try: |
|
248 | try: | |
273 | action_logger(self.rhodecode_user, 'admin_deleted_repo', |
|
249 | action_logger(self.rhodecode_user, 'admin_deleted_repo', | |
274 | repo_name, '', self.sa) |
|
250 | repo_name, '', self.sa) | |
275 | repo_model.delete(repo) |
|
251 | repo_model.delete(repo) | |
276 | invalidate_cache('get_repo_cached_%s' % repo_name) |
|
252 | invalidate_cache('get_repo_cached_%s' % repo_name) | |
277 | h.flash(_('deleted repository %s') % repo_name, category='success') |
|
253 | h.flash(_('deleted repository %s') % repo_name, category='success') | |
278 |
|
254 | |||
279 | except IntegrityError, e: |
|
255 | except IntegrityError, e: | |
280 | if e.message.find('repositories_fork_id_fkey'): |
|
256 | if e.message.find('repositories_fork_id_fkey'): | |
281 | log.error(traceback.format_exc()) |
|
257 | log.error(traceback.format_exc()) | |
282 | h.flash(_('Cannot delete %s it still contains attached ' |
|
258 | h.flash(_('Cannot delete %s it still contains attached ' | |
283 | 'forks') % repo_name, |
|
259 | 'forks') % repo_name, | |
284 | category='warning') |
|
260 | category='warning') | |
285 | else: |
|
261 | else: | |
286 | log.error(traceback.format_exc()) |
|
262 | log.error(traceback.format_exc()) | |
287 | h.flash(_('An error occurred during ' |
|
263 | h.flash(_('An error occurred during ' | |
288 | 'deletion of %s') % repo_name, |
|
264 | 'deletion of %s') % repo_name, | |
289 | category='error') |
|
265 | category='error') | |
290 |
|
266 | |||
291 | except Exception, e: |
|
267 | except Exception, e: | |
292 | log.error(traceback.format_exc()) |
|
268 | log.error(traceback.format_exc()) | |
293 | h.flash(_('An error occurred during deletion of %s') % repo_name, |
|
269 | h.flash(_('An error occurred during deletion of %s') % repo_name, | |
294 | category='error') |
|
270 | category='error') | |
295 |
|
271 | |||
296 | return redirect(url('repos')) |
|
272 | return redirect(url('repos')) | |
297 |
|
273 | |||
298 | @HasPermissionAllDecorator('hg.admin') |
|
274 | @HasPermissionAllDecorator('hg.admin') | |
299 | def delete_perm_user(self, repo_name): |
|
275 | def delete_perm_user(self, repo_name): | |
300 | """ |
|
276 | """ | |
301 | DELETE an existing repository permission user |
|
277 | DELETE an existing repository permission user | |
302 |
|
278 | |||
303 | :param repo_name: |
|
279 | :param repo_name: | |
304 | """ |
|
280 | """ | |
305 |
|
281 | |||
306 | try: |
|
282 | try: | |
307 | repo_model = RepoModel() |
|
283 | repo_model = RepoModel() | |
308 | repo_model.delete_perm_user(request.POST, repo_name) |
|
284 | repo_model.delete_perm_user(request.POST, repo_name) | |
309 | except Exception, e: |
|
285 | except Exception, e: | |
310 | h.flash(_('An error occurred during deletion of repository user'), |
|
286 | h.flash(_('An error occurred during deletion of repository user'), | |
311 | category='error') |
|
287 | category='error') | |
312 | raise HTTPInternalServerError() |
|
288 | raise HTTPInternalServerError() | |
313 |
|
289 | |||
314 | @HasPermissionAllDecorator('hg.admin') |
|
290 | @HasPermissionAllDecorator('hg.admin') | |
315 | def delete_perm_users_group(self, repo_name): |
|
291 | def delete_perm_users_group(self, repo_name): | |
316 | """ |
|
292 | """ | |
317 | DELETE an existing repository permission users group |
|
293 | DELETE an existing repository permission users group | |
318 |
|
294 | |||
319 | :param repo_name: |
|
295 | :param repo_name: | |
320 | """ |
|
296 | """ | |
321 | try: |
|
297 | try: | |
322 | repo_model = RepoModel() |
|
298 | repo_model = RepoModel() | |
323 | repo_model.delete_perm_users_group(request.POST, repo_name) |
|
299 | repo_model.delete_perm_users_group(request.POST, repo_name) | |
324 | except Exception, e: |
|
300 | except Exception, e: | |
325 | h.flash(_('An error occurred during deletion of repository' |
|
301 | h.flash(_('An error occurred during deletion of repository' | |
326 | ' users groups'), |
|
302 | ' users groups'), | |
327 | category='error') |
|
303 | category='error') | |
328 | raise HTTPInternalServerError() |
|
304 | raise HTTPInternalServerError() | |
329 |
|
305 | |||
330 | @HasPermissionAllDecorator('hg.admin') |
|
306 | @HasPermissionAllDecorator('hg.admin') | |
331 | def repo_stats(self, repo_name): |
|
307 | def repo_stats(self, repo_name): | |
332 | """ |
|
308 | """ | |
333 | DELETE an existing repository statistics |
|
309 | DELETE an existing repository statistics | |
334 |
|
310 | |||
335 | :param repo_name: |
|
311 | :param repo_name: | |
336 | """ |
|
312 | """ | |
337 |
|
313 | |||
338 | try: |
|
314 | try: | |
339 | repo_model = RepoModel() |
|
315 | repo_model = RepoModel() | |
340 | repo_model.delete_stats(repo_name) |
|
316 | repo_model.delete_stats(repo_name) | |
341 | except Exception, e: |
|
317 | except Exception, e: | |
342 | h.flash(_('An error occurred during deletion of repository stats'), |
|
318 | h.flash(_('An error occurred during deletion of repository stats'), | |
343 | category='error') |
|
319 | category='error') | |
344 | return redirect(url('edit_repo', repo_name=repo_name)) |
|
320 | return redirect(url('edit_repo', repo_name=repo_name)) | |
345 |
|
321 | |||
346 | @HasPermissionAllDecorator('hg.admin') |
|
322 | @HasPermissionAllDecorator('hg.admin') | |
347 | def repo_cache(self, repo_name): |
|
323 | def repo_cache(self, repo_name): | |
348 | """ |
|
324 | """ | |
349 | INVALIDATE existing repository cache |
|
325 | INVALIDATE existing repository cache | |
350 |
|
326 | |||
351 | :param repo_name: |
|
327 | :param repo_name: | |
352 | """ |
|
328 | """ | |
353 |
|
329 | |||
354 | try: |
|
330 | try: | |
355 | ScmModel().mark_for_invalidation(repo_name) |
|
331 | ScmModel().mark_for_invalidation(repo_name) | |
356 | except Exception, e: |
|
332 | except Exception, e: | |
357 | h.flash(_('An error occurred during cache invalidation'), |
|
333 | h.flash(_('An error occurred during cache invalidation'), | |
358 | category='error') |
|
334 | category='error') | |
359 | return redirect(url('edit_repo', repo_name=repo_name)) |
|
335 | return redirect(url('edit_repo', repo_name=repo_name)) | |
360 |
|
336 | |||
361 | @HasPermissionAllDecorator('hg.admin') |
|
337 | @HasPermissionAllDecorator('hg.admin') | |
362 | def repo_public_journal(self, repo_name): |
|
338 | def repo_public_journal(self, repo_name): | |
363 | """ |
|
339 | """ | |
364 | Set's this repository to be visible in public journal, |
|
340 | Set's this repository to be visible in public journal, | |
365 | in other words assing default user to follow this repo |
|
341 | in other words assing default user to follow this repo | |
366 |
|
342 | |||
367 | :param repo_name: |
|
343 | :param repo_name: | |
368 | """ |
|
344 | """ | |
369 |
|
345 | |||
370 | cur_token = request.POST.get('auth_token') |
|
346 | cur_token = request.POST.get('auth_token') | |
371 | token = get_token() |
|
347 | token = get_token() | |
372 | if cur_token == token: |
|
348 | if cur_token == token: | |
373 | try: |
|
349 | try: | |
374 | repo_id = Repository.get_by_repo_name(repo_name).repo_id |
|
350 | repo_id = Repository.get_by_repo_name(repo_name).repo_id | |
375 | user_id = User.get_by_username('default').user_id |
|
351 | user_id = User.get_by_username('default').user_id | |
376 | self.scm_model.toggle_following_repo(repo_id, user_id) |
|
352 | self.scm_model.toggle_following_repo(repo_id, user_id) | |
377 | h.flash(_('Updated repository visibility in public journal'), |
|
353 | h.flash(_('Updated repository visibility in public journal'), | |
378 | category='success') |
|
354 | category='success') | |
379 | except: |
|
355 | except: | |
380 | h.flash(_('An error occurred during setting this' |
|
356 | h.flash(_('An error occurred during setting this' | |
381 | ' repository in public journal'), |
|
357 | ' repository in public journal'), | |
382 | category='error') |
|
358 | category='error') | |
383 |
|
359 | |||
384 | else: |
|
360 | else: | |
385 | h.flash(_('Token mismatch'), category='error') |
|
361 | h.flash(_('Token mismatch'), category='error') | |
386 | return redirect(url('edit_repo', repo_name=repo_name)) |
|
362 | return redirect(url('edit_repo', repo_name=repo_name)) | |
387 |
|
363 | |||
388 | @HasPermissionAllDecorator('hg.admin') |
|
364 | @HasPermissionAllDecorator('hg.admin') | |
389 | def repo_pull(self, repo_name): |
|
365 | def repo_pull(self, repo_name): | |
390 | """ |
|
366 | """ | |
391 | Runs task to update given repository with remote changes, |
|
367 | Runs task to update given repository with remote changes, | |
392 | ie. make pull on remote location |
|
368 | ie. make pull on remote location | |
393 |
|
369 | |||
394 | :param repo_name: |
|
370 | :param repo_name: | |
395 | """ |
|
371 | """ | |
396 | try: |
|
372 | try: | |
397 | ScmModel().pull_changes(repo_name, self.rhodecode_user.username) |
|
373 | ScmModel().pull_changes(repo_name, self.rhodecode_user.username) | |
398 | h.flash(_('Pulled from remote location'), category='success') |
|
374 | h.flash(_('Pulled from remote location'), category='success') | |
399 | except Exception, e: |
|
375 | except Exception, e: | |
400 | h.flash(_('An error occurred during pull from remote location'), |
|
376 | h.flash(_('An error occurred during pull from remote location'), | |
401 | category='error') |
|
377 | category='error') | |
402 |
|
378 | |||
403 | return redirect(url('edit_repo', repo_name=repo_name)) |
|
379 | return redirect(url('edit_repo', repo_name=repo_name)) | |
404 |
|
380 | |||
405 | @HasPermissionAllDecorator('hg.admin') |
|
381 | @HasPermissionAllDecorator('hg.admin') | |
406 | def show(self, repo_name, format='html'): |
|
382 | def show(self, repo_name, format='html'): | |
407 | """GET /repos/repo_name: Show a specific item""" |
|
383 | """GET /repos/repo_name: Show a specific item""" | |
408 | # url('repo', repo_name=ID) |
|
384 | # url('repo', repo_name=ID) | |
409 |
|
385 | |||
410 | @HasPermissionAllDecorator('hg.admin') |
|
386 | @HasPermissionAllDecorator('hg.admin') | |
411 | def edit(self, repo_name, format='html'): |
|
387 | def edit(self, repo_name, format='html'): | |
412 | """GET /repos/repo_name/edit: Form to edit an existing item""" |
|
388 | """GET /repos/repo_name/edit: Form to edit an existing item""" | |
413 | # url('edit_repo', repo_name=ID) |
|
389 | # url('edit_repo', repo_name=ID) | |
414 | defaults = self.__load_data(repo_name) |
|
390 | defaults = self.__load_data(repo_name) | |
415 |
|
391 | |||
416 | return htmlfill.render( |
|
392 | return htmlfill.render( | |
417 | render('admin/repos/repo_edit.html'), |
|
393 | render('admin/repos/repo_edit.html'), | |
418 | defaults=defaults, |
|
394 | defaults=defaults, | |
419 | encoding="UTF-8", |
|
395 | encoding="UTF-8", | |
420 | force_defaults=False |
|
396 | force_defaults=False | |
421 | ) |
|
397 | ) |
@@ -1,98 +1,374 b'' | |||||
1 | import traceback |
|
1 | import traceback | |
2 | import logging |
|
2 | import logging | |
3 |
|
3 | |||
4 | from rhodecode.controllers.api import JSONRPCController, JSONRPCError |
|
4 | from rhodecode.controllers.api import JSONRPCController, JSONRPCError | |
5 | from rhodecode.lib.auth import HasPermissionAllDecorator |
|
5 | from rhodecode.lib.auth import HasPermissionAllDecorator, \ | |
|
6 | HasPermissionAnyDecorator | |||
6 | from rhodecode.model.scm import ScmModel |
|
7 | from rhodecode.model.scm import ScmModel | |
7 |
|
8 | |||
8 | from rhodecode.model.db import User, UsersGroup, Repository |
|
9 | from rhodecode.model.db import User, UsersGroup, Group, Repository | |
|
10 | from rhodecode.model.repo import RepoModel | |||
|
11 | from rhodecode.model.user import UserModel | |||
|
12 | from rhodecode.model.repo_permission import RepositoryPermissionModel | |||
|
13 | from rhodecode.model.users_group import UsersGroupModel | |||
|
14 | from rhodecode.model import users_group | |||
|
15 | from rhodecode.model.repos_group import ReposGroupModel | |||
|
16 | from sqlalchemy.orm.exc import NoResultFound | |||
9 |
|
17 | |||
10 | log = logging.getLogger(__name__) |
|
18 | log = logging.getLogger(__name__) | |
11 |
|
19 | |||
12 |
|
20 | |||
13 | class ApiController(JSONRPCController): |
|
21 | class ApiController(JSONRPCController): | |
14 | """ |
|
22 | """ | |
15 | API Controller |
|
23 | API Controller | |
16 |
|
24 | |||
17 |
|
25 | |||
18 | Each method needs to have USER as argument this is then based on given |
|
26 | Each method needs to have USER as argument this is then based on given | |
19 | API_KEY propagated as instance of user object |
|
27 | API_KEY propagated as instance of user object | |
20 |
|
28 | |||
21 | Preferably this should be first argument also |
|
29 | Preferably this should be first argument also | |
22 |
|
30 | |||
23 |
|
31 | |||
24 |
Each function should also **raise** JSONRPCError for any |
|
32 | Each function should also **raise** JSONRPCError for any | |
25 | errors that happens |
|
33 | errors that happens | |
26 |
|
34 | |||
27 | """ |
|
35 | """ | |
28 |
|
36 | |||
29 | @HasPermissionAllDecorator('hg.admin') |
|
37 | @HasPermissionAllDecorator('hg.admin') | |
30 | def pull(self, apiuser, repo): |
|
38 | def pull(self, apiuser, repo): | |
31 | """ |
|
39 | """ | |
32 | Dispatch pull action on given repo |
|
40 | Dispatch pull action on given repo | |
33 |
|
41 | |||
34 |
|
42 | |||
35 | :param user: |
|
43 | :param user: | |
36 | :param repo: |
|
44 | :param repo: | |
37 | """ |
|
45 | """ | |
38 |
|
46 | |||
39 | if Repository.is_valid(repo) is False: |
|
47 | if Repository.is_valid(repo) is False: | |
40 | raise JSONRPCError('Unknown repo "%s"' % repo) |
|
48 | raise JSONRPCError('Unknown repo "%s"' % repo) | |
41 |
|
49 | |||
42 | try: |
|
50 | try: | |
43 | ScmModel().pull_changes(repo, self.rhodecode_user.username) |
|
51 | ScmModel().pull_changes(repo, self.rhodecode_user.username) | |
44 | return 'Pulled from %s' % repo |
|
52 | return 'Pulled from %s' % repo | |
45 | except Exception: |
|
53 | except Exception: | |
46 | raise JSONRPCError('Unable to pull changes from "%s"' % repo) |
|
54 | raise JSONRPCError('Unable to pull changes from "%s"' % repo) | |
47 |
|
55 | |||
|
56 | @HasPermissionAllDecorator('hg.admin') | |||
|
57 | def get_user(self, apiuser, username): | |||
|
58 | """" | |||
|
59 | Get a user by username | |||
|
60 | ||||
|
61 | :param apiuser | |||
|
62 | :param username | |||
|
63 | """ | |||
|
64 | ||||
|
65 | user = User.get_by_username(username) | |||
|
66 | if not user: | |||
|
67 | return None | |||
|
68 | ||||
|
69 | return dict(id=user.user_id, | |||
|
70 | username=user.username, | |||
|
71 | firstname=user.name, | |||
|
72 | lastname=user.lastname, | |||
|
73 | email=user.email, | |||
|
74 | active=user.active, | |||
|
75 | admin=user.admin, | |||
|
76 | ldap=user.ldap_dn) | |||
48 |
|
77 | |||
49 | @HasPermissionAllDecorator('hg.admin') |
|
78 | @HasPermissionAllDecorator('hg.admin') | |
50 | def create_user(self, apiuser, username, password, active, admin, name, |
|
79 | def get_users(self, apiuser): | |
51 | lastname, email): |
|
80 | """" | |
|
81 | Get all users | |||
|
82 | ||||
|
83 | :param apiuser | |||
52 |
|
|
84 | """ | |
53 | Creates new user |
|
85 | ||
54 |
|
86 | result = [] | ||
|
87 | for user in User.getAll(): | |||
|
88 | result.append(dict(id=user.user_id, | |||
|
89 | username=user.username, | |||
|
90 | firstname=user.name, | |||
|
91 | lastname=user.lastname, | |||
|
92 | email=user.email, | |||
|
93 | active=user.active, | |||
|
94 | admin=user.admin, | |||
|
95 | ldap=user.ldap_dn)) | |||
|
96 | return result | |||
|
97 | ||||
|
98 | @HasPermissionAllDecorator('hg.admin') | |||
|
99 | def create_user(self, apiuser, username, password, firstname, | |||
|
100 | lastname, email, active=True, admin=False, ldap_dn=None): | |||
|
101 | """ | |||
|
102 | Create new user | |||
|
103 | ||||
55 | :param apiuser: |
|
104 | :param apiuser: | |
56 | :param username: |
|
105 | :param username: | |
57 | :param password: |
|
106 | :param password: | |
58 | :param active: |
|
|||
59 | :param admin: |
|
|||
60 | :param name: |
|
107 | :param name: | |
61 | :param lastname: |
|
108 | :param lastname: | |
62 | :param email: |
|
109 | :param email: | |
|
110 | :param active: | |||
|
111 | :param admin: | |||
|
112 | :param ldap_dn: | |||
63 | """ |
|
113 | """ | |
64 |
|
114 | |||
65 | form_data = dict(username=username, |
|
115 | if self.get_user(apiuser, username): | |
66 | password=password, |
|
116 | raise JSONRPCError("user %s already exist" % username) | |
67 | active=active, |
|
117 | ||
68 | admin=admin, |
|
|||
69 | name=name, |
|
|||
70 | lastname=lastname, |
|
|||
71 | email=email) |
|
|||
72 | try: |
|
118 | try: | |
73 | u = User.create(form_data) |
|
119 | form_data = dict(username=username, | |
74 | return {'id':u.user_id, |
|
120 | password=password, | |
75 | 'msg':'created new user %s' % name} |
|
121 | active=active, | |
|
122 | admin=admin, | |||
|
123 | name=firstname, | |||
|
124 | lastname=lastname, | |||
|
125 | email=email, | |||
|
126 | ldap_dn=ldap_dn) | |||
|
127 | UserModel().create_ldap(username, password, ldap_dn, form_data) | |||
|
128 | return dict(msg='created new user %s' % username) | |||
76 | except Exception: |
|
129 | except Exception: | |
77 | log.error(traceback.format_exc()) |
|
130 | log.error(traceback.format_exc()) | |
78 | raise JSONRPCError('failed to create user %s' % name) |
|
131 | raise JSONRPCError('failed to create user %s' % username) | |
|
132 | ||||
|
133 | @HasPermissionAllDecorator('hg.admin') | |||
|
134 | def get_users_group(self, apiuser, group_name): | |||
|
135 | """" | |||
|
136 | Get users group by name | |||
|
137 | ||||
|
138 | :param apiuser | |||
|
139 | :param group_name | |||
|
140 | """ | |||
|
141 | ||||
|
142 | users_group = UsersGroup.get_by_group_name(group_name) | |||
|
143 | if not users_group: | |||
|
144 | return None | |||
79 |
|
145 | |||
|
146 | members = [] | |||
|
147 | for user in users_group.members: | |||
|
148 | user = user.user | |||
|
149 | members.append(dict(id=user.user_id, | |||
|
150 | username=user.username, | |||
|
151 | firstname=user.name, | |||
|
152 | lastname=user.lastname, | |||
|
153 | email=user.email, | |||
|
154 | active=user.active, | |||
|
155 | admin=user.admin, | |||
|
156 | ldap=user.ldap_dn)) | |||
|
157 | ||||
|
158 | return dict(id=users_group.users_group_id, | |||
|
159 | name=users_group.users_group_name, | |||
|
160 | active=users_group.users_group_active, | |||
|
161 | members=members) | |||
80 |
|
162 | |||
81 | @HasPermissionAllDecorator('hg.admin') |
|
163 | @HasPermissionAllDecorator('hg.admin') | |
82 |
def |
|
164 | def get_users_groups(self, apiuser): | |
|
165 | """" | |||
|
166 | Get all users groups | |||
|
167 | ||||
|
168 | :param apiuser | |||
|
169 | """ | |||
|
170 | ||||
|
171 | result = [] | |||
|
172 | for users_group in UsersGroup.getAll(): | |||
|
173 | members = [] | |||
|
174 | for user in users_group.members: | |||
|
175 | user = user.user | |||
|
176 | members.append(dict(id=user.user_id, | |||
|
177 | username=user.username, | |||
|
178 | firstname=user.name, | |||
|
179 | lastname=user.lastname, | |||
|
180 | email=user.email, | |||
|
181 | active=user.active, | |||
|
182 | admin=user.admin, | |||
|
183 | ldap=user.ldap_dn)) | |||
|
184 | ||||
|
185 | result.append(dict(id=users_group.users_group_id, | |||
|
186 | name=users_group.users_group_name, | |||
|
187 | active=users_group.users_group_active, | |||
|
188 | members=members)) | |||
|
189 | return result | |||
|
190 | ||||
|
191 | @HasPermissionAllDecorator('hg.admin') | |||
|
192 | def create_users_group(self, apiuser, name, active=True): | |||
83 | """ |
|
193 | """ | |
84 | Creates an new usergroup |
|
194 | Creates an new usergroup | |
85 |
|
195 | |||
86 | :param name: |
|
196 | :param name: | |
87 | :param active: |
|
197 | :param active: | |
88 | """ |
|
198 | """ | |
89 | form_data = {'users_group_name':name, |
|
199 | ||
90 | 'users_group_active':active} |
|
200 | if self.get_users_group(apiuser, name): | |
|
201 | raise JSONRPCError("users group %s already exist" % name) | |||
|
202 | ||||
91 | try: |
|
203 | try: | |
|
204 | form_data = dict(users_group_name=name, | |||
|
205 | users_group_active=active) | |||
92 | ug = UsersGroup.create(form_data) |
|
206 | ug = UsersGroup.create(form_data) | |
93 |
return |
|
207 | return dict(id=ug.users_group_id, | |
94 |
|
|
208 | msg='created new users group %s' % name) | |
95 | except Exception: |
|
209 | except Exception: | |
96 | log.error(traceback.format_exc()) |
|
210 | log.error(traceback.format_exc()) | |
97 | raise JSONRPCError('failed to create group %s' % name) |
|
211 | raise JSONRPCError('failed to create group %s' % name) | |
98 | No newline at end of file |
|
212 | ||
|
213 | @HasPermissionAllDecorator('hg.admin') | |||
|
214 | def add_user_to_users_group(self, apiuser, group_name, user_name): | |||
|
215 | """" | |||
|
216 | Add a user to a group | |||
|
217 | ||||
|
218 | :param apiuser | |||
|
219 | :param group_name | |||
|
220 | :param user_name | |||
|
221 | """ | |||
|
222 | ||||
|
223 | try: | |||
|
224 | users_group = UsersGroup.get_by_group_name(group_name) | |||
|
225 | if not users_group: | |||
|
226 | raise JSONRPCError('unknown users group %s' % group_name) | |||
|
227 | ||||
|
228 | try: | |||
|
229 | user = User.get_by_username(user_name) | |||
|
230 | except NoResultFound: | |||
|
231 | raise JSONRPCError('unknown user %s' % user_name) | |||
|
232 | ||||
|
233 | ugm = UsersGroupModel().add_user_to_group(users_group, user) | |||
|
234 | ||||
|
235 | return dict(id=ugm.users_group_member_id, | |||
|
236 | msg='created new users group member') | |||
|
237 | except Exception: | |||
|
238 | log.error(traceback.format_exc()) | |||
|
239 | raise JSONRPCError('failed to create users group member') | |||
|
240 | ||||
|
241 | @HasPermissionAnyDecorator('hg.admin') | |||
|
242 | def get_repo(self, apiuser, repo_name): | |||
|
243 | """" | |||
|
244 | Get repository by name | |||
|
245 | ||||
|
246 | :param apiuser | |||
|
247 | :param repo_name | |||
|
248 | """ | |||
|
249 | ||||
|
250 | try: | |||
|
251 | repo = Repository.get_by_repo_name(repo_name) | |||
|
252 | except NoResultFound: | |||
|
253 | return None | |||
|
254 | ||||
|
255 | members = [] | |||
|
256 | for user in repo.repo_to_perm: | |||
|
257 | perm = user.permission.permission_name | |||
|
258 | user = user.user | |||
|
259 | members.append(dict(type_="user", | |||
|
260 | id=user.user_id, | |||
|
261 | username=user.username, | |||
|
262 | firstname=user.name, | |||
|
263 | lastname=user.lastname, | |||
|
264 | email=user.email, | |||
|
265 | active=user.active, | |||
|
266 | admin=user.admin, | |||
|
267 | ldap=user.ldap_dn, | |||
|
268 | permission=perm)) | |||
|
269 | for users_group in repo.users_group_to_perm: | |||
|
270 | perm = users_group.permission.permission_name | |||
|
271 | users_group = users_group.users_group | |||
|
272 | members.append(dict(type_="users_group", | |||
|
273 | id=users_group.users_group_id, | |||
|
274 | name=users_group.users_group_name, | |||
|
275 | active=users_group.users_group_active, | |||
|
276 | permission=perm)) | |||
|
277 | ||||
|
278 | return dict(id=repo.repo_id, | |||
|
279 | name=repo.repo_name, | |||
|
280 | type=repo.repo_type, | |||
|
281 | description=repo.description, | |||
|
282 | members=members) | |||
|
283 | ||||
|
284 | @HasPermissionAnyDecorator('hg.admin') | |||
|
285 | def get_repos(self, apiuser): | |||
|
286 | """" | |||
|
287 | Get all repositories | |||
|
288 | ||||
|
289 | :param apiuser | |||
|
290 | """ | |||
|
291 | ||||
|
292 | result = [] | |||
|
293 | for repository in Repository.getAll(): | |||
|
294 | result.append(dict(id=repository.repo_id, | |||
|
295 | name=repository.repo_name, | |||
|
296 | type=repository.repo_type, | |||
|
297 | description=repository.description)) | |||
|
298 | return result | |||
|
299 | ||||
|
300 | @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository') | |||
|
301 | def create_repo(self, apiuser, name, owner_name, description='', | |||
|
302 | repo_type='hg', private=False): | |||
|
303 | """ | |||
|
304 | Create a repository | |||
|
305 | ||||
|
306 | :param apiuser | |||
|
307 | :param name | |||
|
308 | :param description | |||
|
309 | :param type | |||
|
310 | :param private | |||
|
311 | :param owner_name | |||
|
312 | """ | |||
|
313 | ||||
|
314 | try: | |||
|
315 | try: | |||
|
316 | owner = User.get_by_username(owner_name) | |||
|
317 | except NoResultFound: | |||
|
318 | raise JSONRPCError('unknown user %s' % owner) | |||
|
319 | ||||
|
320 | if self.get_repo(apiuser, name): | |||
|
321 | raise JSONRPCError("repo %s already exist" % name) | |||
|
322 | ||||
|
323 | groups = name.split('/') | |||
|
324 | real_name = groups[-1] | |||
|
325 | groups = groups[:-1] | |||
|
326 | parent_id = None | |||
|
327 | for g in groups: | |||
|
328 | group = Group.get_by_group_name(g) | |||
|
329 | if not group: | |||
|
330 | group = ReposGroupModel().create(dict(group_name=g, | |||
|
331 | group_description='', | |||
|
332 | group_parent_id=parent_id)) | |||
|
333 | parent_id = group.group_id | |||
|
334 | ||||
|
335 | RepoModel().create(dict(repo_name=real_name, | |||
|
336 | repo_name_full=name, | |||
|
337 | description=description, | |||
|
338 | private=private, | |||
|
339 | repo_type=repo_type, | |||
|
340 | repo_group=parent_id, | |||
|
341 | clone_uri=None), owner) | |||
|
342 | except Exception: | |||
|
343 | log.error(traceback.format_exc()) | |||
|
344 | raise JSONRPCError('failed to create repository %s' % name) | |||
|
345 | ||||
|
346 | @HasPermissionAnyDecorator('hg.admin') | |||
|
347 | def add_user_to_repo(self, apiuser, repo_name, user_name, perm): | |||
|
348 | """ | |||
|
349 | Add permission for a user to a repository | |||
|
350 | ||||
|
351 | :param apiuser | |||
|
352 | :param repo_name | |||
|
353 | :param user_name | |||
|
354 | :param perm | |||
|
355 | """ | |||
|
356 | ||||
|
357 | try: | |||
|
358 | try: | |||
|
359 | repo = Repository.get_by_repo_name(repo_name) | |||
|
360 | except NoResultFound: | |||
|
361 | raise JSONRPCError('unknown repository %s' % repo) | |||
|
362 | ||||
|
363 | try: | |||
|
364 | user = User.get_by_username(user_name) | |||
|
365 | except NoResultFound: | |||
|
366 | raise JSONRPCError('unknown user %s' % user) | |||
|
367 | ||||
|
368 | RepositoryPermissionModel()\ | |||
|
369 | .update_or_delete_user_permission(repo, user, perm) | |||
|
370 | except Exception: | |||
|
371 | log.error(traceback.format_exc()) | |||
|
372 | raise JSONRPCError('failed to edit permission %(repo)s for %(user)s' | |||
|
373 | % dict(user=user_name, repo=repo_name)) | |||
|
374 |
@@ -1,214 +1,208 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """ |
|
2 | """ | |
3 | rhodecode.controllers.settings |
|
3 | rhodecode.controllers.settings | |
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
5 |
|
5 | |||
6 | Settings controller for rhodecode |
|
6 | Settings controller for rhodecode | |
7 |
|
7 | |||
8 | :created_on: Jun 30, 2010 |
|
8 | :created_on: Jun 30, 2010 | |
9 | :author: marcink |
|
9 | :author: marcink | |
10 | :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> |
|
10 | :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> | |
11 | :license: GPLv3, see COPYING for more details. |
|
11 | :license: GPLv3, see COPYING for more details. | |
12 | """ |
|
12 | """ | |
13 | # This program is free software: you can redistribute it and/or modify |
|
13 | # This program is free software: you can redistribute it and/or modify | |
14 | # it under the terms of the GNU General Public License as published by |
|
14 | # it under the terms of the GNU General Public License as published by | |
15 | # the Free Software Foundation, either version 3 of the License, or |
|
15 | # the Free Software Foundation, either version 3 of the License, or | |
16 | # (at your option) any later version. |
|
16 | # (at your option) any later version. | |
17 | # |
|
17 | # | |
18 | # This program is distributed in the hope that it will be useful, |
|
18 | # This program is distributed in the hope that it will be useful, | |
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 | # GNU General Public License for more details. |
|
21 | # GNU General Public License for more details. | |
22 | # |
|
22 | # | |
23 | # You should have received a copy of the GNU General Public License |
|
23 | # You should have received a copy of the GNU General Public License | |
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
25 |
|
25 | |||
26 | import logging |
|
26 | import logging | |
27 | import traceback |
|
27 | import traceback | |
28 | import formencode |
|
28 | import formencode | |
29 |
|
29 | |||
30 | from formencode import htmlfill |
|
30 | from formencode import htmlfill | |
31 |
|
31 | |||
32 | from pylons import tmpl_context as c, request, url |
|
32 | from pylons import tmpl_context as c, request, url | |
33 | from pylons.controllers.util import redirect |
|
33 | from pylons.controllers.util import redirect | |
34 | from pylons.i18n.translation import _ |
|
34 | from pylons.i18n.translation import _ | |
35 |
|
35 | |||
36 | import rhodecode.lib.helpers as h |
|
36 | import rhodecode.lib.helpers as h | |
37 |
|
37 | |||
38 | from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator, \ |
|
38 | from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator, \ | |
39 | HasRepoPermissionAnyDecorator, NotAnonymous |
|
39 | HasRepoPermissionAnyDecorator, NotAnonymous | |
40 | from rhodecode.lib.base import BaseRepoController, render |
|
40 | from rhodecode.lib.base import BaseRepoController, render | |
41 | from rhodecode.lib.utils import invalidate_cache, action_logger |
|
41 | from rhodecode.lib.utils import invalidate_cache, action_logger | |
42 |
|
42 | |||
43 | from rhodecode.model.forms import RepoSettingsForm, RepoForkForm |
|
43 | from rhodecode.model.forms import RepoSettingsForm, RepoForkForm | |
44 | from rhodecode.model.repo import RepoModel |
|
44 | from rhodecode.model.repo import RepoModel | |
45 |
from rhodecode.model.db import |
|
45 | from rhodecode.model.db import Group | |
46 |
|
46 | |||
47 | log = logging.getLogger(__name__) |
|
47 | log = logging.getLogger(__name__) | |
48 |
|
48 | |||
49 |
|
49 | |||
50 | class SettingsController(BaseRepoController): |
|
50 | class SettingsController(BaseRepoController): | |
51 |
|
51 | |||
52 | @LoginRequired() |
|
52 | @LoginRequired() | |
53 | def __before__(self): |
|
53 | def __before__(self): | |
54 | super(SettingsController, self).__before__() |
|
54 | super(SettingsController, self).__before__() | |
55 |
|
55 | |||
|
56 | def __load_defaults(self): | |||
|
57 | c.repo_groups = Group.groups_choices() | |||
|
58 | c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups) | |||
|
59 | ||||
|
60 | repo_model = RepoModel() | |||
|
61 | c.users_array = repo_model.get_users_js() | |||
|
62 | c.users_groups_array = repo_model.get_users_groups_js() | |||
|
63 | ||||
56 | @HasRepoPermissionAllDecorator('repository.admin') |
|
64 | @HasRepoPermissionAllDecorator('repository.admin') | |
57 | def index(self, repo_name): |
|
65 | def index(self, repo_name): | |
58 | repo_model = RepoModel() |
|
66 | repo_model = RepoModel() | |
59 | c.repo_info = repo = repo_model.get_by_repo_name(repo_name) |
|
67 | c.repo_info = repo = repo_model.get_by_repo_name(repo_name) | |
60 | if not repo: |
|
68 | if not repo: | |
61 | h.flash(_('%s repository is not mapped to db perhaps' |
|
69 | h.flash(_('%s repository is not mapped to db perhaps' | |
62 | ' it was created or renamed from the file system' |
|
70 | ' it was created or renamed from the file system' | |
63 | ' please run the application again' |
|
71 | ' please run the application again' | |
64 | ' in order to rescan repositories') % repo_name, |
|
72 | ' in order to rescan repositories') % repo_name, | |
65 | category='error') |
|
73 | category='error') | |
66 |
|
74 | |||
67 | return redirect(url('home')) |
|
75 | return redirect(url('home')) | |
68 |
|
76 | |||
69 | c.users_array = repo_model.get_users_js() |
|
77 | self.__load_defaults() | |
70 | c.users_groups_array = repo_model.get_users_groups_js() |
|
|||
71 |
|
||||
72 | defaults = c.repo_info.get_dict() |
|
|||
73 |
|
78 | |||
74 | #fill owner |
|
79 | defaults = RepoModel()._get_defaults(repo_name) | |
75 | if c.repo_info.user: |
|
|||
76 | defaults.update({'user': c.repo_info.user.username}) |
|
|||
77 | else: |
|
|||
78 | replacement_user = self.sa.query(User)\ |
|
|||
79 | .filter(User.admin == True).first().username |
|
|||
80 | defaults.update({'user': replacement_user}) |
|
|||
81 |
|
||||
82 | #fill repository users |
|
|||
83 | for p in c.repo_info.repo_to_perm: |
|
|||
84 | defaults.update({'u_perm_%s' % p.user.username: |
|
|||
85 | p.permission.permission_name}) |
|
|||
86 |
|
||||
87 | #fill repository groups |
|
|||
88 | for p in c.repo_info.users_group_to_perm: |
|
|||
89 | defaults.update({'g_perm_%s' % p.users_group.users_group_name: |
|
|||
90 | p.permission.permission_name}) |
|
|||
91 |
|
80 | |||
92 | return htmlfill.render( |
|
81 | return htmlfill.render( | |
93 | render('settings/repo_settings.html'), |
|
82 | render('settings/repo_settings.html'), | |
94 | defaults=defaults, |
|
83 | defaults=defaults, | |
95 | encoding="UTF-8", |
|
84 | encoding="UTF-8", | |
96 | force_defaults=False |
|
85 | force_defaults=False | |
97 | ) |
|
86 | ) | |
98 |
|
87 | |||
99 | @HasRepoPermissionAllDecorator('repository.admin') |
|
88 | @HasRepoPermissionAllDecorator('repository.admin') | |
100 | def update(self, repo_name): |
|
89 | def update(self, repo_name): | |
101 | repo_model = RepoModel() |
|
90 | repo_model = RepoModel() | |
102 | changed_name = repo_name |
|
91 | changed_name = repo_name | |
|
92 | ||||
|
93 | self.__load_defaults() | |||
|
94 | ||||
103 | _form = RepoSettingsForm(edit=True, |
|
95 | _form = RepoSettingsForm(edit=True, | |
104 |
old_data={'repo_name': repo_name} |
|
96 | old_data={'repo_name': repo_name}, | |
|
97 | repo_groups=c.repo_groups_choices)() | |||
105 | try: |
|
98 | try: | |
106 | form_result = _form.to_python(dict(request.POST)) |
|
99 | form_result = _form.to_python(dict(request.POST)) | |
|
100 | ||||
107 | repo_model.update(repo_name, form_result) |
|
101 | repo_model.update(repo_name, form_result) | |
108 | invalidate_cache('get_repo_cached_%s' % repo_name) |
|
102 | invalidate_cache('get_repo_cached_%s' % repo_name) | |
109 | h.flash(_('Repository %s updated successfully' % repo_name), |
|
103 | h.flash(_('Repository %s updated successfully' % repo_name), | |
110 | category='success') |
|
104 | category='success') | |
111 | changed_name = form_result['repo_name'] |
|
105 | changed_name = form_result['repo_name_full'] | |
112 | action_logger(self.rhodecode_user, 'user_updated_repo', |
|
106 | action_logger(self.rhodecode_user, 'user_updated_repo', | |
113 |
|
|
107 | changed_name, '', self.sa) | |
114 | except formencode.Invalid, errors: |
|
108 | except formencode.Invalid, errors: | |
115 | c.repo_info = repo_model.get_by_repo_name(repo_name) |
|
109 | c.repo_info = repo_model.get_by_repo_name(repo_name) | |
116 | c.users_array = repo_model.get_users_js() |
|
110 | c.users_array = repo_model.get_users_js() | |
117 | errors.value.update({'user': c.repo_info.user.username}) |
|
111 | errors.value.update({'user': c.repo_info.user.username}) | |
118 | return htmlfill.render( |
|
112 | return htmlfill.render( | |
119 | render('settings/repo_settings.html'), |
|
113 | render('settings/repo_settings.html'), | |
120 | defaults=errors.value, |
|
114 | defaults=errors.value, | |
121 | errors=errors.error_dict or {}, |
|
115 | errors=errors.error_dict or {}, | |
122 | prefix_error=False, |
|
116 | prefix_error=False, | |
123 | encoding="UTF-8") |
|
117 | encoding="UTF-8") | |
124 | except Exception: |
|
118 | except Exception: | |
125 | log.error(traceback.format_exc()) |
|
119 | log.error(traceback.format_exc()) | |
126 | h.flash(_('error occurred during update of repository %s') \ |
|
120 | h.flash(_('error occurred during update of repository %s') \ | |
127 | % repo_name, category='error') |
|
121 | % repo_name, category='error') | |
128 |
|
122 | |||
129 | return redirect(url('repo_settings_home', repo_name=changed_name)) |
|
123 | return redirect(url('repo_settings_home', repo_name=changed_name)) | |
130 |
|
124 | |||
131 | @HasRepoPermissionAllDecorator('repository.admin') |
|
125 | @HasRepoPermissionAllDecorator('repository.admin') | |
132 | def delete(self, repo_name): |
|
126 | def delete(self, repo_name): | |
133 | """DELETE /repos/repo_name: Delete an existing item""" |
|
127 | """DELETE /repos/repo_name: Delete an existing item""" | |
134 | # Forms posted to this method should contain a hidden field: |
|
128 | # Forms posted to this method should contain a hidden field: | |
135 | # <input type="hidden" name="_method" value="DELETE" /> |
|
129 | # <input type="hidden" name="_method" value="DELETE" /> | |
136 | # Or using helpers: |
|
130 | # Or using helpers: | |
137 | # h.form(url('repo_settings_delete', repo_name=ID), |
|
131 | # h.form(url('repo_settings_delete', repo_name=ID), | |
138 | # method='delete') |
|
132 | # method='delete') | |
139 | # url('repo_settings_delete', repo_name=ID) |
|
133 | # url('repo_settings_delete', repo_name=ID) | |
140 |
|
134 | |||
141 | repo_model = RepoModel() |
|
135 | repo_model = RepoModel() | |
142 | repo = repo_model.get_by_repo_name(repo_name) |
|
136 | repo = repo_model.get_by_repo_name(repo_name) | |
143 | if not repo: |
|
137 | if not repo: | |
144 | h.flash(_('%s repository is not mapped to db perhaps' |
|
138 | h.flash(_('%s repository is not mapped to db perhaps' | |
145 | ' it was moved or renamed from the filesystem' |
|
139 | ' it was moved or renamed from the filesystem' | |
146 | ' please run the application again' |
|
140 | ' please run the application again' | |
147 | ' in order to rescan repositories') % repo_name, |
|
141 | ' in order to rescan repositories') % repo_name, | |
148 | category='error') |
|
142 | category='error') | |
149 |
|
143 | |||
150 | return redirect(url('home')) |
|
144 | return redirect(url('home')) | |
151 | try: |
|
145 | try: | |
152 | action_logger(self.rhodecode_user, 'user_deleted_repo', |
|
146 | action_logger(self.rhodecode_user, 'user_deleted_repo', | |
153 | repo_name, '', self.sa) |
|
147 | repo_name, '', self.sa) | |
154 | repo_model.delete(repo) |
|
148 | repo_model.delete(repo) | |
155 | invalidate_cache('get_repo_cached_%s' % repo_name) |
|
149 | invalidate_cache('get_repo_cached_%s' % repo_name) | |
156 | h.flash(_('deleted repository %s') % repo_name, category='success') |
|
150 | h.flash(_('deleted repository %s') % repo_name, category='success') | |
157 | except Exception: |
|
151 | except Exception: | |
158 | log.error(traceback.format_exc()) |
|
152 | log.error(traceback.format_exc()) | |
159 | h.flash(_('An error occurred during deletion of %s') % repo_name, |
|
153 | h.flash(_('An error occurred during deletion of %s') % repo_name, | |
160 | category='error') |
|
154 | category='error') | |
161 |
|
155 | |||
162 | return redirect(url('home')) |
|
156 | return redirect(url('home')) | |
163 |
|
157 | |||
164 | @NotAnonymous() |
|
158 | @NotAnonymous() | |
165 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
159 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', | |
166 | 'repository.admin') |
|
160 | 'repository.admin') | |
167 | def fork(self, repo_name): |
|
161 | def fork(self, repo_name): | |
168 | repo_model = RepoModel() |
|
162 | repo_model = RepoModel() | |
169 | c.repo_info = repo = repo_model.get_by_repo_name(repo_name) |
|
163 | c.repo_info = repo = repo_model.get_by_repo_name(repo_name) | |
170 | if not repo: |
|
164 | if not repo: | |
171 | h.flash(_('%s repository is not mapped to db perhaps' |
|
165 | h.flash(_('%s repository is not mapped to db perhaps' | |
172 | ' it was created or renamed from the file system' |
|
166 | ' it was created or renamed from the file system' | |
173 | ' please run the application again' |
|
167 | ' please run the application again' | |
174 | ' in order to rescan repositories') % repo_name, |
|
168 | ' in order to rescan repositories') % repo_name, | |
175 | category='error') |
|
169 | category='error') | |
176 |
|
170 | |||
177 | return redirect(url('home')) |
|
171 | return redirect(url('home')) | |
178 |
|
172 | |||
179 | return render('settings/repo_fork.html') |
|
173 | return render('settings/repo_fork.html') | |
180 |
|
174 | |||
181 | @NotAnonymous() |
|
175 | @NotAnonymous() | |
182 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
176 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', | |
183 | 'repository.admin') |
|
177 | 'repository.admin') | |
184 | def fork_create(self, repo_name): |
|
178 | def fork_create(self, repo_name): | |
185 | repo_model = RepoModel() |
|
179 | repo_model = RepoModel() | |
186 | c.repo_info = repo_model.get_by_repo_name(repo_name) |
|
180 | c.repo_info = repo_model.get_by_repo_name(repo_name) | |
187 | _form = RepoForkForm(old_data={'repo_type': c.repo_info.repo_type})() |
|
181 | _form = RepoForkForm(old_data={'repo_type': c.repo_info.repo_type})() | |
188 | form_result = {} |
|
182 | form_result = {} | |
189 | try: |
|
183 | try: | |
190 | form_result = _form.to_python(dict(request.POST)) |
|
184 | form_result = _form.to_python(dict(request.POST)) | |
191 | form_result.update({'repo_name': repo_name}) |
|
185 | form_result.update({'repo_name': repo_name}) | |
192 | repo_model.create_fork(form_result, self.rhodecode_user) |
|
186 | repo_model.create_fork(form_result, self.rhodecode_user) | |
193 | h.flash(_('forked %s repository as %s') \ |
|
187 | h.flash(_('forked %s repository as %s') \ | |
194 | % (repo_name, form_result['fork_name']), |
|
188 | % (repo_name, form_result['fork_name']), | |
195 | category='success') |
|
189 | category='success') | |
196 | action_logger(self.rhodecode_user, |
|
190 | action_logger(self.rhodecode_user, | |
197 | 'user_forked_repo:%s' % form_result['fork_name'], |
|
191 | 'user_forked_repo:%s' % form_result['fork_name'], | |
198 | repo_name, '', self.sa) |
|
192 | repo_name, '', self.sa) | |
199 | except formencode.Invalid, errors: |
|
193 | except formencode.Invalid, errors: | |
200 | c.new_repo = errors.value['fork_name'] |
|
194 | c.new_repo = errors.value['fork_name'] | |
201 | r = render('settings/repo_fork.html') |
|
195 | r = render('settings/repo_fork.html') | |
202 |
|
196 | |||
203 | return htmlfill.render( |
|
197 | return htmlfill.render( | |
204 | r, |
|
198 | r, | |
205 | defaults=errors.value, |
|
199 | defaults=errors.value, | |
206 | errors=errors.error_dict or {}, |
|
200 | errors=errors.error_dict or {}, | |
207 | prefix_error=False, |
|
201 | prefix_error=False, | |
208 | encoding="UTF-8") |
|
202 | encoding="UTF-8") | |
209 | except Exception: |
|
203 | except Exception: | |
210 | log.error(traceback.format_exc()) |
|
204 | log.error(traceback.format_exc()) | |
211 | h.flash(_('An error occurred during repository forking %s') % |
|
205 | h.flash(_('An error occurred during repository forking %s') % | |
212 | repo_name, category='error') |
|
206 | repo_name, category='error') | |
213 |
|
207 | |||
214 | return redirect(url('home')) |
|
208 | return redirect(url('home')) |
@@ -1,408 +1,407 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """ |
|
2 | """ | |
3 | rhodecode.lib.__init__ |
|
3 | rhodecode.lib.__init__ | |
4 | ~~~~~~~~~~~~~~~~~~~~~~~ |
|
4 | ~~~~~~~~~~~~~~~~~~~~~~~ | |
5 |
|
5 | |||
6 | Some simple helper functions |
|
6 | Some simple helper functions | |
7 |
|
7 | |||
8 | :created_on: Jan 5, 2011 |
|
8 | :created_on: Jan 5, 2011 | |
9 | :author: marcink |
|
9 | :author: marcink | |
10 | :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> |
|
10 | :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |
11 | :license: GPLv3, see COPYING for more details. |
|
11 | :license: GPLv3, see COPYING for more details. | |
12 | """ |
|
12 | """ | |
13 | # This program is free software: you can redistribute it and/or modify |
|
13 | # This program is free software: you can redistribute it and/or modify | |
14 | # it under the terms of the GNU General Public License as published by |
|
14 | # it under the terms of the GNU General Public License as published by | |
15 | # the Free Software Foundation, either version 3 of the License, or |
|
15 | # the Free Software Foundation, either version 3 of the License, or | |
16 | # (at your option) any later version. |
|
16 | # (at your option) any later version. | |
17 | # |
|
17 | # | |
18 | # This program is distributed in the hope that it will be useful, |
|
18 | # This program is distributed in the hope that it will be useful, | |
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 | # GNU General Public License for more details. |
|
21 | # GNU General Public License for more details. | |
22 | # |
|
22 | # | |
23 | # You should have received a copy of the GNU General Public License |
|
23 | # You should have received a copy of the GNU General Public License | |
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
25 |
|
25 | |||
26 | import os |
|
26 | import os | |
27 |
|
27 | |||
28 | def __get_lem(): |
|
28 | def __get_lem(): | |
29 | from pygments import lexers |
|
29 | from pygments import lexers | |
30 | from string import lower |
|
30 | from string import lower | |
31 | from collections import defaultdict |
|
31 | from collections import defaultdict | |
32 |
|
32 | |||
33 | d = defaultdict(lambda: []) |
|
33 | d = defaultdict(lambda: []) | |
34 |
|
34 | |||
35 | def __clean(s): |
|
35 | def __clean(s): | |
36 | s = s.lstrip('*') |
|
36 | s = s.lstrip('*') | |
37 | s = s.lstrip('.') |
|
37 | s = s.lstrip('.') | |
38 |
|
38 | |||
39 | if s.find('[') != -1: |
|
39 | if s.find('[') != -1: | |
40 | exts = [] |
|
40 | exts = [] | |
41 | start, stop = s.find('['), s.find(']') |
|
41 | start, stop = s.find('['), s.find(']') | |
42 |
|
42 | |||
43 | for suffix in s[start + 1:stop]: |
|
43 | for suffix in s[start + 1:stop]: | |
44 | exts.append(s[:s.find('[')] + suffix) |
|
44 | exts.append(s[:s.find('[')] + suffix) | |
45 | return map(lower, exts) |
|
45 | return map(lower, exts) | |
46 | else: |
|
46 | else: | |
47 | return map(lower, [s]) |
|
47 | return map(lower, [s]) | |
48 |
|
48 | |||
49 | for lx, t in sorted(lexers.LEXERS.items()): |
|
49 | for lx, t in sorted(lexers.LEXERS.items()): | |
50 | m = map(__clean, t[-2]) |
|
50 | m = map(__clean, t[-2]) | |
51 | if m: |
|
51 | if m: | |
52 | m = reduce(lambda x, y: x + y, m) |
|
52 | m = reduce(lambda x, y: x + y, m) | |
53 | for ext in m: |
|
53 | for ext in m: | |
54 | desc = lx.replace('Lexer', '') |
|
54 | desc = lx.replace('Lexer', '') | |
55 | d[ext].append(desc) |
|
55 | d[ext].append(desc) | |
56 |
|
56 | |||
57 | return dict(d) |
|
57 | return dict(d) | |
58 |
|
58 | |||
59 | # language map is also used by whoosh indexer, which for those specified |
|
59 | # language map is also used by whoosh indexer, which for those specified | |
60 | # extensions will index it's content |
|
60 | # extensions will index it's content | |
61 | LANGUAGES_EXTENSIONS_MAP = __get_lem() |
|
61 | LANGUAGES_EXTENSIONS_MAP = __get_lem() | |
62 |
|
62 | |||
63 | # Additional mappings that are not present in the pygments lexers |
|
63 | # Additional mappings that are not present in the pygments lexers | |
64 | # NOTE: that this will overide any mappings in LANGUAGES_EXTENSIONS_MAP |
|
64 | # NOTE: that this will overide any mappings in LANGUAGES_EXTENSIONS_MAP | |
65 | ADDITIONAL_MAPPINGS = {'xaml': 'XAML'} |
|
65 | ADDITIONAL_MAPPINGS = {'xaml': 'XAML'} | |
66 |
|
66 | |||
67 | LANGUAGES_EXTENSIONS_MAP.update(ADDITIONAL_MAPPINGS) |
|
67 | LANGUAGES_EXTENSIONS_MAP.update(ADDITIONAL_MAPPINGS) | |
68 |
|
68 | |||
69 |
|
69 | |||
70 | def str2bool(_str): |
|
70 | def str2bool(_str): | |
71 | """ |
|
71 | """ | |
72 | returs True/False value from given string, it tries to translate the |
|
72 | returs True/False value from given string, it tries to translate the | |
73 | string into boolean |
|
73 | string into boolean | |
74 |
|
74 | |||
75 | :param _str: string value to translate into boolean |
|
75 | :param _str: string value to translate into boolean | |
76 | :rtype: boolean |
|
76 | :rtype: boolean | |
77 | :returns: boolean from given string |
|
77 | :returns: boolean from given string | |
78 | """ |
|
78 | """ | |
79 | if _str is None: |
|
79 | if _str is None: | |
80 | return False |
|
80 | return False | |
81 | if _str in (True, False): |
|
81 | if _str in (True, False): | |
82 | return _str |
|
82 | return _str | |
83 | _str = str(_str).strip().lower() |
|
83 | _str = str(_str).strip().lower() | |
84 | return _str in ('t', 'true', 'y', 'yes', 'on', '1') |
|
84 | return _str in ('t', 'true', 'y', 'yes', 'on', '1') | |
85 |
|
85 | |||
86 |
|
86 | |||
87 | def convert_line_endings(line, mode): |
|
87 | def convert_line_endings(line, mode): | |
88 | """ |
|
88 | """ | |
89 | Converts a given line "line end" accordingly to given mode |
|
89 | Converts a given line "line end" accordingly to given mode | |
90 |
|
90 | |||
91 | Available modes are:: |
|
91 | Available modes are:: | |
92 | 0 - Unix |
|
92 | 0 - Unix | |
93 | 1 - Mac |
|
93 | 1 - Mac | |
94 | 2 - DOS |
|
94 | 2 - DOS | |
95 |
|
95 | |||
96 | :param line: given line to convert |
|
96 | :param line: given line to convert | |
97 | :param mode: mode to convert to |
|
97 | :param mode: mode to convert to | |
98 | :rtype: str |
|
98 | :rtype: str | |
99 | :return: converted line according to mode |
|
99 | :return: converted line according to mode | |
100 | """ |
|
100 | """ | |
101 | from string import replace |
|
101 | from string import replace | |
102 |
|
102 | |||
103 | if mode == 0: |
|
103 | if mode == 0: | |
104 | line = replace(line, '\r\n', '\n') |
|
104 | line = replace(line, '\r\n', '\n') | |
105 | line = replace(line, '\r', '\n') |
|
105 | line = replace(line, '\r', '\n') | |
106 | elif mode == 1: |
|
106 | elif mode == 1: | |
107 | line = replace(line, '\r\n', '\r') |
|
107 | line = replace(line, '\r\n', '\r') | |
108 | line = replace(line, '\n', '\r') |
|
108 | line = replace(line, '\n', '\r') | |
109 | elif mode == 2: |
|
109 | elif mode == 2: | |
110 | import re |
|
110 | import re | |
111 | line = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", line) |
|
111 | line = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", line) | |
112 | return line |
|
112 | return line | |
113 |
|
113 | |||
114 |
|
114 | |||
115 | def detect_mode(line, default): |
|
115 | def detect_mode(line, default): | |
116 | """ |
|
116 | """ | |
117 | Detects line break for given line, if line break couldn't be found |
|
117 | Detects line break for given line, if line break couldn't be found | |
118 | given default value is returned |
|
118 | given default value is returned | |
119 |
|
119 | |||
120 | :param line: str line |
|
120 | :param line: str line | |
121 | :param default: default |
|
121 | :param default: default | |
122 | :rtype: int |
|
122 | :rtype: int | |
123 | :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS |
|
123 | :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS | |
124 | """ |
|
124 | """ | |
125 | if line.endswith('\r\n'): |
|
125 | if line.endswith('\r\n'): | |
126 | return 2 |
|
126 | return 2 | |
127 | elif line.endswith('\n'): |
|
127 | elif line.endswith('\n'): | |
128 | return 0 |
|
128 | return 0 | |
129 | elif line.endswith('\r'): |
|
129 | elif line.endswith('\r'): | |
130 | return 1 |
|
130 | return 1 | |
131 | else: |
|
131 | else: | |
132 | return default |
|
132 | return default | |
133 |
|
133 | |||
134 |
|
134 | |||
135 | def generate_api_key(username, salt=None): |
|
135 | def generate_api_key(username, salt=None): | |
136 | """ |
|
136 | """ | |
137 | Generates unique API key for given username, if salt is not given |
|
137 | Generates unique API key for given username, if salt is not given | |
138 | it'll be generated from some random string |
|
138 | it'll be generated from some random string | |
139 |
|
139 | |||
140 | :param username: username as string |
|
140 | :param username: username as string | |
141 | :param salt: salt to hash generate KEY |
|
141 | :param salt: salt to hash generate KEY | |
142 | :rtype: str |
|
142 | :rtype: str | |
143 | :returns: sha1 hash from username+salt |
|
143 | :returns: sha1 hash from username+salt | |
144 | """ |
|
144 | """ | |
145 | from tempfile import _RandomNameSequence |
|
145 | from tempfile import _RandomNameSequence | |
146 | import hashlib |
|
146 | import hashlib | |
147 |
|
147 | |||
148 | if salt is None: |
|
148 | if salt is None: | |
149 | salt = _RandomNameSequence().next() |
|
149 | salt = _RandomNameSequence().next() | |
150 |
|
150 | |||
151 | return hashlib.sha1(username + salt).hexdigest() |
|
151 | return hashlib.sha1(username + salt).hexdigest() | |
152 |
|
152 | |||
153 |
|
153 | |||
154 | def safe_unicode(str_, from_encoding='utf8'): |
|
154 | def safe_unicode(str_, from_encoding='utf8'): | |
155 | """ |
|
155 | """ | |
156 | safe unicode function. Does few trick to turn str_ into unicode |
|
156 | safe unicode function. Does few trick to turn str_ into unicode | |
157 |
|
157 | |||
158 | In case of UnicodeDecode error we try to return it with encoding detected |
|
158 | In case of UnicodeDecode error we try to return it with encoding detected | |
159 | by chardet library if it fails fallback to unicode with errors replaced |
|
159 | by chardet library if it fails fallback to unicode with errors replaced | |
160 |
|
160 | |||
161 | :param str_: string to decode |
|
161 | :param str_: string to decode | |
162 | :rtype: unicode |
|
162 | :rtype: unicode | |
163 | :returns: unicode object |
|
163 | :returns: unicode object | |
164 | """ |
|
164 | """ | |
165 | if isinstance(str_, unicode): |
|
165 | if isinstance(str_, unicode): | |
166 | return str_ |
|
166 | return str_ | |
167 |
|
167 | |||
168 | try: |
|
168 | try: | |
169 | return unicode(str_) |
|
169 | return unicode(str_) | |
170 | except UnicodeDecodeError: |
|
170 | except UnicodeDecodeError: | |
171 | pass |
|
171 | pass | |
172 |
|
172 | |||
173 | try: |
|
173 | try: | |
174 | return unicode(str_, from_encoding) |
|
174 | return unicode(str_, from_encoding) | |
175 | except UnicodeDecodeError: |
|
175 | except UnicodeDecodeError: | |
176 | pass |
|
176 | pass | |
177 |
|
177 | |||
178 | try: |
|
178 | try: | |
179 | import chardet |
|
179 | import chardet | |
180 | encoding = chardet.detect(str_)['encoding'] |
|
180 | encoding = chardet.detect(str_)['encoding'] | |
181 | if encoding is None: |
|
181 | if encoding is None: | |
182 | raise Exception() |
|
182 | raise Exception() | |
183 | return str_.decode(encoding) |
|
183 | return str_.decode(encoding) | |
184 | except (ImportError, UnicodeDecodeError, Exception): |
|
184 | except (ImportError, UnicodeDecodeError, Exception): | |
185 | return unicode(str_, from_encoding, 'replace') |
|
185 | return unicode(str_, from_encoding, 'replace') | |
186 |
|
186 | |||
187 | def safe_str(unicode_, to_encoding='utf8'): |
|
187 | def safe_str(unicode_, to_encoding='utf8'): | |
188 | """ |
|
188 | """ | |
189 | safe str function. Does few trick to turn unicode_ into string |
|
189 | safe str function. Does few trick to turn unicode_ into string | |
190 |
|
190 | |||
191 | In case of UnicodeEncodeError we try to return it with encoding detected |
|
191 | In case of UnicodeEncodeError we try to return it with encoding detected | |
192 | by chardet library if it fails fallback to string with errors replaced |
|
192 | by chardet library if it fails fallback to string with errors replaced | |
193 |
|
193 | |||
194 | :param unicode_: unicode to encode |
|
194 | :param unicode_: unicode to encode | |
195 | :rtype: str |
|
195 | :rtype: str | |
196 | :returns: str object |
|
196 | :returns: str object | |
197 | """ |
|
197 | """ | |
198 |
|
198 | |||
199 | if isinstance(unicode_, str): |
|
199 | if isinstance(unicode_, str): | |
200 | return unicode_ |
|
200 | return unicode_ | |
201 |
|
201 | |||
202 | try: |
|
202 | try: | |
203 | return unicode_.encode(to_encoding) |
|
203 | return unicode_.encode(to_encoding) | |
204 | except UnicodeEncodeError: |
|
204 | except UnicodeEncodeError: | |
205 | pass |
|
205 | pass | |
206 |
|
206 | |||
207 | try: |
|
207 | try: | |
208 | import chardet |
|
208 | import chardet | |
209 | encoding = chardet.detect(unicode_)['encoding'] |
|
209 | encoding = chardet.detect(unicode_)['encoding'] | |
210 | print encoding |
|
210 | print encoding | |
211 | if encoding is None: |
|
211 | if encoding is None: | |
212 | raise UnicodeEncodeError() |
|
212 | raise UnicodeEncodeError() | |
213 |
|
213 | |||
214 | return unicode_.encode(encoding) |
|
214 | return unicode_.encode(encoding) | |
215 | except (ImportError, UnicodeEncodeError): |
|
215 | except (ImportError, UnicodeEncodeError): | |
216 | return unicode_.encode(to_encoding, 'replace') |
|
216 | return unicode_.encode(to_encoding, 'replace') | |
217 |
|
217 | |||
218 | return safe_str |
|
218 | return safe_str | |
219 |
|
219 | |||
220 |
|
220 | |||
221 |
|
221 | |||
222 | def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs): |
|
222 | def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs): | |
223 | """ |
|
223 | """ | |
224 | Custom engine_from_config functions that makes sure we use NullPool for |
|
224 | Custom engine_from_config functions that makes sure we use NullPool for | |
225 | file based sqlite databases. This prevents errors on sqlite. This only |
|
225 | file based sqlite databases. This prevents errors on sqlite. This only | |
226 | applies to sqlalchemy versions < 0.7.0 |
|
226 | applies to sqlalchemy versions < 0.7.0 | |
227 |
|
227 | |||
228 | """ |
|
228 | """ | |
229 | import sqlalchemy |
|
229 | import sqlalchemy | |
230 | from sqlalchemy import engine_from_config as efc |
|
230 | from sqlalchemy import engine_from_config as efc | |
231 | import logging |
|
231 | import logging | |
232 |
|
232 | |||
233 | if int(sqlalchemy.__version__.split('.')[1]) < 7: |
|
233 | if int(sqlalchemy.__version__.split('.')[1]) < 7: | |
234 |
|
234 | |||
235 | # This solution should work for sqlalchemy < 0.7.0, and should use |
|
235 | # This solution should work for sqlalchemy < 0.7.0, and should use | |
236 | # proxy=TimerProxy() for execution time profiling |
|
236 | # proxy=TimerProxy() for execution time profiling | |
237 |
|
237 | |||
238 | from sqlalchemy.pool import NullPool |
|
238 | from sqlalchemy.pool import NullPool | |
239 | url = configuration[prefix + 'url'] |
|
239 | url = configuration[prefix + 'url'] | |
240 |
|
240 | |||
241 | if url.startswith('sqlite'): |
|
241 | if url.startswith('sqlite'): | |
242 | kwargs.update({'poolclass': NullPool}) |
|
242 | kwargs.update({'poolclass': NullPool}) | |
243 | return efc(configuration, prefix, **kwargs) |
|
243 | return efc(configuration, prefix, **kwargs) | |
244 | else: |
|
244 | else: | |
245 | import time |
|
245 | import time | |
246 | from sqlalchemy import event |
|
246 | from sqlalchemy import event | |
247 | from sqlalchemy.engine import Engine |
|
247 | from sqlalchemy.engine import Engine | |
248 |
|
248 | |||
249 | log = logging.getLogger('sqlalchemy.engine') |
|
249 | log = logging.getLogger('sqlalchemy.engine') | |
250 | BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38) |
|
250 | BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38) | |
251 | engine = efc(configuration, prefix, **kwargs) |
|
251 | engine = efc(configuration, prefix, **kwargs) | |
252 |
|
252 | |||
253 | def color_sql(sql): |
|
253 | def color_sql(sql): | |
254 | COLOR_SEQ = "\033[1;%dm" |
|
254 | COLOR_SEQ = "\033[1;%dm" | |
255 | COLOR_SQL = YELLOW |
|
255 | COLOR_SQL = YELLOW | |
256 | normal = '\x1b[0m' |
|
256 | normal = '\x1b[0m' | |
257 | return ''.join([COLOR_SEQ % COLOR_SQL, sql, normal]) |
|
257 | return ''.join([COLOR_SEQ % COLOR_SQL, sql, normal]) | |
258 |
|
258 | |||
259 | if configuration['debug']: |
|
259 | if configuration['debug']: | |
260 | #attach events only for debug configuration |
|
260 | #attach events only for debug configuration | |
261 |
|
261 | |||
262 | def before_cursor_execute(conn, cursor, statement, |
|
262 | def before_cursor_execute(conn, cursor, statement, | |
263 | parameters, context, executemany): |
|
263 | parameters, context, executemany): | |
264 | context._query_start_time = time.time() |
|
264 | context._query_start_time = time.time() | |
265 | log.info(color_sql(">>>>> STARTING QUERY >>>>>")) |
|
265 | log.info(color_sql(">>>>> STARTING QUERY >>>>>")) | |
266 |
|
266 | |||
267 |
|
267 | |||
268 | def after_cursor_execute(conn, cursor, statement, |
|
268 | def after_cursor_execute(conn, cursor, statement, | |
269 | parameters, context, executemany): |
|
269 | parameters, context, executemany): | |
270 | total = time.time() - context._query_start_time |
|
270 | total = time.time() - context._query_start_time | |
271 | log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total)) |
|
271 | log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total)) | |
272 |
|
272 | |||
273 | event.listen(engine, "before_cursor_execute", |
|
273 | event.listen(engine, "before_cursor_execute", | |
274 | before_cursor_execute) |
|
274 | before_cursor_execute) | |
275 | event.listen(engine, "after_cursor_execute", |
|
275 | event.listen(engine, "after_cursor_execute", | |
276 | after_cursor_execute) |
|
276 | after_cursor_execute) | |
277 |
|
277 | |||
278 | return engine |
|
278 | return engine | |
279 |
|
279 | |||
280 |
|
280 | |||
281 | def age(curdate): |
|
281 | def age(curdate): | |
282 | """ |
|
282 | """ | |
283 | turns a datetime into an age string. |
|
283 | turns a datetime into an age string. | |
284 |
|
284 | |||
285 | :param curdate: datetime object |
|
285 | :param curdate: datetime object | |
286 | :rtype: unicode |
|
286 | :rtype: unicode | |
287 | :returns: unicode words describing age |
|
287 | :returns: unicode words describing age | |
288 | """ |
|
288 | """ | |
289 |
|
289 | |||
290 | from datetime import datetime |
|
290 | from datetime import datetime | |
291 | from webhelpers.date import time_ago_in_words |
|
291 | from webhelpers.date import time_ago_in_words | |
292 |
|
292 | |||
293 | _ = lambda s:s |
|
293 | _ = lambda s:s | |
294 |
|
294 | |||
295 | if not curdate: |
|
295 | if not curdate: | |
296 | return '' |
|
296 | return '' | |
297 |
|
297 | |||
298 | agescales = [(_(u"year"), 3600 * 24 * 365), |
|
298 | agescales = [(_(u"year"), 3600 * 24 * 365), | |
299 | (_(u"month"), 3600 * 24 * 30), |
|
299 | (_(u"month"), 3600 * 24 * 30), | |
300 | (_(u"day"), 3600 * 24), |
|
300 | (_(u"day"), 3600 * 24), | |
301 | (_(u"hour"), 3600), |
|
301 | (_(u"hour"), 3600), | |
302 | (_(u"minute"), 60), |
|
302 | (_(u"minute"), 60), | |
303 | (_(u"second"), 1), ] |
|
303 | (_(u"second"), 1), ] | |
304 |
|
304 | |||
305 | age = datetime.now() - curdate |
|
305 | age = datetime.now() - curdate | |
306 | age_seconds = (age.days * agescales[2][1]) + age.seconds |
|
306 | age_seconds = (age.days * agescales[2][1]) + age.seconds | |
307 | pos = 1 |
|
307 | pos = 1 | |
308 | for scale in agescales: |
|
308 | for scale in agescales: | |
309 | if scale[1] <= age_seconds: |
|
309 | if scale[1] <= age_seconds: | |
310 | if pos == 6:pos = 5 |
|
310 | if pos == 6:pos = 5 | |
311 | return '%s %s' % (time_ago_in_words(curdate, |
|
311 | return '%s %s' % (time_ago_in_words(curdate, | |
312 | agescales[pos][0]), _('ago')) |
|
312 | agescales[pos][0]), _('ago')) | |
313 | pos += 1 |
|
313 | pos += 1 | |
314 |
|
314 | |||
315 | return _(u'just now') |
|
315 | return _(u'just now') | |
316 |
|
316 | |||
317 |
|
317 | |||
318 | def uri_filter(uri): |
|
318 | def uri_filter(uri): | |
319 | """ |
|
319 | """ | |
320 | Removes user:password from given url string |
|
320 | Removes user:password from given url string | |
321 |
|
321 | |||
322 | :param uri: |
|
322 | :param uri: | |
323 | :rtype: unicode |
|
323 | :rtype: unicode | |
324 | :returns: filtered list of strings |
|
324 | :returns: filtered list of strings | |
325 | """ |
|
325 | """ | |
326 | if not uri: |
|
326 | if not uri: | |
327 | return '' |
|
327 | return '' | |
328 |
|
328 | |||
329 | proto = '' |
|
329 | proto = '' | |
330 |
|
330 | |||
331 | for pat in ('https://', 'http://'): |
|
331 | for pat in ('https://', 'http://'): | |
332 | if uri.startswith(pat): |
|
332 | if uri.startswith(pat): | |
333 | uri = uri[len(pat):] |
|
333 | uri = uri[len(pat):] | |
334 | proto = pat |
|
334 | proto = pat | |
335 | break |
|
335 | break | |
336 |
|
336 | |||
337 | # remove passwords and username |
|
337 | # remove passwords and username | |
338 | uri = uri[uri.find('@') + 1:] |
|
338 | uri = uri[uri.find('@') + 1:] | |
339 |
|
339 | |||
340 | # get the port |
|
340 | # get the port | |
341 | cred_pos = uri.find(':') |
|
341 | cred_pos = uri.find(':') | |
342 | if cred_pos == -1: |
|
342 | if cred_pos == -1: | |
343 | host, port = uri, None |
|
343 | host, port = uri, None | |
344 | else: |
|
344 | else: | |
345 | host, port = uri[:cred_pos], uri[cred_pos + 1:] |
|
345 | host, port = uri[:cred_pos], uri[cred_pos + 1:] | |
346 |
|
346 | |||
347 | return filter(None, [proto, host, port]) |
|
347 | return filter(None, [proto, host, port]) | |
348 |
|
348 | |||
349 |
|
349 | |||
350 | def credentials_filter(uri): |
|
350 | def credentials_filter(uri): | |
351 | """ |
|
351 | """ | |
352 | Returns a url with removed credentials |
|
352 | Returns a url with removed credentials | |
353 |
|
353 | |||
354 | :param uri: |
|
354 | :param uri: | |
355 | """ |
|
355 | """ | |
356 |
|
356 | |||
357 | uri = uri_filter(uri) |
|
357 | uri = uri_filter(uri) | |
358 | #check if we have port |
|
358 | #check if we have port | |
359 | if len(uri) > 2 and uri[2]: |
|
359 | if len(uri) > 2 and uri[2]: | |
360 | uri[2] = ':' + uri[2] |
|
360 | uri[2] = ':' + uri[2] | |
361 |
|
361 | |||
362 | return ''.join(uri) |
|
362 | return ''.join(uri) | |
363 |
|
363 | |||
364 | def get_changeset_safe(repo, rev): |
|
364 | def get_changeset_safe(repo, rev): | |
365 | """ |
|
365 | """ | |
366 | Safe version of get_changeset if this changeset doesn't exists for a |
|
366 | Safe version of get_changeset if this changeset doesn't exists for a | |
367 | repo it returns a Dummy one instead |
|
367 | repo it returns a Dummy one instead | |
368 |
|
368 | |||
369 | :param repo: |
|
369 | :param repo: | |
370 | :param rev: |
|
370 | :param rev: | |
371 | """ |
|
371 | """ | |
372 | from vcs.backends.base import BaseRepository |
|
372 | from vcs.backends.base import BaseRepository | |
373 | from vcs.exceptions import RepositoryError |
|
373 | from vcs.exceptions import RepositoryError | |
374 | if not isinstance(repo, BaseRepository): |
|
374 | if not isinstance(repo, BaseRepository): | |
375 | raise Exception('You must pass an Repository ' |
|
375 | raise Exception('You must pass an Repository ' | |
376 | 'object as first argument got %s', type(repo)) |
|
376 | 'object as first argument got %s', type(repo)) | |
377 |
|
377 | |||
378 | try: |
|
378 | try: | |
379 | cs = repo.get_changeset(rev) |
|
379 | cs = repo.get_changeset(rev) | |
380 | except RepositoryError: |
|
380 | except RepositoryError: | |
381 | from rhodecode.lib.utils import EmptyChangeset |
|
381 | from rhodecode.lib.utils import EmptyChangeset | |
382 | cs = EmptyChangeset(requested_revision=rev) |
|
382 | cs = EmptyChangeset(requested_revision=rev) | |
383 | return cs |
|
383 | return cs | |
384 |
|
384 | |||
385 |
|
385 | |||
386 | def get_current_revision(quiet=False): |
|
386 | def get_current_revision(quiet=False): | |
387 | """ |
|
387 | """ | |
388 | Returns tuple of (number, id) from repository containing this package |
|
388 | Returns tuple of (number, id) from repository containing this package | |
389 | or None if repository could not be found. |
|
389 | or None if repository could not be found. | |
390 |
|
390 | |||
391 | :param quiet: prints error for fetching revision if True |
|
391 | :param quiet: prints error for fetching revision if True | |
392 | """ |
|
392 | """ | |
393 |
|
393 | |||
394 | try: |
|
394 | try: | |
395 | from vcs import get_repo |
|
395 | from vcs import get_repo | |
396 | from vcs.utils.helpers import get_scm |
|
396 | from vcs.utils.helpers import get_scm | |
397 | from vcs.exceptions import RepositoryError, VCSError |
|
|||
398 | repopath = os.path.join(os.path.dirname(__file__), '..', '..') |
|
397 | repopath = os.path.join(os.path.dirname(__file__), '..', '..') | |
399 | scm = get_scm(repopath)[0] |
|
398 | scm = get_scm(repopath)[0] | |
400 | repo = get_repo(path=repopath, alias=scm) |
|
399 | repo = get_repo(path=repopath, alias=scm) | |
401 | tip = repo.get_changeset() |
|
400 | tip = repo.get_changeset() | |
402 | return (tip.revision, tip.short_id) |
|
401 | return (tip.revision, tip.short_id) | |
403 | except (ImportError, RepositoryError, VCSError), err: |
|
402 | except Exception, err: | |
404 | if not quiet: |
|
403 | if not quiet: | |
405 | print ("Cannot retrieve rhodecode's revision. Original error " |
|
404 | print ("Cannot retrieve rhodecode's revision. Original error " | |
406 | "was: %s" % err) |
|
405 | "was: %s" % err) | |
407 | return None |
|
406 | return None | |
408 |
|
407 |
@@ -1,147 +1,151 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """ |
|
2 | """ | |
3 | rhodecode.controllers.changelog |
|
3 | rhodecode.controllers.changelog | |
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
5 |
|
5 | |||
6 | RhodeCode authentication library for LDAP |
|
6 | RhodeCode authentication library for LDAP | |
7 |
|
7 | |||
8 | :created_on: Created on Nov 17, 2010 |
|
8 | :created_on: Created on Nov 17, 2010 | |
9 | :author: marcink |
|
9 | :author: marcink | |
10 | :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> |
|
10 | :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> | |
11 | :license: GPLv3, see COPYING for more details. |
|
11 | :license: GPLv3, see COPYING for more details. | |
12 | """ |
|
12 | """ | |
13 | # This program is free software: you can redistribute it and/or modify |
|
13 | # This program is free software: you can redistribute it and/or modify | |
14 | # it under the terms of the GNU General Public License as published by |
|
14 | # it under the terms of the GNU General Public License as published by | |
15 | # the Free Software Foundation, either version 3 of the License, or |
|
15 | # the Free Software Foundation, either version 3 of the License, or | |
16 | # (at your option) any later version. |
|
16 | # (at your option) any later version. | |
17 | # |
|
17 | # | |
18 | # This program is distributed in the hope that it will be useful, |
|
18 | # This program is distributed in the hope that it will be useful, | |
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 | # GNU General Public License for more details. |
|
21 | # GNU General Public License for more details. | |
22 | # |
|
22 | # | |
23 | # You should have received a copy of the GNU General Public License |
|
23 | # You should have received a copy of the GNU General Public License | |
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
25 |
|
25 | |||
26 | import logging |
|
26 | import logging | |
27 |
|
27 | |||
28 | from rhodecode.lib.exceptions import LdapConnectionError, LdapUsernameError, \ |
|
28 | from rhodecode.lib.exceptions import LdapConnectionError, LdapUsernameError, \ | |
29 | LdapPasswordError |
|
29 | LdapPasswordError | |
30 |
|
30 | |||
31 | log = logging.getLogger(__name__) |
|
31 | log = logging.getLogger(__name__) | |
32 |
|
32 | |||
33 |
|
33 | |||
34 | try: |
|
34 | try: | |
35 | import ldap |
|
35 | import ldap | |
36 | except ImportError: |
|
36 | except ImportError: | |
37 | # means that python-ldap is not installed |
|
37 | # means that python-ldap is not installed | |
38 | pass |
|
38 | pass | |
39 |
|
39 | |||
40 |
|
40 | |||
41 | class AuthLdap(object): |
|
41 | class AuthLdap(object): | |
42 |
|
42 | |||
43 | def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='', |
|
43 | def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='', | |
44 | tls_kind='PLAIN', tls_reqcert='DEMAND', ldap_version=3, |
|
44 | tls_kind='PLAIN', tls_reqcert='DEMAND', ldap_version=3, | |
45 | ldap_filter='(&(objectClass=user)(!(objectClass=computer)))', |
|
45 | ldap_filter='(&(objectClass=user)(!(objectClass=computer)))', | |
46 | search_scope='SUBTREE', |
|
46 | search_scope='SUBTREE', | |
47 | attr_login='uid'): |
|
47 | attr_login='uid'): | |
48 | self.ldap_version = ldap_version |
|
48 | self.ldap_version = ldap_version | |
49 | ldap_server_type = 'ldap' |
|
49 | ldap_server_type = 'ldap' | |
50 |
|
50 | |||
51 | self.TLS_KIND = tls_kind |
|
51 | self.TLS_KIND = tls_kind | |
52 |
|
52 | |||
53 | if self.TLS_KIND == 'LDAPS': |
|
53 | if self.TLS_KIND == 'LDAPS': | |
54 | port = port or 689 |
|
54 | port = port or 689 | |
55 | ldap_server_type = ldap_server_type + 's' |
|
55 | ldap_server_type = ldap_server_type + 's' | |
56 |
|
56 | |||
57 | self.TLS_REQCERT = ldap.__dict__['OPT_X_TLS_' + tls_reqcert] |
|
57 | OPT_X_TLS_DEMAND = 2 | |
|
58 | self.TLS_REQCERT = getattr(ldap, 'OPT_X_TLS_%s' % tls_reqcert, | |||
|
59 | OPT_X_TLS_DEMAND) | |||
58 | self.LDAP_SERVER_ADDRESS = server |
|
60 | self.LDAP_SERVER_ADDRESS = server | |
59 | self.LDAP_SERVER_PORT = port |
|
61 | self.LDAP_SERVER_PORT = port | |
60 |
|
62 | |||
61 | #USE FOR READ ONLY BIND TO LDAP SERVER |
|
63 | #USE FOR READ ONLY BIND TO LDAP SERVER | |
62 | self.LDAP_BIND_DN = bind_dn |
|
64 | self.LDAP_BIND_DN = bind_dn | |
63 | self.LDAP_BIND_PASS = bind_pass |
|
65 | self.LDAP_BIND_PASS = bind_pass | |
64 |
|
66 | |||
65 | self.LDAP_SERVER = "%s://%s:%s" % (ldap_server_type, |
|
67 | self.LDAP_SERVER = "%s://%s:%s" % (ldap_server_type, | |
66 |
|
|
68 | self.LDAP_SERVER_ADDRESS, | |
67 |
|
|
69 | self.LDAP_SERVER_PORT) | |
68 |
|
70 | |||
69 | self.BASE_DN = base_dn |
|
71 | self.BASE_DN = base_dn | |
70 | self.LDAP_FILTER = ldap_filter |
|
72 | self.LDAP_FILTER = ldap_filter | |
71 |
self.SEARCH_SCOPE = ldap |
|
73 | self.SEARCH_SCOPE = getattr(ldap, 'SCOPE_%s' % search_scope) | |
72 | self.attr_login = attr_login |
|
74 | self.attr_login = attr_login | |
73 |
|
75 | |||
74 | def authenticate_ldap(self, username, password): |
|
76 | def authenticate_ldap(self, username, password): | |
75 | """Authenticate a user via LDAP and return his/her LDAP properties. |
|
77 | """Authenticate a user via LDAP and return his/her LDAP properties. | |
76 |
|
78 | |||
77 | Raises AuthenticationError if the credentials are rejected, or |
|
79 | Raises AuthenticationError if the credentials are rejected, or | |
78 | EnvironmentError if the LDAP server can't be reached. |
|
80 | EnvironmentError if the LDAP server can't be reached. | |
79 |
|
81 | |||
80 | :param username: username |
|
82 | :param username: username | |
81 | :param password: password |
|
83 | :param password: password | |
82 | """ |
|
84 | """ | |
83 |
|
85 | |||
84 | from rhodecode.lib.helpers import chop_at |
|
86 | from rhodecode.lib.helpers import chop_at | |
85 |
|
87 | |||
86 | uid = chop_at(username, "@%s" % self.LDAP_SERVER_ADDRESS) |
|
88 | uid = chop_at(username, "@%s" % self.LDAP_SERVER_ADDRESS) | |
87 |
|
89 | |||
88 | if "," in username: |
|
90 | if "," in username: | |
89 | raise LdapUsernameError("invalid character in username: ,") |
|
91 | raise LdapUsernameError("invalid character in username: ,") | |
90 | try: |
|
92 | try: | |
91 |
|
|
93 | if hasattr(ldap,'OPT_X_TLS_CACERTDIR'): | |
|
94 | ldap.set_option(ldap.OPT_X_TLS_CACERTDIR, | |||
|
95 | '/etc/openldap/cacerts') | |||
92 | ldap.set_option(ldap.OPT_REFERRALS, ldap.OPT_OFF) |
|
96 | ldap.set_option(ldap.OPT_REFERRALS, ldap.OPT_OFF) | |
93 | ldap.set_option(ldap.OPT_RESTART, ldap.OPT_ON) |
|
97 | ldap.set_option(ldap.OPT_RESTART, ldap.OPT_ON) | |
94 | ldap.set_option(ldap.OPT_TIMEOUT, 20) |
|
98 | ldap.set_option(ldap.OPT_TIMEOUT, 20) | |
95 | ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 10) |
|
99 | ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 10) | |
96 | ldap.set_option(ldap.OPT_TIMELIMIT, 15) |
|
100 | ldap.set_option(ldap.OPT_TIMELIMIT, 15) | |
97 | if self.TLS_KIND != 'PLAIN': |
|
101 | if self.TLS_KIND != 'PLAIN': | |
98 | ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, self.TLS_REQCERT) |
|
102 | ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, self.TLS_REQCERT) | |
99 | server = ldap.initialize(self.LDAP_SERVER) |
|
103 | server = ldap.initialize(self.LDAP_SERVER) | |
100 | if self.ldap_version == 2: |
|
104 | if self.ldap_version == 2: | |
101 | server.protocol = ldap.VERSION2 |
|
105 | server.protocol = ldap.VERSION2 | |
102 | else: |
|
106 | else: | |
103 | server.protocol = ldap.VERSION3 |
|
107 | server.protocol = ldap.VERSION3 | |
104 |
|
108 | |||
105 | if self.TLS_KIND == 'START_TLS': |
|
109 | if self.TLS_KIND == 'START_TLS': | |
106 | server.start_tls_s() |
|
110 | server.start_tls_s() | |
107 |
|
111 | |||
108 | if self.LDAP_BIND_DN and self.LDAP_BIND_PASS: |
|
112 | if self.LDAP_BIND_DN and self.LDAP_BIND_PASS: | |
109 | server.simple_bind_s(self.LDAP_BIND_DN, self.LDAP_BIND_PASS) |
|
113 | server.simple_bind_s(self.LDAP_BIND_DN, self.LDAP_BIND_PASS) | |
110 |
|
114 | |||
111 | filt = '(&%s(%s=%s))' % (self.LDAP_FILTER, self.attr_login, |
|
115 | filt = '(&%s(%s=%s))' % (self.LDAP_FILTER, self.attr_login, | |
112 | username) |
|
116 | username) | |
113 | log.debug("Authenticating %r filt %s at %s", self.BASE_DN, |
|
117 | log.debug("Authenticating %r filt %s at %s", self.BASE_DN, | |
114 | filt, self.LDAP_SERVER) |
|
118 | filt, self.LDAP_SERVER) | |
115 | lobjects = server.search_ext_s(self.BASE_DN, self.SEARCH_SCOPE, |
|
119 | lobjects = server.search_ext_s(self.BASE_DN, self.SEARCH_SCOPE, | |
116 | filt) |
|
120 | filt) | |
117 |
|
121 | |||
118 | if not lobjects: |
|
122 | if not lobjects: | |
119 | raise ldap.NO_SUCH_OBJECT() |
|
123 | raise ldap.NO_SUCH_OBJECT() | |
120 |
|
124 | |||
121 | for (dn, _attrs) in lobjects: |
|
125 | for (dn, _attrs) in lobjects: | |
122 | if dn is None: |
|
126 | if dn is None: | |
123 | continue |
|
127 | continue | |
124 |
|
128 | |||
125 | try: |
|
129 | try: | |
126 | server.simple_bind_s(dn, password) |
|
130 | server.simple_bind_s(dn, password) | |
127 | attrs = server.search_ext_s(dn, ldap.SCOPE_BASE, |
|
131 | attrs = server.search_ext_s(dn, ldap.SCOPE_BASE, | |
128 | '(objectClass=*)')[0][1] |
|
132 | '(objectClass=*)')[0][1] | |
129 | break |
|
133 | break | |
130 |
|
134 | |||
131 | except ldap.INVALID_CREDENTIALS, e: |
|
135 | except ldap.INVALID_CREDENTIALS, e: | |
132 | log.debug("LDAP rejected password for user '%s' (%s): %s", |
|
136 | log.debug("LDAP rejected password for user '%s' (%s): %s", | |
133 | uid, username, dn) |
|
137 | uid, username, dn) | |
134 |
|
138 | |||
135 | else: |
|
139 | else: | |
136 | log.debug("No matching LDAP objects for authentication " |
|
140 | log.debug("No matching LDAP objects for authentication " | |
137 | "of '%s' (%s)", uid, username) |
|
141 | "of '%s' (%s)", uid, username) | |
138 | raise LdapPasswordError() |
|
142 | raise LdapPasswordError() | |
139 |
|
143 | |||
140 | except ldap.NO_SUCH_OBJECT, e: |
|
144 | except ldap.NO_SUCH_OBJECT, e: | |
141 | log.debug("LDAP says no such user '%s' (%s)", uid, username) |
|
145 | log.debug("LDAP says no such user '%s' (%s)", uid, username) | |
142 | raise LdapUsernameError() |
|
146 | raise LdapUsernameError() | |
143 | except ldap.SERVER_DOWN, e: |
|
147 | except ldap.SERVER_DOWN, e: | |
144 | raise LdapConnectionError("LDAP can't access " |
|
148 | raise LdapConnectionError("LDAP can't access " | |
145 | "authentication server") |
|
149 | "authentication server") | |
146 |
|
150 | |||
147 | return (dn, attrs) |
|
151 | return (dn, attrs) |
@@ -1,411 +1,412 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """ |
|
2 | """ | |
3 | rhodecode.lib.celerylib.tasks |
|
3 | rhodecode.lib.celerylib.tasks | |
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
5 |
|
5 | |||
6 | RhodeCode task modules, containing all task that suppose to be run |
|
6 | RhodeCode task modules, containing all task that suppose to be run | |
7 | by celery daemon |
|
7 | by celery daemon | |
8 |
|
8 | |||
9 | :created_on: Oct 6, 2010 |
|
9 | :created_on: Oct 6, 2010 | |
10 | :author: marcink |
|
10 | :author: marcink | |
11 | :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> |
|
11 | :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> | |
12 | :license: GPLv3, see COPYING for more details. |
|
12 | :license: GPLv3, see COPYING for more details. | |
13 | """ |
|
13 | """ | |
14 | # This program is free software: you can redistribute it and/or modify |
|
14 | # This program is free software: you can redistribute it and/or modify | |
15 | # it under the terms of the GNU General Public License as published by |
|
15 | # it under the terms of the GNU General Public License as published by | |
16 | # the Free Software Foundation, either version 3 of the License, or |
|
16 | # the Free Software Foundation, either version 3 of the License, or | |
17 | # (at your option) any later version. |
|
17 | # (at your option) any later version. | |
18 | # |
|
18 | # | |
19 | # This program is distributed in the hope that it will be useful, |
|
19 | # This program is distributed in the hope that it will be useful, | |
20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
22 | # GNU General Public License for more details. |
|
22 | # GNU General Public License for more details. | |
23 | # |
|
23 | # | |
24 | # You should have received a copy of the GNU General Public License |
|
24 | # You should have received a copy of the GNU General Public License | |
25 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
25 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
26 | from celery.decorators import task |
|
26 | from celery.decorators import task | |
27 |
|
27 | |||
28 | import os |
|
28 | import os | |
29 | import traceback |
|
29 | import traceback | |
30 | import logging |
|
30 | import logging | |
31 | from os.path import dirname as dn, join as jn |
|
31 | from os.path import dirname as dn, join as jn | |
32 |
|
32 | |||
33 | from time import mktime |
|
33 | from time import mktime | |
34 | from operator import itemgetter |
|
34 | from operator import itemgetter | |
35 | from string import lower |
|
35 | from string import lower | |
36 |
|
36 | |||
37 | from pylons import config, url |
|
37 | from pylons import config, url | |
38 | from pylons.i18n.translation import _ |
|
38 | from pylons.i18n.translation import _ | |
39 |
|
39 | |||
40 | from rhodecode.lib import LANGUAGES_EXTENSIONS_MAP, safe_str |
|
40 | from rhodecode.lib import LANGUAGES_EXTENSIONS_MAP, safe_str | |
41 | from rhodecode.lib.celerylib import run_task, locked_task, str2bool, \ |
|
41 | from rhodecode.lib.celerylib import run_task, locked_task, str2bool, \ | |
42 | __get_lockkey, LockHeld, DaemonLock |
|
42 | __get_lockkey, LockHeld, DaemonLock | |
43 | from rhodecode.lib.helpers import person |
|
43 | from rhodecode.lib.helpers import person | |
44 | from rhodecode.lib.smtp_mailer import SmtpMailer |
|
44 | from rhodecode.lib.smtp_mailer import SmtpMailer | |
45 | from rhodecode.lib.utils import add_cache |
|
45 | from rhodecode.lib.utils import add_cache | |
46 | from rhodecode.lib.compat import json, OrderedDict |
|
46 | from rhodecode.lib.compat import json, OrderedDict | |
47 |
|
47 | |||
48 | from rhodecode.model import init_model |
|
48 | from rhodecode.model import init_model | |
49 | from rhodecode.model import meta |
|
49 | from rhodecode.model import meta | |
50 | from rhodecode.model.db import RhodeCodeUi, Statistics, Repository |
|
50 | from rhodecode.model.db import RhodeCodeUi, Statistics, Repository | |
51 |
|
51 | |||
52 | from vcs.backends import get_repo |
|
52 | from vcs.backends import get_repo | |
53 |
|
53 | |||
54 | from sqlalchemy import engine_from_config |
|
54 | from sqlalchemy import engine_from_config | |
55 |
|
55 | |||
56 | add_cache(config) |
|
56 | add_cache(config) | |
57 |
|
57 | |||
58 |
|
58 | |||
59 |
|
59 | |||
60 | __all__ = ['whoosh_index', 'get_commits_stats', |
|
60 | __all__ = ['whoosh_index', 'get_commits_stats', | |
61 | 'reset_user_password', 'send_email'] |
|
61 | 'reset_user_password', 'send_email'] | |
62 |
|
62 | |||
63 | CELERY_ON = str2bool(config['app_conf'].get('use_celery')) |
|
63 | CELERY_ON = str2bool(config['app_conf'].get('use_celery')) | |
64 |
|
64 | |||
65 |
|
65 | |||
66 | def get_session(): |
|
66 | def get_session(): | |
67 | if CELERY_ON: |
|
67 | if CELERY_ON: | |
68 | engine = engine_from_config(config, 'sqlalchemy.db1.') |
|
68 | engine = engine_from_config(config, 'sqlalchemy.db1.') | |
69 | init_model(engine) |
|
69 | init_model(engine) | |
70 | sa = meta.Session() |
|
70 | sa = meta.Session() | |
71 | return sa |
|
71 | return sa | |
72 |
|
72 | |||
73 |
|
73 | |||
74 | def get_repos_path(): |
|
74 | def get_repos_path(): | |
75 | sa = get_session() |
|
75 | sa = get_session() | |
76 | q = sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one() |
|
76 | q = sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one() | |
77 | return q.ui_value |
|
77 | return q.ui_value | |
78 |
|
78 | |||
79 |
|
79 | |||
80 | @task(ignore_result=True) |
|
80 | @task(ignore_result=True) | |
81 | @locked_task |
|
81 | @locked_task | |
82 | def whoosh_index(repo_location, full_index): |
|
82 | def whoosh_index(repo_location, full_index): | |
83 | #log = whoosh_index.get_logger() |
|
83 | #log = whoosh_index.get_logger() | |
84 | from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon |
|
84 | from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon | |
85 | index_location = config['index_dir'] |
|
85 | index_location = config['index_dir'] | |
86 | WhooshIndexingDaemon(index_location=index_location, |
|
86 | WhooshIndexingDaemon(index_location=index_location, | |
87 | repo_location=repo_location, sa=get_session())\ |
|
87 | repo_location=repo_location, sa=get_session())\ | |
88 | .run(full_index=full_index) |
|
88 | .run(full_index=full_index) | |
89 |
|
89 | |||
90 |
|
90 | |||
91 | @task(ignore_result=True) |
|
91 | @task(ignore_result=True) | |
92 | def get_commits_stats(repo_name, ts_min_y, ts_max_y): |
|
92 | def get_commits_stats(repo_name, ts_min_y, ts_max_y): | |
93 | try: |
|
93 | try: | |
94 | log = get_commits_stats.get_logger() |
|
94 | log = get_commits_stats.get_logger() | |
95 | except: |
|
95 | except: | |
96 | log = logging.getLogger(__name__) |
|
96 | log = logging.getLogger(__name__) | |
97 |
|
97 | |||
98 | lockkey = __get_lockkey('get_commits_stats', repo_name, ts_min_y, |
|
98 | lockkey = __get_lockkey('get_commits_stats', repo_name, ts_min_y, | |
99 | ts_max_y) |
|
99 | ts_max_y) | |
100 | lockkey_path = config['here'] |
|
100 | lockkey_path = config['here'] | |
101 |
|
101 | |||
102 | log.info('running task with lockkey %s', lockkey) |
|
102 | log.info('running task with lockkey %s', lockkey) | |
103 | try: |
|
103 | try: | |
104 | lock = l = DaemonLock(file_=jn(lockkey_path, lockkey)) |
|
104 | lock = l = DaemonLock(file_=jn(lockkey_path, lockkey)) | |
105 |
|
105 | |||
106 | #for js data compatibilty cleans the key for person from ' |
|
106 | #for js data compatibilty cleans the key for person from ' | |
107 | akc = lambda k: person(k).replace('"', "") |
|
107 | akc = lambda k: person(k).replace('"', "") | |
108 |
|
108 | |||
109 | co_day_auth_aggr = {} |
|
109 | co_day_auth_aggr = {} | |
110 | commits_by_day_aggregate = {} |
|
110 | commits_by_day_aggregate = {} | |
111 | repos_path = get_repos_path() |
|
111 | repos_path = get_repos_path() | |
112 | repo = get_repo(safe_str(os.path.join(repos_path, repo_name))) |
|
112 | repo = get_repo(safe_str(os.path.join(repos_path, repo_name))) | |
113 | repo_size = len(repo.revisions) |
|
113 | repo_size = len(repo.revisions) | |
114 | #return if repo have no revisions |
|
114 | #return if repo have no revisions | |
115 | if repo_size < 1: |
|
115 | if repo_size < 1: | |
116 | lock.release() |
|
116 | lock.release() | |
117 | return True |
|
117 | return True | |
118 |
|
118 | |||
119 | skip_date_limit = True |
|
119 | skip_date_limit = True | |
120 | parse_limit = int(config['app_conf'].get('commit_parse_limit')) |
|
120 | parse_limit = int(config['app_conf'].get('commit_parse_limit')) | |
121 | last_rev = 0 |
|
121 | last_rev = 0 | |
122 | last_cs = None |
|
122 | last_cs = None | |
123 | timegetter = itemgetter('time') |
|
123 | timegetter = itemgetter('time') | |
124 |
|
124 | |||
125 | sa = get_session() |
|
125 | sa = get_session() | |
126 |
|
126 | |||
127 | dbrepo = sa.query(Repository)\ |
|
127 | dbrepo = sa.query(Repository)\ | |
128 | .filter(Repository.repo_name == repo_name).scalar() |
|
128 | .filter(Repository.repo_name == repo_name).scalar() | |
129 | cur_stats = sa.query(Statistics)\ |
|
129 | cur_stats = sa.query(Statistics)\ | |
130 | .filter(Statistics.repository == dbrepo).scalar() |
|
130 | .filter(Statistics.repository == dbrepo).scalar() | |
131 |
|
131 | |||
132 | if cur_stats is not None: |
|
132 | if cur_stats is not None: | |
133 | last_rev = cur_stats.stat_on_revision |
|
133 | last_rev = cur_stats.stat_on_revision | |
134 |
|
134 | |||
135 | if last_rev == repo.get_changeset().revision and repo_size > 1: |
|
135 | if last_rev == repo.get_changeset().revision and repo_size > 1: | |
136 | #pass silently without any work if we're not on first revision or |
|
136 | #pass silently without any work if we're not on first revision or | |
137 | #current state of parsing revision(from db marker) is the |
|
137 | #current state of parsing revision(from db marker) is the | |
138 | #last revision |
|
138 | #last revision | |
139 | lock.release() |
|
139 | lock.release() | |
140 | return True |
|
140 | return True | |
141 |
|
141 | |||
142 | if cur_stats: |
|
142 | if cur_stats: | |
143 | commits_by_day_aggregate = OrderedDict(json.loads( |
|
143 | commits_by_day_aggregate = OrderedDict(json.loads( | |
144 | cur_stats.commit_activity_combined)) |
|
144 | cur_stats.commit_activity_combined)) | |
145 | co_day_auth_aggr = json.loads(cur_stats.commit_activity) |
|
145 | co_day_auth_aggr = json.loads(cur_stats.commit_activity) | |
146 |
|
146 | |||
147 | log.debug('starting parsing %s', parse_limit) |
|
147 | log.debug('starting parsing %s', parse_limit) | |
148 | lmktime = mktime |
|
148 | lmktime = mktime | |
149 |
|
149 | |||
150 | last_rev = last_rev + 1 if last_rev > 0 else last_rev |
|
150 | last_rev = last_rev + 1 if last_rev > 0 else last_rev | |
151 |
|
151 | |||
152 | for cs in repo[last_rev:last_rev + parse_limit]: |
|
152 | for cs in repo[last_rev:last_rev + parse_limit]: | |
153 | last_cs = cs # remember last parsed changeset |
|
153 | last_cs = cs # remember last parsed changeset | |
154 | k = lmktime([cs.date.timetuple()[0], cs.date.timetuple()[1], |
|
154 | k = lmktime([cs.date.timetuple()[0], cs.date.timetuple()[1], | |
155 | cs.date.timetuple()[2], 0, 0, 0, 0, 0, 0]) |
|
155 | cs.date.timetuple()[2], 0, 0, 0, 0, 0, 0]) | |
156 |
|
156 | |||
157 | if akc(cs.author) in co_day_auth_aggr: |
|
157 | if akc(cs.author) in co_day_auth_aggr: | |
158 | try: |
|
158 | try: | |
159 | l = [timegetter(x) for x in |
|
159 | l = [timegetter(x) for x in | |
160 | co_day_auth_aggr[akc(cs.author)]['data']] |
|
160 | co_day_auth_aggr[akc(cs.author)]['data']] | |
161 | time_pos = l.index(k) |
|
161 | time_pos = l.index(k) | |
162 | except ValueError: |
|
162 | except ValueError: | |
163 | time_pos = False |
|
163 | time_pos = False | |
164 |
|
164 | |||
165 | if time_pos >= 0 and time_pos is not False: |
|
165 | if time_pos >= 0 and time_pos is not False: | |
166 |
|
166 | |||
167 | datadict = \ |
|
167 | datadict = \ | |
168 | co_day_auth_aggr[akc(cs.author)]['data'][time_pos] |
|
168 | co_day_auth_aggr[akc(cs.author)]['data'][time_pos] | |
169 |
|
169 | |||
170 | datadict["commits"] += 1 |
|
170 | datadict["commits"] += 1 | |
171 | datadict["added"] += len(cs.added) |
|
171 | datadict["added"] += len(cs.added) | |
172 | datadict["changed"] += len(cs.changed) |
|
172 | datadict["changed"] += len(cs.changed) | |
173 | datadict["removed"] += len(cs.removed) |
|
173 | datadict["removed"] += len(cs.removed) | |
174 |
|
174 | |||
175 | else: |
|
175 | else: | |
176 | if k >= ts_min_y and k <= ts_max_y or skip_date_limit: |
|
176 | if k >= ts_min_y and k <= ts_max_y or skip_date_limit: | |
177 |
|
177 | |||
178 | datadict = {"time": k, |
|
178 | datadict = {"time": k, | |
179 | "commits": 1, |
|
179 | "commits": 1, | |
180 | "added": len(cs.added), |
|
180 | "added": len(cs.added), | |
181 | "changed": len(cs.changed), |
|
181 | "changed": len(cs.changed), | |
182 | "removed": len(cs.removed), |
|
182 | "removed": len(cs.removed), | |
183 | } |
|
183 | } | |
184 | co_day_auth_aggr[akc(cs.author)]['data']\ |
|
184 | co_day_auth_aggr[akc(cs.author)]['data']\ | |
185 | .append(datadict) |
|
185 | .append(datadict) | |
186 |
|
186 | |||
187 | else: |
|
187 | else: | |
188 | if k >= ts_min_y and k <= ts_max_y or skip_date_limit: |
|
188 | if k >= ts_min_y and k <= ts_max_y or skip_date_limit: | |
189 | co_day_auth_aggr[akc(cs.author)] = { |
|
189 | co_day_auth_aggr[akc(cs.author)] = { | |
190 | "label": akc(cs.author), |
|
190 | "label": akc(cs.author), | |
191 | "data": [{"time":k, |
|
191 | "data": [{"time":k, | |
192 | "commits":1, |
|
192 | "commits":1, | |
193 | "added":len(cs.added), |
|
193 | "added":len(cs.added), | |
194 | "changed":len(cs.changed), |
|
194 | "changed":len(cs.changed), | |
195 | "removed":len(cs.removed), |
|
195 | "removed":len(cs.removed), | |
196 | }], |
|
196 | }], | |
197 | "schema": ["commits"], |
|
197 | "schema": ["commits"], | |
198 | } |
|
198 | } | |
199 |
|
199 | |||
200 | #gather all data by day |
|
200 | #gather all data by day | |
201 | if k in commits_by_day_aggregate: |
|
201 | if k in commits_by_day_aggregate: | |
202 | commits_by_day_aggregate[k] += 1 |
|
202 | commits_by_day_aggregate[k] += 1 | |
203 | else: |
|
203 | else: | |
204 | commits_by_day_aggregate[k] = 1 |
|
204 | commits_by_day_aggregate[k] = 1 | |
205 |
|
205 | |||
206 | overview_data = sorted(commits_by_day_aggregate.items(), |
|
206 | overview_data = sorted(commits_by_day_aggregate.items(), | |
207 | key=itemgetter(0)) |
|
207 | key=itemgetter(0)) | |
208 |
|
208 | |||
209 | if not co_day_auth_aggr: |
|
209 | if not co_day_auth_aggr: | |
210 | co_day_auth_aggr[akc(repo.contact)] = { |
|
210 | co_day_auth_aggr[akc(repo.contact)] = { | |
211 | "label": akc(repo.contact), |
|
211 | "label": akc(repo.contact), | |
212 | "data": [0, 1], |
|
212 | "data": [0, 1], | |
213 | "schema": ["commits"], |
|
213 | "schema": ["commits"], | |
214 | } |
|
214 | } | |
215 |
|
215 | |||
216 | stats = cur_stats if cur_stats else Statistics() |
|
216 | stats = cur_stats if cur_stats else Statistics() | |
217 | stats.commit_activity = json.dumps(co_day_auth_aggr) |
|
217 | stats.commit_activity = json.dumps(co_day_auth_aggr) | |
218 | stats.commit_activity_combined = json.dumps(overview_data) |
|
218 | stats.commit_activity_combined = json.dumps(overview_data) | |
219 |
|
219 | |||
220 | log.debug('last revison %s', last_rev) |
|
220 | log.debug('last revison %s', last_rev) | |
221 | leftovers = len(repo.revisions[last_rev:]) |
|
221 | leftovers = len(repo.revisions[last_rev:]) | |
222 | log.debug('revisions to parse %s', leftovers) |
|
222 | log.debug('revisions to parse %s', leftovers) | |
223 |
|
223 | |||
224 | if last_rev == 0 or leftovers < parse_limit: |
|
224 | if last_rev == 0 or leftovers < parse_limit: | |
225 | log.debug('getting code trending stats') |
|
225 | log.debug('getting code trending stats') | |
226 | stats.languages = json.dumps(__get_codes_stats(repo_name)) |
|
226 | stats.languages = json.dumps(__get_codes_stats(repo_name)) | |
227 |
|
227 | |||
228 | try: |
|
228 | try: | |
229 | stats.repository = dbrepo |
|
229 | stats.repository = dbrepo | |
230 | stats.stat_on_revision = last_cs.revision if last_cs else 0 |
|
230 | stats.stat_on_revision = last_cs.revision if last_cs else 0 | |
231 | sa.add(stats) |
|
231 | sa.add(stats) | |
232 | sa.commit() |
|
232 | sa.commit() | |
233 | except: |
|
233 | except: | |
234 | log.error(traceback.format_exc()) |
|
234 | log.error(traceback.format_exc()) | |
235 | sa.rollback() |
|
235 | sa.rollback() | |
236 | lock.release() |
|
236 | lock.release() | |
237 | return False |
|
237 | return False | |
238 |
|
238 | |||
239 | #final release |
|
239 | #final release | |
240 | lock.release() |
|
240 | lock.release() | |
241 |
|
241 | |||
242 | #execute another task if celery is enabled |
|
242 | #execute another task if celery is enabled | |
243 | if len(repo.revisions) > 1 and CELERY_ON: |
|
243 | if len(repo.revisions) > 1 and CELERY_ON: | |
244 | run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y) |
|
244 | run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y) | |
245 | return True |
|
245 | return True | |
246 | except LockHeld: |
|
246 | except LockHeld: | |
247 | log.info('LockHeld') |
|
247 | log.info('LockHeld') | |
248 | return 'Task with key %s already running' % lockkey |
|
248 | return 'Task with key %s already running' % lockkey | |
249 |
|
249 | |||
250 | @task(ignore_result=True) |
|
250 | @task(ignore_result=True) | |
251 | def send_password_link(user_email): |
|
251 | def send_password_link(user_email): | |
252 | try: |
|
252 | try: | |
253 | log = reset_user_password.get_logger() |
|
253 | log = reset_user_password.get_logger() | |
254 | except: |
|
254 | except: | |
255 | log = logging.getLogger(__name__) |
|
255 | log = logging.getLogger(__name__) | |
256 |
|
256 | |||
257 | from rhodecode.lib import auth |
|
257 | from rhodecode.lib import auth | |
258 | from rhodecode.model.db import User |
|
258 | from rhodecode.model.db import User | |
259 |
|
259 | |||
260 | try: |
|
260 | try: | |
261 | sa = get_session() |
|
261 | sa = get_session() | |
262 | user = sa.query(User).filter(User.email == user_email).scalar() |
|
262 | user = sa.query(User).filter(User.email == user_email).scalar() | |
263 |
|
263 | |||
264 | if user: |
|
264 | if user: | |
265 | link = url('reset_password_confirmation', key=user.api_key, |
|
265 | link = url('reset_password_confirmation', key=user.api_key, | |
266 | qualified=True) |
|
266 | qualified=True) | |
267 | tmpl = """ |
|
267 | tmpl = """ | |
268 | Hello %s |
|
268 | Hello %s | |
269 |
|
269 | |||
270 | We received a request to create a new password for your account. |
|
270 | We received a request to create a new password for your account. | |
271 |
|
271 | |||
272 | You can generate it by clicking following URL: |
|
272 | You can generate it by clicking following URL: | |
273 |
|
273 | |||
274 | %s |
|
274 | %s | |
275 |
|
275 | |||
276 | If you didn't request new password please ignore this email. |
|
276 | If you didn't request new password please ignore this email. | |
277 | """ |
|
277 | """ | |
278 | run_task(send_email, user_email, |
|
278 | run_task(send_email, user_email, | |
279 | "RhodeCode password reset link", |
|
279 | "RhodeCode password reset link", | |
280 | tmpl % (user.short_contact, link)) |
|
280 | tmpl % (user.short_contact, link)) | |
281 | log.info('send new password mail to %s', user_email) |
|
281 | log.info('send new password mail to %s', user_email) | |
282 |
|
282 | |||
283 | except: |
|
283 | except: | |
284 | log.error('Failed to update user password') |
|
284 | log.error('Failed to update user password') | |
285 | log.error(traceback.format_exc()) |
|
285 | log.error(traceback.format_exc()) | |
286 | return False |
|
286 | return False | |
287 |
|
287 | |||
288 | return True |
|
288 | return True | |
289 |
|
289 | |||
290 | @task(ignore_result=True) |
|
290 | @task(ignore_result=True) | |
291 | def reset_user_password(user_email): |
|
291 | def reset_user_password(user_email): | |
292 | try: |
|
292 | try: | |
293 | log = reset_user_password.get_logger() |
|
293 | log = reset_user_password.get_logger() | |
294 | except: |
|
294 | except: | |
295 | log = logging.getLogger(__name__) |
|
295 | log = logging.getLogger(__name__) | |
296 |
|
296 | |||
297 | from rhodecode.lib import auth |
|
297 | from rhodecode.lib import auth | |
298 | from rhodecode.model.db import User |
|
298 | from rhodecode.model.db import User | |
299 |
|
299 | |||
300 | try: |
|
300 | try: | |
301 | try: |
|
301 | try: | |
302 | sa = get_session() |
|
302 | sa = get_session() | |
303 | user = sa.query(User).filter(User.email == user_email).scalar() |
|
303 | user = sa.query(User).filter(User.email == user_email).scalar() | |
304 | new_passwd = auth.PasswordGenerator().gen_password(8, |
|
304 | new_passwd = auth.PasswordGenerator().gen_password(8, | |
305 | auth.PasswordGenerator.ALPHABETS_BIG_SMALL) |
|
305 | auth.PasswordGenerator.ALPHABETS_BIG_SMALL) | |
306 | if user: |
|
306 | if user: | |
307 | user.password = auth.get_crypt_password(new_passwd) |
|
307 | user.password = auth.get_crypt_password(new_passwd) | |
308 | user.api_key = auth.generate_api_key(user.username) |
|
308 | user.api_key = auth.generate_api_key(user.username) | |
309 | sa.add(user) |
|
309 | sa.add(user) | |
310 | sa.commit() |
|
310 | sa.commit() | |
311 | log.info('change password for %s', user_email) |
|
311 | log.info('change password for %s', user_email) | |
312 | if new_passwd is None: |
|
312 | if new_passwd is None: | |
313 | raise Exception('unable to generate new password') |
|
313 | raise Exception('unable to generate new password') | |
314 |
|
314 | |||
315 | except: |
|
315 | except: | |
316 | log.error(traceback.format_exc()) |
|
316 | log.error(traceback.format_exc()) | |
317 | sa.rollback() |
|
317 | sa.rollback() | |
318 |
|
318 | |||
319 | run_task(send_email, user_email, |
|
319 | run_task(send_email, user_email, | |
320 | "Your new RhodeCode password", |
|
320 | "Your new RhodeCode password", | |
321 | 'Your new RhodeCode password:%s' % (new_passwd)) |
|
321 | 'Your new RhodeCode password:%s' % (new_passwd)) | |
322 | log.info('send new password mail to %s', user_email) |
|
322 | log.info('send new password mail to %s', user_email) | |
323 |
|
323 | |||
324 | except: |
|
324 | except: | |
325 | log.error('Failed to update user password') |
|
325 | log.error('Failed to update user password') | |
326 | log.error(traceback.format_exc()) |
|
326 | log.error(traceback.format_exc()) | |
327 |
|
327 | |||
328 | return True |
|
328 | return True | |
329 |
|
329 | |||
330 |
|
330 | |||
331 | @task(ignore_result=True) |
|
331 | @task(ignore_result=True) | |
332 | def send_email(recipients, subject, body): |
|
332 | def send_email(recipients, subject, body): | |
333 | """ |
|
333 | """ | |
334 | Sends an email with defined parameters from the .ini files. |
|
334 | Sends an email with defined parameters from the .ini files. | |
335 |
|
335 | |||
336 | :param recipients: list of recipients, it this is empty the defined email |
|
336 | :param recipients: list of recipients, it this is empty the defined email | |
337 | address from field 'email_to' is used instead |
|
337 | address from field 'email_to' is used instead | |
338 | :param subject: subject of the mail |
|
338 | :param subject: subject of the mail | |
339 | :param body: body of the mail |
|
339 | :param body: body of the mail | |
340 | """ |
|
340 | """ | |
341 | try: |
|
341 | try: | |
342 | log = send_email.get_logger() |
|
342 | log = send_email.get_logger() | |
343 | except: |
|
343 | except: | |
344 | log = logging.getLogger(__name__) |
|
344 | log = logging.getLogger(__name__) | |
345 |
|
345 | |||
346 | email_config = config |
|
346 | email_config = config | |
347 |
|
347 | |||
348 | if not recipients: |
|
348 | if not recipients: | |
349 | recipients = [email_config.get('email_to')] |
|
349 | recipients = [email_config.get('email_to')] | |
350 |
|
350 | |||
351 | mail_from = email_config.get('app_email_from') |
|
351 | mail_from = email_config.get('app_email_from') | |
352 | user = email_config.get('smtp_username') |
|
352 | user = email_config.get('smtp_username') | |
353 | passwd = email_config.get('smtp_password') |
|
353 | passwd = email_config.get('smtp_password') | |
354 | mail_server = email_config.get('smtp_server') |
|
354 | mail_server = email_config.get('smtp_server') | |
355 | mail_port = email_config.get('smtp_port') |
|
355 | mail_port = email_config.get('smtp_port') | |
356 | tls = str2bool(email_config.get('smtp_use_tls')) |
|
356 | tls = str2bool(email_config.get('smtp_use_tls')) | |
357 | ssl = str2bool(email_config.get('smtp_use_ssl')) |
|
357 | ssl = str2bool(email_config.get('smtp_use_ssl')) | |
358 | debug = str2bool(config.get('debug')) |
|
358 | debug = str2bool(config.get('debug')) | |
|
359 | smtp_auth = email_config.get('smtp_auth') | |||
359 |
|
360 | |||
360 | try: |
|
361 | try: | |
361 | m = SmtpMailer(mail_from, user, passwd, mail_server, |
|
362 | m = SmtpMailer(mail_from, user, passwd, mail_server,smtp_auth, | |
362 | mail_port, ssl, tls, debug=debug) |
|
363 | mail_port, ssl, tls, debug=debug) | |
363 | m.send(recipients, subject, body) |
|
364 | m.send(recipients, subject, body) | |
364 | except: |
|
365 | except: | |
365 | log.error('Mail sending failed') |
|
366 | log.error('Mail sending failed') | |
366 | log.error(traceback.format_exc()) |
|
367 | log.error(traceback.format_exc()) | |
367 | return False |
|
368 | return False | |
368 | return True |
|
369 | return True | |
369 |
|
370 | |||
370 |
|
371 | |||
371 | @task(ignore_result=True) |
|
372 | @task(ignore_result=True) | |
372 | def create_repo_fork(form_data, cur_user): |
|
373 | def create_repo_fork(form_data, cur_user): | |
373 | from rhodecode.model.repo import RepoModel |
|
374 | from rhodecode.model.repo import RepoModel | |
374 | from vcs import get_backend |
|
375 | from vcs import get_backend | |
375 |
|
376 | |||
376 | try: |
|
377 | try: | |
377 | log = create_repo_fork.get_logger() |
|
378 | log = create_repo_fork.get_logger() | |
378 | except: |
|
379 | except: | |
379 | log = logging.getLogger(__name__) |
|
380 | log = logging.getLogger(__name__) | |
380 |
|
381 | |||
381 | repo_model = RepoModel(get_session()) |
|
382 | repo_model = RepoModel(get_session()) | |
382 | repo_model.create(form_data, cur_user, just_db=True, fork=True) |
|
383 | repo_model.create(form_data, cur_user, just_db=True, fork=True) | |
383 | repo_name = form_data['repo_name'] |
|
384 | repo_name = form_data['repo_name'] | |
384 | repos_path = get_repos_path() |
|
385 | repos_path = get_repos_path() | |
385 | repo_path = os.path.join(repos_path, repo_name) |
|
386 | repo_path = os.path.join(repos_path, repo_name) | |
386 | repo_fork_path = os.path.join(repos_path, form_data['fork_name']) |
|
387 | repo_fork_path = os.path.join(repos_path, form_data['fork_name']) | |
387 | alias = form_data['repo_type'] |
|
388 | alias = form_data['repo_type'] | |
388 |
|
389 | |||
389 | log.info('creating repo fork %s as %s', repo_name, repo_path) |
|
390 | log.info('creating repo fork %s as %s', repo_name, repo_path) | |
390 | backend = get_backend(alias) |
|
391 | backend = get_backend(alias) | |
391 | backend(str(repo_fork_path), create=True, src_url=str(repo_path)) |
|
392 | backend(str(repo_fork_path), create=True, src_url=str(repo_path)) | |
392 |
|
393 | |||
393 |
|
394 | |||
394 | def __get_codes_stats(repo_name): |
|
395 | def __get_codes_stats(repo_name): | |
395 | repos_path = get_repos_path() |
|
396 | repos_path = get_repos_path() | |
396 | repo = get_repo(safe_str(os.path.join(repos_path, repo_name))) |
|
397 | repo = get_repo(safe_str(os.path.join(repos_path, repo_name))) | |
397 | tip = repo.get_changeset() |
|
398 | tip = repo.get_changeset() | |
398 | code_stats = {} |
|
399 | code_stats = {} | |
399 |
|
400 | |||
400 | def aggregate(cs): |
|
401 | def aggregate(cs): | |
401 | for f in cs[2]: |
|
402 | for f in cs[2]: | |
402 | ext = lower(f.extension) |
|
403 | ext = lower(f.extension) | |
403 | if ext in LANGUAGES_EXTENSIONS_MAP.keys() and not f.is_binary: |
|
404 | if ext in LANGUAGES_EXTENSIONS_MAP.keys() and not f.is_binary: | |
404 | if ext in code_stats: |
|
405 | if ext in code_stats: | |
405 | code_stats[ext] += 1 |
|
406 | code_stats[ext] += 1 | |
406 | else: |
|
407 | else: | |
407 | code_stats[ext] = 1 |
|
408 | code_stats[ext] = 1 | |
408 |
|
409 | |||
409 | map(aggregate, tip.walk('/')) |
|
410 | map(aggregate, tip.walk('/')) | |
410 |
|
411 | |||
411 | return code_stats or {} |
|
412 | return code_stats or {} |
@@ -1,289 +1,291 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """ |
|
2 | """ | |
3 | rhodecode.lib.middleware.simplegit |
|
3 | rhodecode.lib.middleware.simplegit | |
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
5 |
|
5 | |||
6 | SimpleGit middleware for handling git protocol request (push/clone etc.) |
|
6 | SimpleGit middleware for handling git protocol request (push/clone etc.) | |
7 | It's implemented with basic auth function |
|
7 | It's implemented with basic auth function | |
8 |
|
8 | |||
9 | :created_on: Apr 28, 2010 |
|
9 | :created_on: Apr 28, 2010 | |
10 | :author: marcink |
|
10 | :author: marcink | |
11 | :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> |
|
11 | :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |
12 | :license: GPLv3, see COPYING for more details. |
|
12 | :license: GPLv3, see COPYING for more details. | |
13 | """ |
|
13 | """ | |
14 | # This program is free software: you can redistribute it and/or modify |
|
14 | # This program is free software: you can redistribute it and/or modify | |
15 | # it under the terms of the GNU General Public License as published by |
|
15 | # it under the terms of the GNU General Public License as published by | |
16 | # the Free Software Foundation, either version 3 of the License, or |
|
16 | # the Free Software Foundation, either version 3 of the License, or | |
17 | # (at your option) any later version. |
|
17 | # (at your option) any later version. | |
18 | # |
|
18 | # | |
19 | # This program is distributed in the hope that it will be useful, |
|
19 | # This program is distributed in the hope that it will be useful, | |
20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
22 | # GNU General Public License for more details. |
|
22 | # GNU General Public License for more details. | |
23 | # |
|
23 | # | |
24 | # You should have received a copy of the GNU General Public License |
|
24 | # You should have received a copy of the GNU General Public License | |
25 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
25 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
26 |
|
26 | |||
27 | import os |
|
27 | import os | |
28 | import logging |
|
28 | import logging | |
29 | import traceback |
|
29 | import traceback | |
30 |
|
30 | |||
31 | from dulwich import server as dulserver |
|
31 | from dulwich import server as dulserver | |
32 |
|
32 | |||
33 |
|
33 | |||
34 | class SimpleGitUploadPackHandler(dulserver.UploadPackHandler): |
|
34 | class SimpleGitUploadPackHandler(dulserver.UploadPackHandler): | |
35 |
|
35 | |||
36 | def handle(self): |
|
36 | def handle(self): | |
37 | write = lambda x: self.proto.write_sideband(1, x) |
|
37 | write = lambda x: self.proto.write_sideband(1, x) | |
38 |
|
38 | |||
39 | graph_walker = dulserver.ProtocolGraphWalker(self, |
|
39 | graph_walker = dulserver.ProtocolGraphWalker(self, | |
40 | self.repo.object_store, |
|
40 | self.repo.object_store, | |
41 | self.repo.get_peeled) |
|
41 | self.repo.get_peeled) | |
42 | objects_iter = self.repo.fetch_objects( |
|
42 | objects_iter = self.repo.fetch_objects( | |
43 | graph_walker.determine_wants, graph_walker, self.progress, |
|
43 | graph_walker.determine_wants, graph_walker, self.progress, | |
44 | get_tagged=self.get_tagged) |
|
44 | get_tagged=self.get_tagged) | |
45 |
|
45 | |||
46 | # Do they want any objects? |
|
46 | # Do they want any objects? | |
47 | if objects_iter is None or len(objects_iter) == 0: |
|
47 | if objects_iter is None or len(objects_iter) == 0: | |
48 | return |
|
48 | return | |
49 |
|
49 | |||
50 | self.progress("counting objects: %d, done.\n" % len(objects_iter)) |
|
50 | self.progress("counting objects: %d, done.\n" % len(objects_iter)) | |
51 | dulserver.write_pack_objects(dulserver.ProtocolFile(None, write), |
|
51 | dulserver.write_pack_objects(dulserver.ProtocolFile(None, write), | |
52 | objects_iter, len(objects_iter)) |
|
52 | objects_iter, len(objects_iter)) | |
53 | messages = [] |
|
53 | messages = [] | |
54 | messages.append('thank you for using rhodecode') |
|
54 | messages.append('thank you for using rhodecode') | |
55 |
|
55 | |||
56 | for msg in messages: |
|
56 | for msg in messages: | |
57 | self.progress(msg + "\n") |
|
57 | self.progress(msg + "\n") | |
58 | # we are done |
|
58 | # we are done | |
59 | self.proto.write("0000") |
|
59 | self.proto.write("0000") | |
60 |
|
60 | |||
61 | dulserver.DEFAULT_HANDLERS = { |
|
61 | dulserver.DEFAULT_HANDLERS = { | |
62 | 'git-upload-pack': SimpleGitUploadPackHandler, |
|
62 | 'git-upload-pack': SimpleGitUploadPackHandler, | |
63 | 'git-receive-pack': dulserver.ReceivePackHandler, |
|
63 | 'git-receive-pack': dulserver.ReceivePackHandler, | |
64 | } |
|
64 | } | |
65 |
|
65 | |||
66 | from dulwich.repo import Repo |
|
66 | from dulwich.repo import Repo | |
67 | from dulwich.web import HTTPGitApplication |
|
67 | from dulwich.web import HTTPGitApplication | |
68 |
|
68 | |||
69 | from paste.auth.basic import AuthBasicAuthenticator |
|
69 | from paste.auth.basic import AuthBasicAuthenticator | |
70 | from paste.httpheaders import REMOTE_USER, AUTH_TYPE |
|
70 | from paste.httpheaders import REMOTE_USER, AUTH_TYPE | |
71 |
|
71 | |||
72 | from rhodecode.lib import safe_str |
|
72 | from rhodecode.lib import safe_str | |
73 | from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware |
|
73 | from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware | |
74 | from rhodecode.lib.utils import invalidate_cache, is_valid_repo |
|
74 | from rhodecode.lib.utils import invalidate_cache, is_valid_repo | |
75 | from rhodecode.model.db import User |
|
75 | from rhodecode.model.db import User | |
76 |
|
76 | |||
77 | from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError |
|
77 | from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError | |
78 |
|
78 | |||
79 | log = logging.getLogger(__name__) |
|
79 | log = logging.getLogger(__name__) | |
80 |
|
80 | |||
81 |
|
81 | |||
82 | def is_git(environ): |
|
82 | def is_git(environ): | |
83 | """Returns True if request's target is git server. |
|
83 | """Returns True if request's target is git server. | |
84 | ``HTTP_USER_AGENT`` would then have git client version given. |
|
84 | ``HTTP_USER_AGENT`` would then have git client version given. | |
85 |
|
85 | |||
86 | :param environ: |
|
86 | :param environ: | |
87 | """ |
|
87 | """ | |
88 | http_user_agent = environ.get('HTTP_USER_AGENT') |
|
88 | http_user_agent = environ.get('HTTP_USER_AGENT') | |
89 | if http_user_agent and http_user_agent.startswith('git'): |
|
89 | if http_user_agent and http_user_agent.startswith('git'): | |
90 | return True |
|
90 | return True | |
91 | return False |
|
91 | return False | |
92 |
|
92 | |||
93 |
|
93 | |||
94 | class SimpleGit(object): |
|
94 | class SimpleGit(object): | |
95 |
|
95 | |||
96 | def __init__(self, application, config): |
|
96 | def __init__(self, application, config): | |
97 | self.application = application |
|
97 | self.application = application | |
98 | self.config = config |
|
98 | self.config = config | |
99 | # base path of repo locations |
|
99 | # base path of repo locations | |
100 | self.basepath = self.config['base_path'] |
|
100 | self.basepath = self.config['base_path'] | |
101 | #authenticate this mercurial request using authfunc |
|
101 | #authenticate this mercurial request using authfunc | |
102 | self.authenticate = AuthBasicAuthenticator('', authfunc) |
|
102 | self.authenticate = AuthBasicAuthenticator('', authfunc) | |
103 |
|
103 | |||
104 | def __call__(self, environ, start_response): |
|
104 | def __call__(self, environ, start_response): | |
105 | if not is_git(environ): |
|
105 | if not is_git(environ): | |
106 | return self.application(environ, start_response) |
|
106 | return self.application(environ, start_response) | |
107 |
|
107 | |||
108 | proxy_key = 'HTTP_X_REAL_IP' |
|
108 | proxy_key = 'HTTP_X_REAL_IP' | |
109 | def_key = 'REMOTE_ADDR' |
|
109 | def_key = 'REMOTE_ADDR' | |
110 | ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0')) |
|
110 | ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0')) | |
111 | username = None |
|
111 | username = None | |
112 | # skip passing error to error controller |
|
112 | # skip passing error to error controller | |
113 | environ['pylons.status_code_redirect'] = True |
|
113 | environ['pylons.status_code_redirect'] = True | |
114 |
|
114 | |||
115 | #====================================================================== |
|
115 | #====================================================================== | |
116 | # EXTRACT REPOSITORY NAME FROM ENV |
|
116 | # EXTRACT REPOSITORY NAME FROM ENV | |
117 | #====================================================================== |
|
117 | #====================================================================== | |
118 | try: |
|
118 | try: | |
119 | repo_name = self.__get_repository(environ) |
|
119 | repo_name = self.__get_repository(environ) | |
120 | log.debug('Extracted repo name is %s' % repo_name) |
|
120 | log.debug('Extracted repo name is %s' % repo_name) | |
121 | except: |
|
121 | except: | |
122 | return HTTPInternalServerError()(environ, start_response) |
|
122 | return HTTPInternalServerError()(environ, start_response) | |
123 |
|
123 | |||
124 | #====================================================================== |
|
124 | #====================================================================== | |
125 | # GET ACTION PULL or PUSH |
|
125 | # GET ACTION PULL or PUSH | |
126 | #====================================================================== |
|
126 | #====================================================================== | |
127 | action = self.__get_action(environ) |
|
127 | action = self.__get_action(environ) | |
128 |
|
128 | |||
129 | #====================================================================== |
|
129 | #====================================================================== | |
130 | # CHECK ANONYMOUS PERMISSION |
|
130 | # CHECK ANONYMOUS PERMISSION | |
131 | #====================================================================== |
|
131 | #====================================================================== | |
132 | if action in ['pull', 'push']: |
|
132 | if action in ['pull', 'push']: | |
133 | anonymous_user = self.__get_user('default') |
|
133 | anonymous_user = self.__get_user('default') | |
134 | username = anonymous_user.username |
|
134 | username = anonymous_user.username | |
135 | anonymous_perm = self.__check_permission(action, |
|
135 | anonymous_perm = self.__check_permission(action, | |
136 | anonymous_user, |
|
136 | anonymous_user, | |
137 | repo_name) |
|
137 | repo_name) | |
138 |
|
138 | |||
139 | if anonymous_perm is not True or anonymous_user.active is False: |
|
139 | if anonymous_perm is not True or anonymous_user.active is False: | |
140 | if anonymous_perm is not True: |
|
140 | if anonymous_perm is not True: | |
141 | log.debug('Not enough credentials to access this ' |
|
141 | log.debug('Not enough credentials to access this ' | |
142 | 'repository as anonymous user') |
|
142 | 'repository as anonymous user') | |
143 | if anonymous_user.active is False: |
|
143 | if anonymous_user.active is False: | |
144 | log.debug('Anonymous access is disabled, running ' |
|
144 | log.debug('Anonymous access is disabled, running ' | |
145 | 'authentication') |
|
145 | 'authentication') | |
146 | #============================================================== |
|
146 | #============================================================== | |
147 | # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE |
|
147 | # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE | |
148 | # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS |
|
148 | # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS | |
149 | #============================================================== |
|
149 | #============================================================== | |
150 |
|
150 | |||
151 | if not REMOTE_USER(environ): |
|
151 | if not REMOTE_USER(environ): | |
152 | self.authenticate.realm = \ |
|
152 | self.authenticate.realm = \ | |
153 | safe_str(self.config['rhodecode_realm']) |
|
153 | safe_str(self.config['rhodecode_realm']) | |
154 | result = self.authenticate(environ) |
|
154 | result = self.authenticate(environ) | |
155 | if isinstance(result, str): |
|
155 | if isinstance(result, str): | |
156 | AUTH_TYPE.update(environ, 'basic') |
|
156 | AUTH_TYPE.update(environ, 'basic') | |
157 | REMOTE_USER.update(environ, result) |
|
157 | REMOTE_USER.update(environ, result) | |
158 | else: |
|
158 | else: | |
159 | return result.wsgi_application(environ, start_response) |
|
159 | return result.wsgi_application(environ, start_response) | |
160 |
|
160 | |||
161 | #============================================================== |
|
161 | #============================================================== | |
162 | # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME FROM |
|
162 | # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME FROM | |
163 | # BASIC AUTH |
|
163 | # BASIC AUTH | |
164 | #============================================================== |
|
164 | #============================================================== | |
165 |
|
165 | |||
166 | if action in ['pull', 'push']: |
|
166 | if action in ['pull', 'push']: | |
167 | username = REMOTE_USER(environ) |
|
167 | username = REMOTE_USER(environ) | |
168 | try: |
|
168 | try: | |
169 | user = self.__get_user(username) |
|
169 | user = self.__get_user(username) | |
|
170 | if user is None: | |||
|
171 | return HTTPForbidden()(environ, start_response) | |||
170 | username = user.username |
|
172 | username = user.username | |
171 | except: |
|
173 | except: | |
172 | log.error(traceback.format_exc()) |
|
174 | log.error(traceback.format_exc()) | |
173 | return HTTPInternalServerError()(environ, |
|
175 | return HTTPInternalServerError()(environ, | |
174 | start_response) |
|
176 | start_response) | |
175 |
|
177 | |||
176 | #check permissions for this repository |
|
178 | #check permissions for this repository | |
177 | perm = self.__check_permission(action, user, |
|
179 | perm = self.__check_permission(action, user, | |
178 | repo_name) |
|
180 | repo_name) | |
179 | if perm is not True: |
|
181 | if perm is not True: | |
180 | return HTTPForbidden()(environ, start_response) |
|
182 | return HTTPForbidden()(environ, start_response) | |
181 |
|
183 | |||
182 | extras = {'ip': ipaddr, |
|
184 | extras = {'ip': ipaddr, | |
183 | 'username': username, |
|
185 | 'username': username, | |
184 | 'action': action, |
|
186 | 'action': action, | |
185 | 'repository': repo_name} |
|
187 | 'repository': repo_name} | |
186 |
|
188 | |||
187 | #=================================================================== |
|
189 | #=================================================================== | |
188 | # GIT REQUEST HANDLING |
|
190 | # GIT REQUEST HANDLING | |
189 | #=================================================================== |
|
191 | #=================================================================== | |
190 |
|
192 | |||
191 | repo_path = safe_str(os.path.join(self.basepath, repo_name)) |
|
193 | repo_path = safe_str(os.path.join(self.basepath, repo_name)) | |
192 | log.debug('Repository path is %s' % repo_path) |
|
194 | log.debug('Repository path is %s' % repo_path) | |
193 |
|
195 | |||
194 | # quick check if that dir exists... |
|
196 | # quick check if that dir exists... | |
195 | if is_valid_repo(repo_name, self.basepath) is False: |
|
197 | if is_valid_repo(repo_name, self.basepath) is False: | |
196 | return HTTPNotFound()(environ, start_response) |
|
198 | return HTTPNotFound()(environ, start_response) | |
197 |
|
199 | |||
198 | try: |
|
200 | try: | |
199 | #invalidate cache on push |
|
201 | #invalidate cache on push | |
200 | if action == 'push': |
|
202 | if action == 'push': | |
201 | self.__invalidate_cache(repo_name) |
|
203 | self.__invalidate_cache(repo_name) | |
202 |
|
204 | |||
203 | app = self.__make_app(repo_name, repo_path) |
|
205 | app = self.__make_app(repo_name, repo_path) | |
204 | return app(environ, start_response) |
|
206 | return app(environ, start_response) | |
205 | except Exception: |
|
207 | except Exception: | |
206 | log.error(traceback.format_exc()) |
|
208 | log.error(traceback.format_exc()) | |
207 | return HTTPInternalServerError()(environ, start_response) |
|
209 | return HTTPInternalServerError()(environ, start_response) | |
208 |
|
210 | |||
209 | def __make_app(self, repo_name, repo_path): |
|
211 | def __make_app(self, repo_name, repo_path): | |
210 | """ |
|
212 | """ | |
211 | Make an wsgi application using dulserver |
|
213 | Make an wsgi application using dulserver | |
212 |
|
214 | |||
213 | :param repo_name: name of the repository |
|
215 | :param repo_name: name of the repository | |
214 | :param repo_path: full path to the repository |
|
216 | :param repo_path: full path to the repository | |
215 | """ |
|
217 | """ | |
216 |
|
218 | |||
217 | _d = {'/' + repo_name: Repo(repo_path)} |
|
219 | _d = {'/' + repo_name: Repo(repo_path)} | |
218 | backend = dulserver.DictBackend(_d) |
|
220 | backend = dulserver.DictBackend(_d) | |
219 | gitserve = HTTPGitApplication(backend) |
|
221 | gitserve = HTTPGitApplication(backend) | |
220 |
|
222 | |||
221 | return gitserve |
|
223 | return gitserve | |
222 |
|
224 | |||
223 | def __check_permission(self, action, user, repo_name): |
|
225 | def __check_permission(self, action, user, repo_name): | |
224 | """ |
|
226 | """ | |
225 | Checks permissions using action (push/pull) user and repository |
|
227 | Checks permissions using action (push/pull) user and repository | |
226 | name |
|
228 | name | |
227 |
|
229 | |||
228 | :param action: push or pull action |
|
230 | :param action: push or pull action | |
229 | :param user: user instance |
|
231 | :param user: user instance | |
230 | :param repo_name: repository name |
|
232 | :param repo_name: repository name | |
231 | """ |
|
233 | """ | |
232 | if action == 'push': |
|
234 | if action == 'push': | |
233 | if not HasPermissionAnyMiddleware('repository.write', |
|
235 | if not HasPermissionAnyMiddleware('repository.write', | |
234 | 'repository.admin')(user, |
|
236 | 'repository.admin')(user, | |
235 | repo_name): |
|
237 | repo_name): | |
236 | return False |
|
238 | return False | |
237 |
|
239 | |||
238 | else: |
|
240 | else: | |
239 | #any other action need at least read permission |
|
241 | #any other action need at least read permission | |
240 | if not HasPermissionAnyMiddleware('repository.read', |
|
242 | if not HasPermissionAnyMiddleware('repository.read', | |
241 | 'repository.write', |
|
243 | 'repository.write', | |
242 | 'repository.admin')(user, |
|
244 | 'repository.admin')(user, | |
243 | repo_name): |
|
245 | repo_name): | |
244 | return False |
|
246 | return False | |
245 |
|
247 | |||
246 | return True |
|
248 | return True | |
247 |
|
249 | |||
248 | def __get_repository(self, environ): |
|
250 | def __get_repository(self, environ): | |
249 | """ |
|
251 | """ | |
250 | Get's repository name out of PATH_INFO header |
|
252 | Get's repository name out of PATH_INFO header | |
251 |
|
253 | |||
252 | :param environ: environ where PATH_INFO is stored |
|
254 | :param environ: environ where PATH_INFO is stored | |
253 | """ |
|
255 | """ | |
254 | try: |
|
256 | try: | |
255 | repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:]) |
|
257 | repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:]) | |
256 | if repo_name.endswith('/'): |
|
258 | if repo_name.endswith('/'): | |
257 | repo_name = repo_name.rstrip('/') |
|
259 | repo_name = repo_name.rstrip('/') | |
258 | except: |
|
260 | except: | |
259 | log.error(traceback.format_exc()) |
|
261 | log.error(traceback.format_exc()) | |
260 | raise |
|
262 | raise | |
261 | repo_name = repo_name.split('/')[0] |
|
263 | repo_name = repo_name.split('/')[0] | |
262 | return repo_name |
|
264 | return repo_name | |
263 |
|
265 | |||
264 | def __get_user(self, username): |
|
266 | def __get_user(self, username): | |
265 | return User.get_by_username(username) |
|
267 | return User.get_by_username(username) | |
266 |
|
268 | |||
267 | def __get_action(self, environ): |
|
269 | def __get_action(self, environ): | |
268 | """Maps git request commands into a pull or push command. |
|
270 | """Maps git request commands into a pull or push command. | |
269 |
|
271 | |||
270 | :param environ: |
|
272 | :param environ: | |
271 | """ |
|
273 | """ | |
272 | service = environ['QUERY_STRING'].split('=') |
|
274 | service = environ['QUERY_STRING'].split('=') | |
273 | if len(service) > 1: |
|
275 | if len(service) > 1: | |
274 | service_cmd = service[1] |
|
276 | service_cmd = service[1] | |
275 | mapping = {'git-receive-pack': 'push', |
|
277 | mapping = {'git-receive-pack': 'push', | |
276 | 'git-upload-pack': 'pull', |
|
278 | 'git-upload-pack': 'pull', | |
277 | } |
|
279 | } | |
278 |
|
280 | |||
279 | return mapping.get(service_cmd, |
|
281 | return mapping.get(service_cmd, | |
280 | service_cmd if service_cmd else 'other') |
|
282 | service_cmd if service_cmd else 'other') | |
281 | else: |
|
283 | else: | |
282 | return 'other' |
|
284 | return 'other' | |
283 |
|
285 | |||
284 | def __invalidate_cache(self, repo_name): |
|
286 | def __invalidate_cache(self, repo_name): | |
285 | """we know that some change was made to repositories and we should |
|
287 | """we know that some change was made to repositories and we should | |
286 | invalidate the cache to see the changes right away but only for |
|
288 | invalidate the cache to see the changes right away but only for | |
287 | push requests""" |
|
289 | push requests""" | |
288 | invalidate_cache('get_repo_cached_%s' % repo_name) |
|
290 | invalidate_cache('get_repo_cached_%s' % repo_name) | |
289 |
|
291 |
@@ -1,288 +1,290 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """ |
|
2 | """ | |
3 | rhodecode.lib.middleware.simplehg |
|
3 | rhodecode.lib.middleware.simplehg | |
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
5 |
|
5 | |||
6 | SimpleHG middleware for handling mercurial protocol request |
|
6 | SimpleHG middleware for handling mercurial protocol request | |
7 | (push/clone etc.). It's implemented with basic auth function |
|
7 | (push/clone etc.). It's implemented with basic auth function | |
8 |
|
8 | |||
9 | :created_on: Apr 28, 2010 |
|
9 | :created_on: Apr 28, 2010 | |
10 | :author: marcink |
|
10 | :author: marcink | |
11 | :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> |
|
11 | :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |
12 | :license: GPLv3, see COPYING for more details. |
|
12 | :license: GPLv3, see COPYING for more details. | |
13 | """ |
|
13 | """ | |
14 | # This program is free software: you can redistribute it and/or modify |
|
14 | # This program is free software: you can redistribute it and/or modify | |
15 | # it under the terms of the GNU General Public License as published by |
|
15 | # it under the terms of the GNU General Public License as published by | |
16 | # the Free Software Foundation, either version 3 of the License, or |
|
16 | # the Free Software Foundation, either version 3 of the License, or | |
17 | # (at your option) any later version. |
|
17 | # (at your option) any later version. | |
18 | # |
|
18 | # | |
19 | # This program is distributed in the hope that it will be useful, |
|
19 | # This program is distributed in the hope that it will be useful, | |
20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
22 | # GNU General Public License for more details. |
|
22 | # GNU General Public License for more details. | |
23 | # |
|
23 | # | |
24 | # You should have received a copy of the GNU General Public License |
|
24 | # You should have received a copy of the GNU General Public License | |
25 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
25 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
26 |
|
26 | |||
27 | import os |
|
27 | import os | |
28 | import logging |
|
28 | import logging | |
29 | import traceback |
|
29 | import traceback | |
30 |
|
30 | |||
31 | from mercurial.error import RepoError |
|
31 | from mercurial.error import RepoError | |
32 | from mercurial.hgweb import hgweb_mod |
|
32 | from mercurial.hgweb import hgweb_mod | |
33 |
|
33 | |||
34 | from paste.auth.basic import AuthBasicAuthenticator |
|
34 | from paste.auth.basic import AuthBasicAuthenticator | |
35 | from paste.httpheaders import REMOTE_USER, AUTH_TYPE |
|
35 | from paste.httpheaders import REMOTE_USER, AUTH_TYPE | |
36 |
|
36 | |||
37 | from rhodecode.lib import safe_str |
|
37 | from rhodecode.lib import safe_str | |
38 | from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware |
|
38 | from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware | |
39 | from rhodecode.lib.utils import make_ui, invalidate_cache, \ |
|
39 | from rhodecode.lib.utils import make_ui, invalidate_cache, \ | |
40 | is_valid_repo, ui_sections |
|
40 | is_valid_repo, ui_sections | |
41 | from rhodecode.model.db import User |
|
41 | from rhodecode.model.db import User | |
42 |
|
42 | |||
43 | from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError |
|
43 | from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError | |
44 |
|
44 | |||
45 | log = logging.getLogger(__name__) |
|
45 | log = logging.getLogger(__name__) | |
46 |
|
46 | |||
47 |
|
47 | |||
48 | def is_mercurial(environ): |
|
48 | def is_mercurial(environ): | |
49 | """Returns True if request's target is mercurial server - header |
|
49 | """Returns True if request's target is mercurial server - header | |
50 | ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``. |
|
50 | ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``. | |
51 | """ |
|
51 | """ | |
52 | http_accept = environ.get('HTTP_ACCEPT') |
|
52 | http_accept = environ.get('HTTP_ACCEPT') | |
53 | if http_accept and http_accept.startswith('application/mercurial'): |
|
53 | if http_accept and http_accept.startswith('application/mercurial'): | |
54 | return True |
|
54 | return True | |
55 | return False |
|
55 | return False | |
56 |
|
56 | |||
57 |
|
57 | |||
58 | class SimpleHg(object): |
|
58 | class SimpleHg(object): | |
59 |
|
59 | |||
60 | def __init__(self, application, config): |
|
60 | def __init__(self, application, config): | |
61 | self.application = application |
|
61 | self.application = application | |
62 | self.config = config |
|
62 | self.config = config | |
63 | # base path of repo locations |
|
63 | # base path of repo locations | |
64 | self.basepath = self.config['base_path'] |
|
64 | self.basepath = self.config['base_path'] | |
65 | #authenticate this mercurial request using authfunc |
|
65 | #authenticate this mercurial request using authfunc | |
66 | self.authenticate = AuthBasicAuthenticator('', authfunc) |
|
66 | self.authenticate = AuthBasicAuthenticator('', authfunc) | |
67 | self.ipaddr = '0.0.0.0' |
|
67 | self.ipaddr = '0.0.0.0' | |
68 |
|
68 | |||
69 | def __call__(self, environ, start_response): |
|
69 | def __call__(self, environ, start_response): | |
70 | if not is_mercurial(environ): |
|
70 | if not is_mercurial(environ): | |
71 | return self.application(environ, start_response) |
|
71 | return self.application(environ, start_response) | |
72 |
|
72 | |||
73 | proxy_key = 'HTTP_X_REAL_IP' |
|
73 | proxy_key = 'HTTP_X_REAL_IP' | |
74 | def_key = 'REMOTE_ADDR' |
|
74 | def_key = 'REMOTE_ADDR' | |
75 | ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0')) |
|
75 | ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0')) | |
76 |
|
76 | |||
77 | # skip passing error to error controller |
|
77 | # skip passing error to error controller | |
78 | environ['pylons.status_code_redirect'] = True |
|
78 | environ['pylons.status_code_redirect'] = True | |
79 |
|
79 | |||
80 | #====================================================================== |
|
80 | #====================================================================== | |
81 | # EXTRACT REPOSITORY NAME FROM ENV |
|
81 | # EXTRACT REPOSITORY NAME FROM ENV | |
82 | #====================================================================== |
|
82 | #====================================================================== | |
83 | try: |
|
83 | try: | |
84 | repo_name = environ['REPO_NAME'] = self.__get_repository(environ) |
|
84 | repo_name = environ['REPO_NAME'] = self.__get_repository(environ) | |
85 | log.debug('Extracted repo name is %s' % repo_name) |
|
85 | log.debug('Extracted repo name is %s' % repo_name) | |
86 | except: |
|
86 | except: | |
87 | return HTTPInternalServerError()(environ, start_response) |
|
87 | return HTTPInternalServerError()(environ, start_response) | |
88 |
|
88 | |||
89 | #====================================================================== |
|
89 | #====================================================================== | |
90 | # GET ACTION PULL or PUSH |
|
90 | # GET ACTION PULL or PUSH | |
91 | #====================================================================== |
|
91 | #====================================================================== | |
92 | action = self.__get_action(environ) |
|
92 | action = self.__get_action(environ) | |
93 |
|
93 | |||
94 | #====================================================================== |
|
94 | #====================================================================== | |
95 | # CHECK ANONYMOUS PERMISSION |
|
95 | # CHECK ANONYMOUS PERMISSION | |
96 | #====================================================================== |
|
96 | #====================================================================== | |
97 | if action in ['pull', 'push']: |
|
97 | if action in ['pull', 'push']: | |
98 | anonymous_user = self.__get_user('default') |
|
98 | anonymous_user = self.__get_user('default') | |
99 |
|
99 | |||
100 | username = anonymous_user.username |
|
100 | username = anonymous_user.username | |
101 | anonymous_perm = self.__check_permission(action, |
|
101 | anonymous_perm = self.__check_permission(action, | |
102 | anonymous_user, |
|
102 | anonymous_user, | |
103 | repo_name) |
|
103 | repo_name) | |
104 |
|
104 | |||
105 | if anonymous_perm is not True or anonymous_user.active is False: |
|
105 | if anonymous_perm is not True or anonymous_user.active is False: | |
106 | if anonymous_perm is not True: |
|
106 | if anonymous_perm is not True: | |
107 | log.debug('Not enough credentials to access this ' |
|
107 | log.debug('Not enough credentials to access this ' | |
108 | 'repository as anonymous user') |
|
108 | 'repository as anonymous user') | |
109 | if anonymous_user.active is False: |
|
109 | if anonymous_user.active is False: | |
110 | log.debug('Anonymous access is disabled, running ' |
|
110 | log.debug('Anonymous access is disabled, running ' | |
111 | 'authentication') |
|
111 | 'authentication') | |
112 | #============================================================== |
|
112 | #============================================================== | |
113 | # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE |
|
113 | # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE | |
114 | # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS |
|
114 | # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS | |
115 | #============================================================== |
|
115 | #============================================================== | |
116 |
|
116 | |||
117 | if not REMOTE_USER(environ): |
|
117 | if not REMOTE_USER(environ): | |
118 | self.authenticate.realm = \ |
|
118 | self.authenticate.realm = \ | |
119 | safe_str(self.config['rhodecode_realm']) |
|
119 | safe_str(self.config['rhodecode_realm']) | |
120 | result = self.authenticate(environ) |
|
120 | result = self.authenticate(environ) | |
121 | if isinstance(result, str): |
|
121 | if isinstance(result, str): | |
122 | AUTH_TYPE.update(environ, 'basic') |
|
122 | AUTH_TYPE.update(environ, 'basic') | |
123 | REMOTE_USER.update(environ, result) |
|
123 | REMOTE_USER.update(environ, result) | |
124 | else: |
|
124 | else: | |
125 | return result.wsgi_application(environ, start_response) |
|
125 | return result.wsgi_application(environ, start_response) | |
126 |
|
126 | |||
127 | #============================================================== |
|
127 | #============================================================== | |
128 | # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME FROM |
|
128 | # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME FROM | |
129 | # BASIC AUTH |
|
129 | # BASIC AUTH | |
130 | #============================================================== |
|
130 | #============================================================== | |
131 |
|
131 | |||
132 | if action in ['pull', 'push']: |
|
132 | if action in ['pull', 'push']: | |
133 | username = REMOTE_USER(environ) |
|
133 | username = REMOTE_USER(environ) | |
134 | try: |
|
134 | try: | |
135 | user = self.__get_user(username) |
|
135 | user = self.__get_user(username) | |
|
136 | if user is None: | |||
|
137 | return HTTPForbidden()(environ, start_response) | |||
136 | username = user.username |
|
138 | username = user.username | |
137 | except: |
|
139 | except: | |
138 | log.error(traceback.format_exc()) |
|
140 | log.error(traceback.format_exc()) | |
139 | return HTTPInternalServerError()(environ, |
|
141 | return HTTPInternalServerError()(environ, | |
140 | start_response) |
|
142 | start_response) | |
141 |
|
143 | |||
142 | #check permissions for this repository |
|
144 | #check permissions for this repository | |
143 | perm = self.__check_permission(action, user, |
|
145 | perm = self.__check_permission(action, user, | |
144 | repo_name) |
|
146 | repo_name) | |
145 | if perm is not True: |
|
147 | if perm is not True: | |
146 | return HTTPForbidden()(environ, start_response) |
|
148 | return HTTPForbidden()(environ, start_response) | |
147 |
|
149 | |||
148 | extras = {'ip': ipaddr, |
|
150 | extras = {'ip': ipaddr, | |
149 | 'username': username, |
|
151 | 'username': username, | |
150 | 'action': action, |
|
152 | 'action': action, | |
151 | 'repository': repo_name} |
|
153 | 'repository': repo_name} | |
152 |
|
154 | |||
153 | #====================================================================== |
|
155 | #====================================================================== | |
154 | # MERCURIAL REQUEST HANDLING |
|
156 | # MERCURIAL REQUEST HANDLING | |
155 | #====================================================================== |
|
157 | #====================================================================== | |
156 |
|
158 | |||
157 | repo_path = safe_str(os.path.join(self.basepath, repo_name)) |
|
159 | repo_path = safe_str(os.path.join(self.basepath, repo_name)) | |
158 | log.debug('Repository path is %s' % repo_path) |
|
160 | log.debug('Repository path is %s' % repo_path) | |
159 |
|
161 | |||
160 | baseui = make_ui('db') |
|
162 | baseui = make_ui('db') | |
161 | self.__inject_extras(repo_path, baseui, extras) |
|
163 | self.__inject_extras(repo_path, baseui, extras) | |
162 |
|
164 | |||
163 |
|
165 | |||
164 | # quick check if that dir exists... |
|
166 | # quick check if that dir exists... | |
165 | if is_valid_repo(repo_name, self.basepath) is False: |
|
167 | if is_valid_repo(repo_name, self.basepath) is False: | |
166 | return HTTPNotFound()(environ, start_response) |
|
168 | return HTTPNotFound()(environ, start_response) | |
167 |
|
169 | |||
168 | try: |
|
170 | try: | |
169 | #invalidate cache on push |
|
171 | #invalidate cache on push | |
170 | if action == 'push': |
|
172 | if action == 'push': | |
171 | self.__invalidate_cache(repo_name) |
|
173 | self.__invalidate_cache(repo_name) | |
172 |
|
174 | |||
173 | app = self.__make_app(repo_path, baseui, extras) |
|
175 | app = self.__make_app(repo_path, baseui, extras) | |
174 | return app(environ, start_response) |
|
176 | return app(environ, start_response) | |
175 | except RepoError, e: |
|
177 | except RepoError, e: | |
176 | if str(e).find('not found') != -1: |
|
178 | if str(e).find('not found') != -1: | |
177 | return HTTPNotFound()(environ, start_response) |
|
179 | return HTTPNotFound()(environ, start_response) | |
178 | except Exception: |
|
180 | except Exception: | |
179 | log.error(traceback.format_exc()) |
|
181 | log.error(traceback.format_exc()) | |
180 | return HTTPInternalServerError()(environ, start_response) |
|
182 | return HTTPInternalServerError()(environ, start_response) | |
181 |
|
183 | |||
182 | def __make_app(self, repo_name, baseui, extras): |
|
184 | def __make_app(self, repo_name, baseui, extras): | |
183 | """ |
|
185 | """ | |
184 | Make an wsgi application using hgweb, and inject generated baseui |
|
186 | Make an wsgi application using hgweb, and inject generated baseui | |
185 | instance, additionally inject some extras into ui object |
|
187 | instance, additionally inject some extras into ui object | |
186 | """ |
|
188 | """ | |
187 | return hgweb_mod.hgweb(repo_name, name=repo_name, baseui=baseui) |
|
189 | return hgweb_mod.hgweb(repo_name, name=repo_name, baseui=baseui) | |
188 |
|
190 | |||
189 |
|
191 | |||
190 | def __check_permission(self, action, user, repo_name): |
|
192 | def __check_permission(self, action, user, repo_name): | |
191 | """ |
|
193 | """ | |
192 | Checks permissions using action (push/pull) user and repository |
|
194 | Checks permissions using action (push/pull) user and repository | |
193 | name |
|
195 | name | |
194 |
|
196 | |||
195 | :param action: push or pull action |
|
197 | :param action: push or pull action | |
196 | :param user: user instance |
|
198 | :param user: user instance | |
197 | :param repo_name: repository name |
|
199 | :param repo_name: repository name | |
198 | """ |
|
200 | """ | |
199 | if action == 'push': |
|
201 | if action == 'push': | |
200 | if not HasPermissionAnyMiddleware('repository.write', |
|
202 | if not HasPermissionAnyMiddleware('repository.write', | |
201 | 'repository.admin')(user, |
|
203 | 'repository.admin')(user, | |
202 | repo_name): |
|
204 | repo_name): | |
203 | return False |
|
205 | return False | |
204 |
|
206 | |||
205 | else: |
|
207 | else: | |
206 | #any other action need at least read permission |
|
208 | #any other action need at least read permission | |
207 | if not HasPermissionAnyMiddleware('repository.read', |
|
209 | if not HasPermissionAnyMiddleware('repository.read', | |
208 | 'repository.write', |
|
210 | 'repository.write', | |
209 | 'repository.admin')(user, |
|
211 | 'repository.admin')(user, | |
210 | repo_name): |
|
212 | repo_name): | |
211 | return False |
|
213 | return False | |
212 |
|
214 | |||
213 | return True |
|
215 | return True | |
214 |
|
216 | |||
215 | def __get_repository(self, environ): |
|
217 | def __get_repository(self, environ): | |
216 | """ |
|
218 | """ | |
217 | Get's repository name out of PATH_INFO header |
|
219 | Get's repository name out of PATH_INFO header | |
218 |
|
220 | |||
219 | :param environ: environ where PATH_INFO is stored |
|
221 | :param environ: environ where PATH_INFO is stored | |
220 | """ |
|
222 | """ | |
221 | try: |
|
223 | try: | |
222 | repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:]) |
|
224 | repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:]) | |
223 | if repo_name.endswith('/'): |
|
225 | if repo_name.endswith('/'): | |
224 | repo_name = repo_name.rstrip('/') |
|
226 | repo_name = repo_name.rstrip('/') | |
225 | except: |
|
227 | except: | |
226 | log.error(traceback.format_exc()) |
|
228 | log.error(traceback.format_exc()) | |
227 | raise |
|
229 | raise | |
228 |
|
230 | |||
229 | return repo_name |
|
231 | return repo_name | |
230 |
|
232 | |||
231 | def __get_user(self, username): |
|
233 | def __get_user(self, username): | |
232 | return User.get_by_username(username) |
|
234 | return User.get_by_username(username) | |
233 |
|
235 | |||
234 | def __get_action(self, environ): |
|
236 | def __get_action(self, environ): | |
235 | """ |
|
237 | """ | |
236 | Maps mercurial request commands into a clone,pull or push command. |
|
238 | Maps mercurial request commands into a clone,pull or push command. | |
237 | This should always return a valid command string |
|
239 | This should always return a valid command string | |
238 |
|
240 | |||
239 | :param environ: |
|
241 | :param environ: | |
240 | """ |
|
242 | """ | |
241 | mapping = {'changegroup': 'pull', |
|
243 | mapping = {'changegroup': 'pull', | |
242 | 'changegroupsubset': 'pull', |
|
244 | 'changegroupsubset': 'pull', | |
243 | 'stream_out': 'pull', |
|
245 | 'stream_out': 'pull', | |
244 | 'listkeys': 'pull', |
|
246 | 'listkeys': 'pull', | |
245 | 'unbundle': 'push', |
|
247 | 'unbundle': 'push', | |
246 | 'pushkey': 'push', } |
|
248 | 'pushkey': 'push', } | |
247 | for qry in environ['QUERY_STRING'].split('&'): |
|
249 | for qry in environ['QUERY_STRING'].split('&'): | |
248 | if qry.startswith('cmd'): |
|
250 | if qry.startswith('cmd'): | |
249 | cmd = qry.split('=')[-1] |
|
251 | cmd = qry.split('=')[-1] | |
250 | if cmd in mapping: |
|
252 | if cmd in mapping: | |
251 | return mapping[cmd] |
|
253 | return mapping[cmd] | |
252 | else: |
|
254 | else: | |
253 | return 'pull' |
|
255 | return 'pull' | |
254 |
|
256 | |||
255 | def __invalidate_cache(self, repo_name): |
|
257 | def __invalidate_cache(self, repo_name): | |
256 | """we know that some change was made to repositories and we should |
|
258 | """we know that some change was made to repositories and we should | |
257 | invalidate the cache to see the changes right away but only for |
|
259 | invalidate the cache to see the changes right away but only for | |
258 | push requests""" |
|
260 | push requests""" | |
259 | invalidate_cache('get_repo_cached_%s' % repo_name) |
|
261 | invalidate_cache('get_repo_cached_%s' % repo_name) | |
260 |
|
262 | |||
261 | def __inject_extras(self, repo_path, baseui, extras={}): |
|
263 | def __inject_extras(self, repo_path, baseui, extras={}): | |
262 | """ |
|
264 | """ | |
263 | Injects some extra params into baseui instance |
|
265 | Injects some extra params into baseui instance | |
264 |
|
266 | |||
265 | also overwrites global settings with those takes from local hgrc file |
|
267 | also overwrites global settings with those takes from local hgrc file | |
266 |
|
268 | |||
267 | :param baseui: baseui instance |
|
269 | :param baseui: baseui instance | |
268 | :param extras: dict with extra params to put into baseui |
|
270 | :param extras: dict with extra params to put into baseui | |
269 | """ |
|
271 | """ | |
270 |
|
272 | |||
271 | hgrc = os.path.join(repo_path, '.hg', 'hgrc') |
|
273 | hgrc = os.path.join(repo_path, '.hg', 'hgrc') | |
272 |
|
274 | |||
273 | # make our hgweb quiet so it doesn't print output |
|
275 | # make our hgweb quiet so it doesn't print output | |
274 | baseui.setconfig('ui', 'quiet', 'true') |
|
276 | baseui.setconfig('ui', 'quiet', 'true') | |
275 |
|
277 | |||
276 | #inject some additional parameters that will be available in ui |
|
278 | #inject some additional parameters that will be available in ui | |
277 | #for hooks |
|
279 | #for hooks | |
278 | for k, v in extras.items(): |
|
280 | for k, v in extras.items(): | |
279 | baseui.setconfig('rhodecode_extras', k, v) |
|
281 | baseui.setconfig('rhodecode_extras', k, v) | |
280 |
|
282 | |||
281 | repoui = make_ui('file', hgrc, False) |
|
283 | repoui = make_ui('file', hgrc, False) | |
282 |
|
284 | |||
283 | if repoui: |
|
285 | if repoui: | |
284 | #overwrite our ui instance with the section from hgrc file |
|
286 | #overwrite our ui instance with the section from hgrc file | |
285 | for section in ui_sections: |
|
287 | for section in ui_sections: | |
286 | for k, v in repoui.configitems(section): |
|
288 | for k, v in repoui.configitems(section): | |
287 | baseui.setconfig(section, k, v) |
|
289 | baseui.setconfig(section, k, v) | |
288 |
|
290 |
@@ -1,161 +1,165 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """ |
|
2 | """ | |
3 | rhodecode.lib.smtp_mailer |
|
3 | rhodecode.lib.smtp_mailer | |
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~ | |
5 |
|
5 | |||
6 | Simple smtp mailer used in RhodeCode |
|
6 | Simple smtp mailer used in RhodeCode | |
7 |
|
7 | |||
8 | :created_on: Sep 13, 2010 |
|
8 | :created_on: Sep 13, 2010 | |
9 | :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> |
|
9 | :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> | |
10 | :license: GPLv3, see COPYING for more details. |
|
10 | :license: GPLv3, see COPYING for more details. | |
11 | """ |
|
11 | """ | |
12 | # This program is free software: you can redistribute it and/or modify |
|
12 | # This program is free software: you can redistribute it and/or modify | |
13 | # it under the terms of the GNU General Public License as published by |
|
13 | # it under the terms of the GNU General Public License as published by | |
14 | # the Free Software Foundation, either version 3 of the License, or |
|
14 | # the Free Software Foundation, either version 3 of the License, or | |
15 | # (at your option) any later version. |
|
15 | # (at your option) any later version. | |
16 | # |
|
16 | # | |
17 | # This program is distributed in the hope that it will be useful, |
|
17 | # This program is distributed in the hope that it will be useful, | |
18 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
18 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
19 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | # GNU General Public License for more details. |
|
20 | # GNU General Public License for more details. | |
21 | # |
|
21 | # | |
22 | # You should have received a copy of the GNU General Public License |
|
22 | # You should have received a copy of the GNU General Public License | |
23 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
23 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
24 |
|
24 | |||
25 | import logging |
|
25 | import logging | |
26 | import smtplib |
|
26 | import smtplib | |
27 | import mimetypes |
|
27 | import mimetypes | |
28 | from socket import sslerror |
|
28 | from socket import sslerror | |
29 |
|
29 | |||
30 | from email.mime.multipart import MIMEMultipart |
|
30 | from email.mime.multipart import MIMEMultipart | |
31 | from email.mime.image import MIMEImage |
|
31 | from email.mime.image import MIMEImage | |
32 | from email.mime.audio import MIMEAudio |
|
32 | from email.mime.audio import MIMEAudio | |
33 | from email.mime.base import MIMEBase |
|
33 | from email.mime.base import MIMEBase | |
34 | from email.mime.text import MIMEText |
|
34 | from email.mime.text import MIMEText | |
35 | from email.utils import formatdate |
|
35 | from email.utils import formatdate | |
36 | from email import encoders |
|
36 | from email import encoders | |
37 |
|
37 | |||
38 |
|
38 | |||
39 | class SmtpMailer(object): |
|
39 | class SmtpMailer(object): | |
40 | """SMTP mailer class |
|
40 | """SMTP mailer class | |
41 |
|
41 | |||
42 | mailer = SmtpMailer(mail_from, user, passwd, mail_server, |
|
42 | mailer = SmtpMailer(mail_from, user, passwd, mail_server, smtp_auth | |
43 | mail_port, ssl, tls) |
|
43 | mail_port, ssl, tls) | |
44 | mailer.send(recipients, subject, body, attachment_files) |
|
44 | mailer.send(recipients, subject, body, attachment_files) | |
45 |
|
45 | |||
46 | :param recipients might be a list of string or single string |
|
46 | :param recipients might be a list of string or single string | |
47 | :param attachment_files is a dict of {filename:location} |
|
47 | :param attachment_files is a dict of {filename:location} | |
48 | it tries to guess the mimetype and attach the file |
|
48 | it tries to guess the mimetype and attach the file | |
49 |
|
49 | |||
50 | """ |
|
50 | """ | |
51 |
|
51 | |||
52 | def __init__(self, mail_from, user, passwd, mail_server, |
|
52 | def __init__(self, mail_from, user, passwd, mail_server, smtp_auth=None, | |
53 |
|
|
53 | mail_port=None, ssl=False, tls=False, debug=False): | |
54 |
|
54 | |||
55 | self.mail_from = mail_from |
|
55 | self.mail_from = mail_from | |
56 | self.mail_server = mail_server |
|
56 | self.mail_server = mail_server | |
57 | self.mail_port = mail_port |
|
57 | self.mail_port = mail_port | |
58 | self.user = user |
|
58 | self.user = user | |
59 | self.passwd = passwd |
|
59 | self.passwd = passwd | |
60 | self.ssl = ssl |
|
60 | self.ssl = ssl | |
61 | self.tls = tls |
|
61 | self.tls = tls | |
62 | self.debug = debug |
|
62 | self.debug = debug | |
|
63 | self.auth = smtp_auth | |||
63 |
|
64 | |||
64 | def send(self, recipients=[], subject='', body='', attachment_files=None): |
|
65 | def send(self, recipients=[], subject='', body='', attachment_files=None): | |
65 |
|
66 | |||
66 | if isinstance(recipients, basestring): |
|
67 | if isinstance(recipients, basestring): | |
67 | recipients = [recipients] |
|
68 | recipients = [recipients] | |
68 | if self.ssl: |
|
69 | if self.ssl: | |
69 | smtp_serv = smtplib.SMTP_SSL(self.mail_server, self.mail_port) |
|
70 | smtp_serv = smtplib.SMTP_SSL(self.mail_server, self.mail_port) | |
70 | else: |
|
71 | else: | |
71 | smtp_serv = smtplib.SMTP(self.mail_server, self.mail_port) |
|
72 | smtp_serv = smtplib.SMTP(self.mail_server, self.mail_port) | |
72 |
|
73 | |||
73 | if self.tls: |
|
74 | if self.tls: | |
74 | smtp_serv.ehlo() |
|
75 | smtp_serv.ehlo() | |
75 | smtp_serv.starttls() |
|
76 | smtp_serv.starttls() | |
76 |
|
77 | |||
77 | if self.debug: |
|
78 | if self.debug: | |
78 | smtp_serv.set_debuglevel(1) |
|
79 | smtp_serv.set_debuglevel(1) | |
79 |
|
80 | |||
80 | smtp_serv.ehlo() |
|
81 | smtp_serv.ehlo() | |
|
82 | if self.auth: | |||
|
83 | smtp_serv.esmtp_features["auth"] = self.auth | |||
81 |
|
84 | |||
82 | #if server requires authorization you must provide login and password |
|
85 | # if server requires authorization you must provide login and password | |
83 | #but only if we have them |
|
86 | # but only if we have them | |
84 | if self.user and self.passwd: |
|
87 | if self.user and self.passwd: | |
85 | smtp_serv.login(self.user, self.passwd) |
|
88 | smtp_serv.login(self.user, self.passwd) | |
86 |
|
89 | |||
87 | date_ = formatdate(localtime=True) |
|
90 | date_ = formatdate(localtime=True) | |
88 | msg = MIMEMultipart() |
|
91 | msg = MIMEMultipart() | |
89 | msg.set_type('multipart/alternative') |
|
92 | msg.set_type('multipart/alternative') | |
90 | msg.preamble = 'You will not see this in a MIME-aware mail reader.\n' |
|
93 | msg.preamble = 'You will not see this in a MIME-aware mail reader.\n' | |
91 |
|
94 | |||
92 | text_msg = MIMEText(body) |
|
95 | text_msg = MIMEText(body) | |
93 | text_msg.set_type('text/plain') |
|
96 | text_msg.set_type('text/plain') | |
94 | text_msg.set_param('charset', 'UTF-8') |
|
97 | text_msg.set_param('charset', 'UTF-8') | |
95 |
|
98 | |||
96 | msg['From'] = self.mail_from |
|
99 | msg['From'] = self.mail_from | |
97 | msg['To'] = ','.join(recipients) |
|
100 | msg['To'] = ','.join(recipients) | |
98 | msg['Date'] = date_ |
|
101 | msg['Date'] = date_ | |
99 | msg['Subject'] = subject |
|
102 | msg['Subject'] = subject | |
100 |
|
103 | |||
101 | msg.attach(text_msg) |
|
104 | msg.attach(text_msg) | |
102 |
|
105 | |||
103 | if attachment_files: |
|
106 | if attachment_files: | |
104 | self.__atach_files(msg, attachment_files) |
|
107 | self.__atach_files(msg, attachment_files) | |
105 |
|
108 | |||
106 | smtp_serv.sendmail(self.mail_from, recipients, msg.as_string()) |
|
109 | smtp_serv.sendmail(self.mail_from, recipients, msg.as_string()) | |
107 | logging.info('MAIL SEND TO: %s' % recipients) |
|
110 | logging.info('MAIL SEND TO: %s' % recipients) | |
108 |
|
111 | |||
109 | try: |
|
112 | try: | |
110 | smtp_serv.quit() |
|
113 | smtp_serv.quit() | |
111 | except sslerror: |
|
114 | except sslerror: | |
112 | # sslerror is raised in tls connections on closing sometimes |
|
115 | # sslerror is raised in tls connections on closing sometimes | |
113 | pass |
|
116 | pass | |
114 |
|
117 | |||
115 | def __atach_files(self, msg, attachment_files): |
|
118 | def __atach_files(self, msg, attachment_files): | |
116 | if isinstance(attachment_files, dict): |
|
119 | if isinstance(attachment_files, dict): | |
117 | for f_name, msg_file in attachment_files.items(): |
|
120 | for f_name, msg_file in attachment_files.items(): | |
118 | ctype, encoding = mimetypes.guess_type(f_name) |
|
121 | ctype, encoding = mimetypes.guess_type(f_name) | |
119 | logging.info("guessing file %s type based on %s", ctype, |
|
122 | logging.info("guessing file %s type based on %s", ctype, | |
120 | f_name) |
|
123 | f_name) | |
121 | if ctype is None or encoding is not None: |
|
124 | if ctype is None or encoding is not None: | |
122 | # No guess could be made, or the file is encoded |
|
125 | # No guess could be made, or the file is encoded | |
123 | # (compressed), so use a generic bag-of-bits type. |
|
126 | # (compressed), so use a generic bag-of-bits type. | |
124 | ctype = 'application/octet-stream' |
|
127 | ctype = 'application/octet-stream' | |
125 | maintype, subtype = ctype.split('/', 1) |
|
128 | maintype, subtype = ctype.split('/', 1) | |
126 | if maintype == 'text': |
|
129 | if maintype == 'text': | |
127 | # Note: we should handle calculating the charset |
|
130 | # Note: we should handle calculating the charset | |
128 | file_part = MIMEText(self.get_content(msg_file), |
|
131 | file_part = MIMEText(self.get_content(msg_file), | |
129 | _subtype=subtype) |
|
132 | _subtype=subtype) | |
130 | elif maintype == 'image': |
|
133 | elif maintype == 'image': | |
131 | file_part = MIMEImage(self.get_content(msg_file), |
|
134 | file_part = MIMEImage(self.get_content(msg_file), | |
132 | _subtype=subtype) |
|
135 | _subtype=subtype) | |
133 | elif maintype == 'audio': |
|
136 | elif maintype == 'audio': | |
134 | file_part = MIMEAudio(self.get_content(msg_file), |
|
137 | file_part = MIMEAudio(self.get_content(msg_file), | |
135 | _subtype=subtype) |
|
138 | _subtype=subtype) | |
136 | else: |
|
139 | else: | |
137 | file_part = MIMEBase(maintype, subtype) |
|
140 | file_part = MIMEBase(maintype, subtype) | |
138 | file_part.set_payload(self.get_content(msg_file)) |
|
141 | file_part.set_payload(self.get_content(msg_file)) | |
139 | # Encode the payload using Base64 |
|
142 | # Encode the payload using Base64 | |
140 | encoders.encode_base64(msg) |
|
143 | encoders.encode_base64(msg) | |
141 | # Set the filename parameter |
|
144 | # Set the filename parameter | |
142 | file_part.add_header('Content-Disposition', 'attachment', |
|
145 | file_part.add_header('Content-Disposition', 'attachment', | |
143 | filename=f_name) |
|
146 | filename=f_name) | |
144 | file_part.add_header('Content-Type', ctype, name=f_name) |
|
147 | file_part.add_header('Content-Type', ctype, name=f_name) | |
145 | msg.attach(file_part) |
|
148 | msg.attach(file_part) | |
146 | else: |
|
149 | else: | |
147 | raise Exception('Attachment files should be' |
|
150 | raise Exception('Attachment files should be' | |
148 | 'a dict in format {"filename":"filepath"}') |
|
151 | 'a dict in format {"filename":"filepath"}') | |
149 |
|
152 | |||
150 | def get_content(self, msg_file): |
|
153 | def get_content(self, msg_file): | |
151 | """Get content based on type, if content is a string do open first |
|
154 | """Get content based on type, if content is a string do open first | |
152 | else just read because it's a probably open file object |
|
155 | else just read because it's a probably open file object | |
153 |
|
156 | |||
154 | :param msg_file: |
|
157 | :param msg_file: | |
155 | """ |
|
158 | """ | |
156 | if isinstance(msg_file, str): |
|
159 | if isinstance(msg_file, str): | |
157 | return open(msg_file, "rb").read() |
|
160 | return open(msg_file, "rb").read() | |
158 | else: |
|
161 | else: | |
159 | #just for safe seek to 0 |
|
162 | # just for safe seek to 0 | |
160 | msg_file.seek(0) |
|
163 | msg_file.seek(0) | |
161 | return msg_file.read() |
|
164 | return msg_file.read() | |
|
165 |
@@ -1,1051 +1,1069 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """ |
|
2 | """ | |
3 | rhodecode.model.db |
|
3 | rhodecode.model.db | |
4 | ~~~~~~~~~~~~~~~~~~ |
|
4 | ~~~~~~~~~~~~~~~~~~ | |
5 |
|
5 | |||
6 | Database Models for RhodeCode |
|
6 | Database Models for RhodeCode | |
7 |
|
7 | |||
8 | :created_on: Apr 08, 2010 |
|
8 | :created_on: Apr 08, 2010 | |
9 | :author: marcink |
|
9 | :author: marcink | |
10 | :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> |
|
10 | :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> | |
11 | :license: GPLv3, see COPYING for more details. |
|
11 | :license: GPLv3, see COPYING for more details. | |
12 | """ |
|
12 | """ | |
13 | # This program is free software: you can redistribute it and/or modify |
|
13 | # This program is free software: you can redistribute it and/or modify | |
14 | # it under the terms of the GNU General Public License as published by |
|
14 | # it under the terms of the GNU General Public License as published by | |
15 | # the Free Software Foundation, either version 3 of the License, or |
|
15 | # the Free Software Foundation, either version 3 of the License, or | |
16 | # (at your option) any later version. |
|
16 | # (at your option) any later version. | |
17 | # |
|
17 | # | |
18 | # This program is distributed in the hope that it will be useful, |
|
18 | # This program is distributed in the hope that it will be useful, | |
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 | # GNU General Public License for more details. |
|
21 | # GNU General Public License for more details. | |
22 | # |
|
22 | # | |
23 | # You should have received a copy of the GNU General Public License |
|
23 | # You should have received a copy of the GNU General Public License | |
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
25 |
|
25 | |||
26 | import os |
|
26 | import os | |
27 | import logging |
|
27 | import logging | |
28 | import datetime |
|
28 | import datetime | |
29 | import traceback |
|
29 | import traceback | |
30 | from datetime import date |
|
30 | from datetime import date | |
31 |
|
31 | |||
32 | from sqlalchemy import * |
|
32 | from sqlalchemy import * | |
33 | from sqlalchemy.exc import DatabaseError |
|
|||
34 | from sqlalchemy.ext.hybrid import hybrid_property |
|
33 | from sqlalchemy.ext.hybrid import hybrid_property | |
35 |
from sqlalchemy.orm import relationship, |
|
34 | from sqlalchemy.orm import relationship, joinedload, class_mapper, validates | |
36 | validates |
|
|||
37 | from sqlalchemy.orm.interfaces import MapperExtension |
|
|||
38 | from beaker.cache import cache_region, region_invalidate |
|
35 | from beaker.cache import cache_region, region_invalidate | |
39 |
|
36 | |||
40 | from vcs import get_backend |
|
37 | from vcs import get_backend | |
41 | from vcs.utils.helpers import get_scm |
|
38 | from vcs.utils.helpers import get_scm | |
42 | from vcs.exceptions import VCSError |
|
39 | from vcs.exceptions import VCSError | |
43 | from vcs.utils.lazy import LazyProperty |
|
40 | from vcs.utils.lazy import LazyProperty | |
44 |
|
41 | |||
45 | from rhodecode.lib import str2bool, safe_str, get_changeset_safe, \ |
|
42 | from rhodecode.lib import str2bool, safe_str, get_changeset_safe, \ | |
46 | generate_api_key, safe_unicode |
|
43 | generate_api_key, safe_unicode | |
47 | from rhodecode.lib.exceptions import UsersGroupsAssignedException |
|
44 | from rhodecode.lib.exceptions import UsersGroupsAssignedException | |
48 | from rhodecode.lib.compat import json |
|
45 | from rhodecode.lib.compat import json | |
49 |
|
46 | |||
50 | from rhodecode.model.meta import Base, Session |
|
47 | from rhodecode.model.meta import Base, Session | |
51 | from rhodecode.model.caching_query import FromCache |
|
48 | from rhodecode.model.caching_query import FromCache | |
52 |
|
49 | |||
53 |
|
50 | |||
54 | log = logging.getLogger(__name__) |
|
51 | log = logging.getLogger(__name__) | |
55 |
|
52 | |||
56 | #============================================================================== |
|
53 | #============================================================================== | |
57 | # BASE CLASSES |
|
54 | # BASE CLASSES | |
58 | #============================================================================== |
|
55 | #============================================================================== | |
59 |
|
56 | |||
60 | class ModelSerializer(json.JSONEncoder): |
|
57 | class ModelSerializer(json.JSONEncoder): | |
61 | """ |
|
58 | """ | |
62 | Simple Serializer for JSON, |
|
59 | Simple Serializer for JSON, | |
63 |
|
60 | |||
64 | usage:: |
|
61 | usage:: | |
65 |
|
62 | |||
66 | to make object customized for serialization implement a __json__ |
|
63 | to make object customized for serialization implement a __json__ | |
67 | method that will return a dict for serialization into json |
|
64 | method that will return a dict for serialization into json | |
68 |
|
65 | |||
69 | example:: |
|
66 | example:: | |
70 |
|
67 | |||
71 | class Task(object): |
|
68 | class Task(object): | |
72 |
|
69 | |||
73 | def __init__(self, name, value): |
|
70 | def __init__(self, name, value): | |
74 | self.name = name |
|
71 | self.name = name | |
75 | self.value = value |
|
72 | self.value = value | |
76 |
|
73 | |||
77 | def __json__(self): |
|
74 | def __json__(self): | |
78 | return dict(name=self.name, |
|
75 | return dict(name=self.name, | |
79 |
value=self.value) |
|
76 | value=self.value) | |
80 |
|
77 | |||
81 | """ |
|
78 | """ | |
82 |
|
79 | |||
83 | def default(self, obj): |
|
80 | def default(self, obj): | |
84 |
|
81 | |||
85 | if hasattr(obj, '__json__'): |
|
82 | if hasattr(obj, '__json__'): | |
86 | return obj.__json__() |
|
83 | return obj.__json__() | |
87 | else: |
|
84 | else: | |
88 | return json.JSONEncoder.default(self, obj) |
|
85 | return json.JSONEncoder.default(self, obj) | |
89 |
|
86 | |||
90 | class BaseModel(object): |
|
87 | class BaseModel(object): | |
91 | """Base Model for all classess |
|
88 | """Base Model for all classess | |
92 |
|
89 | |||
93 | """ |
|
90 | """ | |
94 |
|
91 | |||
95 | @classmethod |
|
92 | @classmethod | |
96 | def _get_keys(cls): |
|
93 | def _get_keys(cls): | |
97 | """return column names for this model """ |
|
94 | """return column names for this model """ | |
98 | return class_mapper(cls).c.keys() |
|
95 | return class_mapper(cls).c.keys() | |
99 |
|
96 | |||
100 | def get_dict(self): |
|
97 | def get_dict(self): | |
101 | """return dict with keys and values corresponding |
|
98 | """return dict with keys and values corresponding | |
102 | to this model data """ |
|
99 | to this model data """ | |
103 |
|
100 | |||
104 | d = {} |
|
101 | d = {} | |
105 | for k in self._get_keys(): |
|
102 | for k in self._get_keys(): | |
106 | d[k] = getattr(self, k) |
|
103 | d[k] = getattr(self, k) | |
107 | return d |
|
104 | return d | |
108 |
|
105 | |||
109 | def get_appstruct(self): |
|
106 | def get_appstruct(self): | |
110 | """return list with keys and values tupples corresponding |
|
107 | """return list with keys and values tupples corresponding | |
111 | to this model data """ |
|
108 | to this model data """ | |
112 |
|
109 | |||
113 | l = [] |
|
110 | l = [] | |
114 | for k in self._get_keys(): |
|
111 | for k in self._get_keys(): | |
115 | l.append((k, getattr(self, k),)) |
|
112 | l.append((k, getattr(self, k),)) | |
116 | return l |
|
113 | return l | |
117 |
|
114 | |||
118 | def populate_obj(self, populate_dict): |
|
115 | def populate_obj(self, populate_dict): | |
119 | """populate model with data from given populate_dict""" |
|
116 | """populate model with data from given populate_dict""" | |
120 |
|
117 | |||
121 | for k in self._get_keys(): |
|
118 | for k in self._get_keys(): | |
122 | if k in populate_dict: |
|
119 | if k in populate_dict: | |
123 | setattr(self, k, populate_dict[k]) |
|
120 | setattr(self, k, populate_dict[k]) | |
124 |
|
121 | |||
125 | @classmethod |
|
122 | @classmethod | |
126 | def query(cls): |
|
123 | def query(cls): | |
127 | return Session.query(cls) |
|
124 | return Session.query(cls) | |
128 |
|
125 | |||
129 | @classmethod |
|
126 | @classmethod | |
130 | def get(cls, id_): |
|
127 | def get(cls, id_): | |
131 | if id_: |
|
128 | if id_: | |
132 |
return |
|
129 | return cls.query().get(id_) | |
|
130 | ||||
|
131 | @classmethod | |||
|
132 | def getAll(cls): | |||
|
133 | return cls.query().all() | |||
133 |
|
134 | |||
134 | @classmethod |
|
135 | @classmethod | |
135 | def delete(cls, id_): |
|
136 | def delete(cls, id_): | |
136 |
obj = |
|
137 | obj = cls.query().get(id_) | |
137 | Session.delete(obj) |
|
138 | Session.delete(obj) | |
138 | Session.commit() |
|
139 | Session.commit() | |
139 |
|
140 | |||
140 |
|
141 | |||
141 | class RhodeCodeSettings(Base, BaseModel): |
|
142 | class RhodeCodeSettings(Base, BaseModel): | |
142 | __tablename__ = 'rhodecode_settings' |
|
143 | __tablename__ = 'rhodecode_settings' | |
143 | __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True}) |
|
144 | __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True}) | |
144 | app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) |
|
145 | app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) | |
145 | app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) |
|
146 | app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) | |
146 | _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) |
|
147 | _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) | |
147 |
|
148 | |||
148 | def __init__(self, k='', v=''): |
|
149 | def __init__(self, k='', v=''): | |
149 | self.app_settings_name = k |
|
150 | self.app_settings_name = k | |
150 | self.app_settings_value = v |
|
151 | self.app_settings_value = v | |
151 |
|
152 | |||
152 |
|
153 | |||
153 | @validates('_app_settings_value') |
|
154 | @validates('_app_settings_value') | |
154 | def validate_settings_value(self, key, val): |
|
155 | def validate_settings_value(self, key, val): | |
155 | assert type(val) == unicode |
|
156 | assert type(val) == unicode | |
156 | return val |
|
157 | return val | |
157 |
|
158 | |||
158 | @hybrid_property |
|
159 | @hybrid_property | |
159 | def app_settings_value(self): |
|
160 | def app_settings_value(self): | |
160 | v = self._app_settings_value |
|
161 | v = self._app_settings_value | |
161 | if v == 'ldap_active': |
|
162 | if v == 'ldap_active': | |
162 | v = str2bool(v) |
|
163 | v = str2bool(v) | |
163 |
return v |
|
164 | return v | |
164 |
|
165 | |||
165 | @app_settings_value.setter |
|
166 | @app_settings_value.setter | |
166 | def app_settings_value(self,val): |
|
167 | def app_settings_value(self, val): | |
167 | """ |
|
168 | """ | |
168 | Setter that will always make sure we use unicode in app_settings_value |
|
169 | Setter that will always make sure we use unicode in app_settings_value | |
169 |
|
170 | |||
170 | :param val: |
|
171 | :param val: | |
171 | """ |
|
172 | """ | |
172 | self._app_settings_value = safe_unicode(val) |
|
173 | self._app_settings_value = safe_unicode(val) | |
173 |
|
174 | |||
174 | def __repr__(self): |
|
175 | def __repr__(self): | |
175 | return "<%s('%s:%s')>" % (self.__class__.__name__, |
|
176 | return "<%s('%s:%s')>" % (self.__class__.__name__, | |
176 | self.app_settings_name, self.app_settings_value) |
|
177 | self.app_settings_name, self.app_settings_value) | |
177 |
|
178 | |||
178 |
|
179 | |||
179 | @classmethod |
|
180 | @classmethod | |
180 | def get_by_name(cls, ldap_key): |
|
181 | def get_by_name(cls, ldap_key): | |
181 |
return |
|
182 | return cls.query()\ | |
182 | .filter(cls.app_settings_name == ldap_key).scalar() |
|
183 | .filter(cls.app_settings_name == ldap_key).scalar() | |
183 |
|
184 | |||
184 | @classmethod |
|
185 | @classmethod | |
185 | def get_app_settings(cls, cache=False): |
|
186 | def get_app_settings(cls, cache=False): | |
186 |
|
187 | |||
187 |
ret = |
|
188 | ret = cls.query() | |
188 |
|
189 | |||
189 | if cache: |
|
190 | if cache: | |
190 | ret = ret.options(FromCache("sql_cache_short", "get_hg_settings")) |
|
191 | ret = ret.options(FromCache("sql_cache_short", "get_hg_settings")) | |
191 |
|
192 | |||
192 | if not ret: |
|
193 | if not ret: | |
193 | raise Exception('Could not get application settings !') |
|
194 | raise Exception('Could not get application settings !') | |
194 | settings = {} |
|
195 | settings = {} | |
195 | for each in ret: |
|
196 | for each in ret: | |
196 | settings['rhodecode_' + each.app_settings_name] = \ |
|
197 | settings['rhodecode_' + each.app_settings_name] = \ | |
197 | each.app_settings_value |
|
198 | each.app_settings_value | |
198 |
|
199 | |||
199 | return settings |
|
200 | return settings | |
200 |
|
201 | |||
201 | @classmethod |
|
202 | @classmethod | |
202 | def get_ldap_settings(cls, cache=False): |
|
203 | def get_ldap_settings(cls, cache=False): | |
203 |
ret = |
|
204 | ret = cls.query()\ | |
204 | .filter(cls.app_settings_name.startswith('ldap_')).all() |
|
205 | .filter(cls.app_settings_name.startswith('ldap_')).all() | |
205 | fd = {} |
|
206 | fd = {} | |
206 | for row in ret: |
|
207 | for row in ret: | |
207 | fd.update({row.app_settings_name:row.app_settings_value}) |
|
208 | fd.update({row.app_settings_name:row.app_settings_value}) | |
208 |
|
209 | |||
209 | return fd |
|
210 | return fd | |
210 |
|
211 | |||
211 |
|
212 | |||
212 | class RhodeCodeUi(Base, BaseModel): |
|
213 | class RhodeCodeUi(Base, BaseModel): | |
213 | __tablename__ = 'rhodecode_ui' |
|
214 | __tablename__ = 'rhodecode_ui' | |
214 | __table_args__ = (UniqueConstraint('ui_key'), {'extend_existing':True}) |
|
215 | __table_args__ = (UniqueConstraint('ui_key'), {'extend_existing':True}) | |
215 |
|
216 | |||
216 | HOOK_UPDATE = 'changegroup.update' |
|
217 | HOOK_UPDATE = 'changegroup.update' | |
217 | HOOK_REPO_SIZE = 'changegroup.repo_size' |
|
218 | HOOK_REPO_SIZE = 'changegroup.repo_size' | |
218 | HOOK_PUSH = 'pretxnchangegroup.push_logger' |
|
219 | HOOK_PUSH = 'pretxnchangegroup.push_logger' | |
219 | HOOK_PULL = 'preoutgoing.pull_logger' |
|
220 | HOOK_PULL = 'preoutgoing.pull_logger' | |
220 |
|
221 | |||
221 | ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) |
|
222 | ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) | |
222 | ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) |
|
223 | ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) | |
223 | ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) |
|
224 | ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) | |
224 | ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) |
|
225 | ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) | |
225 | ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True) |
|
226 | ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True) | |
226 |
|
227 | |||
227 |
|
228 | |||
228 | @classmethod |
|
229 | @classmethod | |
229 | def get_by_key(cls, key): |
|
230 | def get_by_key(cls, key): | |
230 |
return |
|
231 | return cls.query().filter(cls.ui_key == key) | |
231 |
|
232 | |||
232 |
|
233 | |||
233 | @classmethod |
|
234 | @classmethod | |
234 | def get_builtin_hooks(cls): |
|
235 | def get_builtin_hooks(cls): | |
235 | q = cls.query() |
|
236 | q = cls.query() | |
236 | q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, |
|
237 | q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, | |
237 | cls.HOOK_REPO_SIZE, |
|
238 | cls.HOOK_REPO_SIZE, | |
238 | cls.HOOK_PUSH, cls.HOOK_PULL])) |
|
239 | cls.HOOK_PUSH, cls.HOOK_PULL])) | |
239 | return q.all() |
|
240 | return q.all() | |
240 |
|
241 | |||
241 | @classmethod |
|
242 | @classmethod | |
242 | def get_custom_hooks(cls): |
|
243 | def get_custom_hooks(cls): | |
243 | q = cls.query() |
|
244 | q = cls.query() | |
244 | q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, |
|
245 | q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, | |
245 | cls.HOOK_REPO_SIZE, |
|
246 | cls.HOOK_REPO_SIZE, | |
246 | cls.HOOK_PUSH, cls.HOOK_PULL])) |
|
247 | cls.HOOK_PUSH, cls.HOOK_PULL])) | |
247 | q = q.filter(cls.ui_section == 'hooks') |
|
248 | q = q.filter(cls.ui_section == 'hooks') | |
248 | return q.all() |
|
249 | return q.all() | |
249 |
|
250 | |||
250 | @classmethod |
|
251 | @classmethod | |
251 | def create_or_update_hook(cls, key, val): |
|
252 | def create_or_update_hook(cls, key, val): | |
252 | new_ui = cls.get_by_key(key).scalar() or cls() |
|
253 | new_ui = cls.get_by_key(key).scalar() or cls() | |
253 | new_ui.ui_section = 'hooks' |
|
254 | new_ui.ui_section = 'hooks' | |
254 | new_ui.ui_active = True |
|
255 | new_ui.ui_active = True | |
255 | new_ui.ui_key = key |
|
256 | new_ui.ui_key = key | |
256 | new_ui.ui_value = val |
|
257 | new_ui.ui_value = val | |
257 |
|
258 | |||
258 | Session.add(new_ui) |
|
259 | Session.add(new_ui) | |
259 | Session.commit() |
|
260 | Session.commit() | |
260 |
|
261 | |||
261 |
|
262 | |||
262 | class User(Base, BaseModel): |
|
263 | class User(Base, BaseModel): | |
263 | __tablename__ = 'users' |
|
264 | __tablename__ = 'users' | |
264 | __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True}) |
|
265 | __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True}) | |
265 | user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) |
|
266 | user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) | |
266 | username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) |
|
267 | username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) | |
267 | password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) |
|
268 | password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) | |
268 | active = Column("active", Boolean(), nullable=True, unique=None, default=None) |
|
269 | active = Column("active", Boolean(), nullable=True, unique=None, default=None) | |
269 | admin = Column("admin", Boolean(), nullable=True, unique=None, default=False) |
|
270 | admin = Column("admin", Boolean(), nullable=True, unique=None, default=False) | |
270 | name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) |
|
271 | name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) | |
271 | lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) |
|
272 | lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) | |
272 | email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) |
|
273 | email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) | |
273 | last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None) |
|
274 | last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None) | |
274 | ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) |
|
275 | ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) | |
275 | api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) |
|
276 | api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) | |
276 |
|
277 | |||
277 | user_log = relationship('UserLog', cascade='all') |
|
278 | user_log = relationship('UserLog', cascade='all') | |
278 | user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all') |
|
279 | user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all') | |
279 |
|
280 | |||
280 | repositories = relationship('Repository') |
|
281 | repositories = relationship('Repository') | |
281 | user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all') |
|
282 | user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all') | |
282 | repo_to_perm = relationship('RepoToPerm', primaryjoin='RepoToPerm.user_id==User.user_id', cascade='all') |
|
283 | repo_to_perm = relationship('RepoToPerm', primaryjoin='RepoToPerm.user_id==User.user_id', cascade='all') | |
283 |
|
284 | |||
284 | group_member = relationship('UsersGroupMember', cascade='all') |
|
285 | group_member = relationship('UsersGroupMember', cascade='all') | |
285 |
|
286 | |||
286 | @property |
|
287 | @property | |
287 | def full_contact(self): |
|
288 | def full_contact(self): | |
288 | return '%s %s <%s>' % (self.name, self.lastname, self.email) |
|
289 | return '%s %s <%s>' % (self.name, self.lastname, self.email) | |
289 |
|
290 | |||
290 | @property |
|
291 | @property | |
291 | def short_contact(self): |
|
292 | def short_contact(self): | |
292 | return '%s %s' % (self.name, self.lastname) |
|
293 | return '%s %s' % (self.name, self.lastname) | |
293 |
|
294 | |||
294 | @property |
|
295 | @property | |
295 | def is_admin(self): |
|
296 | def is_admin(self): | |
296 | return self.admin |
|
297 | return self.admin | |
297 |
|
298 | |||
298 | def __repr__(self): |
|
299 | def __repr__(self): | |
299 | try: |
|
300 | try: | |
300 | return "<%s('id:%s:%s')>" % (self.__class__.__name__, |
|
301 | return "<%s('id:%s:%s')>" % (self.__class__.__name__, | |
301 | self.user_id, self.username) |
|
302 | self.user_id, self.username) | |
302 | except: |
|
303 | except: | |
303 | return self.__class__.__name__ |
|
304 | return self.__class__.__name__ | |
304 |
|
305 | |||
305 | @classmethod |
|
306 | @classmethod | |
306 | def get_by_username(cls, username, case_insensitive=False): |
|
307 | def get_by_username(cls, username, case_insensitive=False): | |
307 | if case_insensitive: |
|
308 | if case_insensitive: | |
308 | return Session.query(cls).filter(cls.username.ilike(username)).scalar() |
|
309 | return Session.query(cls).filter(cls.username.ilike(username)).scalar() | |
309 | else: |
|
310 | else: | |
310 | return Session.query(cls).filter(cls.username == username).scalar() |
|
311 | return Session.query(cls).filter(cls.username == username).scalar() | |
311 |
|
312 | |||
312 | @classmethod |
|
313 | @classmethod | |
313 | def get_by_api_key(cls, api_key): |
|
314 | def get_by_api_key(cls, api_key): | |
314 |
return |
|
315 | return cls.query().filter(cls.api_key == api_key).one() | |
315 |
|
316 | |||
316 | def update_lastlogin(self): |
|
317 | def update_lastlogin(self): | |
317 | """Update user lastlogin""" |
|
318 | """Update user lastlogin""" | |
318 |
|
319 | |||
319 | self.last_login = datetime.datetime.now() |
|
320 | self.last_login = datetime.datetime.now() | |
320 | Session.add(self) |
|
321 | Session.add(self) | |
321 | Session.commit() |
|
322 | Session.commit() | |
322 | log.debug('updated user %s lastlogin', self.username) |
|
323 | log.debug('updated user %s lastlogin', self.username) | |
323 |
|
324 | |||
324 | @classmethod |
|
325 | @classmethod | |
325 | def create(cls, form_data): |
|
326 | def create(cls, form_data): | |
326 | from rhodecode.lib.auth import get_crypt_password |
|
327 | from rhodecode.lib.auth import get_crypt_password | |
327 |
|
328 | |||
328 | try: |
|
329 | try: | |
329 | new_user = cls() |
|
330 | new_user = cls() | |
330 | for k, v in form_data.items(): |
|
331 | for k, v in form_data.items(): | |
331 | if k == 'password': |
|
332 | if k == 'password': | |
332 | v = get_crypt_password(v) |
|
333 | v = get_crypt_password(v) | |
333 | setattr(new_user, k, v) |
|
334 | setattr(new_user, k, v) | |
334 |
|
335 | |||
335 | new_user.api_key = generate_api_key(form_data['username']) |
|
336 | new_user.api_key = generate_api_key(form_data['username']) | |
336 | Session.add(new_user) |
|
337 | Session.add(new_user) | |
337 | Session.commit() |
|
338 | Session.commit() | |
338 | return new_user |
|
339 | return new_user | |
339 | except: |
|
340 | except: | |
340 | log.error(traceback.format_exc()) |
|
341 | log.error(traceback.format_exc()) | |
341 | Session.rollback() |
|
342 | Session.rollback() | |
342 | raise |
|
343 | raise | |
343 |
|
344 | |||
344 | class UserLog(Base, BaseModel): |
|
345 | class UserLog(Base, BaseModel): | |
345 | __tablename__ = 'user_logs' |
|
346 | __tablename__ = 'user_logs' | |
346 | __table_args__ = {'extend_existing':True} |
|
347 | __table_args__ = {'extend_existing':True} | |
347 | user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) |
|
348 | user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) | |
348 | user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None) |
|
349 | user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None) | |
349 | repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None) |
|
350 | repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None) | |
350 | repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) |
|
351 | repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) | |
351 | user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) |
|
352 | user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) | |
352 | action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) |
|
353 | action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) | |
353 | action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None) |
|
354 | action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None) | |
354 |
|
355 | |||
355 | @property |
|
356 | @property | |
356 | def action_as_day(self): |
|
357 | def action_as_day(self): | |
357 | return date(*self.action_date.timetuple()[:3]) |
|
358 | return date(*self.action_date.timetuple()[:3]) | |
358 |
|
359 | |||
359 | user = relationship('User') |
|
360 | user = relationship('User') | |
360 | repository = relationship('Repository') |
|
361 | repository = relationship('Repository') | |
361 |
|
362 | |||
362 |
|
363 | |||
363 | class UsersGroup(Base, BaseModel): |
|
364 | class UsersGroup(Base, BaseModel): | |
364 | __tablename__ = 'users_groups' |
|
365 | __tablename__ = 'users_groups' | |
365 | __table_args__ = {'extend_existing':True} |
|
366 | __table_args__ = {'extend_existing':True} | |
366 |
|
367 | |||
367 | users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) |
|
368 | users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) | |
368 | users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None) |
|
369 | users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None) | |
369 | users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None) |
|
370 | users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None) | |
370 |
|
371 | |||
371 | members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined") |
|
372 | members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined") | |
372 |
|
373 | |||
373 | def __repr__(self): |
|
374 | def __repr__(self): | |
374 | return '<userGroup(%s)>' % (self.users_group_name) |
|
375 | return '<userGroup(%s)>' % (self.users_group_name) | |
375 |
|
376 | |||
376 | @classmethod |
|
377 | @classmethod | |
377 | def get_by_group_name(cls, group_name, cache=False, case_insensitive=False): |
|
378 | def get_by_group_name(cls, group_name, cache=False, case_insensitive=False): | |
378 | if case_insensitive: |
|
379 | if case_insensitive: | |
379 |
gr = |
|
380 | gr = cls.query()\ | |
380 | .filter(cls.users_group_name.ilike(group_name)) |
|
381 | .filter(cls.users_group_name.ilike(group_name)) | |
381 | else: |
|
382 | else: | |
382 |
gr = |
|
383 | gr = cls.query()\ | |
383 |
.filter( |
|
384 | .filter(cls.users_group_name == group_name) | |
384 | if cache: |
|
385 | if cache: | |
385 | gr = gr.options(FromCache("sql_cache_short", |
|
386 | gr = gr.options(FromCache("sql_cache_short", | |
386 | "get_user_%s" % group_name)) |
|
387 | "get_user_%s" % group_name)) | |
387 | return gr.scalar() |
|
388 | return gr.scalar() | |
388 |
|
389 | |||
389 |
|
390 | |||
390 | @classmethod |
|
391 | @classmethod | |
391 | def get(cls, users_group_id, cache=False): |
|
392 | def get(cls, users_group_id, cache=False): | |
392 |
users_group = |
|
393 | users_group = cls.query() | |
393 | if cache: |
|
394 | if cache: | |
394 | users_group = users_group.options(FromCache("sql_cache_short", |
|
395 | users_group = users_group.options(FromCache("sql_cache_short", | |
395 | "get_users_group_%s" % users_group_id)) |
|
396 | "get_users_group_%s" % users_group_id)) | |
396 | return users_group.get(users_group_id) |
|
397 | return users_group.get(users_group_id) | |
397 |
|
398 | |||
398 | @classmethod |
|
399 | @classmethod | |
399 | def create(cls, form_data): |
|
400 | def create(cls, form_data): | |
400 | try: |
|
401 | try: | |
401 | new_users_group = cls() |
|
402 | new_users_group = cls() | |
402 | for k, v in form_data.items(): |
|
403 | for k, v in form_data.items(): | |
403 | setattr(new_users_group, k, v) |
|
404 | setattr(new_users_group, k, v) | |
404 |
|
405 | |||
405 | Session.add(new_users_group) |
|
406 | Session.add(new_users_group) | |
406 | Session.commit() |
|
407 | Session.commit() | |
407 | return new_users_group |
|
408 | return new_users_group | |
408 | except: |
|
409 | except: | |
409 | log.error(traceback.format_exc()) |
|
410 | log.error(traceback.format_exc()) | |
410 | Session.rollback() |
|
411 | Session.rollback() | |
411 | raise |
|
412 | raise | |
412 |
|
413 | |||
413 | @classmethod |
|
414 | @classmethod | |
414 | def update(cls, users_group_id, form_data): |
|
415 | def update(cls, users_group_id, form_data): | |
415 |
|
416 | |||
416 | try: |
|
417 | try: | |
417 | users_group = cls.get(users_group_id, cache=False) |
|
418 | users_group = cls.get(users_group_id, cache=False) | |
418 |
|
419 | |||
419 | for k, v in form_data.items(): |
|
420 | for k, v in form_data.items(): | |
420 | if k == 'users_group_members': |
|
421 | if k == 'users_group_members': | |
421 | users_group.members = [] |
|
422 | users_group.members = [] | |
422 | Session.flush() |
|
423 | Session.flush() | |
423 | members_list = [] |
|
424 | members_list = [] | |
424 | if v: |
|
425 | if v: | |
|
426 | v = [v] if isinstance(v, basestring) else v | |||
425 | for u_id in set(v): |
|
427 | for u_id in set(v): | |
426 |
member |
|
428 | member = UsersGroupMember(users_group_id, u_id) | |
427 | users_group_id, |
|
429 | members_list.append(member) | |
428 | u_id)) |
|
|||
429 | setattr(users_group, 'members', members_list) |
|
430 | setattr(users_group, 'members', members_list) | |
430 | setattr(users_group, k, v) |
|
431 | setattr(users_group, k, v) | |
431 |
|
432 | |||
432 | Session.add(users_group) |
|
433 | Session.add(users_group) | |
433 | Session.commit() |
|
434 | Session.commit() | |
434 | except: |
|
435 | except: | |
435 | log.error(traceback.format_exc()) |
|
436 | log.error(traceback.format_exc()) | |
436 | Session.rollback() |
|
437 | Session.rollback() | |
437 | raise |
|
438 | raise | |
438 |
|
439 | |||
439 | @classmethod |
|
440 | @classmethod | |
440 | def delete(cls, users_group_id): |
|
441 | def delete(cls, users_group_id): | |
441 | try: |
|
442 | try: | |
442 |
|
443 | |||
443 | # check if this group is not assigned to repo |
|
444 | # check if this group is not assigned to repo | |
444 | assigned_groups = UsersGroupRepoToPerm.query()\ |
|
445 | assigned_groups = UsersGroupRepoToPerm.query()\ | |
445 | .filter(UsersGroupRepoToPerm.users_group_id == |
|
446 | .filter(UsersGroupRepoToPerm.users_group_id == | |
446 | users_group_id).all() |
|
447 | users_group_id).all() | |
447 |
|
448 | |||
448 | if assigned_groups: |
|
449 | if assigned_groups: | |
449 | raise UsersGroupsAssignedException('Group assigned to %s' % |
|
450 | raise UsersGroupsAssignedException('Group assigned to %s' % | |
450 | assigned_groups) |
|
451 | assigned_groups) | |
451 |
|
452 | |||
452 | users_group = cls.get(users_group_id, cache=False) |
|
453 | users_group = cls.get(users_group_id, cache=False) | |
453 | Session.delete(users_group) |
|
454 | Session.delete(users_group) | |
454 | Session.commit() |
|
455 | Session.commit() | |
455 | except: |
|
456 | except: | |
456 | log.error(traceback.format_exc()) |
|
457 | log.error(traceback.format_exc()) | |
457 | Session.rollback() |
|
458 | Session.rollback() | |
458 | raise |
|
459 | raise | |
459 |
|
460 | |||
460 |
|
||||
461 | class UsersGroupMember(Base, BaseModel): |
|
461 | class UsersGroupMember(Base, BaseModel): | |
462 | __tablename__ = 'users_groups_members' |
|
462 | __tablename__ = 'users_groups_members' | |
463 | __table_args__ = {'extend_existing':True} |
|
463 | __table_args__ = {'extend_existing':True} | |
464 |
|
464 | |||
465 | users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) |
|
465 | users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) | |
466 | users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None) |
|
466 | users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None) | |
467 | user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None) |
|
467 | user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None) | |
468 |
|
468 | |||
469 | user = relationship('User', lazy='joined') |
|
469 | user = relationship('User', lazy='joined') | |
470 | users_group = relationship('UsersGroup') |
|
470 | users_group = relationship('UsersGroup') | |
471 |
|
471 | |||
472 | def __init__(self, gr_id='', u_id=''): |
|
472 | def __init__(self, gr_id='', u_id=''): | |
473 | self.users_group_id = gr_id |
|
473 | self.users_group_id = gr_id | |
474 | self.user_id = u_id |
|
474 | self.user_id = u_id | |
475 |
|
475 | |||
|
476 | @staticmethod | |||
|
477 | def add_user_to_group(group, user): | |||
|
478 | ugm = UsersGroupMember() | |||
|
479 | ugm.users_group = group | |||
|
480 | ugm.user = user | |||
|
481 | Session.add(ugm) | |||
|
482 | Session.commit() | |||
|
483 | return ugm | |||
|
484 | ||||
476 | class Repository(Base, BaseModel): |
|
485 | class Repository(Base, BaseModel): | |
477 | __tablename__ = 'repositories' |
|
486 | __tablename__ = 'repositories' | |
478 | __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},) |
|
487 | __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},) | |
479 |
|
488 | |||
480 | repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) |
|
489 | repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) | |
481 | repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None) |
|
490 | repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None) | |
482 | clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None) |
|
491 | clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None) | |
483 | repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg') |
|
492 | repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg') | |
484 | user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None) |
|
493 | user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None) | |
485 | private = Column("private", Boolean(), nullable=True, unique=None, default=None) |
|
494 | private = Column("private", Boolean(), nullable=True, unique=None, default=None) | |
486 | enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True) |
|
495 | enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True) | |
487 | enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True) |
|
496 | enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True) | |
488 | description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) |
|
497 | description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) | |
489 | created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now) |
|
498 | created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now) | |
490 |
|
499 | |||
491 | fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None) |
|
500 | fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None) | |
492 | group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None) |
|
501 | group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None) | |
493 |
|
502 | |||
494 |
|
503 | |||
495 | user = relationship('User') |
|
504 | user = relationship('User') | |
496 | fork = relationship('Repository', remote_side=repo_id) |
|
505 | fork = relationship('Repository', remote_side=repo_id) | |
497 | group = relationship('Group') |
|
506 | group = relationship('Group') | |
498 | repo_to_perm = relationship('RepoToPerm', cascade='all', order_by='RepoToPerm.repo_to_perm_id') |
|
507 | repo_to_perm = relationship('RepoToPerm', cascade='all', order_by='RepoToPerm.repo_to_perm_id') | |
499 | users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all') |
|
508 | users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all') | |
500 | stats = relationship('Statistics', cascade='all', uselist=False) |
|
509 | stats = relationship('Statistics', cascade='all', uselist=False) | |
501 |
|
510 | |||
502 | followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all') |
|
511 | followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all') | |
503 |
|
512 | |||
504 | logs = relationship('UserLog', cascade='all') |
|
513 | logs = relationship('UserLog', cascade='all') | |
505 |
|
514 | |||
506 | def __repr__(self): |
|
515 | def __repr__(self): | |
507 | return "<%s('%s:%s')>" % (self.__class__.__name__, |
|
516 | return "<%s('%s:%s')>" % (self.__class__.__name__, | |
508 | self.repo_id, self.repo_name) |
|
517 | self.repo_id, self.repo_name) | |
509 |
|
518 | |||
510 | @classmethod |
|
519 | @classmethod | |
511 | def url_sep(cls): |
|
520 | def url_sep(cls): | |
512 | return '/' |
|
521 | return '/' | |
513 |
|
522 | |||
514 | @classmethod |
|
523 | @classmethod | |
515 | def get_by_repo_name(cls, repo_name): |
|
524 | def get_by_repo_name(cls, repo_name): | |
516 | q = Session.query(cls).filter(cls.repo_name == repo_name) |
|
525 | q = Session.query(cls).filter(cls.repo_name == repo_name) | |
517 |
|
||||
518 | q = q.options(joinedload(Repository.fork))\ |
|
526 | q = q.options(joinedload(Repository.fork))\ | |
519 | .options(joinedload(Repository.user))\ |
|
527 | .options(joinedload(Repository.user))\ | |
520 |
.options(joinedload(Repository.group)) |
|
528 | .options(joinedload(Repository.group)) | |
521 |
|
||||
522 | return q.one() |
|
529 | return q.one() | |
523 |
|
530 | |||
524 | @classmethod |
|
531 | @classmethod | |
525 | def get_repo_forks(cls, repo_id): |
|
532 | def get_repo_forks(cls, repo_id): | |
526 |
return |
|
533 | return cls.query().filter(Repository.fork_id == repo_id) | |
527 |
|
534 | |||
528 | @classmethod |
|
535 | @classmethod | |
529 | def base_path(cls): |
|
536 | def base_path(cls): | |
530 | """ |
|
537 | """ | |
531 | Returns base path when all repos are stored |
|
538 | Returns base path when all repos are stored | |
532 |
|
539 | |||
533 | :param cls: |
|
540 | :param cls: | |
534 | """ |
|
541 | """ | |
535 |
q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == |
|
542 | q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == | |
536 | cls.url_sep()) |
|
543 | cls.url_sep()) | |
537 | q.options(FromCache("sql_cache_short", "repository_repo_path")) |
|
544 | q.options(FromCache("sql_cache_short", "repository_repo_path")) | |
538 | return q.one().ui_value |
|
545 | return q.one().ui_value | |
539 |
|
546 | |||
540 | @property |
|
547 | @property | |
541 | def just_name(self): |
|
548 | def just_name(self): | |
542 | return self.repo_name.split(Repository.url_sep())[-1] |
|
549 | return self.repo_name.split(Repository.url_sep())[-1] | |
543 |
|
550 | |||
544 | @property |
|
551 | @property | |
545 | def groups_with_parents(self): |
|
552 | def groups_with_parents(self): | |
546 | groups = [] |
|
553 | groups = [] | |
547 | if self.group is None: |
|
554 | if self.group is None: | |
548 | return groups |
|
555 | return groups | |
549 |
|
556 | |||
550 | cur_gr = self.group |
|
557 | cur_gr = self.group | |
551 | groups.insert(0, cur_gr) |
|
558 | groups.insert(0, cur_gr) | |
552 | while 1: |
|
559 | while 1: | |
553 | gr = getattr(cur_gr, 'parent_group', None) |
|
560 | gr = getattr(cur_gr, 'parent_group', None) | |
554 | cur_gr = cur_gr.parent_group |
|
561 | cur_gr = cur_gr.parent_group | |
555 | if gr is None: |
|
562 | if gr is None: | |
556 | break |
|
563 | break | |
557 | groups.insert(0, gr) |
|
564 | groups.insert(0, gr) | |
558 |
|
565 | |||
559 | return groups |
|
566 | return groups | |
560 |
|
567 | |||
561 | @property |
|
568 | @property | |
562 | def groups_and_repo(self): |
|
569 | def groups_and_repo(self): | |
563 | return self.groups_with_parents, self.just_name |
|
570 | return self.groups_with_parents, self.just_name | |
564 |
|
571 | |||
565 | @LazyProperty |
|
572 | @LazyProperty | |
566 | def repo_path(self): |
|
573 | def repo_path(self): | |
567 | """ |
|
574 | """ | |
568 | Returns base full path for that repository means where it actually |
|
575 | Returns base full path for that repository means where it actually | |
569 | exists on a filesystem |
|
576 | exists on a filesystem | |
570 | """ |
|
577 | """ | |
571 |
q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == |
|
578 | q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == | |
572 | Repository.url_sep()) |
|
579 | Repository.url_sep()) | |
573 | q.options(FromCache("sql_cache_short", "repository_repo_path")) |
|
580 | q.options(FromCache("sql_cache_short", "repository_repo_path")) | |
574 | return q.one().ui_value |
|
581 | return q.one().ui_value | |
575 |
|
582 | |||
576 | @property |
|
583 | @property | |
577 | def repo_full_path(self): |
|
584 | def repo_full_path(self): | |
578 | p = [self.repo_path] |
|
585 | p = [self.repo_path] | |
579 | # we need to split the name by / since this is how we store the |
|
586 | # we need to split the name by / since this is how we store the | |
580 | # names in the database, but that eventually needs to be converted |
|
587 | # names in the database, but that eventually needs to be converted | |
581 | # into a valid system path |
|
588 | # into a valid system path | |
582 | p += self.repo_name.split(Repository.url_sep()) |
|
589 | p += self.repo_name.split(Repository.url_sep()) | |
583 | return os.path.join(*p) |
|
590 | return os.path.join(*p) | |
584 |
|
591 | |||
585 | def get_new_name(self, repo_name): |
|
592 | def get_new_name(self, repo_name): | |
586 | """ |
|
593 | """ | |
587 | returns new full repository name based on assigned group and new new |
|
594 | returns new full repository name based on assigned group and new new | |
588 |
|
595 | |||
589 | :param group_name: |
|
596 | :param group_name: | |
590 | """ |
|
597 | """ | |
591 | path_prefix = self.group.full_path_splitted if self.group else [] |
|
598 | path_prefix = self.group.full_path_splitted if self.group else [] | |
592 | return Repository.url_sep().join(path_prefix + [repo_name]) |
|
599 | return Repository.url_sep().join(path_prefix + [repo_name]) | |
593 |
|
600 | |||
594 | @property |
|
601 | @property | |
595 | def _ui(self): |
|
602 | def _ui(self): | |
596 | """ |
|
603 | """ | |
597 | Creates an db based ui object for this repository |
|
604 | Creates an db based ui object for this repository | |
598 | """ |
|
605 | """ | |
599 | from mercurial import ui |
|
606 | from mercurial import ui | |
600 | from mercurial import config |
|
607 | from mercurial import config | |
601 | baseui = ui.ui() |
|
608 | baseui = ui.ui() | |
602 |
|
609 | |||
603 | #clean the baseui object |
|
610 | #clean the baseui object | |
604 | baseui._ocfg = config.config() |
|
611 | baseui._ocfg = config.config() | |
605 | baseui._ucfg = config.config() |
|
612 | baseui._ucfg = config.config() | |
606 | baseui._tcfg = config.config() |
|
613 | baseui._tcfg = config.config() | |
607 |
|
614 | |||
608 |
|
615 | |||
609 |
ret = |
|
616 | ret = RhodeCodeUi.query()\ | |
610 | .options(FromCache("sql_cache_short", "repository_repo_ui")).all() |
|
617 | .options(FromCache("sql_cache_short", "repository_repo_ui")).all() | |
611 |
|
618 | |||
612 | hg_ui = ret |
|
619 | hg_ui = ret | |
613 | for ui_ in hg_ui: |
|
620 | for ui_ in hg_ui: | |
614 | if ui_.ui_active: |
|
621 | if ui_.ui_active: | |
615 | log.debug('settings ui from db[%s]%s:%s', ui_.ui_section, |
|
622 | log.debug('settings ui from db[%s]%s:%s', ui_.ui_section, | |
616 | ui_.ui_key, ui_.ui_value) |
|
623 | ui_.ui_key, ui_.ui_value) | |
617 | baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value) |
|
624 | baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value) | |
618 |
|
625 | |||
619 | return baseui |
|
626 | return baseui | |
620 |
|
627 | |||
621 | @classmethod |
|
628 | @classmethod | |
622 | def is_valid(cls, repo_name): |
|
629 | def is_valid(cls, repo_name): | |
623 | """ |
|
630 | """ | |
624 | returns True if given repo name is a valid filesystem repository |
|
631 | returns True if given repo name is a valid filesystem repository | |
625 |
|
632 | |||
626 | @param cls: |
|
633 | @param cls: | |
627 | @param repo_name: |
|
634 | @param repo_name: | |
628 | """ |
|
635 | """ | |
629 | from rhodecode.lib.utils import is_valid_repo |
|
636 | from rhodecode.lib.utils import is_valid_repo | |
630 |
|
637 | |||
631 | return is_valid_repo(repo_name, cls.base_path()) |
|
638 | return is_valid_repo(repo_name, cls.base_path()) | |
632 |
|
639 | |||
633 |
|
640 | |||
634 | #========================================================================== |
|
641 | #========================================================================== | |
635 | # SCM PROPERTIES |
|
642 | # SCM PROPERTIES | |
636 | #========================================================================== |
|
643 | #========================================================================== | |
637 |
|
644 | |||
638 | def get_changeset(self, rev): |
|
645 | def get_changeset(self, rev): | |
639 | return get_changeset_safe(self.scm_instance, rev) |
|
646 | return get_changeset_safe(self.scm_instance, rev) | |
640 |
|
647 | |||
641 | @property |
|
648 | @property | |
642 | def tip(self): |
|
649 | def tip(self): | |
643 | return self.get_changeset('tip') |
|
650 | return self.get_changeset('tip') | |
644 |
|
651 | |||
645 | @property |
|
652 | @property | |
646 | def author(self): |
|
653 | def author(self): | |
647 | return self.tip.author |
|
654 | return self.tip.author | |
648 |
|
655 | |||
649 | @property |
|
656 | @property | |
650 | def last_change(self): |
|
657 | def last_change(self): | |
651 | return self.scm_instance.last_change |
|
658 | return self.scm_instance.last_change | |
652 |
|
659 | |||
653 | #========================================================================== |
|
660 | #========================================================================== | |
654 | # SCM CACHE INSTANCE |
|
661 | # SCM CACHE INSTANCE | |
655 | #========================================================================== |
|
662 | #========================================================================== | |
656 |
|
663 | |||
657 | @property |
|
664 | @property | |
658 | def invalidate(self): |
|
665 | def invalidate(self): | |
659 | """ |
|
666 | """ | |
660 | Returns Invalidation object if this repo should be invalidated |
|
667 | Returns Invalidation object if this repo should be invalidated | |
661 | None otherwise. `cache_active = False` means that this cache |
|
668 | None otherwise. `cache_active = False` means that this cache | |
662 | state is not valid and needs to be invalidated |
|
669 | state is not valid and needs to be invalidated | |
663 | """ |
|
670 | """ | |
664 |
return |
|
671 | return CacheInvalidation.query()\ | |
665 | .filter(CacheInvalidation.cache_key == self.repo_name)\ |
|
672 | .filter(CacheInvalidation.cache_key == self.repo_name)\ | |
666 | .filter(CacheInvalidation.cache_active == False)\ |
|
673 | .filter(CacheInvalidation.cache_active == False)\ | |
667 | .scalar() |
|
674 | .scalar() | |
668 |
|
675 | |||
669 | def set_invalidate(self): |
|
676 | def set_invalidate(self): | |
670 | """ |
|
677 | """ | |
671 | set a cache for invalidation for this instance |
|
678 | set a cache for invalidation for this instance | |
672 | """ |
|
679 | """ | |
673 |
inv = |
|
680 | inv = CacheInvalidation.query()\ | |
674 | .filter(CacheInvalidation.cache_key == self.repo_name)\ |
|
681 | .filter(CacheInvalidation.cache_key == self.repo_name)\ | |
675 | .scalar() |
|
682 | .scalar() | |
676 |
|
683 | |||
677 | if inv is None: |
|
684 | if inv is None: | |
678 | inv = CacheInvalidation(self.repo_name) |
|
685 | inv = CacheInvalidation(self.repo_name) | |
679 | inv.cache_active = True |
|
686 | inv.cache_active = True | |
680 | Session.add(inv) |
|
687 | Session.add(inv) | |
681 | Session.commit() |
|
688 | Session.commit() | |
682 |
|
689 | |||
683 | @LazyProperty |
|
690 | @LazyProperty | |
684 | def scm_instance(self): |
|
691 | def scm_instance(self): | |
685 | return self.__get_instance() |
|
692 | return self.__get_instance() | |
686 |
|
693 | |||
687 | @property |
|
694 | @property | |
688 | def scm_instance_cached(self): |
|
695 | def scm_instance_cached(self): | |
689 | @cache_region('long_term') |
|
696 | @cache_region('long_term') | |
690 | def _c(repo_name): |
|
697 | def _c(repo_name): | |
691 | return self.__get_instance() |
|
698 | return self.__get_instance() | |
692 |
|
699 | |||
693 | # TODO: remove this trick when beaker 1.6 is released |
|
700 | # TODO: remove this trick when beaker 1.6 is released | |
694 | # and have fixed this issue with not supporting unicode keys |
|
701 | # and have fixed this issue with not supporting unicode keys | |
695 | rn = safe_str(self.repo_name) |
|
702 | rn = safe_str(self.repo_name) | |
696 |
|
703 | |||
697 | inv = self.invalidate |
|
704 | inv = self.invalidate | |
698 | if inv is not None: |
|
705 | if inv is not None: | |
699 | region_invalidate(_c, None, rn) |
|
706 | region_invalidate(_c, None, rn) | |
700 | # update our cache |
|
707 | # update our cache | |
701 | inv.cache_active = True |
|
708 | inv.cache_active = True | |
702 | Session.add(inv) |
|
709 | Session.add(inv) | |
703 | Session.commit() |
|
710 | Session.commit() | |
704 |
|
711 | |||
705 | return _c(rn) |
|
712 | return _c(rn) | |
706 |
|
713 | |||
707 | def __get_instance(self): |
|
714 | def __get_instance(self): | |
708 |
|
715 | |||
709 | repo_full_path = self.repo_full_path |
|
716 | repo_full_path = self.repo_full_path | |
710 |
|
717 | |||
711 | try: |
|
718 | try: | |
712 | alias = get_scm(repo_full_path)[0] |
|
719 | alias = get_scm(repo_full_path)[0] | |
713 | log.debug('Creating instance of %s repository', alias) |
|
720 | log.debug('Creating instance of %s repository', alias) | |
714 | backend = get_backend(alias) |
|
721 | backend = get_backend(alias) | |
715 | except VCSError: |
|
722 | except VCSError: | |
716 | log.error(traceback.format_exc()) |
|
723 | log.error(traceback.format_exc()) | |
717 | log.error('Perhaps this repository is in db and not in ' |
|
724 | log.error('Perhaps this repository is in db and not in ' | |
718 | 'filesystem run rescan repositories with ' |
|
725 | 'filesystem run rescan repositories with ' | |
719 | '"destroy old data " option from admin panel') |
|
726 | '"destroy old data " option from admin panel') | |
720 | return |
|
727 | return | |
721 |
|
728 | |||
722 | if alias == 'hg': |
|
729 | if alias == 'hg': | |
723 |
|
730 | |||
724 | repo = backend(safe_str(repo_full_path), create=False, |
|
731 | repo = backend(safe_str(repo_full_path), create=False, | |
725 | baseui=self._ui) |
|
732 | baseui=self._ui) | |
726 | #skip hidden web repository |
|
733 | #skip hidden web repository | |
727 | if repo._get_hidden(): |
|
734 | if repo._get_hidden(): | |
728 | return |
|
735 | return | |
729 | else: |
|
736 | else: | |
730 | repo = backend(repo_full_path, create=False) |
|
737 | repo = backend(repo_full_path, create=False) | |
731 |
|
738 | |||
732 | return repo |
|
739 | return repo | |
733 |
|
740 | |||
734 |
|
741 | |||
735 | class Group(Base, BaseModel): |
|
742 | class Group(Base, BaseModel): | |
736 | __tablename__ = 'groups' |
|
743 | __tablename__ = 'groups' | |
737 | __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'), |
|
744 | __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'), | |
738 | CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},) |
|
745 | CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},) | |
739 | __mapper_args__ = {'order_by':'group_name'} |
|
746 | __mapper_args__ = {'order_by':'group_name'} | |
740 |
|
747 | |||
741 | group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) |
|
748 | group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) | |
742 | group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None) |
|
749 | group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None) | |
743 | group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None) |
|
750 | group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None) | |
744 | group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) |
|
751 | group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) | |
745 |
|
752 | |||
746 | parent_group = relationship('Group', remote_side=group_id) |
|
753 | parent_group = relationship('Group', remote_side=group_id) | |
747 |
|
754 | |||
748 |
|
755 | |||
749 | def __init__(self, group_name='', parent_group=None): |
|
756 | def __init__(self, group_name='', parent_group=None): | |
750 | self.group_name = group_name |
|
757 | self.group_name = group_name | |
751 | self.parent_group = parent_group |
|
758 | self.parent_group = parent_group | |
752 |
|
759 | |||
753 | def __repr__(self): |
|
760 | def __repr__(self): | |
754 | return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id, |
|
761 | return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id, | |
755 | self.group_name) |
|
762 | self.group_name) | |
756 |
|
763 | |||
757 | @classmethod |
|
764 | @classmethod | |
758 | def groups_choices(cls): |
|
765 | def groups_choices(cls): | |
759 | from webhelpers.html import literal as _literal |
|
766 | from webhelpers.html import literal as _literal | |
760 | repo_groups = [('', '')] |
|
767 | repo_groups = [('', '')] | |
761 | sep = ' » ' |
|
768 | sep = ' » ' | |
762 | _name = lambda k: _literal(sep.join(k)) |
|
769 | _name = lambda k: _literal(sep.join(k)) | |
763 |
|
770 | |||
764 | repo_groups.extend([(x.group_id, _name(x.full_path_splitted)) |
|
771 | repo_groups.extend([(x.group_id, _name(x.full_path_splitted)) | |
765 | for x in cls.query().all()]) |
|
772 | for x in cls.query().all()]) | |
766 |
|
773 | |||
767 |
repo_groups = sorted(repo_groups,key=lambda t: t[1].split(sep)[0]) |
|
774 | repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0]) | |
768 | return repo_groups |
|
775 | return repo_groups | |
769 |
|
776 | |||
770 | @classmethod |
|
777 | @classmethod | |
771 | def url_sep(cls): |
|
778 | def url_sep(cls): | |
772 | return '/' |
|
779 | return '/' | |
773 |
|
780 | |||
774 | @classmethod |
|
781 | @classmethod | |
775 | def get_by_group_name(cls, group_name): |
|
782 | def get_by_group_name(cls, group_name, cache=False, case_insensitive=False): | |
776 | return cls.query().filter(cls.group_name == group_name).scalar() |
|
783 | if case_insensitive: | |
|
784 | gr = cls.query()\ | |||
|
785 | .filter(cls.group_name.ilike(group_name)) | |||
|
786 | else: | |||
|
787 | gr = cls.query()\ | |||
|
788 | .filter(cls.group_name == group_name) | |||
|
789 | if cache: | |||
|
790 | gr = gr.options(FromCache("sql_cache_short", | |||
|
791 | "get_group_%s" % group_name)) | |||
|
792 | return gr.scalar() | |||
777 |
|
793 | |||
778 | @property |
|
794 | @property | |
779 | def parents(self): |
|
795 | def parents(self): | |
780 | parents_recursion_limit = 5 |
|
796 | parents_recursion_limit = 5 | |
781 | groups = [] |
|
797 | groups = [] | |
782 | if self.parent_group is None: |
|
798 | if self.parent_group is None: | |
783 | return groups |
|
799 | return groups | |
784 | cur_gr = self.parent_group |
|
800 | cur_gr = self.parent_group | |
785 | groups.insert(0, cur_gr) |
|
801 | groups.insert(0, cur_gr) | |
786 | cnt = 0 |
|
802 | cnt = 0 | |
787 | while 1: |
|
803 | while 1: | |
788 | cnt += 1 |
|
804 | cnt += 1 | |
789 | gr = getattr(cur_gr, 'parent_group', None) |
|
805 | gr = getattr(cur_gr, 'parent_group', None) | |
790 | cur_gr = cur_gr.parent_group |
|
806 | cur_gr = cur_gr.parent_group | |
791 | if gr is None: |
|
807 | if gr is None: | |
792 | break |
|
808 | break | |
793 | if cnt == parents_recursion_limit: |
|
809 | if cnt == parents_recursion_limit: | |
794 | # this will prevent accidental infinit loops |
|
810 | # this will prevent accidental infinit loops | |
795 | log.error('group nested more than %s' % |
|
811 | log.error('group nested more than %s' % | |
796 | parents_recursion_limit) |
|
812 | parents_recursion_limit) | |
797 | break |
|
813 | break | |
798 |
|
814 | |||
799 | groups.insert(0, gr) |
|
815 | groups.insert(0, gr) | |
800 | return groups |
|
816 | return groups | |
801 |
|
817 | |||
802 | @property |
|
818 | @property | |
803 | def children(self): |
|
819 | def children(self): | |
804 |
return |
|
820 | return Group.query().filter(Group.parent_group == self) | |
805 |
|
821 | |||
806 | @property |
|
822 | @property | |
807 | def name(self): |
|
823 | def name(self): | |
808 | return self.group_name.split(Group.url_sep())[-1] |
|
824 | return self.group_name.split(Group.url_sep())[-1] | |
809 |
|
825 | |||
810 | @property |
|
826 | @property | |
811 | def full_path(self): |
|
827 | def full_path(self): | |
812 | return self.group_name |
|
828 | return self.group_name | |
813 |
|
829 | |||
814 | @property |
|
830 | @property | |
815 | def full_path_splitted(self): |
|
831 | def full_path_splitted(self): | |
816 | return self.group_name.split(Group.url_sep()) |
|
832 | return self.group_name.split(Group.url_sep()) | |
817 |
|
833 | |||
818 | @property |
|
834 | @property | |
819 | def repositories(self): |
|
835 | def repositories(self): | |
820 |
return |
|
836 | return Repository.query().filter(Repository.group == self) | |
821 |
|
837 | |||
822 | @property |
|
838 | @property | |
823 | def repositories_recursive_count(self): |
|
839 | def repositories_recursive_count(self): | |
824 | cnt = self.repositories.count() |
|
840 | cnt = self.repositories.count() | |
825 |
|
841 | |||
826 | def children_count(group): |
|
842 | def children_count(group): | |
827 | cnt = 0 |
|
843 | cnt = 0 | |
828 | for child in group.children: |
|
844 | for child in group.children: | |
829 | cnt += child.repositories.count() |
|
845 | cnt += child.repositories.count() | |
830 | cnt += children_count(child) |
|
846 | cnt += children_count(child) | |
831 | return cnt |
|
847 | return cnt | |
832 |
|
848 | |||
833 | return cnt + children_count(self) |
|
849 | return cnt + children_count(self) | |
834 |
|
850 | |||
835 |
|
851 | |||
836 | def get_new_name(self, group_name): |
|
852 | def get_new_name(self, group_name): | |
837 | """ |
|
853 | """ | |
838 | returns new full group name based on parent and new name |
|
854 | returns new full group name based on parent and new name | |
839 |
|
855 | |||
840 | :param group_name: |
|
856 | :param group_name: | |
841 | """ |
|
857 | """ | |
842 |
path_prefix = self.parent_group.full_path_splitted if |
|
858 | path_prefix = (self.parent_group.full_path_splitted if | |
|
859 | self.parent_group else []) | |||
843 | return Group.url_sep().join(path_prefix + [group_name]) |
|
860 | return Group.url_sep().join(path_prefix + [group_name]) | |
844 |
|
861 | |||
845 |
|
862 | |||
846 | class Permission(Base, BaseModel): |
|
863 | class Permission(Base, BaseModel): | |
847 | __tablename__ = 'permissions' |
|
864 | __tablename__ = 'permissions' | |
848 | __table_args__ = {'extend_existing':True} |
|
865 | __table_args__ = {'extend_existing':True} | |
849 | permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) |
|
866 | permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) | |
850 | permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) |
|
867 | permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) | |
851 | permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) |
|
868 | permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) | |
852 |
|
869 | |||
853 | def __repr__(self): |
|
870 | def __repr__(self): | |
854 | return "<%s('%s:%s')>" % (self.__class__.__name__, |
|
871 | return "<%s('%s:%s')>" % (self.__class__.__name__, | |
855 | self.permission_id, self.permission_name) |
|
872 | self.permission_id, self.permission_name) | |
856 |
|
873 | |||
857 | @classmethod |
|
874 | @classmethod | |
858 | def get_by_key(cls, key): |
|
875 | def get_by_key(cls, key): | |
859 |
return |
|
876 | return cls.query().filter(cls.permission_name == key).scalar() | |
860 |
|
877 | |||
861 | class RepoToPerm(Base, BaseModel): |
|
878 | class RepoToPerm(Base, BaseModel): | |
862 | __tablename__ = 'repo_to_perm' |
|
879 | __tablename__ = 'repo_to_perm' | |
863 | __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True}) |
|
880 | __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True}) | |
864 | repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) |
|
881 | repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) | |
865 | user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None) |
|
882 | user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None) | |
866 | permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None) |
|
883 | permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None) | |
867 | repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None) |
|
884 | repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None) | |
868 |
|
885 | |||
869 | user = relationship('User') |
|
886 | user = relationship('User') | |
870 | permission = relationship('Permission') |
|
887 | permission = relationship('Permission') | |
871 | repository = relationship('Repository') |
|
888 | repository = relationship('Repository') | |
872 |
|
889 | |||
873 | class UserToPerm(Base, BaseModel): |
|
890 | class UserToPerm(Base, BaseModel): | |
874 | __tablename__ = 'user_to_perm' |
|
891 | __tablename__ = 'user_to_perm' | |
875 | __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True}) |
|
892 | __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True}) | |
876 | user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) |
|
893 | user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) | |
877 | user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None) |
|
894 | user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None) | |
878 | permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None) |
|
895 | permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None) | |
879 |
|
896 | |||
880 | user = relationship('User') |
|
897 | user = relationship('User') | |
881 | permission = relationship('Permission') |
|
898 | permission = relationship('Permission') | |
882 |
|
899 | |||
883 | @classmethod |
|
900 | @classmethod | |
884 | def has_perm(cls, user_id, perm): |
|
901 | def has_perm(cls, user_id, perm): | |
885 | if not isinstance(perm, Permission): |
|
902 | if not isinstance(perm, Permission): | |
886 | raise Exception('perm needs to be an instance of Permission class') |
|
903 | raise Exception('perm needs to be an instance of Permission class') | |
887 |
|
904 | |||
888 |
return |
|
905 | return cls.query().filter(cls.user_id == user_id)\ | |
889 | .filter(cls.permission == perm).scalar() is not None |
|
906 | .filter(cls.permission == perm).scalar() is not None | |
890 |
|
907 | |||
891 | @classmethod |
|
908 | @classmethod | |
892 | def grant_perm(cls, user_id, perm): |
|
909 | def grant_perm(cls, user_id, perm): | |
893 | if not isinstance(perm, Permission): |
|
910 | if not isinstance(perm, Permission): | |
894 | raise Exception('perm needs to be an instance of Permission class') |
|
911 | raise Exception('perm needs to be an instance of Permission class') | |
895 |
|
912 | |||
896 | new = cls() |
|
913 | new = cls() | |
897 | new.user_id = user_id |
|
914 | new.user_id = user_id | |
898 | new.permission = perm |
|
915 | new.permission = perm | |
899 | try: |
|
916 | try: | |
900 | Session.add(new) |
|
917 | Session.add(new) | |
901 | Session.commit() |
|
918 | Session.commit() | |
902 | except: |
|
919 | except: | |
903 | Session.rollback() |
|
920 | Session.rollback() | |
904 |
|
921 | |||
905 |
|
922 | |||
906 | @classmethod |
|
923 | @classmethod | |
907 | def revoke_perm(cls, user_id, perm): |
|
924 | def revoke_perm(cls, user_id, perm): | |
908 | if not isinstance(perm, Permission): |
|
925 | if not isinstance(perm, Permission): | |
909 | raise Exception('perm needs to be an instance of Permission class') |
|
926 | raise Exception('perm needs to be an instance of Permission class') | |
910 |
|
927 | |||
911 | try: |
|
928 | try: | |
912 |
|
|
929 | cls.query().filter(cls.user_id == user_id)\ | |
913 | .filter(cls.permission == perm).delete() |
|
930 | .filter(cls.permission == perm).delete() | |
914 | Session.commit() |
|
931 | Session.commit() | |
915 | except: |
|
932 | except: | |
916 | Session.rollback() |
|
933 | Session.rollback() | |
917 |
|
934 | |||
918 | class UsersGroupRepoToPerm(Base, BaseModel): |
|
935 | class UsersGroupRepoToPerm(Base, BaseModel): | |
919 | __tablename__ = 'users_group_repo_to_perm' |
|
936 | __tablename__ = 'users_group_repo_to_perm' | |
920 | __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True}) |
|
937 | __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True}) | |
921 | users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) |
|
938 | users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) | |
922 | users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None) |
|
939 | users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None) | |
923 | permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None) |
|
940 | permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None) | |
924 | repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None) |
|
941 | repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None) | |
925 |
|
942 | |||
926 | users_group = relationship('UsersGroup') |
|
943 | users_group = relationship('UsersGroup') | |
927 | permission = relationship('Permission') |
|
944 | permission = relationship('Permission') | |
928 | repository = relationship('Repository') |
|
945 | repository = relationship('Repository') | |
929 |
|
946 | |||
930 | def __repr__(self): |
|
947 | def __repr__(self): | |
931 | return '<userGroup:%s => %s >' % (self.users_group, self.repository) |
|
948 | return '<userGroup:%s => %s >' % (self.users_group, self.repository) | |
932 |
|
949 | |||
933 | class UsersGroupToPerm(Base, BaseModel): |
|
950 | class UsersGroupToPerm(Base, BaseModel): | |
934 | __tablename__ = 'users_group_to_perm' |
|
951 | __tablename__ = 'users_group_to_perm' | |
935 | users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) |
|
952 | users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) | |
936 | users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None) |
|
953 | users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None) | |
937 | permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None) |
|
954 | permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None) | |
938 |
|
955 | |||
939 | users_group = relationship('UsersGroup') |
|
956 | users_group = relationship('UsersGroup') | |
940 | permission = relationship('Permission') |
|
957 | permission = relationship('Permission') | |
941 |
|
958 | |||
942 |
|
959 | |||
943 | @classmethod |
|
960 | @classmethod | |
944 | def has_perm(cls, users_group_id, perm): |
|
961 | def has_perm(cls, users_group_id, perm): | |
945 | if not isinstance(perm, Permission): |
|
962 | if not isinstance(perm, Permission): | |
946 | raise Exception('perm needs to be an instance of Permission class') |
|
963 | raise Exception('perm needs to be an instance of Permission class') | |
947 |
|
964 | |||
948 |
return |
|
965 | return cls.query().filter(cls.users_group_id == | |
949 | users_group_id)\ |
|
966 | users_group_id)\ | |
950 | .filter(cls.permission == perm)\ |
|
967 | .filter(cls.permission == perm)\ | |
951 | .scalar() is not None |
|
968 | .scalar() is not None | |
952 |
|
969 | |||
953 | @classmethod |
|
970 | @classmethod | |
954 | def grant_perm(cls, users_group_id, perm): |
|
971 | def grant_perm(cls, users_group_id, perm): | |
955 | if not isinstance(perm, Permission): |
|
972 | if not isinstance(perm, Permission): | |
956 | raise Exception('perm needs to be an instance of Permission class') |
|
973 | raise Exception('perm needs to be an instance of Permission class') | |
957 |
|
974 | |||
958 | new = cls() |
|
975 | new = cls() | |
959 | new.users_group_id = users_group_id |
|
976 | new.users_group_id = users_group_id | |
960 | new.permission = perm |
|
977 | new.permission = perm | |
961 | try: |
|
978 | try: | |
962 | Session.add(new) |
|
979 | Session.add(new) | |
963 | Session.commit() |
|
980 | Session.commit() | |
964 | except: |
|
981 | except: | |
965 | Session.rollback() |
|
982 | Session.rollback() | |
966 |
|
983 | |||
967 |
|
984 | |||
968 | @classmethod |
|
985 | @classmethod | |
969 | def revoke_perm(cls, users_group_id, perm): |
|
986 | def revoke_perm(cls, users_group_id, perm): | |
970 | if not isinstance(perm, Permission): |
|
987 | if not isinstance(perm, Permission): | |
971 | raise Exception('perm needs to be an instance of Permission class') |
|
988 | raise Exception('perm needs to be an instance of Permission class') | |
972 |
|
989 | |||
973 | try: |
|
990 | try: | |
974 |
|
|
991 | cls.query().filter(cls.users_group_id == users_group_id)\ | |
975 | .filter(cls.permission == perm).delete() |
|
992 | .filter(cls.permission == perm).delete() | |
976 | Session.commit() |
|
993 | Session.commit() | |
977 | except: |
|
994 | except: | |
978 | Session.rollback() |
|
995 | Session.rollback() | |
979 |
|
996 | |||
980 |
|
997 | |||
981 | class GroupToPerm(Base, BaseModel): |
|
998 | class GroupToPerm(Base, BaseModel): | |
982 | __tablename__ = 'group_to_perm' |
|
999 | __tablename__ = 'group_to_perm' | |
983 | __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True}) |
|
1000 | __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True}) | |
984 |
|
1001 | |||
985 | group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) |
|
1002 | group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) | |
986 | user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None) |
|
1003 | user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None) | |
987 | permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None) |
|
1004 | permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None) | |
988 | group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None) |
|
1005 | group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None) | |
989 |
|
1006 | |||
990 | user = relationship('User') |
|
1007 | user = relationship('User') | |
991 | permission = relationship('Permission') |
|
1008 | permission = relationship('Permission') | |
992 | group = relationship('Group') |
|
1009 | group = relationship('Group') | |
993 |
|
1010 | |||
994 | class Statistics(Base, BaseModel): |
|
1011 | class Statistics(Base, BaseModel): | |
995 | __tablename__ = 'statistics' |
|
1012 | __tablename__ = 'statistics' | |
996 | __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True}) |
|
1013 | __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True}) | |
997 | stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) |
|
1014 | stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) | |
998 | repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None) |
|
1015 | repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None) | |
999 | stat_on_revision = Column("stat_on_revision", Integer(), nullable=False) |
|
1016 | stat_on_revision = Column("stat_on_revision", Integer(), nullable=False) | |
1000 | commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data |
|
1017 | commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data | |
1001 | commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data |
|
1018 | commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data | |
1002 | languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data |
|
1019 | languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data | |
1003 |
|
1020 | |||
1004 | repository = relationship('Repository', single_parent=True) |
|
1021 | repository = relationship('Repository', single_parent=True) | |
1005 |
|
1022 | |||
1006 | class UserFollowing(Base, BaseModel): |
|
1023 | class UserFollowing(Base, BaseModel): | |
1007 | __tablename__ = 'user_followings' |
|
1024 | __tablename__ = 'user_followings' | |
1008 | __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'), |
|
1025 | __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'), | |
1009 | UniqueConstraint('user_id', 'follows_user_id') |
|
1026 | UniqueConstraint('user_id', 'follows_user_id') | |
1010 | , {'extend_existing':True}) |
|
1027 | , {'extend_existing':True}) | |
1011 |
|
1028 | |||
1012 | user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) |
|
1029 | user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) | |
1013 | user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None) |
|
1030 | user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None) | |
1014 | follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None) |
|
1031 | follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None) | |
1015 | follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None) |
|
1032 | follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None) | |
1016 | follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now) |
|
1033 | follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now) | |
1017 |
|
1034 | |||
1018 | user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id') |
|
1035 | user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id') | |
1019 |
|
1036 | |||
1020 | follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id') |
|
1037 | follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id') | |
1021 | follows_repository = relationship('Repository', order_by='Repository.repo_name') |
|
1038 | follows_repository = relationship('Repository', order_by='Repository.repo_name') | |
1022 |
|
1039 | |||
1023 |
|
1040 | |||
1024 | @classmethod |
|
1041 | @classmethod | |
1025 | def get_repo_followers(cls, repo_id): |
|
1042 | def get_repo_followers(cls, repo_id): | |
1026 |
return |
|
1043 | return cls.query().filter(cls.follows_repo_id == repo_id) | |
1027 |
|
1044 | |||
1028 | class CacheInvalidation(Base, BaseModel): |
|
1045 | class CacheInvalidation(Base, BaseModel): | |
1029 | __tablename__ = 'cache_invalidation' |
|
1046 | __tablename__ = 'cache_invalidation' | |
1030 | __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True}) |
|
1047 | __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True}) | |
1031 | cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) |
|
1048 | cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) | |
1032 | cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) |
|
1049 | cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) | |
1033 | cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) |
|
1050 | cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) | |
1034 | cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False) |
|
1051 | cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False) | |
1035 |
|
1052 | |||
1036 |
|
1053 | |||
1037 | def __init__(self, cache_key, cache_args=''): |
|
1054 | def __init__(self, cache_key, cache_args=''): | |
1038 | self.cache_key = cache_key |
|
1055 | self.cache_key = cache_key | |
1039 | self.cache_args = cache_args |
|
1056 | self.cache_args = cache_args | |
1040 | self.cache_active = False |
|
1057 | self.cache_active = False | |
1041 |
|
1058 | |||
1042 | def __repr__(self): |
|
1059 | def __repr__(self): | |
1043 | return "<%s('%s:%s')>" % (self.__class__.__name__, |
|
1060 | return "<%s('%s:%s')>" % (self.__class__.__name__, | |
1044 | self.cache_id, self.cache_key) |
|
1061 | self.cache_id, self.cache_key) | |
1045 |
|
1062 | |||
1046 | class DbMigrateVersion(Base, BaseModel): |
|
1063 | class DbMigrateVersion(Base, BaseModel): | |
1047 | __tablename__ = 'db_migrate_version' |
|
1064 | __tablename__ = 'db_migrate_version' | |
1048 | __table_args__ = {'extend_existing':True} |
|
1065 | __table_args__ = {'extend_existing':True} | |
1049 | repository_id = Column('repository_id', String(250), primary_key=True) |
|
1066 | repository_id = Column('repository_id', String(250), primary_key=True) | |
1050 | repository_path = Column('repository_path', Text) |
|
1067 | repository_path = Column('repository_path', Text) | |
1051 | version = Column('version', Integer) |
|
1068 | version = Column('version', Integer) | |
|
1069 |
@@ -1,688 +1,694 b'' | |||||
1 | """ this is forms validation classes |
|
1 | """ this is forms validation classes | |
2 | http://formencode.org/module-formencode.validators.html |
|
2 | http://formencode.org/module-formencode.validators.html | |
3 | for list off all availible validators |
|
3 | for list off all availible validators | |
4 |
|
4 | |||
5 | we can create our own validators |
|
5 | we can create our own validators | |
6 |
|
6 | |||
7 | The table below outlines the options which can be used in a schema in addition to the validators themselves |
|
7 | The table below outlines the options which can be used in a schema in addition to the validators themselves | |
8 | pre_validators [] These validators will be applied before the schema |
|
8 | pre_validators [] These validators will be applied before the schema | |
9 | chained_validators [] These validators will be applied after the schema |
|
9 | chained_validators [] These validators will be applied after the schema | |
10 | allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present |
|
10 | allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present | |
11 | filter_extra_fields False If True, then keys that aren't associated with a validator are removed |
|
11 | filter_extra_fields False If True, then keys that aren't associated with a validator are removed | |
12 | if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value. |
|
12 | if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value. | |
13 | ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already |
|
13 | ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already | |
14 |
|
14 | |||
15 |
|
15 | |||
16 | <name> = formencode.validators.<name of validator> |
|
16 | <name> = formencode.validators.<name of validator> | |
17 | <name> must equal form name |
|
17 | <name> must equal form name | |
18 | list=[1,2,3,4,5] |
|
18 | list=[1,2,3,4,5] | |
19 | for SELECT use formencode.All(OneOf(list), Int()) |
|
19 | for SELECT use formencode.All(OneOf(list), Int()) | |
20 |
|
20 | |||
21 | """ |
|
21 | """ | |
22 | import os |
|
22 | import os | |
23 | import re |
|
23 | import re | |
24 | import logging |
|
24 | import logging | |
25 | import traceback |
|
25 | import traceback | |
26 |
|
26 | |||
27 | import formencode |
|
27 | import formencode | |
28 | from formencode import All |
|
28 | from formencode import All | |
29 | from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \ |
|
29 | from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \ | |
30 | Email, Bool, StringBoolean, Set |
|
30 | Email, Bool, StringBoolean, Set | |
31 |
|
31 | |||
32 | from pylons.i18n.translation import _ |
|
32 | from pylons.i18n.translation import _ | |
33 | from webhelpers.pylonslib.secure_form import authentication_token |
|
33 | from webhelpers.pylonslib.secure_form import authentication_token | |
34 |
|
34 | |||
35 | from rhodecode.config.routing import ADMIN_PREFIX |
|
35 | from rhodecode.config.routing import ADMIN_PREFIX | |
36 | from rhodecode.lib.utils import repo_name_slug |
|
36 | from rhodecode.lib.utils import repo_name_slug | |
37 | from rhodecode.lib.auth import authenticate, get_crypt_password |
|
37 | from rhodecode.lib.auth import authenticate, get_crypt_password | |
38 | from rhodecode.lib.exceptions import LdapImportError |
|
38 | from rhodecode.lib.exceptions import LdapImportError | |
39 | from rhodecode.model.user import UserModel |
|
39 | from rhodecode.model.user import UserModel | |
40 | from rhodecode.model.repo import RepoModel |
|
40 | from rhodecode.model.repo import RepoModel | |
41 | from rhodecode.model.db import User, UsersGroup, Group |
|
41 | from rhodecode.model.db import User, UsersGroup, Group | |
42 | from rhodecode import BACKENDS |
|
42 | from rhodecode import BACKENDS | |
43 |
|
43 | |||
44 | log = logging.getLogger(__name__) |
|
44 | log = logging.getLogger(__name__) | |
45 |
|
45 | |||
46 | #this is needed to translate the messages using _() in validators |
|
46 | #this is needed to translate the messages using _() in validators | |
47 | class State_obj(object): |
|
47 | class State_obj(object): | |
48 | _ = staticmethod(_) |
|
48 | _ = staticmethod(_) | |
49 |
|
49 | |||
50 | #============================================================================== |
|
50 | #============================================================================== | |
51 | # VALIDATORS |
|
51 | # VALIDATORS | |
52 | #============================================================================== |
|
52 | #============================================================================== | |
53 | class ValidAuthToken(formencode.validators.FancyValidator): |
|
53 | class ValidAuthToken(formencode.validators.FancyValidator): | |
54 | messages = {'invalid_token':_('Token mismatch')} |
|
54 | messages = {'invalid_token':_('Token mismatch')} | |
55 |
|
55 | |||
56 | def validate_python(self, value, state): |
|
56 | def validate_python(self, value, state): | |
57 |
|
57 | |||
58 | if value != authentication_token(): |
|
58 | if value != authentication_token(): | |
59 | raise formencode.Invalid(self.message('invalid_token', state, |
|
59 | raise formencode.Invalid(self.message('invalid_token', state, | |
60 | search_number=value), value, state) |
|
60 | search_number=value), value, state) | |
61 |
|
61 | |||
62 | def ValidUsername(edit, old_data): |
|
62 | def ValidUsername(edit, old_data): | |
63 | class _ValidUsername(formencode.validators.FancyValidator): |
|
63 | class _ValidUsername(formencode.validators.FancyValidator): | |
64 |
|
64 | |||
65 | def validate_python(self, value, state): |
|
65 | def validate_python(self, value, state): | |
66 | if value in ['default', 'new_user']: |
|
66 | if value in ['default', 'new_user']: | |
67 | raise formencode.Invalid(_('Invalid username'), value, state) |
|
67 | raise formencode.Invalid(_('Invalid username'), value, state) | |
68 | #check if user is unique |
|
68 | #check if user is unique | |
69 | old_un = None |
|
69 | old_un = None | |
70 | if edit: |
|
70 | if edit: | |
71 | old_un = UserModel().get(old_data.get('user_id')).username |
|
71 | old_un = UserModel().get(old_data.get('user_id')).username | |
72 |
|
72 | |||
73 | if old_un != value or not edit: |
|
73 | if old_un != value or not edit: | |
74 | if User.get_by_username(value, case_insensitive=True): |
|
74 | if User.get_by_username(value, case_insensitive=True): | |
75 | raise formencode.Invalid(_('This username already ' |
|
75 | raise formencode.Invalid(_('This username already ' | |
76 | 'exists') , value, state) |
|
76 | 'exists') , value, state) | |
77 |
|
77 | |||
78 | if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None: |
|
78 | if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None: | |
79 | raise formencode.Invalid(_('Username may only contain ' |
|
79 | raise formencode.Invalid(_('Username may only contain ' | |
80 | 'alphanumeric characters ' |
|
80 | 'alphanumeric characters ' | |
81 | 'underscores, periods or dashes ' |
|
81 | 'underscores, periods or dashes ' | |
82 | 'and must begin with alphanumeric ' |
|
82 | 'and must begin with alphanumeric ' | |
83 | 'character'), value, state) |
|
83 | 'character'), value, state) | |
84 |
|
84 | |||
85 | return _ValidUsername |
|
85 | return _ValidUsername | |
86 |
|
86 | |||
87 |
|
87 | |||
88 | def ValidUsersGroup(edit, old_data): |
|
88 | def ValidUsersGroup(edit, old_data): | |
89 |
|
89 | |||
90 | class _ValidUsersGroup(formencode.validators.FancyValidator): |
|
90 | class _ValidUsersGroup(formencode.validators.FancyValidator): | |
91 |
|
91 | |||
92 | def validate_python(self, value, state): |
|
92 | def validate_python(self, value, state): | |
93 | if value in ['default']: |
|
93 | if value in ['default']: | |
94 | raise formencode.Invalid(_('Invalid group name'), value, state) |
|
94 | raise formencode.Invalid(_('Invalid group name'), value, state) | |
95 | #check if group is unique |
|
95 | #check if group is unique | |
96 | old_ugname = None |
|
96 | old_ugname = None | |
97 | if edit: |
|
97 | if edit: | |
98 | old_ugname = UsersGroup.get( |
|
98 | old_ugname = UsersGroup.get( | |
99 | old_data.get('users_group_id')).users_group_name |
|
99 | old_data.get('users_group_id')).users_group_name | |
100 |
|
100 | |||
101 | if old_ugname != value or not edit: |
|
101 | if old_ugname != value or not edit: | |
102 | if UsersGroup.get_by_group_name(value, cache=False, |
|
102 | if UsersGroup.get_by_group_name(value, cache=False, | |
103 | case_insensitive=True): |
|
103 | case_insensitive=True): | |
104 | raise formencode.Invalid(_('This users group ' |
|
104 | raise formencode.Invalid(_('This users group ' | |
105 | 'already exists') , value, |
|
105 | 'already exists') , value, | |
106 | state) |
|
106 | state) | |
107 |
|
107 | |||
108 |
|
108 | |||
109 | if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None: |
|
109 | if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None: | |
110 | raise formencode.Invalid(_('Group name may only contain ' |
|
110 | raise formencode.Invalid(_('Group name may only contain ' | |
111 | 'alphanumeric characters ' |
|
111 | 'alphanumeric characters ' | |
112 | 'underscores, periods or dashes ' |
|
112 | 'underscores, periods or dashes ' | |
113 | 'and must begin with alphanumeric ' |
|
113 | 'and must begin with alphanumeric ' | |
114 | 'character'), value, state) |
|
114 | 'character'), value, state) | |
115 |
|
115 | |||
116 | return _ValidUsersGroup |
|
116 | return _ValidUsersGroup | |
117 |
|
117 | |||
118 |
|
118 | |||
119 | def ValidReposGroup(edit, old_data): |
|
119 | def ValidReposGroup(edit, old_data): | |
120 | class _ValidReposGroup(formencode.validators.FancyValidator): |
|
120 | class _ValidReposGroup(formencode.validators.FancyValidator): | |
121 |
|
121 | |||
122 | def validate_python(self, value, state): |
|
122 | def validate_python(self, value, state): | |
123 | #TODO WRITE VALIDATIONS |
|
123 | #TODO WRITE VALIDATIONS | |
124 | group_name = value.get('group_name') |
|
124 | group_name = value.get('group_name') | |
125 | group_parent_id = int(value.get('group_parent_id') or -1) |
|
125 | group_parent_id = int(value.get('group_parent_id') or -1) | |
126 |
|
126 | |||
127 | # slugify repo group just in case :) |
|
127 | # slugify repo group just in case :) | |
128 | slug = repo_name_slug(group_name) |
|
128 | slug = repo_name_slug(group_name) | |
129 |
|
129 | |||
130 | # check for parent of self |
|
130 | # check for parent of self | |
131 | if edit and old_data['group_id'] == group_parent_id: |
|
131 | if edit and old_data['group_id'] == group_parent_id: | |
132 | e_dict = {'group_parent_id':_('Cannot assign this group ' |
|
132 | e_dict = {'group_parent_id':_('Cannot assign this group ' | |
133 | 'as parent')} |
|
133 | 'as parent')} | |
134 | raise formencode.Invalid('', value, state, |
|
134 | raise formencode.Invalid('', value, state, | |
135 | error_dict=e_dict) |
|
135 | error_dict=e_dict) | |
136 |
|
136 | |||
137 | old_gname = None |
|
137 | old_gname = None | |
138 | if edit: |
|
138 | if edit: | |
139 | old_gname = Group.get( |
|
139 | old_gname = Group.get( | |
140 | old_data.get('group_id')).group_name |
|
140 | old_data.get('group_id')).group_name | |
141 |
|
141 | |||
142 | if old_gname != group_name or not edit: |
|
142 | if old_gname != group_name or not edit: | |
143 | # check filesystem |
|
143 | # check filesystem | |
144 | gr = Group.query().filter(Group.group_name == slug)\ |
|
144 | gr = Group.query().filter(Group.group_name == slug)\ | |
145 | .filter(Group.group_parent_id == group_parent_id).scalar() |
|
145 | .filter(Group.group_parent_id == group_parent_id).scalar() | |
146 |
|
146 | |||
147 | if gr: |
|
147 | if gr: | |
148 | e_dict = {'group_name':_('This group already exists')} |
|
148 | e_dict = {'group_name':_('This group already exists')} | |
149 | raise formencode.Invalid('', value, state, |
|
149 | raise formencode.Invalid('', value, state, | |
150 | error_dict=e_dict) |
|
150 | error_dict=e_dict) | |
151 |
|
151 | |||
152 | return _ValidReposGroup |
|
152 | return _ValidReposGroup | |
153 |
|
153 | |||
154 | class ValidPassword(formencode.validators.FancyValidator): |
|
154 | class ValidPassword(formencode.validators.FancyValidator): | |
155 |
|
155 | |||
156 | def to_python(self, value, state): |
|
156 | def to_python(self, value, state): | |
157 |
|
157 | |||
158 | if value: |
|
158 | if value: | |
159 |
|
159 | |||
160 | if value.get('password'): |
|
160 | if value.get('password'): | |
161 | try: |
|
161 | try: | |
162 | value['password'] = get_crypt_password(value['password']) |
|
162 | value['password'] = get_crypt_password(value['password']) | |
163 | except UnicodeEncodeError: |
|
163 | except UnicodeEncodeError: | |
164 | e_dict = {'password':_('Invalid characters in password')} |
|
164 | e_dict = {'password':_('Invalid characters in password')} | |
165 | raise formencode.Invalid('', value, state, error_dict=e_dict) |
|
165 | raise formencode.Invalid('', value, state, error_dict=e_dict) | |
166 |
|
166 | |||
167 | if value.get('password_confirmation'): |
|
167 | if value.get('password_confirmation'): | |
168 | try: |
|
168 | try: | |
169 | value['password_confirmation'] = \ |
|
169 | value['password_confirmation'] = \ | |
170 | get_crypt_password(value['password_confirmation']) |
|
170 | get_crypt_password(value['password_confirmation']) | |
171 | except UnicodeEncodeError: |
|
171 | except UnicodeEncodeError: | |
172 | e_dict = {'password_confirmation':_('Invalid characters in password')} |
|
172 | e_dict = {'password_confirmation':_('Invalid characters in password')} | |
173 | raise formencode.Invalid('', value, state, error_dict=e_dict) |
|
173 | raise formencode.Invalid('', value, state, error_dict=e_dict) | |
174 |
|
174 | |||
175 | if value.get('new_password'): |
|
175 | if value.get('new_password'): | |
176 | try: |
|
176 | try: | |
177 | value['new_password'] = \ |
|
177 | value['new_password'] = \ | |
178 | get_crypt_password(value['new_password']) |
|
178 | get_crypt_password(value['new_password']) | |
179 | except UnicodeEncodeError: |
|
179 | except UnicodeEncodeError: | |
180 | e_dict = {'new_password':_('Invalid characters in password')} |
|
180 | e_dict = {'new_password':_('Invalid characters in password')} | |
181 | raise formencode.Invalid('', value, state, error_dict=e_dict) |
|
181 | raise formencode.Invalid('', value, state, error_dict=e_dict) | |
182 |
|
182 | |||
183 | return value |
|
183 | return value | |
184 |
|
184 | |||
185 | class ValidPasswordsMatch(formencode.validators.FancyValidator): |
|
185 | class ValidPasswordsMatch(formencode.validators.FancyValidator): | |
186 |
|
186 | |||
187 | def validate_python(self, value, state): |
|
187 | def validate_python(self, value, state): | |
188 |
|
188 | |||
189 |
|
|
189 | pass_val = value.get('password') or value.get('new_password') | |
|
190 | if pass_val != value['password_confirmation']: | |||
190 | e_dict = {'password_confirmation': |
|
191 | e_dict = {'password_confirmation': | |
191 | _('Passwords do not match')} |
|
192 | _('Passwords do not match')} | |
192 | raise formencode.Invalid('', value, state, error_dict=e_dict) |
|
193 | raise formencode.Invalid('', value, state, error_dict=e_dict) | |
193 |
|
194 | |||
194 | class ValidAuth(formencode.validators.FancyValidator): |
|
195 | class ValidAuth(formencode.validators.FancyValidator): | |
195 | messages = { |
|
196 | messages = { | |
196 |
|
|
197 | 'invalid_password':_('invalid password'), | |
197 |
|
|
198 | 'invalid_login':_('invalid user name'), | |
198 |
|
|
199 | 'disabled_account':_('Your account is disabled') | |
199 |
|
200 | } | ||
200 | } |
|
201 | ||
201 | #error mapping |
|
202 | # error mapping | |
202 | e_dict = {'username':messages['invalid_login'], |
|
203 | e_dict = {'username':messages['invalid_login'], | |
203 | 'password':messages['invalid_password']} |
|
204 | 'password':messages['invalid_password']} | |
204 | e_dict_disable = {'username':messages['disabled_account']} |
|
205 | e_dict_disable = {'username':messages['disabled_account']} | |
205 |
|
206 | |||
206 | def validate_python(self, value, state): |
|
207 | def validate_python(self, value, state): | |
207 | password = value['password'] |
|
208 | password = value['password'] | |
208 | username = value['username'] |
|
209 | username = value['username'] | |
209 | user = User.get_by_username(username) |
|
210 | user = User.get_by_username(username) | |
210 |
|
211 | |||
211 | if authenticate(username, password): |
|
212 | if authenticate(username, password): | |
212 | return value |
|
213 | return value | |
213 | else: |
|
214 | else: | |
214 | if user and user.active is False: |
|
215 | if user and user.active is False: | |
215 | log.warning('user %s is disabled', username) |
|
216 | log.warning('user %s is disabled', username) | |
216 | raise formencode.Invalid(self.message('disabled_account', |
|
217 | raise formencode.Invalid(self.message('disabled_account', | |
217 | state=State_obj), |
|
218 | state=State_obj), | |
218 | value, state, |
|
219 | value, state, | |
219 | error_dict=self.e_dict_disable) |
|
220 | error_dict=self.e_dict_disable) | |
220 | else: |
|
221 | else: | |
221 | log.warning('user %s not authenticated', username) |
|
222 | log.warning('user %s not authenticated', username) | |
222 | raise formencode.Invalid(self.message('invalid_password', |
|
223 | raise formencode.Invalid(self.message('invalid_password', | |
223 | state=State_obj), value, state, |
|
224 | state=State_obj), value, state, | |
224 | error_dict=self.e_dict) |
|
225 | error_dict=self.e_dict) | |
225 |
|
226 | |||
226 | class ValidRepoUser(formencode.validators.FancyValidator): |
|
227 | class ValidRepoUser(formencode.validators.FancyValidator): | |
227 |
|
228 | |||
228 | def to_python(self, value, state): |
|
229 | def to_python(self, value, state): | |
229 | try: |
|
230 | try: | |
230 | User.query().filter(User.active == True)\ |
|
231 | User.query().filter(User.active == True)\ | |
231 | .filter(User.username == value).one() |
|
232 | .filter(User.username == value).one() | |
232 | except Exception: |
|
233 | except Exception: | |
233 | raise formencode.Invalid(_('This username is not valid'), |
|
234 | raise formencode.Invalid(_('This username is not valid'), | |
234 | value, state) |
|
235 | value, state) | |
235 | return value |
|
236 | return value | |
236 |
|
237 | |||
237 | def ValidRepoName(edit, old_data): |
|
238 | def ValidRepoName(edit, old_data): | |
238 | class _ValidRepoName(formencode.validators.FancyValidator): |
|
239 | class _ValidRepoName(formencode.validators.FancyValidator): | |
239 | def to_python(self, value, state): |
|
240 | def to_python(self, value, state): | |
240 |
|
241 | |||
241 | repo_name = value.get('repo_name') |
|
242 | repo_name = value.get('repo_name') | |
242 |
|
243 | |||
243 | slug = repo_name_slug(repo_name) |
|
244 | slug = repo_name_slug(repo_name) | |
244 | if slug in [ADMIN_PREFIX, '']: |
|
245 | if slug in [ADMIN_PREFIX, '']: | |
245 | e_dict = {'repo_name': _('This repository name is disallowed')} |
|
246 | e_dict = {'repo_name': _('This repository name is disallowed')} | |
246 | raise formencode.Invalid('', value, state, error_dict=e_dict) |
|
247 | raise formencode.Invalid('', value, state, error_dict=e_dict) | |
247 |
|
248 | |||
248 |
|
249 | |||
249 | if value.get('repo_group'): |
|
250 | if value.get('repo_group'): | |
250 | gr = Group.get(value.get('repo_group')) |
|
251 | gr = Group.get(value.get('repo_group')) | |
251 | group_path = gr.full_path |
|
252 | group_path = gr.full_path | |
252 | # value needs to be aware of group name in order to check |
|
253 | # value needs to be aware of group name in order to check | |
253 | # db key This is an actual just the name to store in the |
|
254 | # db key This is an actual just the name to store in the | |
254 | # database |
|
255 | # database | |
255 | repo_name_full = group_path + Group.url_sep() + repo_name |
|
256 | repo_name_full = group_path + Group.url_sep() + repo_name | |
|
257 | ||||
256 | else: |
|
258 | else: | |
257 | group_path = '' |
|
259 | group_path = '' | |
258 | repo_name_full = repo_name |
|
260 | repo_name_full = repo_name | |
259 |
|
261 | |||
260 |
|
262 | |||
261 | value['repo_name_full'] = repo_name_full |
|
263 | value['repo_name_full'] = repo_name_full | |
262 | rename = old_data.get('repo_name') != repo_name_full |
|
264 | rename = old_data.get('repo_name') != repo_name_full | |
263 | create = not edit |
|
265 | create = not edit | |
264 | if rename or create: |
|
266 | if rename or create: | |
265 |
|
267 | |||
266 | if group_path != '': |
|
268 | if group_path != '': | |
267 | if RepoModel().get_by_repo_name(repo_name_full,): |
|
269 | if RepoModel().get_by_repo_name(repo_name_full,): | |
268 | e_dict = {'repo_name':_('This repository already ' |
|
270 | e_dict = {'repo_name':_('This repository already ' | |
269 | 'exists in a group "%s"') % |
|
271 | 'exists in a group "%s"') % | |
270 | gr.group_name} |
|
272 | gr.group_name} | |
271 | raise formencode.Invalid('', value, state, |
|
273 | raise formencode.Invalid('', value, state, | |
272 | error_dict=e_dict) |
|
274 | error_dict=e_dict) | |
273 | elif Group.get_by_group_name(repo_name_full): |
|
275 | elif Group.get_by_group_name(repo_name_full): | |
274 | e_dict = {'repo_name':_('There is a group with this' |
|
276 | e_dict = {'repo_name':_('There is a group with this' | |
275 | ' name already "%s"') % |
|
277 | ' name already "%s"') % | |
276 | repo_name_full} |
|
278 | repo_name_full} | |
277 | raise formencode.Invalid('', value, state, |
|
279 | raise formencode.Invalid('', value, state, | |
278 | error_dict=e_dict) |
|
280 | error_dict=e_dict) | |
279 |
|
281 | |||
280 | elif RepoModel().get_by_repo_name(repo_name_full): |
|
282 | elif RepoModel().get_by_repo_name(repo_name_full): | |
281 | e_dict = {'repo_name':_('This repository ' |
|
283 | e_dict = {'repo_name':_('This repository ' | |
282 | 'already exists')} |
|
284 | 'already exists')} | |
283 | raise formencode.Invalid('', value, state, |
|
285 | raise formencode.Invalid('', value, state, | |
284 | error_dict=e_dict) |
|
286 | error_dict=e_dict) | |
285 |
|
287 | |||
286 | return value |
|
288 | return value | |
287 |
|
289 | |||
288 | return _ValidRepoName |
|
290 | return _ValidRepoName | |
289 |
|
291 | |||
290 | def ValidForkName(): |
|
292 | def ValidForkName(): | |
291 | class _ValidForkName(formencode.validators.FancyValidator): |
|
293 | class _ValidForkName(formencode.validators.FancyValidator): | |
292 | def to_python(self, value, state): |
|
294 | def to_python(self, value, state): | |
293 |
|
295 | |||
294 | repo_name = value.get('fork_name') |
|
296 | repo_name = value.get('fork_name') | |
295 |
|
297 | |||
296 | slug = repo_name_slug(repo_name) |
|
298 | slug = repo_name_slug(repo_name) | |
297 | if slug in [ADMIN_PREFIX, '']: |
|
299 | if slug in [ADMIN_PREFIX, '']: | |
298 | e_dict = {'repo_name': _('This repository name is disallowed')} |
|
300 | e_dict = {'repo_name': _('This repository name is disallowed')} | |
299 | raise formencode.Invalid('', value, state, error_dict=e_dict) |
|
301 | raise formencode.Invalid('', value, state, error_dict=e_dict) | |
300 |
|
302 | |||
301 | if RepoModel().get_by_repo_name(repo_name): |
|
303 | if RepoModel().get_by_repo_name(repo_name): | |
302 | e_dict = {'fork_name':_('This repository ' |
|
304 | e_dict = {'fork_name':_('This repository ' | |
303 | 'already exists')} |
|
305 | 'already exists')} | |
304 | raise formencode.Invalid('', value, state, |
|
306 | raise formencode.Invalid('', value, state, | |
305 | error_dict=e_dict) |
|
307 | error_dict=e_dict) | |
306 | return value |
|
308 | return value | |
307 | return _ValidForkName |
|
309 | return _ValidForkName | |
308 |
|
310 | |||
309 |
|
311 | |||
310 | def SlugifyName(): |
|
312 | def SlugifyName(): | |
311 | class _SlugifyName(formencode.validators.FancyValidator): |
|
313 | class _SlugifyName(formencode.validators.FancyValidator): | |
312 |
|
314 | |||
313 | def to_python(self, value, state): |
|
315 | def to_python(self, value, state): | |
314 | return repo_name_slug(value) |
|
316 | return repo_name_slug(value) | |
315 |
|
317 | |||
316 | return _SlugifyName |
|
318 | return _SlugifyName | |
317 |
|
319 | |||
318 | def ValidCloneUri(): |
|
320 | def ValidCloneUri(): | |
319 | from mercurial.httprepo import httprepository, httpsrepository |
|
321 | from mercurial.httprepo import httprepository, httpsrepository | |
320 | from rhodecode.lib.utils import make_ui |
|
322 | from rhodecode.lib.utils import make_ui | |
321 |
|
323 | |||
322 | class _ValidCloneUri(formencode.validators.FancyValidator): |
|
324 | class _ValidCloneUri(formencode.validators.FancyValidator): | |
323 |
|
325 | |||
324 | def to_python(self, value, state): |
|
326 | def to_python(self, value, state): | |
325 | if not value: |
|
327 | if not value: | |
326 | pass |
|
328 | pass | |
327 | elif value.startswith('https'): |
|
329 | elif value.startswith('https'): | |
328 | try: |
|
330 | try: | |
329 | httpsrepository(make_ui('db'), value).capabilities |
|
331 | httpsrepository(make_ui('db'), value).capabilities | |
330 | except Exception, e: |
|
332 | except Exception, e: | |
331 | log.error(traceback.format_exc()) |
|
333 | log.error(traceback.format_exc()) | |
332 | raise formencode.Invalid(_('invalid clone url'), value, |
|
334 | raise formencode.Invalid(_('invalid clone url'), value, | |
333 | state) |
|
335 | state) | |
334 | elif value.startswith('http'): |
|
336 | elif value.startswith('http'): | |
335 | try: |
|
337 | try: | |
336 | httprepository(make_ui('db'), value).capabilities |
|
338 | httprepository(make_ui('db'), value).capabilities | |
337 | except Exception, e: |
|
339 | except Exception, e: | |
338 | log.error(traceback.format_exc()) |
|
340 | log.error(traceback.format_exc()) | |
339 | raise formencode.Invalid(_('invalid clone url'), value, |
|
341 | raise formencode.Invalid(_('invalid clone url'), value, | |
340 | state) |
|
342 | state) | |
341 | else: |
|
343 | else: | |
342 | raise formencode.Invalid(_('Invalid clone url, provide a ' |
|
344 | raise formencode.Invalid(_('Invalid clone url, provide a ' | |
343 | 'valid clone http\s url'), value, |
|
345 | 'valid clone http\s url'), value, | |
344 | state) |
|
346 | state) | |
345 | return value |
|
347 | return value | |
346 |
|
348 | |||
347 | return _ValidCloneUri |
|
349 | return _ValidCloneUri | |
348 |
|
350 | |||
349 | def ValidForkType(old_data): |
|
351 | def ValidForkType(old_data): | |
350 | class _ValidForkType(formencode.validators.FancyValidator): |
|
352 | class _ValidForkType(formencode.validators.FancyValidator): | |
351 |
|
353 | |||
352 | def to_python(self, value, state): |
|
354 | def to_python(self, value, state): | |
353 | if old_data['repo_type'] != value: |
|
355 | if old_data['repo_type'] != value: | |
354 | raise formencode.Invalid(_('Fork have to be the same ' |
|
356 | raise formencode.Invalid(_('Fork have to be the same ' | |
355 | 'type as original'), value, state) |
|
357 | 'type as original'), value, state) | |
356 |
|
358 | |||
357 | return value |
|
359 | return value | |
358 | return _ValidForkType |
|
360 | return _ValidForkType | |
359 |
|
361 | |||
360 | class ValidPerms(formencode.validators.FancyValidator): |
|
362 | class ValidPerms(formencode.validators.FancyValidator): | |
361 | messages = {'perm_new_member_name':_('This username or users group name' |
|
363 | messages = {'perm_new_member_name':_('This username or users group name' | |
362 | ' is not valid')} |
|
364 | ' is not valid')} | |
363 |
|
365 | |||
364 | def to_python(self, value, state): |
|
366 | def to_python(self, value, state): | |
365 | perms_update = [] |
|
367 | perms_update = [] | |
366 | perms_new = [] |
|
368 | perms_new = [] | |
367 | #build a list of permission to update and new permission to create |
|
369 | #build a list of permission to update and new permission to create | |
368 | for k, v in value.items(): |
|
370 | for k, v in value.items(): | |
369 | #means new added member to permissions |
|
371 | #means new added member to permissions | |
370 | if k.startswith('perm_new_member'): |
|
372 | if k.startswith('perm_new_member'): | |
371 | new_perm = value.get('perm_new_member', False) |
|
373 | new_perm = value.get('perm_new_member', False) | |
372 | new_member = value.get('perm_new_member_name', False) |
|
374 | new_member = value.get('perm_new_member_name', False) | |
373 | new_type = value.get('perm_new_member_type') |
|
375 | new_type = value.get('perm_new_member_type') | |
374 |
|
376 | |||
375 | if new_member and new_perm: |
|
377 | if new_member and new_perm: | |
376 | if (new_member, new_perm, new_type) not in perms_new: |
|
378 | if (new_member, new_perm, new_type) not in perms_new: | |
377 | perms_new.append((new_member, new_perm, new_type)) |
|
379 | perms_new.append((new_member, new_perm, new_type)) | |
378 | elif k.startswith('u_perm_') or k.startswith('g_perm_'): |
|
380 | elif k.startswith('u_perm_') or k.startswith('g_perm_'): | |
379 | member = k[7:] |
|
381 | member = k[7:] | |
380 | t = {'u':'user', |
|
382 | t = {'u':'user', | |
381 | 'g':'users_group'}[k[0]] |
|
383 | 'g':'users_group'}[k[0]] | |
382 | if member == 'default': |
|
384 | if member == 'default': | |
383 | if value['private']: |
|
385 | if value['private']: | |
384 | #set none for default when updating to private repo |
|
386 | #set none for default when updating to private repo | |
385 | v = 'repository.none' |
|
387 | v = 'repository.none' | |
386 | perms_update.append((member, v, t)) |
|
388 | perms_update.append((member, v, t)) | |
387 |
|
389 | |||
388 | value['perms_updates'] = perms_update |
|
390 | value['perms_updates'] = perms_update | |
389 | value['perms_new'] = perms_new |
|
391 | value['perms_new'] = perms_new | |
390 |
|
392 | |||
391 | #update permissions |
|
393 | #update permissions | |
392 | for k, v, t in perms_new: |
|
394 | for k, v, t in perms_new: | |
393 | try: |
|
395 | try: | |
394 | if t is 'user': |
|
396 | if t is 'user': | |
395 | self.user_db = User.query()\ |
|
397 | self.user_db = User.query()\ | |
396 | .filter(User.active == True)\ |
|
398 | .filter(User.active == True)\ | |
397 | .filter(User.username == k).one() |
|
399 | .filter(User.username == k).one() | |
398 | if t is 'users_group': |
|
400 | if t is 'users_group': | |
399 | self.user_db = UsersGroup.query()\ |
|
401 | self.user_db = UsersGroup.query()\ | |
400 | .filter(UsersGroup.users_group_active == True)\ |
|
402 | .filter(UsersGroup.users_group_active == True)\ | |
401 | .filter(UsersGroup.users_group_name == k).one() |
|
403 | .filter(UsersGroup.users_group_name == k).one() | |
402 |
|
404 | |||
403 | except Exception: |
|
405 | except Exception: | |
404 | msg = self.message('perm_new_member_name', |
|
406 | msg = self.message('perm_new_member_name', | |
405 | state=State_obj) |
|
407 | state=State_obj) | |
406 | raise formencode.Invalid(msg, value, state, |
|
408 | raise formencode.Invalid(msg, value, state, | |
407 | error_dict={'perm_new_member_name':msg}) |
|
409 | error_dict={'perm_new_member_name':msg}) | |
408 | return value |
|
410 | return value | |
409 |
|
411 | |||
410 | class ValidSettings(formencode.validators.FancyValidator): |
|
412 | class ValidSettings(formencode.validators.FancyValidator): | |
411 |
|
413 | |||
412 | def to_python(self, value, state): |
|
414 | def to_python(self, value, state): | |
413 | #settings form can't edit user |
|
415 | #settings form can't edit user | |
414 | if value.has_key('user'): |
|
416 | if value.has_key('user'): | |
415 | del['value']['user'] |
|
417 | del['value']['user'] | |
416 |
|
418 | |||
417 | return value |
|
419 | return value | |
418 |
|
420 | |||
419 | class ValidPath(formencode.validators.FancyValidator): |
|
421 | class ValidPath(formencode.validators.FancyValidator): | |
420 | def to_python(self, value, state): |
|
422 | def to_python(self, value, state): | |
421 |
|
423 | |||
422 | if not os.path.isdir(value): |
|
424 | if not os.path.isdir(value): | |
423 | msg = _('This is not a valid path') |
|
425 | msg = _('This is not a valid path') | |
424 | raise formencode.Invalid(msg, value, state, |
|
426 | raise formencode.Invalid(msg, value, state, | |
425 | error_dict={'paths_root_path':msg}) |
|
427 | error_dict={'paths_root_path':msg}) | |
426 | return value |
|
428 | return value | |
427 |
|
429 | |||
428 | def UniqSystemEmail(old_data): |
|
430 | def UniqSystemEmail(old_data): | |
429 | class _UniqSystemEmail(formencode.validators.FancyValidator): |
|
431 | class _UniqSystemEmail(formencode.validators.FancyValidator): | |
430 | def to_python(self, value, state): |
|
432 | def to_python(self, value, state): | |
431 | value = value.lower() |
|
433 | value = value.lower() | |
432 | if old_data.get('email') != value: |
|
434 | if old_data.get('email') != value: | |
433 | user = User.query().filter(User.email == value).scalar() |
|
435 | user = User.query().filter(User.email == value).scalar() | |
434 | if user: |
|
436 | if user: | |
435 | raise formencode.Invalid( |
|
437 | raise formencode.Invalid( | |
436 | _("This e-mail address is already taken"), |
|
438 | _("This e-mail address is already taken"), | |
437 | value, state) |
|
439 | value, state) | |
438 | return value |
|
440 | return value | |
439 |
|
441 | |||
440 | return _UniqSystemEmail |
|
442 | return _UniqSystemEmail | |
441 |
|
443 | |||
442 | class ValidSystemEmail(formencode.validators.FancyValidator): |
|
444 | class ValidSystemEmail(formencode.validators.FancyValidator): | |
443 | def to_python(self, value, state): |
|
445 | def to_python(self, value, state): | |
444 | value = value.lower() |
|
446 | value = value.lower() | |
445 | user = User.query().filter(User.email == value).scalar() |
|
447 | user = User.query().filter(User.email == value).scalar() | |
446 | if user is None: |
|
448 | if user is None: | |
447 | raise formencode.Invalid(_("This e-mail address doesn't exist.") , |
|
449 | raise formencode.Invalid(_("This e-mail address doesn't exist.") , | |
448 | value, state) |
|
450 | value, state) | |
449 |
|
451 | |||
450 | return value |
|
452 | return value | |
451 |
|
453 | |||
452 | class LdapLibValidator(formencode.validators.FancyValidator): |
|
454 | class LdapLibValidator(formencode.validators.FancyValidator): | |
453 |
|
455 | |||
454 | def to_python(self, value, state): |
|
456 | def to_python(self, value, state): | |
455 |
|
457 | |||
456 | try: |
|
458 | try: | |
457 | import ldap |
|
459 | import ldap | |
458 | except ImportError: |
|
460 | except ImportError: | |
459 | raise LdapImportError |
|
461 | raise LdapImportError | |
460 | return value |
|
462 | return value | |
461 |
|
463 | |||
462 | class AttrLoginValidator(formencode.validators.FancyValidator): |
|
464 | class AttrLoginValidator(formencode.validators.FancyValidator): | |
463 |
|
465 | |||
464 | def to_python(self, value, state): |
|
466 | def to_python(self, value, state): | |
465 |
|
467 | |||
466 | if not value or not isinstance(value, (str, unicode)): |
|
468 | if not value or not isinstance(value, (str, unicode)): | |
467 | raise formencode.Invalid(_("The LDAP Login attribute of the CN " |
|
469 | raise formencode.Invalid(_("The LDAP Login attribute of the CN " | |
468 | "must be specified - this is the name " |
|
470 | "must be specified - this is the name " | |
469 | "of the attribute that is equivalent " |
|
471 | "of the attribute that is equivalent " | |
470 | "to 'username'"), |
|
472 | "to 'username'"), | |
471 | value, state) |
|
473 | value, state) | |
472 |
|
474 | |||
473 | return value |
|
475 | return value | |
474 |
|
476 | |||
475 | #=============================================================================== |
|
477 | #=============================================================================== | |
476 | # FORMS |
|
478 | # FORMS | |
477 | #=============================================================================== |
|
479 | #=============================================================================== | |
478 | class LoginForm(formencode.Schema): |
|
480 | class LoginForm(formencode.Schema): | |
479 | allow_extra_fields = True |
|
481 | allow_extra_fields = True | |
480 | filter_extra_fields = True |
|
482 | filter_extra_fields = True | |
481 | username = UnicodeString( |
|
483 | username = UnicodeString( | |
482 | strip=True, |
|
484 | strip=True, | |
483 | min=1, |
|
485 | min=1, | |
484 | not_empty=True, |
|
486 | not_empty=True, | |
485 | messages={ |
|
487 | messages={ | |
486 | 'empty':_('Please enter a login'), |
|
488 | 'empty':_('Please enter a login'), | |
487 | 'tooShort':_('Enter a value %(min)i characters long or more')} |
|
489 | 'tooShort':_('Enter a value %(min)i characters long or more')} | |
488 | ) |
|
490 | ) | |
489 |
|
491 | |||
490 | password = UnicodeString( |
|
492 | password = UnicodeString( | |
491 | strip=True, |
|
493 | strip=True, | |
492 | min=3, |
|
494 | min=3, | |
493 | not_empty=True, |
|
495 | not_empty=True, | |
494 | messages={ |
|
496 | messages={ | |
495 | 'empty':_('Please enter a password'), |
|
497 | 'empty':_('Please enter a password'), | |
496 | 'tooShort':_('Enter %(min)i characters or more')} |
|
498 | 'tooShort':_('Enter %(min)i characters or more')} | |
497 | ) |
|
499 | ) | |
498 |
|
500 | |||
499 |
|
||||
500 | #chained validators have access to all data |
|
|||
501 | chained_validators = [ValidAuth] |
|
501 | chained_validators = [ValidAuth] | |
502 |
|
502 | |||
503 | def UserForm(edit=False, old_data={}): |
|
503 | def UserForm(edit=False, old_data={}): | |
504 | class _UserForm(formencode.Schema): |
|
504 | class _UserForm(formencode.Schema): | |
505 | allow_extra_fields = True |
|
505 | allow_extra_fields = True | |
506 | filter_extra_fields = True |
|
506 | filter_extra_fields = True | |
507 | username = All(UnicodeString(strip=True, min=1, not_empty=True), |
|
507 | username = All(UnicodeString(strip=True, min=1, not_empty=True), | |
508 | ValidUsername(edit, old_data)) |
|
508 | ValidUsername(edit, old_data)) | |
509 | if edit: |
|
509 | if edit: | |
510 | new_password = All(UnicodeString(strip=True, min=6, not_empty=False)) |
|
510 | new_password = All(UnicodeString(strip=True, min=6, not_empty=False)) | |
|
511 | password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=False)) | |||
511 | admin = StringBoolean(if_missing=False) |
|
512 | admin = StringBoolean(if_missing=False) | |
512 | else: |
|
513 | else: | |
513 | password = All(UnicodeString(strip=True, min=6, not_empty=True)) |
|
514 | password = All(UnicodeString(strip=True, min=6, not_empty=True)) | |
|
515 | password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=False)) | |||
|
516 | ||||
514 | active = StringBoolean(if_missing=False) |
|
517 | active = StringBoolean(if_missing=False) | |
515 | name = UnicodeString(strip=True, min=1, not_empty=True) |
|
518 | name = UnicodeString(strip=True, min=1, not_empty=True) | |
516 | lastname = UnicodeString(strip=True, min=1, not_empty=True) |
|
519 | lastname = UnicodeString(strip=True, min=1, not_empty=True) | |
517 | email = All(Email(not_empty=True), UniqSystemEmail(old_data)) |
|
520 | email = All(Email(not_empty=True), UniqSystemEmail(old_data)) | |
518 |
|
521 | |||
519 | chained_validators = [ValidPassword] |
|
522 | chained_validators = [ValidPasswordsMatch, ValidPassword] | |
520 |
|
523 | |||
521 | return _UserForm |
|
524 | return _UserForm | |
522 |
|
525 | |||
523 |
|
526 | |||
524 | def UsersGroupForm(edit=False, old_data={}, available_members=[]): |
|
527 | def UsersGroupForm(edit=False, old_data={}, available_members=[]): | |
525 | class _UsersGroupForm(formencode.Schema): |
|
528 | class _UsersGroupForm(formencode.Schema): | |
526 | allow_extra_fields = True |
|
529 | allow_extra_fields = True | |
527 | filter_extra_fields = True |
|
530 | filter_extra_fields = True | |
528 |
|
531 | |||
529 | users_group_name = All(UnicodeString(strip=True, min=1, not_empty=True), |
|
532 | users_group_name = All(UnicodeString(strip=True, min=1, not_empty=True), | |
530 | ValidUsersGroup(edit, old_data)) |
|
533 | ValidUsersGroup(edit, old_data)) | |
531 |
|
534 | |||
532 | users_group_active = StringBoolean(if_missing=False) |
|
535 | users_group_active = StringBoolean(if_missing=False) | |
533 |
|
536 | |||
534 | if edit: |
|
537 | if edit: | |
535 | users_group_members = OneOf(available_members, hideList=False, |
|
538 | users_group_members = OneOf(available_members, hideList=False, | |
536 | testValueList=True, |
|
539 | testValueList=True, | |
537 | if_missing=None, not_empty=False) |
|
540 | if_missing=None, not_empty=False) | |
538 |
|
541 | |||
539 | return _UsersGroupForm |
|
542 | return _UsersGroupForm | |
540 |
|
543 | |||
541 | def ReposGroupForm(edit=False, old_data={}, available_groups=[]): |
|
544 | def ReposGroupForm(edit=False, old_data={}, available_groups=[]): | |
542 | class _ReposGroupForm(formencode.Schema): |
|
545 | class _ReposGroupForm(formencode.Schema): | |
543 | allow_extra_fields = True |
|
546 | allow_extra_fields = True | |
544 | filter_extra_fields = True |
|
547 | filter_extra_fields = True | |
545 |
|
548 | |||
546 | group_name = All(UnicodeString(strip=True, min=1, not_empty=True), |
|
549 | group_name = All(UnicodeString(strip=True, min=1, not_empty=True), | |
547 | SlugifyName()) |
|
550 | SlugifyName()) | |
548 | group_description = UnicodeString(strip=True, min=1, |
|
551 | group_description = UnicodeString(strip=True, min=1, | |
549 | not_empty=True) |
|
552 | not_empty=True) | |
550 | group_parent_id = OneOf(available_groups, hideList=False, |
|
553 | group_parent_id = OneOf(available_groups, hideList=False, | |
551 | testValueList=True, |
|
554 | testValueList=True, | |
552 | if_missing=None, not_empty=False) |
|
555 | if_missing=None, not_empty=False) | |
553 |
|
556 | |||
554 | chained_validators = [ValidReposGroup(edit, old_data)] |
|
557 | chained_validators = [ValidReposGroup(edit, old_data)] | |
555 |
|
558 | |||
556 | return _ReposGroupForm |
|
559 | return _ReposGroupForm | |
557 |
|
560 | |||
558 | def RegisterForm(edit=False, old_data={}): |
|
561 | def RegisterForm(edit=False, old_data={}): | |
559 | class _RegisterForm(formencode.Schema): |
|
562 | class _RegisterForm(formencode.Schema): | |
560 | allow_extra_fields = True |
|
563 | allow_extra_fields = True | |
561 | filter_extra_fields = True |
|
564 | filter_extra_fields = True | |
562 | username = All(ValidUsername(edit, old_data), |
|
565 | username = All(ValidUsername(edit, old_data), | |
563 | UnicodeString(strip=True, min=1, not_empty=True)) |
|
566 | UnicodeString(strip=True, min=1, not_empty=True)) | |
564 | password = All(UnicodeString(strip=True, min=6, not_empty=True)) |
|
567 | password = All(UnicodeString(strip=True, min=6, not_empty=True)) | |
565 | password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True)) |
|
568 | password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True)) | |
566 | active = StringBoolean(if_missing=False) |
|
569 | active = StringBoolean(if_missing=False) | |
567 | name = UnicodeString(strip=True, min=1, not_empty=True) |
|
570 | name = UnicodeString(strip=True, min=1, not_empty=True) | |
568 | lastname = UnicodeString(strip=True, min=1, not_empty=True) |
|
571 | lastname = UnicodeString(strip=True, min=1, not_empty=True) | |
569 | email = All(Email(not_empty=True), UniqSystemEmail(old_data)) |
|
572 | email = All(Email(not_empty=True), UniqSystemEmail(old_data)) | |
570 |
|
573 | |||
571 | chained_validators = [ValidPasswordsMatch, ValidPassword] |
|
574 | chained_validators = [ValidPasswordsMatch, ValidPassword] | |
572 |
|
575 | |||
573 | return _RegisterForm |
|
576 | return _RegisterForm | |
574 |
|
577 | |||
575 | def PasswordResetForm(): |
|
578 | def PasswordResetForm(): | |
576 | class _PasswordResetForm(formencode.Schema): |
|
579 | class _PasswordResetForm(formencode.Schema): | |
577 | allow_extra_fields = True |
|
580 | allow_extra_fields = True | |
578 | filter_extra_fields = True |
|
581 | filter_extra_fields = True | |
579 | email = All(ValidSystemEmail(), Email(not_empty=True)) |
|
582 | email = All(ValidSystemEmail(), Email(not_empty=True)) | |
580 | return _PasswordResetForm |
|
583 | return _PasswordResetForm | |
581 |
|
584 | |||
582 | def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(), |
|
585 | def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(), | |
583 | repo_groups=[]): |
|
586 | repo_groups=[]): | |
584 | class _RepoForm(formencode.Schema): |
|
587 | class _RepoForm(formencode.Schema): | |
585 | allow_extra_fields = True |
|
588 | allow_extra_fields = True | |
586 | filter_extra_fields = False |
|
589 | filter_extra_fields = False | |
587 | repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), |
|
590 | repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), | |
588 | SlugifyName()) |
|
591 | SlugifyName()) | |
589 | clone_uri = All(UnicodeString(strip=True, min=1, not_empty=False), |
|
592 | clone_uri = All(UnicodeString(strip=True, min=1, not_empty=False), | |
590 | ValidCloneUri()()) |
|
593 | ValidCloneUri()()) | |
591 | repo_group = OneOf(repo_groups, hideList=True) |
|
594 | repo_group = OneOf(repo_groups, hideList=True) | |
592 | repo_type = OneOf(supported_backends) |
|
595 | repo_type = OneOf(supported_backends) | |
593 | description = UnicodeString(strip=True, min=1, not_empty=True) |
|
596 | description = UnicodeString(strip=True, min=1, not_empty=True) | |
594 | private = StringBoolean(if_missing=False) |
|
597 | private = StringBoolean(if_missing=False) | |
595 | enable_statistics = StringBoolean(if_missing=False) |
|
598 | enable_statistics = StringBoolean(if_missing=False) | |
596 | enable_downloads = StringBoolean(if_missing=False) |
|
599 | enable_downloads = StringBoolean(if_missing=False) | |
597 |
|
600 | |||
598 | if edit: |
|
601 | if edit: | |
599 | #this is repo owner |
|
602 | #this is repo owner | |
600 | user = All(UnicodeString(not_empty=True), ValidRepoUser) |
|
603 | user = All(UnicodeString(not_empty=True), ValidRepoUser) | |
601 |
|
604 | |||
602 | chained_validators = [ValidRepoName(edit, old_data), ValidPerms] |
|
605 | chained_validators = [ValidRepoName(edit, old_data), ValidPerms] | |
603 | return _RepoForm |
|
606 | return _RepoForm | |
604 |
|
607 | |||
605 | def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()): |
|
608 | def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()): | |
606 | class _RepoForkForm(formencode.Schema): |
|
609 | class _RepoForkForm(formencode.Schema): | |
607 | allow_extra_fields = True |
|
610 | allow_extra_fields = True | |
608 | filter_extra_fields = False |
|
611 | filter_extra_fields = False | |
609 | fork_name = All(UnicodeString(strip=True, min=1, not_empty=True), |
|
612 | fork_name = All(UnicodeString(strip=True, min=1, not_empty=True), | |
610 | SlugifyName()) |
|
613 | SlugifyName()) | |
611 | description = UnicodeString(strip=True, min=1, not_empty=True) |
|
614 | description = UnicodeString(strip=True, min=1, not_empty=True) | |
612 | private = StringBoolean(if_missing=False) |
|
615 | private = StringBoolean(if_missing=False) | |
613 | repo_type = All(ValidForkType(old_data), OneOf(supported_backends)) |
|
616 | repo_type = All(ValidForkType(old_data), OneOf(supported_backends)) | |
614 |
|
617 | |||
615 | chained_validators = [ValidForkName()] |
|
618 | chained_validators = [ValidForkName()] | |
616 |
|
619 | |||
617 | return _RepoForkForm |
|
620 | return _RepoForkForm | |
618 |
|
621 | |||
619 |
def RepoSettingsForm(edit=False, old_data={}) |
|
622 | def RepoSettingsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(), | |
|
623 | repo_groups=[]): | |||
620 | class _RepoForm(formencode.Schema): |
|
624 | class _RepoForm(formencode.Schema): | |
621 | allow_extra_fields = True |
|
625 | allow_extra_fields = True | |
622 | filter_extra_fields = False |
|
626 | filter_extra_fields = False | |
623 | repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), |
|
627 | repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), | |
624 | SlugifyName()) |
|
628 | SlugifyName()) | |
625 | description = UnicodeString(strip=True, min=1, not_empty=True) |
|
629 | description = UnicodeString(strip=True, min=1, not_empty=True) | |
|
630 | repo_group = OneOf(repo_groups, hideList=True) | |||
626 | private = StringBoolean(if_missing=False) |
|
631 | private = StringBoolean(if_missing=False) | |
627 |
|
632 | |||
628 |
chained_validators = [ValidRepoName(edit, old_data), ValidPerms, |
|
633 | chained_validators = [ValidRepoName(edit, old_data), ValidPerms, | |
|
634 | ValidSettings] | |||
629 | return _RepoForm |
|
635 | return _RepoForm | |
630 |
|
636 | |||
631 |
|
637 | |||
632 | def ApplicationSettingsForm(): |
|
638 | def ApplicationSettingsForm(): | |
633 | class _ApplicationSettingsForm(formencode.Schema): |
|
639 | class _ApplicationSettingsForm(formencode.Schema): | |
634 | allow_extra_fields = True |
|
640 | allow_extra_fields = True | |
635 | filter_extra_fields = False |
|
641 | filter_extra_fields = False | |
636 | rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True) |
|
642 | rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True) | |
637 | rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True) |
|
643 | rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True) | |
638 | rhodecode_ga_code = UnicodeString(strip=True, min=1, not_empty=False) |
|
644 | rhodecode_ga_code = UnicodeString(strip=True, min=1, not_empty=False) | |
639 |
|
645 | |||
640 | return _ApplicationSettingsForm |
|
646 | return _ApplicationSettingsForm | |
641 |
|
647 | |||
642 | def ApplicationUiSettingsForm(): |
|
648 | def ApplicationUiSettingsForm(): | |
643 | class _ApplicationUiSettingsForm(formencode.Schema): |
|
649 | class _ApplicationUiSettingsForm(formencode.Schema): | |
644 | allow_extra_fields = True |
|
650 | allow_extra_fields = True | |
645 | filter_extra_fields = False |
|
651 | filter_extra_fields = False | |
646 | web_push_ssl = OneOf(['true', 'false'], if_missing='false') |
|
652 | web_push_ssl = OneOf(['true', 'false'], if_missing='false') | |
647 | paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True)) |
|
653 | paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True)) | |
648 | hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False) |
|
654 | hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False) | |
649 | hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False) |
|
655 | hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False) | |
650 | hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False) |
|
656 | hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False) | |
651 | hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False) |
|
657 | hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False) | |
652 |
|
658 | |||
653 | return _ApplicationUiSettingsForm |
|
659 | return _ApplicationUiSettingsForm | |
654 |
|
660 | |||
655 | def DefaultPermissionsForm(perms_choices, register_choices, create_choices): |
|
661 | def DefaultPermissionsForm(perms_choices, register_choices, create_choices): | |
656 | class _DefaultPermissionsForm(formencode.Schema): |
|
662 | class _DefaultPermissionsForm(formencode.Schema): | |
657 | allow_extra_fields = True |
|
663 | allow_extra_fields = True | |
658 | filter_extra_fields = True |
|
664 | filter_extra_fields = True | |
659 | overwrite_default = StringBoolean(if_missing=False) |
|
665 | overwrite_default = StringBoolean(if_missing=False) | |
660 | anonymous = OneOf(['True', 'False'], if_missing=False) |
|
666 | anonymous = OneOf(['True', 'False'], if_missing=False) | |
661 | default_perm = OneOf(perms_choices) |
|
667 | default_perm = OneOf(perms_choices) | |
662 | default_register = OneOf(register_choices) |
|
668 | default_register = OneOf(register_choices) | |
663 | default_create = OneOf(create_choices) |
|
669 | default_create = OneOf(create_choices) | |
664 |
|
670 | |||
665 | return _DefaultPermissionsForm |
|
671 | return _DefaultPermissionsForm | |
666 |
|
672 | |||
667 |
|
673 | |||
668 | def LdapSettingsForm(tls_reqcert_choices, search_scope_choices, tls_kind_choices): |
|
674 | def LdapSettingsForm(tls_reqcert_choices, search_scope_choices, tls_kind_choices): | |
669 | class _LdapSettingsForm(formencode.Schema): |
|
675 | class _LdapSettingsForm(formencode.Schema): | |
670 | allow_extra_fields = True |
|
676 | allow_extra_fields = True | |
671 | filter_extra_fields = True |
|
677 | filter_extra_fields = True | |
672 | pre_validators = [LdapLibValidator] |
|
678 | pre_validators = [LdapLibValidator] | |
673 | ldap_active = StringBoolean(if_missing=False) |
|
679 | ldap_active = StringBoolean(if_missing=False) | |
674 | ldap_host = UnicodeString(strip=True,) |
|
680 | ldap_host = UnicodeString(strip=True,) | |
675 | ldap_port = Number(strip=True,) |
|
681 | ldap_port = Number(strip=True,) | |
676 | ldap_tls_kind = OneOf(tls_kind_choices) |
|
682 | ldap_tls_kind = OneOf(tls_kind_choices) | |
677 | ldap_tls_reqcert = OneOf(tls_reqcert_choices) |
|
683 | ldap_tls_reqcert = OneOf(tls_reqcert_choices) | |
678 | ldap_dn_user = UnicodeString(strip=True,) |
|
684 | ldap_dn_user = UnicodeString(strip=True,) | |
679 | ldap_dn_pass = UnicodeString(strip=True,) |
|
685 | ldap_dn_pass = UnicodeString(strip=True,) | |
680 | ldap_base_dn = UnicodeString(strip=True,) |
|
686 | ldap_base_dn = UnicodeString(strip=True,) | |
681 | ldap_filter = UnicodeString(strip=True,) |
|
687 | ldap_filter = UnicodeString(strip=True,) | |
682 | ldap_search_scope = OneOf(search_scope_choices) |
|
688 | ldap_search_scope = OneOf(search_scope_choices) | |
683 | ldap_attr_login = All(AttrLoginValidator, UnicodeString(strip=True,)) |
|
689 | ldap_attr_login = All(AttrLoginValidator, UnicodeString(strip=True,)) | |
684 | ldap_attr_firstname = UnicodeString(strip=True,) |
|
690 | ldap_attr_firstname = UnicodeString(strip=True,) | |
685 | ldap_attr_lastname = UnicodeString(strip=True,) |
|
691 | ldap_attr_lastname = UnicodeString(strip=True,) | |
686 | ldap_attr_email = UnicodeString(strip=True,) |
|
692 | ldap_attr_email = UnicodeString(strip=True,) | |
687 |
|
693 | |||
688 | return _LdapSettingsForm |
|
694 | return _LdapSettingsForm |
@@ -1,370 +1,411 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """ |
|
2 | """ | |
3 | rhodecode.model.repo |
|
3 | rhodecode.model.repo | |
4 | ~~~~~~~~~~~~~~~~~~~~ |
|
4 | ~~~~~~~~~~~~~~~~~~~~ | |
5 |
|
5 | |||
6 | Repository model for rhodecode |
|
6 | Repository model for rhodecode | |
7 |
|
7 | |||
8 | :created_on: Jun 5, 2010 |
|
8 | :created_on: Jun 5, 2010 | |
9 | :author: marcink |
|
9 | :author: marcink | |
10 | :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> |
|
10 | :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> | |
11 | :license: GPLv3, see COPYING for more details. |
|
11 | :license: GPLv3, see COPYING for more details. | |
12 | """ |
|
12 | """ | |
13 | # This program is free software: you can redistribute it and/or modify |
|
13 | # This program is free software: you can redistribute it and/or modify | |
14 | # it under the terms of the GNU General Public License as published by |
|
14 | # it under the terms of the GNU General Public License as published by | |
15 | # the Free Software Foundation, either version 3 of the License, or |
|
15 | # the Free Software Foundation, either version 3 of the License, or | |
16 | # (at your option) any later version. |
|
16 | # (at your option) any later version. | |
17 | # |
|
17 | # | |
18 | # This program is distributed in the hope that it will be useful, |
|
18 | # This program is distributed in the hope that it will be useful, | |
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 | # GNU General Public License for more details. |
|
21 | # GNU General Public License for more details. | |
22 | # |
|
22 | # | |
23 | # You should have received a copy of the GNU General Public License |
|
23 | # You should have received a copy of the GNU General Public License | |
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
25 | import os |
|
25 | import os | |
26 | import shutil |
|
26 | import shutil | |
27 | import logging |
|
27 | import logging | |
28 | import traceback |
|
28 | import traceback | |
29 | from datetime import datetime |
|
29 | from datetime import datetime | |
30 |
|
30 | |||
31 | from sqlalchemy.orm import joinedload, make_transient |
|
31 | from sqlalchemy.orm import joinedload, make_transient | |
32 |
|
32 | |||
33 | from vcs.utils.lazy import LazyProperty |
|
33 | from vcs.utils.lazy import LazyProperty | |
34 | from vcs.backends import get_backend |
|
34 | from vcs.backends import get_backend | |
35 |
|
35 | |||
36 | from rhodecode.lib import safe_str |
|
36 | from rhodecode.lib import safe_str | |
37 |
|
37 | |||
38 | from rhodecode.model import BaseModel |
|
38 | from rhodecode.model import BaseModel | |
39 | from rhodecode.model.caching_query import FromCache |
|
39 | from rhodecode.model.caching_query import FromCache | |
40 | from rhodecode.model.db import Repository, RepoToPerm, User, Permission, \ |
|
40 | from rhodecode.model.db import Repository, RepoToPerm, User, Permission, \ | |
41 | Statistics, UsersGroup, UsersGroupRepoToPerm, RhodeCodeUi, Group |
|
41 | Statistics, UsersGroup, UsersGroupRepoToPerm, RhodeCodeUi, Group | |
42 | from rhodecode.model.user import UserModel |
|
42 | from rhodecode.model.user import UserModel | |
43 |
|
43 | |||
44 | log = logging.getLogger(__name__) |
|
44 | log = logging.getLogger(__name__) | |
45 |
|
45 | |||
46 |
|
46 | |||
47 | class RepoModel(BaseModel): |
|
47 | class RepoModel(BaseModel): | |
48 |
|
48 | |||
49 | @LazyProperty |
|
49 | @LazyProperty | |
50 | def repos_path(self): |
|
50 | def repos_path(self): | |
51 | """Get's the repositories root path from database |
|
51 | """Get's the repositories root path from database | |
52 | """ |
|
52 | """ | |
53 |
|
53 | |||
54 | q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one() |
|
54 | q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one() | |
55 | return q.ui_value |
|
55 | return q.ui_value | |
56 |
|
56 | |||
57 | def get(self, repo_id, cache=False): |
|
57 | def get(self, repo_id, cache=False): | |
58 | repo = self.sa.query(Repository)\ |
|
58 | repo = self.sa.query(Repository)\ | |
59 | .filter(Repository.repo_id == repo_id) |
|
59 | .filter(Repository.repo_id == repo_id) | |
60 |
|
60 | |||
61 | if cache: |
|
61 | if cache: | |
62 | repo = repo.options(FromCache("sql_cache_short", |
|
62 | repo = repo.options(FromCache("sql_cache_short", | |
63 | "get_repo_%s" % repo_id)) |
|
63 | "get_repo_%s" % repo_id)) | |
64 | return repo.scalar() |
|
64 | return repo.scalar() | |
65 |
|
65 | |||
66 | def get_by_repo_name(self, repo_name, cache=False): |
|
66 | def get_by_repo_name(self, repo_name, cache=False): | |
67 | repo = self.sa.query(Repository)\ |
|
67 | repo = self.sa.query(Repository)\ | |
68 | .filter(Repository.repo_name == repo_name) |
|
68 | .filter(Repository.repo_name == repo_name) | |
69 |
|
69 | |||
70 | if cache: |
|
70 | if cache: | |
71 | repo = repo.options(FromCache("sql_cache_short", |
|
71 | repo = repo.options(FromCache("sql_cache_short", | |
72 | "get_repo_%s" % repo_name)) |
|
72 | "get_repo_%s" % repo_name)) | |
73 | return repo.scalar() |
|
73 | return repo.scalar() | |
74 |
|
74 | |||
75 |
|
75 | |||
76 | def get_users_js(self): |
|
76 | def get_users_js(self): | |
77 |
|
77 | |||
78 | users = self.sa.query(User).filter(User.active == True).all() |
|
78 | users = self.sa.query(User).filter(User.active == True).all() | |
79 | u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},''' |
|
79 | u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},''' | |
80 | users_array = '[%s]' % '\n'.join([u_tmpl % (u.user_id, u.name, |
|
80 | users_array = '[%s]' % '\n'.join([u_tmpl % (u.user_id, u.name, | |
81 | u.lastname, u.username) |
|
81 | u.lastname, u.username) | |
82 | for u in users]) |
|
82 | for u in users]) | |
83 | return users_array |
|
83 | return users_array | |
84 |
|
84 | |||
85 | def get_users_groups_js(self): |
|
85 | def get_users_groups_js(self): | |
86 | users_groups = self.sa.query(UsersGroup)\ |
|
86 | users_groups = self.sa.query(UsersGroup)\ | |
87 | .filter(UsersGroup.users_group_active == True).all() |
|
87 | .filter(UsersGroup.users_group_active == True).all() | |
88 |
|
88 | |||
89 | g_tmpl = '''{id:%s, grname:"%s",grmembers:"%s"},''' |
|
89 | g_tmpl = '''{id:%s, grname:"%s",grmembers:"%s"},''' | |
90 |
|
90 | |||
91 | users_groups_array = '[%s]' % '\n'.join([g_tmpl % \ |
|
91 | users_groups_array = '[%s]' % '\n'.join([g_tmpl % \ | |
92 | (gr.users_group_id, gr.users_group_name, |
|
92 | (gr.users_group_id, gr.users_group_name, | |
93 | len(gr.members)) |
|
93 | len(gr.members)) | |
94 | for gr in users_groups]) |
|
94 | for gr in users_groups]) | |
95 | return users_groups_array |
|
95 | return users_groups_array | |
96 |
|
96 | |||
|
97 | def _get_defaults(self, repo_name): | |||
|
98 | """ | |||
|
99 | Get's information about repository, and returns a dict for | |||
|
100 | usage in forms | |||
|
101 | ||||
|
102 | :param repo_name: | |||
|
103 | """ | |||
|
104 | ||||
|
105 | repo_info = Repository.get_by_repo_name(repo_name) | |||
|
106 | ||||
|
107 | if repo_info is None: | |||
|
108 | return None | |||
|
109 | ||||
|
110 | defaults = repo_info.get_dict() | |||
|
111 | group, repo_name = repo_info.groups_and_repo | |||
|
112 | defaults['repo_name'] = repo_name | |||
|
113 | defaults['repo_group'] = getattr(group[-1] if group else None, | |||
|
114 | 'group_id', None) | |||
|
115 | ||||
|
116 | # fill owner | |||
|
117 | if repo_info.user: | |||
|
118 | defaults.update({'user': repo_info.user.username}) | |||
|
119 | else: | |||
|
120 | replacement_user = User.query().filter(User.admin == | |||
|
121 | True).first().username | |||
|
122 | defaults.update({'user': replacement_user}) | |||
|
123 | ||||
|
124 | # fill repository users | |||
|
125 | for p in repo_info.repo_to_perm: | |||
|
126 | defaults.update({'u_perm_%s' % p.user.username: | |||
|
127 | p.permission.permission_name}) | |||
|
128 | ||||
|
129 | # fill repository groups | |||
|
130 | for p in repo_info.users_group_to_perm: | |||
|
131 | defaults.update({'g_perm_%s' % p.users_group.users_group_name: | |||
|
132 | p.permission.permission_name}) | |||
|
133 | ||||
|
134 | return defaults | |||
|
135 | ||||
|
136 | ||||
97 | def update(self, repo_name, form_data): |
|
137 | def update(self, repo_name, form_data): | |
98 | try: |
|
138 | try: | |
99 | cur_repo = self.get_by_repo_name(repo_name, cache=False) |
|
139 | cur_repo = self.get_by_repo_name(repo_name, cache=False) | |
100 |
|
140 | |||
101 | # update permissions |
|
141 | # update permissions | |
102 | for member, perm, member_type in form_data['perms_updates']: |
|
142 | for member, perm, member_type in form_data['perms_updates']: | |
103 | if member_type == 'user': |
|
143 | if member_type == 'user': | |
104 | r2p = self.sa.query(RepoToPerm)\ |
|
144 | r2p = self.sa.query(RepoToPerm)\ | |
105 | .filter(RepoToPerm.user == User.get_by_username(member))\ |
|
145 | .filter(RepoToPerm.user == User.get_by_username(member))\ | |
106 | .filter(RepoToPerm.repository == cur_repo)\ |
|
146 | .filter(RepoToPerm.repository == cur_repo)\ | |
107 | .one() |
|
147 | .one() | |
108 |
|
148 | |||
109 | r2p.permission = self.sa.query(Permission)\ |
|
149 | r2p.permission = self.sa.query(Permission)\ | |
110 | .filter(Permission.permission_name == |
|
150 | .filter(Permission.permission_name == | |
111 | perm).scalar() |
|
151 | perm).scalar() | |
112 | self.sa.add(r2p) |
|
152 | self.sa.add(r2p) | |
113 | else: |
|
153 | else: | |
114 | g2p = self.sa.query(UsersGroupRepoToPerm)\ |
|
154 | g2p = self.sa.query(UsersGroupRepoToPerm)\ | |
115 | .filter(UsersGroupRepoToPerm.users_group == |
|
155 | .filter(UsersGroupRepoToPerm.users_group == | |
116 | UsersGroup.get_by_group_name(member))\ |
|
156 | UsersGroup.get_by_group_name(member))\ | |
117 | .filter(UsersGroupRepoToPerm.repository == |
|
157 | .filter(UsersGroupRepoToPerm.repository == | |
118 | cur_repo).one() |
|
158 | cur_repo).one() | |
119 |
|
159 | |||
120 | g2p.permission = self.sa.query(Permission)\ |
|
160 | g2p.permission = self.sa.query(Permission)\ | |
121 | .filter(Permission.permission_name == |
|
161 | .filter(Permission.permission_name == | |
122 | perm).scalar() |
|
162 | perm).scalar() | |
123 | self.sa.add(g2p) |
|
163 | self.sa.add(g2p) | |
124 |
|
164 | |||
125 | # set new permissions |
|
165 | # set new permissions | |
126 | for member, perm, member_type in form_data['perms_new']: |
|
166 | for member, perm, member_type in form_data['perms_new']: | |
127 | if member_type == 'user': |
|
167 | if member_type == 'user': | |
128 | r2p = RepoToPerm() |
|
168 | r2p = RepoToPerm() | |
129 | r2p.repository = cur_repo |
|
169 | r2p.repository = cur_repo | |
130 | r2p.user = User.get_by_username(member) |
|
170 | r2p.user = User.get_by_username(member) | |
131 |
|
171 | |||
132 | r2p.permission = self.sa.query(Permission)\ |
|
172 | r2p.permission = self.sa.query(Permission)\ | |
133 | .filter(Permission. |
|
173 | .filter(Permission. | |
134 | permission_name == perm)\ |
|
174 | permission_name == perm)\ | |
135 | .scalar() |
|
175 | .scalar() | |
136 | self.sa.add(r2p) |
|
176 | self.sa.add(r2p) | |
137 | else: |
|
177 | else: | |
138 | g2p = UsersGroupRepoToPerm() |
|
178 | g2p = UsersGroupRepoToPerm() | |
139 | g2p.repository = cur_repo |
|
179 | g2p.repository = cur_repo | |
140 | g2p.users_group = UsersGroup.get_by_group_name(member) |
|
180 | g2p.users_group = UsersGroup.get_by_group_name(member) | |
141 | g2p.permission = self.sa.query(Permission)\ |
|
181 | g2p.permission = self.sa.query(Permission)\ | |
142 | .filter(Permission. |
|
182 | .filter(Permission. | |
143 | permission_name == perm)\ |
|
183 | permission_name == perm)\ | |
144 | .scalar() |
|
184 | .scalar() | |
145 | self.sa.add(g2p) |
|
185 | self.sa.add(g2p) | |
146 |
|
186 | |||
147 | # update current repo |
|
187 | # update current repo | |
148 | for k, v in form_data.items(): |
|
188 | for k, v in form_data.items(): | |
149 | if k == 'user': |
|
189 | if k == 'user': | |
150 | cur_repo.user = User.get_by_username(v) |
|
190 | cur_repo.user = User.get_by_username(v) | |
151 | elif k == 'repo_name': |
|
191 | elif k == 'repo_name': | |
152 | pass |
|
192 | pass | |
153 | elif k == 'repo_group': |
|
193 | elif k == 'repo_group': | |
154 |
cur_repo.group |
|
194 | cur_repo.group = Group.get(v) | |
155 |
|
195 | |||
156 | else: |
|
196 | else: | |
157 | setattr(cur_repo, k, v) |
|
197 | setattr(cur_repo, k, v) | |
158 |
|
198 | |||
159 | new_name = cur_repo.get_new_name(form_data['repo_name']) |
|
199 | new_name = cur_repo.get_new_name(form_data['repo_name']) | |
160 | cur_repo.repo_name = new_name |
|
200 | cur_repo.repo_name = new_name | |
161 |
|
201 | |||
162 | self.sa.add(cur_repo) |
|
202 | self.sa.add(cur_repo) | |
163 |
|
203 | |||
164 | if repo_name != new_name: |
|
204 | if repo_name != new_name: | |
165 | # rename repository |
|
205 | # rename repository | |
166 | self.__rename_repo(old=repo_name, new=new_name) |
|
206 | self.__rename_repo(old=repo_name, new=new_name) | |
167 |
|
207 | |||
168 | self.sa.commit() |
|
208 | self.sa.commit() | |
169 | return cur_repo |
|
209 | return cur_repo | |
170 | except: |
|
210 | except: | |
171 | log.error(traceback.format_exc()) |
|
211 | log.error(traceback.format_exc()) | |
172 | self.sa.rollback() |
|
212 | self.sa.rollback() | |
173 | raise |
|
213 | raise | |
174 |
|
214 | |||
175 | def create(self, form_data, cur_user, just_db=False, fork=False): |
|
215 | def create(self, form_data, cur_user, just_db=False, fork=False): | |
176 |
|
216 | |||
177 | try: |
|
217 | try: | |
178 | if fork: |
|
218 | if fork: | |
179 | repo_name = form_data['fork_name'] |
|
219 | repo_name = form_data['fork_name'] | |
180 | org_name = form_data['repo_name'] |
|
220 | org_name = form_data['repo_name'] | |
181 | org_full_name = org_name |
|
221 | org_full_name = org_name | |
182 |
|
222 | |||
183 | else: |
|
223 | else: | |
184 | org_name = repo_name = form_data['repo_name'] |
|
224 | org_name = repo_name = form_data['repo_name'] | |
185 | repo_name_full = form_data['repo_name_full'] |
|
225 | repo_name_full = form_data['repo_name_full'] | |
186 |
|
226 | |||
187 | new_repo = Repository() |
|
227 | new_repo = Repository() | |
188 | new_repo.enable_statistics = False |
|
228 | new_repo.enable_statistics = False | |
189 | for k, v in form_data.items(): |
|
229 | for k, v in form_data.items(): | |
190 | if k == 'repo_name': |
|
230 | if k == 'repo_name': | |
191 | if fork: |
|
231 | if fork: | |
192 | v = repo_name |
|
232 | v = repo_name | |
193 | else: |
|
233 | else: | |
194 | v = repo_name_full |
|
234 | v = repo_name_full | |
195 | if k == 'repo_group': |
|
235 | if k == 'repo_group': | |
196 | k = 'group_id' |
|
236 | k = 'group_id' | |
197 |
|
237 | |||
198 | if k == 'description': |
|
238 | if k == 'description': | |
199 | v = v or repo_name |
|
239 | v = v or repo_name | |
200 |
|
240 | |||
201 | setattr(new_repo, k, v) |
|
241 | setattr(new_repo, k, v) | |
202 |
|
242 | |||
203 | if fork: |
|
243 | if fork: | |
204 | parent_repo = self.sa.query(Repository)\ |
|
244 | parent_repo = self.sa.query(Repository)\ | |
205 | .filter(Repository.repo_name == org_full_name).one() |
|
245 | .filter(Repository.repo_name == org_full_name).one() | |
206 | new_repo.fork = parent_repo |
|
246 | new_repo.fork = parent_repo | |
207 |
|
247 | |||
208 | new_repo.user_id = cur_user.user_id |
|
248 | new_repo.user_id = cur_user.user_id | |
209 | self.sa.add(new_repo) |
|
249 | self.sa.add(new_repo) | |
210 |
|
250 | |||
211 | #create default permission |
|
251 | #create default permission | |
212 | repo_to_perm = RepoToPerm() |
|
252 | repo_to_perm = RepoToPerm() | |
213 | default = 'repository.read' |
|
253 | default = 'repository.read' | |
214 | for p in User.get_by_username('default').user_perms: |
|
254 | for p in User.get_by_username('default').user_perms: | |
215 | if p.permission.permission_name.startswith('repository.'): |
|
255 | if p.permission.permission_name.startswith('repository.'): | |
216 | default = p.permission.permission_name |
|
256 | default = p.permission.permission_name | |
217 | break |
|
257 | break | |
218 |
|
258 | |||
219 | default_perm = 'repository.none' if form_data['private'] else default |
|
259 | default_perm = 'repository.none' if form_data['private'] else default | |
220 |
|
260 | |||
221 | repo_to_perm.permission_id = self.sa.query(Permission)\ |
|
261 | repo_to_perm.permission_id = self.sa.query(Permission)\ | |
222 | .filter(Permission.permission_name == default_perm)\ |
|
262 | .filter(Permission.permission_name == default_perm)\ | |
223 | .one().permission_id |
|
263 | .one().permission_id | |
224 |
|
264 | |||
225 | repo_to_perm.repository = new_repo |
|
265 | repo_to_perm.repository = new_repo | |
226 | repo_to_perm.user_id = User.get_by_username('default').user_id |
|
266 | repo_to_perm.user_id = User.get_by_username('default').user_id | |
227 |
|
267 | |||
228 | self.sa.add(repo_to_perm) |
|
268 | self.sa.add(repo_to_perm) | |
229 |
|
269 | |||
230 | if not just_db: |
|
270 | if not just_db: | |
231 | self.__create_repo(repo_name, form_data['repo_type'], |
|
271 | self.__create_repo(repo_name, form_data['repo_type'], | |
232 | form_data['repo_group'], |
|
272 | form_data['repo_group'], | |
233 | form_data['clone_uri']) |
|
273 | form_data['clone_uri']) | |
234 |
|
274 | |||
235 | self.sa.commit() |
|
275 | self.sa.commit() | |
236 |
|
276 | |||
237 | #now automatically start following this repository as owner |
|
277 | #now automatically start following this repository as owner | |
238 | from rhodecode.model.scm import ScmModel |
|
278 | from rhodecode.model.scm import ScmModel | |
239 | ScmModel(self.sa).toggle_following_repo(new_repo.repo_id, |
|
279 | ScmModel(self.sa).toggle_following_repo(new_repo.repo_id, | |
240 | cur_user.user_id) |
|
280 | cur_user.user_id) | |
241 | return new_repo |
|
281 | return new_repo | |
242 | except: |
|
282 | except: | |
243 | log.error(traceback.format_exc()) |
|
283 | log.error(traceback.format_exc()) | |
244 | self.sa.rollback() |
|
284 | self.sa.rollback() | |
245 | raise |
|
285 | raise | |
246 |
|
286 | |||
247 | def create_fork(self, form_data, cur_user): |
|
287 | def create_fork(self, form_data, cur_user): | |
248 | from rhodecode.lib.celerylib import tasks, run_task |
|
288 | from rhodecode.lib.celerylib import tasks, run_task | |
249 | run_task(tasks.create_repo_fork, form_data, cur_user) |
|
289 | run_task(tasks.create_repo_fork, form_data, cur_user) | |
250 |
|
290 | |||
251 | def delete(self, repo): |
|
291 | def delete(self, repo): | |
252 | try: |
|
292 | try: | |
253 | self.sa.delete(repo) |
|
293 | self.sa.delete(repo) | |
254 | self.__delete_repo(repo) |
|
294 | self.__delete_repo(repo) | |
255 | self.sa.commit() |
|
295 | self.sa.commit() | |
256 | except: |
|
296 | except: | |
257 | log.error(traceback.format_exc()) |
|
297 | log.error(traceback.format_exc()) | |
258 | self.sa.rollback() |
|
298 | self.sa.rollback() | |
259 | raise |
|
299 | raise | |
260 |
|
300 | |||
261 | def delete_perm_user(self, form_data, repo_name): |
|
301 | def delete_perm_user(self, form_data, repo_name): | |
262 | try: |
|
302 | try: | |
263 | self.sa.query(RepoToPerm)\ |
|
303 | self.sa.query(RepoToPerm)\ | |
264 | .filter(RepoToPerm.repository \ |
|
304 | .filter(RepoToPerm.repository \ | |
265 | == self.get_by_repo_name(repo_name))\ |
|
305 | == self.get_by_repo_name(repo_name))\ | |
266 | .filter(RepoToPerm.user_id == form_data['user_id']).delete() |
|
306 | .filter(RepoToPerm.user_id == form_data['user_id']).delete() | |
267 | self.sa.commit() |
|
307 | self.sa.commit() | |
268 | except: |
|
308 | except: | |
269 | log.error(traceback.format_exc()) |
|
309 | log.error(traceback.format_exc()) | |
270 | self.sa.rollback() |
|
310 | self.sa.rollback() | |
271 | raise |
|
311 | raise | |
272 |
|
312 | |||
273 | def delete_perm_users_group(self, form_data, repo_name): |
|
313 | def delete_perm_users_group(self, form_data, repo_name): | |
274 | try: |
|
314 | try: | |
275 | self.sa.query(UsersGroupRepoToPerm)\ |
|
315 | self.sa.query(UsersGroupRepoToPerm)\ | |
276 | .filter(UsersGroupRepoToPerm.repository \ |
|
316 | .filter(UsersGroupRepoToPerm.repository \ | |
277 | == self.get_by_repo_name(repo_name))\ |
|
317 | == self.get_by_repo_name(repo_name))\ | |
278 | .filter(UsersGroupRepoToPerm.users_group_id \ |
|
318 | .filter(UsersGroupRepoToPerm.users_group_id \ | |
279 | == form_data['users_group_id']).delete() |
|
319 | == form_data['users_group_id']).delete() | |
280 | self.sa.commit() |
|
320 | self.sa.commit() | |
281 | except: |
|
321 | except: | |
282 | log.error(traceback.format_exc()) |
|
322 | log.error(traceback.format_exc()) | |
283 | self.sa.rollback() |
|
323 | self.sa.rollback() | |
284 | raise |
|
324 | raise | |
285 |
|
325 | |||
286 | def delete_stats(self, repo_name): |
|
326 | def delete_stats(self, repo_name): | |
287 | try: |
|
327 | try: | |
288 | self.sa.query(Statistics)\ |
|
328 | self.sa.query(Statistics)\ | |
289 | .filter(Statistics.repository == \ |
|
329 | .filter(Statistics.repository == \ | |
290 | self.get_by_repo_name(repo_name)).delete() |
|
330 | self.get_by_repo_name(repo_name)).delete() | |
291 | self.sa.commit() |
|
331 | self.sa.commit() | |
292 | except: |
|
332 | except: | |
293 | log.error(traceback.format_exc()) |
|
333 | log.error(traceback.format_exc()) | |
294 | self.sa.rollback() |
|
334 | self.sa.rollback() | |
295 | raise |
|
335 | raise | |
296 |
|
336 | |||
297 | def __create_repo(self, repo_name, alias, new_parent_id, clone_uri=False): |
|
337 | def __create_repo(self, repo_name, alias, new_parent_id, clone_uri=False): | |
298 | """ |
|
338 | """ | |
299 | makes repository on filesystem. It's group aware means it'll create |
|
339 | makes repository on filesystem. It's group aware means it'll create | |
300 | a repository within a group, and alter the paths accordingly of |
|
340 | a repository within a group, and alter the paths accordingly of | |
301 | group location |
|
341 | group location | |
302 |
|
342 | |||
303 | :param repo_name: |
|
343 | :param repo_name: | |
304 | :param alias: |
|
344 | :param alias: | |
305 | :param parent_id: |
|
345 | :param parent_id: | |
306 | :param clone_uri: |
|
346 | :param clone_uri: | |
307 | """ |
|
347 | """ | |
308 | from rhodecode.lib.utils import is_valid_repo,is_valid_repos_group |
|
348 | from rhodecode.lib.utils import is_valid_repo, is_valid_repos_group | |
309 |
|
349 | |||
310 | if new_parent_id: |
|
350 | if new_parent_id: | |
311 | paths = Group.get(new_parent_id).full_path.split(Group.url_sep()) |
|
351 | paths = Group.get(new_parent_id).full_path.split(Group.url_sep()) | |
312 | new_parent_path = os.sep.join(paths) |
|
352 | new_parent_path = os.sep.join(paths) | |
313 | else: |
|
353 | else: | |
314 | new_parent_path = '' |
|
354 | new_parent_path = '' | |
315 |
|
355 | |||
316 | repo_path = os.path.join(*map(lambda x:safe_str(x), |
|
356 | repo_path = os.path.join(*map(lambda x:safe_str(x), | |
317 | [self.repos_path, new_parent_path, repo_name])) |
|
357 | [self.repos_path, new_parent_path, repo_name])) | |
318 |
|
358 | |||
319 |
|
359 | |||
320 | # check if this path is not a repository |
|
360 | # check if this path is not a repository | |
321 | if is_valid_repo(repo_path, self.repos_path): |
|
361 | if is_valid_repo(repo_path, self.repos_path): | |
322 | raise Exception('This path %s is a valid repository' % repo_path) |
|
362 | raise Exception('This path %s is a valid repository' % repo_path) | |
323 |
|
363 | |||
324 | # check if this path is a group |
|
364 | # check if this path is a group | |
325 | if is_valid_repos_group(repo_path, self.repos_path): |
|
365 | if is_valid_repos_group(repo_path, self.repos_path): | |
326 | raise Exception('This path %s is a valid group' % repo_path) |
|
366 | raise Exception('This path %s is a valid group' % repo_path) | |
327 |
|
367 | |||
328 | log.info('creating repo %s in %s @ %s', repo_name, repo_path, |
|
368 | log.info('creating repo %s in %s @ %s', repo_name, repo_path, | |
329 | clone_uri) |
|
369 | clone_uri) | |
330 | backend = get_backend(alias) |
|
370 | backend = get_backend(alias) | |
331 |
|
371 | |||
332 | backend(repo_path, create=True, src_url=clone_uri) |
|
372 | backend(repo_path, create=True, src_url=clone_uri) | |
333 |
|
373 | |||
334 |
|
374 | |||
335 | def __rename_repo(self, old, new): |
|
375 | def __rename_repo(self, old, new): | |
336 | """ |
|
376 | """ | |
337 | renames repository on filesystem |
|
377 | renames repository on filesystem | |
338 |
|
378 | |||
339 | :param old: old name |
|
379 | :param old: old name | |
340 | :param new: new name |
|
380 | :param new: new name | |
341 | """ |
|
381 | """ | |
342 | log.info('renaming repo from %s to %s', old, new) |
|
382 | log.info('renaming repo from %s to %s', old, new) | |
343 |
|
383 | |||
344 | old_path = os.path.join(self.repos_path, old) |
|
384 | old_path = os.path.join(self.repos_path, old) | |
345 | new_path = os.path.join(self.repos_path, new) |
|
385 | new_path = os.path.join(self.repos_path, new) | |
346 | if os.path.isdir(new_path): |
|
386 | if os.path.isdir(new_path): | |
347 | raise Exception('Was trying to rename to already existing dir %s' \ |
|
387 | raise Exception('Was trying to rename to already existing dir %s' \ | |
348 | % new_path) |
|
388 | % new_path) | |
349 | shutil.move(old_path, new_path) |
|
389 | shutil.move(old_path, new_path) | |
350 |
|
390 | |||
351 | def __delete_repo(self, repo): |
|
391 | def __delete_repo(self, repo): | |
352 | """ |
|
392 | """ | |
353 | removes repo from filesystem, the removal is acctually made by |
|
393 | removes repo from filesystem, the removal is acctually made by | |
354 | added rm__ prefix into dir, and rename internat .hg/.git dirs so this |
|
394 | added rm__ prefix into dir, and rename internat .hg/.git dirs so this | |
355 | repository is no longer valid for rhodecode, can be undeleted later on |
|
395 | repository is no longer valid for rhodecode, can be undeleted later on | |
356 | by reverting the renames on this repository |
|
396 | by reverting the renames on this repository | |
357 |
|
397 | |||
358 | :param repo: repo object |
|
398 | :param repo: repo object | |
359 | """ |
|
399 | """ | |
360 | rm_path = os.path.join(self.repos_path, repo.repo_name) |
|
400 | rm_path = os.path.join(self.repos_path, repo.repo_name) | |
361 | log.info("Removing %s", rm_path) |
|
401 | log.info("Removing %s", rm_path) | |
362 | #disable hg/git |
|
402 | #disable hg/git | |
363 | alias = repo.repo_type |
|
403 | alias = repo.repo_type | |
364 | shutil.move(os.path.join(rm_path, '.%s' % alias), |
|
404 | shutil.move(os.path.join(rm_path, '.%s' % alias), | |
365 | os.path.join(rm_path, 'rm__.%s' % alias)) |
|
405 | os.path.join(rm_path, 'rm__.%s' % alias)) | |
366 | #disable repo |
|
406 | #disable repo | |
367 | shutil.move(rm_path, os.path.join(self.repos_path, 'rm__%s__%s' \ |
|
407 | shutil.move(rm_path, os.path.join(self.repos_path, 'rm__%s__%s' \ | |
368 | % (datetime.today()\ |
|
408 | % (datetime.today()\ | |
369 | .strftime('%Y%m%d_%H%M%S_%f'), |
|
409 | .strftime('%Y%m%d_%H%M%S_%f'), | |
370 | repo.repo_name))) |
|
410 | repo.repo_name))) | |
|
411 |
@@ -1,164 +1,164 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """ |
|
2 | """ | |
3 | rhodecode.model.user_group |
|
3 | rhodecode.model.user_group | |
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
5 |
|
5 | |||
6 | users groups model for RhodeCode |
|
6 | users groups model for RhodeCode | |
7 |
|
7 | |||
8 | :created_on: Jan 25, 2011 |
|
8 | :created_on: Jan 25, 2011 | |
9 | :author: marcink |
|
9 | :author: marcink | |
10 | :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> |
|
10 | :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> | |
11 | :license: GPLv3, see COPYING for more details. |
|
11 | :license: GPLv3, see COPYING for more details. | |
12 | """ |
|
12 | """ | |
13 | # This program is free software: you can redistribute it and/or modify |
|
13 | # This program is free software: you can redistribute it and/or modify | |
14 | # it under the terms of the GNU General Public License as published by |
|
14 | # it under the terms of the GNU General Public License as published by | |
15 | # the Free Software Foundation, either version 3 of the License, or |
|
15 | # the Free Software Foundation, either version 3 of the License, or | |
16 | # (at your option) any later version. |
|
16 | # (at your option) any later version. | |
17 | # |
|
17 | # | |
18 | # This program is distributed in the hope that it will be useful, |
|
18 | # This program is distributed in the hope that it will be useful, | |
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 | # GNU General Public License for more details. |
|
21 | # GNU General Public License for more details. | |
22 | # |
|
22 | # | |
23 | # You should have received a copy of the GNU General Public License |
|
23 | # You should have received a copy of the GNU General Public License | |
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
25 |
|
25 | |||
26 | import os |
|
26 | import os | |
27 | import logging |
|
27 | import logging | |
28 | import traceback |
|
28 | import traceback | |
29 | import shutil |
|
29 | import shutil | |
30 |
|
30 | |||
31 | from pylons.i18n.translation import _ |
|
31 | from pylons.i18n.translation import _ | |
32 |
|
32 | |||
33 | from vcs.utils.lazy import LazyProperty |
|
33 | from vcs.utils.lazy import LazyProperty | |
34 |
|
34 | |||
35 | from rhodecode.model import BaseModel |
|
35 | from rhodecode.model import BaseModel | |
36 | from rhodecode.model.caching_query import FromCache |
|
36 | from rhodecode.model.caching_query import FromCache | |
37 | from rhodecode.model.db import Group, RhodeCodeUi |
|
37 | from rhodecode.model.db import Group, RhodeCodeUi | |
38 |
|
38 | |||
39 | log = logging.getLogger(__name__) |
|
39 | log = logging.getLogger(__name__) | |
40 |
|
40 | |||
41 |
|
41 | |||
42 | class ReposGroupModel(BaseModel): |
|
42 | class ReposGroupModel(BaseModel): | |
43 |
|
43 | |||
44 | @LazyProperty |
|
44 | @LazyProperty | |
45 | def repos_path(self): |
|
45 | def repos_path(self): | |
46 | """ |
|
46 | """ | |
47 | Get's the repositories root path from database |
|
47 | Get's the repositories root path from database | |
48 | """ |
|
48 | """ | |
49 |
|
49 | |||
50 | q = RhodeCodeUi.get_by_key('/').one() |
|
50 | q = RhodeCodeUi.get_by_key('/').one() | |
51 | return q.ui_value |
|
51 | return q.ui_value | |
52 |
|
52 | |||
53 | def __create_group(self, group_name): |
|
53 | def __create_group(self, group_name): | |
54 | """ |
|
54 | """ | |
55 | makes repositories group on filesystem |
|
55 | makes repositories group on filesystem | |
56 |
|
56 | |||
57 | :param repo_name: |
|
57 | :param repo_name: | |
58 | :param parent_id: |
|
58 | :param parent_id: | |
59 | """ |
|
59 | """ | |
60 |
|
60 | |||
61 | create_path = os.path.join(self.repos_path, group_name) |
|
61 | create_path = os.path.join(self.repos_path, group_name) | |
62 | log.debug('creating new group in %s', create_path) |
|
62 | log.debug('creating new group in %s', create_path) | |
63 |
|
63 | |||
64 | if os.path.isdir(create_path): |
|
64 | if os.path.isdir(create_path): | |
65 | raise Exception('That directory already exists !') |
|
65 | raise Exception('That directory already exists !') | |
66 |
|
66 | |||
67 | os.makedirs(create_path) |
|
67 | os.makedirs(create_path) | |
68 |
|
68 | |||
69 | def __rename_group(self, old, new): |
|
69 | def __rename_group(self, old, new): | |
70 | """ |
|
70 | """ | |
71 | Renames a group on filesystem |
|
71 | Renames a group on filesystem | |
72 |
|
72 | |||
73 | :param group_name: |
|
73 | :param group_name: | |
74 | """ |
|
74 | """ | |
75 |
|
75 | |||
76 | if old == new: |
|
76 | if old == new: | |
77 | log.debug('skipping group rename') |
|
77 | log.debug('skipping group rename') | |
78 | return |
|
78 | return | |
79 |
|
79 | |||
80 | log.debug('renaming repos group from %s to %s', old, new) |
|
80 | log.debug('renaming repos group from %s to %s', old, new) | |
81 |
|
81 | |||
82 |
|
82 | |||
83 | old_path = os.path.join(self.repos_path, old) |
|
83 | old_path = os.path.join(self.repos_path, old) | |
84 | new_path = os.path.join(self.repos_path, new) |
|
84 | new_path = os.path.join(self.repos_path, new) | |
85 |
|
85 | |||
86 | log.debug('renaming repos paths from %s to %s', old_path, new_path) |
|
86 | log.debug('renaming repos paths from %s to %s', old_path, new_path) | |
87 |
|
87 | |||
88 | if os.path.isdir(new_path): |
|
88 | if os.path.isdir(new_path): | |
89 | raise Exception('Was trying to rename to already ' |
|
89 | raise Exception('Was trying to rename to already ' | |
90 | 'existing dir %s' % new_path) |
|
90 | 'existing dir %s' % new_path) | |
91 | shutil.move(old_path, new_path) |
|
91 | shutil.move(old_path, new_path) | |
92 |
|
92 | |||
93 | def __delete_group(self, group): |
|
93 | def __delete_group(self, group): | |
94 | """ |
|
94 | """ | |
95 | Deletes a group from a filesystem |
|
95 | Deletes a group from a filesystem | |
96 |
|
96 | |||
97 | :param group: instance of group from database |
|
97 | :param group: instance of group from database | |
98 | """ |
|
98 | """ | |
99 | paths = group.full_path.split(Group.url_sep()) |
|
99 | paths = group.full_path.split(Group.url_sep()) | |
100 | paths = os.sep.join(paths) |
|
100 | paths = os.sep.join(paths) | |
101 |
|
101 | |||
102 | rm_path = os.path.join(self.repos_path, paths) |
|
102 | rm_path = os.path.join(self.repos_path, paths) | |
103 | if os.path.isdir(rm_path): |
|
103 | if os.path.isdir(rm_path): | |
104 | # delete only if that path really exists |
|
104 | # delete only if that path really exists | |
105 | os.rmdir(rm_path) |
|
105 | os.rmdir(rm_path) | |
106 |
|
106 | |||
107 | def create(self, form_data): |
|
107 | def create(self, form_data): | |
108 | try: |
|
108 | try: | |
109 | new_repos_group = Group() |
|
109 | new_repos_group = Group() | |
110 | new_repos_group.group_description = form_data['group_description'] |
|
110 | new_repos_group.group_description = form_data['group_description'] | |
111 | new_repos_group.parent_group = Group.get(form_data['group_parent_id']) |
|
111 | new_repos_group.parent_group = Group.get(form_data['group_parent_id']) | |
112 | new_repos_group.group_name = new_repos_group.get_new_name(form_data['group_name']) |
|
112 | new_repos_group.group_name = new_repos_group.get_new_name(form_data['group_name']) | |
113 |
|
113 | |||
114 | self.sa.add(new_repos_group) |
|
114 | self.sa.add(new_repos_group) | |
115 |
|
115 | |||
116 | self.__create_group(new_repos_group.group_name) |
|
116 | self.__create_group(new_repos_group.group_name) | |
117 |
|
117 | |||
118 | self.sa.commit() |
|
118 | self.sa.commit() | |
119 | return new_repos_group |
|
119 | return new_repos_group | |
120 | except: |
|
120 | except: | |
121 | log.error(traceback.format_exc()) |
|
121 | log.error(traceback.format_exc()) | |
122 | self.sa.rollback() |
|
122 | self.sa.rollback() | |
123 | raise |
|
123 | raise | |
124 |
|
124 | |||
125 | def update(self, repos_group_id, form_data): |
|
125 | def update(self, repos_group_id, form_data): | |
126 |
|
126 | |||
127 | try: |
|
127 | try: | |
128 | repos_group = Group.get(repos_group_id) |
|
128 | repos_group = Group.get(repos_group_id) | |
129 | old_path = repos_group.full_path |
|
129 | old_path = repos_group.full_path | |
130 |
|
130 | |||
131 | #change properties |
|
131 | # change properties | |
132 | repos_group.group_description = form_data['group_description'] |
|
132 | repos_group.group_description = form_data['group_description'] | |
133 | repos_group.parent_group = Group.get(form_data['group_parent_id']) |
|
133 | repos_group.parent_group = Group.get(form_data['group_parent_id']) | |
134 | repos_group.group_name = repos_group.get_new_name(form_data['group_name']) |
|
134 | repos_group.group_name = repos_group.get_new_name(form_data['group_name']) | |
135 |
|
135 | |||
136 | new_path = repos_group.full_path |
|
136 | new_path = repos_group.full_path | |
137 |
|
137 | |||
138 | self.sa.add(repos_group) |
|
138 | self.sa.add(repos_group) | |
139 |
|
139 | |||
140 | self.__rename_group(old_path, new_path) |
|
140 | self.__rename_group(old_path, new_path) | |
141 |
|
141 | |||
142 | # we need to get all repositories from this new group and |
|
142 | # we need to get all repositories from this new group and | |
143 | # rename them accordingly to new group path |
|
143 | # rename them accordingly to new group path | |
144 | for r in repos_group.repositories: |
|
144 | for r in repos_group.repositories: | |
145 | r.repo_name = r.get_new_name(r.just_name) |
|
145 | r.repo_name = r.get_new_name(r.just_name) | |
146 | self.sa.add(r) |
|
146 | self.sa.add(r) | |
147 |
|
147 | |||
148 | self.sa.commit() |
|
148 | self.sa.commit() | |
149 | return repos_group |
|
149 | return repos_group | |
150 | except: |
|
150 | except: | |
151 | log.error(traceback.format_exc()) |
|
151 | log.error(traceback.format_exc()) | |
152 | self.sa.rollback() |
|
152 | self.sa.rollback() | |
153 | raise |
|
153 | raise | |
154 |
|
154 | |||
155 | def delete(self, users_group_id): |
|
155 | def delete(self, users_group_id): | |
156 | try: |
|
156 | try: | |
157 | users_group = Group.get(users_group_id) |
|
157 | users_group = Group.get(users_group_id) | |
158 | self.sa.delete(users_group) |
|
158 | self.sa.delete(users_group) | |
159 | self.__delete_group(users_group) |
|
159 | self.__delete_group(users_group) | |
160 | self.sa.commit() |
|
160 | self.sa.commit() | |
161 | except: |
|
161 | except: | |
162 | log.error(traceback.format_exc()) |
|
162 | log.error(traceback.format_exc()) | |
163 | self.sa.rollback() |
|
163 | self.sa.rollback() | |
164 | raise |
|
164 | raise |
@@ -1,388 +1,390 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """ |
|
2 | """ | |
3 | rhodecode.model.user |
|
3 | rhodecode.model.user | |
4 | ~~~~~~~~~~~~~~~~~~~~ |
|
4 | ~~~~~~~~~~~~~~~~~~~~ | |
5 |
|
5 | |||
6 | users model for RhodeCode |
|
6 | users model for RhodeCode | |
7 |
|
7 | |||
8 | :created_on: Apr 9, 2010 |
|
8 | :created_on: Apr 9, 2010 | |
9 | :author: marcink |
|
9 | :author: marcink | |
10 | :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> |
|
10 | :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> | |
11 | :license: GPLv3, see COPYING for more details. |
|
11 | :license: GPLv3, see COPYING for more details. | |
12 | """ |
|
12 | """ | |
13 | # This program is free software: you can redistribute it and/or modify |
|
13 | # This program is free software: you can redistribute it and/or modify | |
14 | # it under the terms of the GNU General Public License as published by |
|
14 | # it under the terms of the GNU General Public License as published by | |
15 | # the Free Software Foundation, either version 3 of the License, or |
|
15 | # the Free Software Foundation, either version 3 of the License, or | |
16 | # (at your option) any later version. |
|
16 | # (at your option) any later version. | |
17 | # |
|
17 | # | |
18 | # This program is distributed in the hope that it will be useful, |
|
18 | # This program is distributed in the hope that it will be useful, | |
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 | # GNU General Public License for more details. |
|
21 | # GNU General Public License for more details. | |
22 | # |
|
22 | # | |
23 | # You should have received a copy of the GNU General Public License |
|
23 | # You should have received a copy of the GNU General Public License | |
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
25 |
|
25 | |||
26 | import logging |
|
26 | import logging | |
27 | import traceback |
|
27 | import traceback | |
28 |
|
28 | |||
29 | from pylons.i18n.translation import _ |
|
29 | from pylons.i18n.translation import _ | |
30 |
|
30 | |||
31 | from rhodecode.lib import safe_unicode |
|
31 | from rhodecode.lib import safe_unicode | |
32 | from rhodecode.model import BaseModel |
|
32 | from rhodecode.model import BaseModel | |
33 | from rhodecode.model.caching_query import FromCache |
|
33 | from rhodecode.model.caching_query import FromCache | |
34 | from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \ |
|
34 | from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \ | |
35 | UserToPerm, UsersGroupRepoToPerm, UsersGroupToPerm, UsersGroupMember |
|
35 | UserToPerm, UsersGroupRepoToPerm, UsersGroupToPerm, UsersGroupMember | |
36 | from rhodecode.lib.exceptions import DefaultUserException, \ |
|
36 | from rhodecode.lib.exceptions import DefaultUserException, \ | |
37 | UserOwnsReposException |
|
37 | UserOwnsReposException | |
38 |
|
38 | |||
39 | from sqlalchemy.exc import DatabaseError |
|
39 | from sqlalchemy.exc import DatabaseError | |
40 | from rhodecode.lib import generate_api_key |
|
40 | from rhodecode.lib import generate_api_key | |
41 | from sqlalchemy.orm import joinedload |
|
41 | from sqlalchemy.orm import joinedload | |
42 |
|
42 | |||
43 | log = logging.getLogger(__name__) |
|
43 | log = logging.getLogger(__name__) | |
44 |
|
44 | |||
45 | PERM_WEIGHTS = {'repository.none': 0, |
|
45 | PERM_WEIGHTS = {'repository.none': 0, | |
46 | 'repository.read': 1, |
|
46 | 'repository.read': 1, | |
47 | 'repository.write': 3, |
|
47 | 'repository.write': 3, | |
48 | 'repository.admin': 3} |
|
48 | 'repository.admin': 3} | |
49 |
|
49 | |||
50 |
|
50 | |||
51 | class UserModel(BaseModel): |
|
51 | class UserModel(BaseModel): | |
52 |
|
||||
53 | def get(self, user_id, cache=False): |
|
52 | def get(self, user_id, cache=False): | |
54 | user = self.sa.query(User) |
|
53 | user = self.sa.query(User) | |
55 | if cache: |
|
54 | if cache: | |
56 | user = user.options(FromCache("sql_cache_short", |
|
55 | user = user.options(FromCache("sql_cache_short", | |
57 | "get_user_%s" % user_id)) |
|
56 | "get_user_%s" % user_id)) | |
58 | return user.get(user_id) |
|
57 | return user.get(user_id) | |
59 |
|
58 | |||
60 | def get_by_username(self, username, cache=False, case_insensitive=False): |
|
59 | def get_by_username(self, username, cache=False, case_insensitive=False): | |
61 |
|
60 | |||
62 | if case_insensitive: |
|
61 | if case_insensitive: | |
63 | user = self.sa.query(User).filter(User.username.ilike(username)) |
|
62 | user = self.sa.query(User).filter(User.username.ilike(username)) | |
64 | else: |
|
63 | else: | |
65 | user = self.sa.query(User)\ |
|
64 | user = self.sa.query(User)\ | |
66 | .filter(User.username == username) |
|
65 | .filter(User.username == username) | |
67 | if cache: |
|
66 | if cache: | |
68 | user = user.options(FromCache("sql_cache_short", |
|
67 | user = user.options(FromCache("sql_cache_short", | |
69 | "get_user_%s" % username)) |
|
68 | "get_user_%s" % username)) | |
70 | return user.scalar() |
|
69 | return user.scalar() | |
71 |
|
70 | |||
72 | def get_by_api_key(self, api_key, cache=False): |
|
71 | def get_by_api_key(self, api_key, cache=False): | |
73 |
|
72 | |||
74 | user = self.sa.query(User)\ |
|
73 | user = self.sa.query(User)\ | |
75 | .filter(User.api_key == api_key) |
|
74 | .filter(User.api_key == api_key) | |
76 | if cache: |
|
75 | if cache: | |
77 | user = user.options(FromCache("sql_cache_short", |
|
76 | user = user.options(FromCache("sql_cache_short", | |
78 | "get_user_%s" % api_key)) |
|
77 | "get_user_%s" % api_key)) | |
79 | return user.scalar() |
|
78 | return user.scalar() | |
80 |
|
79 | |||
81 | def create(self, form_data): |
|
80 | def create(self, form_data): | |
82 | try: |
|
81 | try: | |
83 | new_user = User() |
|
82 | new_user = User() | |
84 | for k, v in form_data.items(): |
|
83 | for k, v in form_data.items(): | |
85 | setattr(new_user, k, v) |
|
84 | setattr(new_user, k, v) | |
86 |
|
85 | |||
87 | new_user.api_key = generate_api_key(form_data['username']) |
|
86 | new_user.api_key = generate_api_key(form_data['username']) | |
88 | self.sa.add(new_user) |
|
87 | self.sa.add(new_user) | |
89 | self.sa.commit() |
|
88 | self.sa.commit() | |
|
89 | return new_user | |||
90 | except: |
|
90 | except: | |
91 | log.error(traceback.format_exc()) |
|
91 | log.error(traceback.format_exc()) | |
92 | self.sa.rollback() |
|
92 | self.sa.rollback() | |
93 | raise |
|
93 | raise | |
94 |
|
94 | |||
95 | def create_ldap(self, username, password, user_dn, attrs): |
|
95 | def create_ldap(self, username, password, user_dn, attrs): | |
96 | """ |
|
96 | """ | |
97 | Checks if user is in database, if not creates this user marked |
|
97 | Checks if user is in database, if not creates this user marked | |
98 | as ldap user |
|
98 | as ldap user | |
|
99 | ||||
99 | :param username: |
|
100 | :param username: | |
100 | :param password: |
|
101 | :param password: | |
101 | :param user_dn: |
|
102 | :param user_dn: | |
102 | :param attrs: |
|
103 | :param attrs: | |
103 | """ |
|
104 | """ | |
104 | from rhodecode.lib.auth import get_crypt_password |
|
105 | from rhodecode.lib.auth import get_crypt_password | |
105 | log.debug('Checking for such ldap account in RhodeCode database') |
|
106 | log.debug('Checking for such ldap account in RhodeCode database') | |
106 | if self.get_by_username(username, case_insensitive=True) is None: |
|
107 | if self.get_by_username(username, case_insensitive=True) is None: | |
107 | try: |
|
108 | try: | |
108 | new_user = User() |
|
109 | new_user = User() | |
109 | # add ldap account always lowercase |
|
110 | # add ldap account always lowercase | |
110 | new_user.username = username.lower() |
|
111 | new_user.username = username.lower() | |
111 | new_user.password = get_crypt_password(password) |
|
112 | new_user.password = get_crypt_password(password) | |
112 | new_user.api_key = generate_api_key(username) |
|
113 | new_user.api_key = generate_api_key(username) | |
113 | new_user.email = attrs['email'] |
|
114 | new_user.email = attrs['email'] | |
114 | new_user.active = True |
|
115 | new_user.active = True | |
115 | new_user.ldap_dn = safe_unicode(user_dn) |
|
116 | new_user.ldap_dn = safe_unicode(user_dn) | |
116 | new_user.name = attrs['name'] |
|
117 | new_user.name = attrs['name'] | |
117 | new_user.lastname = attrs['lastname'] |
|
118 | new_user.lastname = attrs['lastname'] | |
118 |
|
119 | |||
119 | self.sa.add(new_user) |
|
120 | self.sa.add(new_user) | |
120 | self.sa.commit() |
|
121 | self.sa.commit() | |
121 | return True |
|
122 | return True | |
122 | except (DatabaseError,): |
|
123 | except (DatabaseError,): | |
123 | log.error(traceback.format_exc()) |
|
124 | log.error(traceback.format_exc()) | |
124 | self.sa.rollback() |
|
125 | self.sa.rollback() | |
125 | raise |
|
126 | raise | |
126 | log.debug('this %s user exists skipping creation of ldap account', |
|
127 | log.debug('this %s user exists skipping creation of ldap account', | |
127 | username) |
|
128 | username) | |
128 | return False |
|
129 | return False | |
129 |
|
130 | |||
130 | def create_registration(self, form_data): |
|
131 | def create_registration(self, form_data): | |
131 | from rhodecode.lib.celerylib import tasks, run_task |
|
132 | from rhodecode.lib.celerylib import tasks, run_task | |
132 | try: |
|
133 | try: | |
133 | new_user = User() |
|
134 | new_user = User() | |
134 | for k, v in form_data.items(): |
|
135 | for k, v in form_data.items(): | |
135 | if k != 'admin': |
|
136 | if k != 'admin': | |
136 | setattr(new_user, k, v) |
|
137 | setattr(new_user, k, v) | |
137 |
|
138 | |||
138 | self.sa.add(new_user) |
|
139 | self.sa.add(new_user) | |
139 | self.sa.commit() |
|
140 | self.sa.commit() | |
140 | body = ('New user registration\n' |
|
141 | body = ('New user registration\n' | |
141 | 'username: %s\n' |
|
142 | 'username: %s\n' | |
142 | 'email: %s\n') |
|
143 | 'email: %s\n') | |
143 | body = body % (form_data['username'], form_data['email']) |
|
144 | body = body % (form_data['username'], form_data['email']) | |
144 |
|
145 | |||
145 | run_task(tasks.send_email, None, |
|
146 | run_task(tasks.send_email, None, | |
146 | _('[RhodeCode] New User registration'), |
|
147 | _('[RhodeCode] New User registration'), | |
147 | body) |
|
148 | body) | |
148 | except: |
|
149 | except: | |
149 | log.error(traceback.format_exc()) |
|
150 | log.error(traceback.format_exc()) | |
150 | self.sa.rollback() |
|
151 | self.sa.rollback() | |
151 | raise |
|
152 | raise | |
152 |
|
153 | |||
153 | def update(self, user_id, form_data): |
|
154 | def update(self, user_id, form_data): | |
154 | try: |
|
155 | try: | |
155 | user = self.get(user_id, cache=False) |
|
156 | user = self.get(user_id, cache=False) | |
156 | if user.username == 'default': |
|
157 | if user.username == 'default': | |
157 | raise DefaultUserException( |
|
158 | raise DefaultUserException( | |
158 | _("You can't Edit this user since it's" |
|
159 | _("You can't Edit this user since it's" | |
159 | " crucial for entire application")) |
|
160 | " crucial for entire application")) | |
160 |
|
161 | |||
161 | for k, v in form_data.items(): |
|
162 | for k, v in form_data.items(): | |
162 | if k == 'new_password' and v != '': |
|
163 | if k == 'new_password' and v != '': | |
163 | user.password = v |
|
164 | user.password = v | |
164 | user.api_key = generate_api_key(user.username) |
|
165 | user.api_key = generate_api_key(user.username) | |
165 | else: |
|
166 | else: | |
166 | setattr(user, k, v) |
|
167 | setattr(user, k, v) | |
167 |
|
168 | |||
168 | self.sa.add(user) |
|
169 | self.sa.add(user) | |
169 | self.sa.commit() |
|
170 | self.sa.commit() | |
170 | except: |
|
171 | except: | |
171 | log.error(traceback.format_exc()) |
|
172 | log.error(traceback.format_exc()) | |
172 | self.sa.rollback() |
|
173 | self.sa.rollback() | |
173 | raise |
|
174 | raise | |
174 |
|
175 | |||
175 | def update_my_account(self, user_id, form_data): |
|
176 | def update_my_account(self, user_id, form_data): | |
176 | try: |
|
177 | try: | |
177 | user = self.get(user_id, cache=False) |
|
178 | user = self.get(user_id, cache=False) | |
178 | if user.username == 'default': |
|
179 | if user.username == 'default': | |
179 | raise DefaultUserException( |
|
180 | raise DefaultUserException( | |
180 | _("You can't Edit this user since it's" |
|
181 | _("You can't Edit this user since it's" | |
181 | " crucial for entire application")) |
|
182 | " crucial for entire application")) | |
182 | for k, v in form_data.items(): |
|
183 | for k, v in form_data.items(): | |
183 | if k == 'new_password' and v != '': |
|
184 | if k == 'new_password' and v != '': | |
184 | user.password = v |
|
185 | user.password = v | |
185 | user.api_key = generate_api_key(user.username) |
|
186 | user.api_key = generate_api_key(user.username) | |
186 | else: |
|
187 | else: | |
187 | if k not in ['admin', 'active']: |
|
188 | if k not in ['admin', 'active']: | |
188 | setattr(user, k, v) |
|
189 | setattr(user, k, v) | |
189 |
|
190 | |||
190 | self.sa.add(user) |
|
191 | self.sa.add(user) | |
191 | self.sa.commit() |
|
192 | self.sa.commit() | |
192 | except: |
|
193 | except: | |
193 | log.error(traceback.format_exc()) |
|
194 | log.error(traceback.format_exc()) | |
194 | self.sa.rollback() |
|
195 | self.sa.rollback() | |
195 | raise |
|
196 | raise | |
196 |
|
197 | |||
197 | def delete(self, user_id): |
|
198 | def delete(self, user_id): | |
198 | try: |
|
199 | try: | |
199 | user = self.get(user_id, cache=False) |
|
200 | user = self.get(user_id, cache=False) | |
200 | if user.username == 'default': |
|
201 | if user.username == 'default': | |
201 | raise DefaultUserException( |
|
202 | raise DefaultUserException( | |
202 | _("You can't remove this user since it's" |
|
203 | _("You can't remove this user since it's" | |
203 | " crucial for entire application")) |
|
204 | " crucial for entire application")) | |
204 | if user.repositories: |
|
205 | if user.repositories: | |
205 | raise UserOwnsReposException(_('This user still owns %s ' |
|
206 | raise UserOwnsReposException(_('This user still owns %s ' | |
206 | 'repositories and cannot be ' |
|
207 | 'repositories and cannot be ' | |
207 | 'removed. Switch owners or ' |
|
208 | 'removed. Switch owners or ' | |
208 | 'remove those repositories') \ |
|
209 | 'remove those repositories') \ | |
209 | % user.repositories) |
|
210 | % user.repositories) | |
210 | self.sa.delete(user) |
|
211 | self.sa.delete(user) | |
211 | self.sa.commit() |
|
212 | self.sa.commit() | |
212 | except: |
|
213 | except: | |
213 | log.error(traceback.format_exc()) |
|
214 | log.error(traceback.format_exc()) | |
214 | self.sa.rollback() |
|
215 | self.sa.rollback() | |
215 | raise |
|
216 | raise | |
216 |
|
217 | |||
217 | def reset_password_link(self, data): |
|
218 | def reset_password_link(self, data): | |
218 | from rhodecode.lib.celerylib import tasks, run_task |
|
219 | from rhodecode.lib.celerylib import tasks, run_task | |
219 | run_task(tasks.send_password_link, data['email']) |
|
220 | run_task(tasks.send_password_link, data['email']) | |
220 |
|
221 | |||
221 | def reset_password(self, data): |
|
222 | def reset_password(self, data): | |
222 | from rhodecode.lib.celerylib import tasks, run_task |
|
223 | from rhodecode.lib.celerylib import tasks, run_task | |
223 | run_task(tasks.reset_user_password, data['email']) |
|
224 | run_task(tasks.reset_user_password, data['email']) | |
224 |
|
225 | |||
225 | def fill_data(self, auth_user, user_id=None, api_key=None): |
|
226 | def fill_data(self, auth_user, user_id=None, api_key=None): | |
226 | """ |
|
227 | """ | |
227 | Fetches auth_user by user_id,or api_key if present. |
|
228 | Fetches auth_user by user_id,or api_key if present. | |
228 | Fills auth_user attributes with those taken from database. |
|
229 | Fills auth_user attributes with those taken from database. | |
229 | Additionally set's is_authenitated if lookup fails |
|
230 | Additionally set's is_authenitated if lookup fails | |
230 | present in database |
|
231 | present in database | |
231 |
|
232 | |||
232 | :param auth_user: instance of user to set attributes |
|
233 | :param auth_user: instance of user to set attributes | |
233 | :param user_id: user id to fetch by |
|
234 | :param user_id: user id to fetch by | |
234 | :param api_key: api key to fetch by |
|
235 | :param api_key: api key to fetch by | |
235 | """ |
|
236 | """ | |
236 | if user_id is None and api_key is None: |
|
237 | if user_id is None and api_key is None: | |
237 | raise Exception('You need to pass user_id or api_key') |
|
238 | raise Exception('You need to pass user_id or api_key') | |
238 |
|
239 | |||
239 | try: |
|
240 | try: | |
240 | if api_key: |
|
241 | if api_key: | |
241 | dbuser = self.get_by_api_key(api_key) |
|
242 | dbuser = self.get_by_api_key(api_key) | |
242 | else: |
|
243 | else: | |
243 | dbuser = self.get(user_id) |
|
244 | dbuser = self.get(user_id) | |
244 |
|
245 | |||
245 | if dbuser is not None: |
|
246 | if dbuser is not None: | |
246 | log.debug('filling %s data', dbuser) |
|
247 | log.debug('filling %s data', dbuser) | |
247 | for k, v in dbuser.get_dict().items(): |
|
248 | for k, v in dbuser.get_dict().items(): | |
248 | setattr(auth_user, k, v) |
|
249 | setattr(auth_user, k, v) | |
249 |
|
250 | |||
250 | except: |
|
251 | except: | |
251 | log.error(traceback.format_exc()) |
|
252 | log.error(traceback.format_exc()) | |
252 | auth_user.is_authenticated = False |
|
253 | auth_user.is_authenticated = False | |
253 |
|
254 | |||
254 | return auth_user |
|
255 | return auth_user | |
255 |
|
256 | |||
256 | def fill_perms(self, user): |
|
257 | def fill_perms(self, user): | |
257 | """ |
|
258 | """ | |
258 | Fills user permission attribute with permissions taken from database |
|
259 | Fills user permission attribute with permissions taken from database | |
259 | works for permissions given for repositories, and for permissions that |
|
260 | works for permissions given for repositories, and for permissions that | |
260 | are granted to groups |
|
261 | are granted to groups | |
261 |
|
262 | |||
262 | :param user: user instance to fill his perms |
|
263 | :param user: user instance to fill his perms | |
263 | """ |
|
264 | """ | |
264 |
|
265 | |||
265 | user.permissions['repositories'] = {} |
|
266 | user.permissions['repositories'] = {} | |
266 | user.permissions['global'] = set() |
|
267 | user.permissions['global'] = set() | |
267 |
|
268 | |||
268 | #====================================================================== |
|
269 | #====================================================================== | |
269 | # fetch default permissions |
|
270 | # fetch default permissions | |
270 | #====================================================================== |
|
271 | #====================================================================== | |
271 | default_user = self.get_by_username('default', cache=True) |
|
272 | default_user = self.get_by_username('default', cache=True) | |
272 |
|
273 | |||
273 | default_perms = self.sa.query(RepoToPerm, Repository, Permission)\ |
|
274 | default_perms = self.sa.query(RepoToPerm, Repository, Permission)\ | |
274 | .join((Repository, RepoToPerm.repository_id == |
|
275 | .join((Repository, RepoToPerm.repository_id == | |
275 | Repository.repo_id))\ |
|
276 | Repository.repo_id))\ | |
276 | .join((Permission, RepoToPerm.permission_id == |
|
277 | .join((Permission, RepoToPerm.permission_id == | |
277 | Permission.permission_id))\ |
|
278 | Permission.permission_id))\ | |
278 | .filter(RepoToPerm.user == default_user).all() |
|
279 | .filter(RepoToPerm.user == default_user).all() | |
279 |
|
280 | |||
280 | if user.is_admin: |
|
281 | if user.is_admin: | |
281 | #================================================================== |
|
282 | #================================================================== | |
282 | # #admin have all default rights set to admin |
|
283 | # #admin have all default rights set to admin | |
283 | #================================================================== |
|
284 | #================================================================== | |
284 | user.permissions['global'].add('hg.admin') |
|
285 | user.permissions['global'].add('hg.admin') | |
285 |
|
286 | |||
286 | for perm in default_perms: |
|
287 | for perm in default_perms: | |
287 | p = 'repository.admin' |
|
288 | p = 'repository.admin' | |
288 | user.permissions['repositories'][perm.RepoToPerm. |
|
289 | user.permissions['repositories'][perm.RepoToPerm. | |
289 | repository.repo_name] = p |
|
290 | repository.repo_name] = p | |
290 |
|
291 | |||
291 | else: |
|
292 | else: | |
292 | #================================================================== |
|
293 | #================================================================== | |
293 | # set default permissions |
|
294 | # set default permissions | |
294 | #================================================================== |
|
295 | #================================================================== | |
295 | uid = user.user_id |
|
296 | uid = user.user_id | |
296 |
|
297 | |||
297 | #default global |
|
298 | #default global | |
298 | default_global_perms = self.sa.query(UserToPerm)\ |
|
299 | default_global_perms = self.sa.query(UserToPerm)\ | |
299 | .filter(UserToPerm.user == default_user) |
|
300 | .filter(UserToPerm.user == default_user) | |
300 |
|
301 | |||
301 | for perm in default_global_perms: |
|
302 | for perm in default_global_perms: | |
302 | user.permissions['global'].add(perm.permission.permission_name) |
|
303 | user.permissions['global'].add(perm.permission.permission_name) | |
303 |
|
304 | |||
304 | #default for repositories |
|
305 | #default for repositories | |
305 | for perm in default_perms: |
|
306 | for perm in default_perms: | |
306 | if perm.Repository.private and not (perm.Repository.user_id == |
|
307 | if perm.Repository.private and not (perm.Repository.user_id == | |
307 | uid): |
|
308 | uid): | |
308 | #diself.sable defaults for private repos, |
|
309 | #diself.sable defaults for private repos, | |
309 | p = 'repository.none' |
|
310 | p = 'repository.none' | |
310 | elif perm.Repository.user_id == uid: |
|
311 | elif perm.Repository.user_id == uid: | |
311 | #set admin if owner |
|
312 | #set admin if owner | |
312 | p = 'repository.admin' |
|
313 | p = 'repository.admin' | |
313 | else: |
|
314 | else: | |
314 | p = perm.Permission.permission_name |
|
315 | p = perm.Permission.permission_name | |
315 |
|
316 | |||
316 | user.permissions['repositories'][perm.RepoToPerm. |
|
317 | user.permissions['repositories'][perm.RepoToPerm. | |
317 | repository.repo_name] = p |
|
318 | repository.repo_name] = p | |
318 |
|
319 | |||
319 | #================================================================== |
|
320 | #================================================================== | |
320 | # overwrite default with user permissions if any |
|
321 | # overwrite default with user permissions if any | |
321 | #================================================================== |
|
322 | #================================================================== | |
322 |
|
323 | |||
323 | #user global |
|
324 | #user global | |
324 | user_perms = self.sa.query(UserToPerm)\ |
|
325 | user_perms = self.sa.query(UserToPerm)\ | |
325 | .options(joinedload(UserToPerm.permission))\ |
|
326 | .options(joinedload(UserToPerm.permission))\ | |
326 | .filter(UserToPerm.user_id == uid).all() |
|
327 | .filter(UserToPerm.user_id == uid).all() | |
327 |
|
328 | |||
328 | for perm in user_perms: |
|
329 | for perm in user_perms: | |
329 | user.permissions['global'].add(perm.permission. |
|
330 | user.permissions['global'].add(perm.permission. | |
330 | permission_name) |
|
331 | permission_name) | |
331 |
|
332 | |||
332 | #user repositories |
|
333 | #user repositories | |
333 | user_repo_perms = self.sa.query(RepoToPerm, Permission, |
|
334 | user_repo_perms = self.sa.query(RepoToPerm, Permission, | |
334 | Repository)\ |
|
335 | Repository)\ | |
335 | .join((Repository, RepoToPerm.repository_id == |
|
336 | .join((Repository, RepoToPerm.repository_id == | |
336 | Repository.repo_id))\ |
|
337 | Repository.repo_id))\ | |
337 | .join((Permission, RepoToPerm.permission_id == |
|
338 | .join((Permission, RepoToPerm.permission_id == | |
338 | Permission.permission_id))\ |
|
339 | Permission.permission_id))\ | |
339 | .filter(RepoToPerm.user_id == uid).all() |
|
340 | .filter(RepoToPerm.user_id == uid).all() | |
340 |
|
341 | |||
341 | for perm in user_repo_perms: |
|
342 | for perm in user_repo_perms: | |
342 | # set admin if owner |
|
343 | # set admin if owner | |
343 | if perm.Repository.user_id == uid: |
|
344 | if perm.Repository.user_id == uid: | |
344 | p = 'repository.admin' |
|
345 | p = 'repository.admin' | |
345 | else: |
|
346 | else: | |
346 | p = perm.Permission.permission_name |
|
347 | p = perm.Permission.permission_name | |
347 | user.permissions['repositories'][perm.RepoToPerm. |
|
348 | user.permissions['repositories'][perm.RepoToPerm. | |
348 | repository.repo_name] = p |
|
349 | repository.repo_name] = p | |
349 |
|
350 | |||
350 | #================================================================== |
|
351 | #================================================================== | |
351 | # check if user is part of groups for this repository and fill in |
|
352 | # check if user is part of groups for this repository and fill in | |
352 | # (or replace with higher) permissions |
|
353 | # (or replace with higher) permissions | |
353 | #================================================================== |
|
354 | #================================================================== | |
354 |
|
355 | |||
355 | #users group global |
|
356 | #users group global | |
356 | user_perms_from_users_groups = self.sa.query(UsersGroupToPerm)\ |
|
357 | user_perms_from_users_groups = self.sa.query(UsersGroupToPerm)\ | |
357 | .options(joinedload(UsersGroupToPerm.permission))\ |
|
358 | .options(joinedload(UsersGroupToPerm.permission))\ | |
358 | .join((UsersGroupMember, UsersGroupToPerm.users_group_id == |
|
359 | .join((UsersGroupMember, UsersGroupToPerm.users_group_id == | |
359 | UsersGroupMember.users_group_id))\ |
|
360 | UsersGroupMember.users_group_id))\ | |
360 | .filter(UsersGroupMember.user_id == uid).all() |
|
361 | .filter(UsersGroupMember.user_id == uid).all() | |
361 |
|
362 | |||
362 | for perm in user_perms_from_users_groups: |
|
363 | for perm in user_perms_from_users_groups: | |
363 | user.permissions['global'].add(perm.permission.permission_name) |
|
364 | user.permissions['global'].add(perm.permission.permission_name) | |
364 |
|
365 | |||
365 | #users group repositories |
|
366 | #users group repositories | |
366 | user_repo_perms_from_users_groups = self.sa.query( |
|
367 | user_repo_perms_from_users_groups = self.sa.query( | |
367 | UsersGroupRepoToPerm, |
|
368 | UsersGroupRepoToPerm, | |
368 | Permission, Repository,)\ |
|
369 | Permission, Repository,)\ | |
369 | .join((Repository, UsersGroupRepoToPerm.repository_id == |
|
370 | .join((Repository, UsersGroupRepoToPerm.repository_id == | |
370 | Repository.repo_id))\ |
|
371 | Repository.repo_id))\ | |
371 | .join((Permission, UsersGroupRepoToPerm.permission_id == |
|
372 | .join((Permission, UsersGroupRepoToPerm.permission_id == | |
372 | Permission.permission_id))\ |
|
373 | Permission.permission_id))\ | |
373 | .join((UsersGroupMember, UsersGroupRepoToPerm.users_group_id == |
|
374 | .join((UsersGroupMember, UsersGroupRepoToPerm.users_group_id == | |
374 | UsersGroupMember.users_group_id))\ |
|
375 | UsersGroupMember.users_group_id))\ | |
375 | .filter(UsersGroupMember.user_id == uid).all() |
|
376 | .filter(UsersGroupMember.user_id == uid).all() | |
376 |
|
377 | |||
377 | for perm in user_repo_perms_from_users_groups: |
|
378 | for perm in user_repo_perms_from_users_groups: | |
378 | p = perm.Permission.permission_name |
|
379 | p = perm.Permission.permission_name | |
379 | cur_perm = user.permissions['repositories'][perm. |
|
380 | cur_perm = user.permissions['repositories'][perm. | |
380 | UsersGroupRepoToPerm. |
|
381 | UsersGroupRepoToPerm. | |
381 | repository.repo_name] |
|
382 | repository.repo_name] | |
382 | #overwrite permission only if it's greater than permission |
|
383 | #overwrite permission only if it's greater than permission | |
383 | # given from other sources |
|
384 | # given from other sources | |
384 | if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]: |
|
385 | if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]: | |
385 | user.permissions['repositories'][perm.UsersGroupRepoToPerm. |
|
386 | user.permissions['repositories'][perm.UsersGroupRepoToPerm. | |
386 | repository.repo_name] = p |
|
387 | repository.repo_name] = p | |
387 |
|
388 | |||
388 | return user |
|
389 | return user | |
|
390 |
@@ -1,64 +1,64 b'' | |||||
1 | ## -*- coding: utf-8 -*- |
|
1 | ## -*- coding: utf-8 -*- | |
2 | <%inherit file="/base/base.html"/> |
|
2 | <%inherit file="/base/base.html"/> | |
3 |
|
3 | |||
4 | <%def name="title()"> |
|
4 | <%def name="title()"> | |
5 | ${_('Add repos group')} - ${c.rhodecode_name} |
|
5 | ${_('Add repos group')} - ${c.rhodecode_name} | |
6 | </%def> |
|
6 | </%def> | |
7 | <%def name="breadcrumbs_links()"> |
|
7 | <%def name="breadcrumbs_links()"> | |
8 | ${h.link_to(_('Admin'),h.url('admin_home'))} |
|
8 | ${h.link_to(_('Admin'),h.url('admin_home'))} | |
9 | » |
|
9 | » | |
10 | ${h.link_to(_('Repos groups'),h.url('repos_groups'))} |
|
10 | ${h.link_to(_('Repos groups'),h.url('repos_groups'))} | |
11 | » |
|
11 | » | |
12 | ${_('add new repos group')} |
|
12 | ${_('add new repos group')} | |
13 | </%def> |
|
13 | </%def> | |
14 |
|
14 | |||
15 | <%def name="page_nav()"> |
|
15 | <%def name="page_nav()"> | |
16 | ${self.menu('admin')} |
|
16 | ${self.menu('admin')} | |
17 | </%def> |
|
17 | </%def> | |
18 |
|
18 | |||
19 | <%def name="main()"> |
|
19 | <%def name="main()"> | |
20 | <div class="box"> |
|
20 | <div class="box"> | |
21 | <!-- box / title --> |
|
21 | <!-- box / title --> | |
22 | <div class="title"> |
|
22 | <div class="title"> | |
23 | ${self.breadcrumbs()} |
|
23 | ${self.breadcrumbs()} | |
24 | </div> |
|
24 | </div> | |
25 | <!-- end box / title --> |
|
25 | <!-- end box / title --> | |
26 | ${h.form(url('repos_groups'))} |
|
26 | ${h.form(url('repos_groups'))} | |
27 | <div class="form"> |
|
27 | <div class="form"> | |
28 | <!-- fields --> |
|
28 | <!-- fields --> | |
29 | <div class="fields"> |
|
29 | <div class="fields"> | |
30 | <div class="field"> |
|
30 | <div class="field"> | |
31 | <div class="label"> |
|
31 | <div class="label"> | |
32 |
<label for=" |
|
32 | <label for="group_name">${_('Group name')}:</label> | |
33 | </div> |
|
33 | </div> | |
34 | <div class="input"> |
|
34 | <div class="input"> | |
35 | ${h.text('group_name',class_='medium')} |
|
35 | ${h.text('group_name',class_='medium')} | |
36 | </div> |
|
36 | </div> | |
37 | </div> |
|
37 | </div> | |
38 |
|
38 | |||
39 | <div class="field"> |
|
39 | <div class="field"> | |
40 | <div class="label label-textarea"> |
|
40 | <div class="label label-textarea"> | |
41 | <label for="description">${_('Description')}:</label> |
|
41 | <label for="group_description">${_('Description')}:</label> | |
42 | </div> |
|
42 | </div> | |
43 | <div class="textarea text-area editor"> |
|
43 | <div class="textarea text-area editor"> | |
44 | ${h.textarea('group_description',cols=23,rows=5,class_="medium")} |
|
44 | ${h.textarea('group_description',cols=23,rows=5,class_="medium")} | |
45 | </div> |
|
45 | </div> | |
46 | </div> |
|
46 | </div> | |
47 |
|
47 | |||
48 | <div class="field"> |
|
48 | <div class="field"> | |
49 | <div class="label"> |
|
49 | <div class="label"> | |
50 |
<label for=" |
|
50 | <label for="group_parent_id">${_('Group parent')}:</label> | |
51 | </div> |
|
51 | </div> | |
52 | <div class="input"> |
|
52 | <div class="input"> | |
53 | ${h.select('group_parent_id','',c.repo_groups,class_="medium")} |
|
53 | ${h.select('group_parent_id','',c.repo_groups,class_="medium")} | |
54 | </div> |
|
54 | </div> | |
55 | </div> |
|
55 | </div> | |
56 |
|
56 | |||
57 | <div class="buttons"> |
|
57 | <div class="buttons"> | |
58 | ${h.submit('save',_('save'),class_="ui-button")} |
|
58 | ${h.submit('save',_('save'),class_="ui-button")} | |
59 | </div> |
|
59 | </div> | |
60 | </div> |
|
60 | </div> | |
61 | </div> |
|
61 | </div> | |
62 | ${h.end_form()} |
|
62 | ${h.end_form()} | |
63 | </div> |
|
63 | </div> | |
64 | </%def> |
|
64 | </%def> |
@@ -1,64 +1,64 b'' | |||||
1 | ## -*- coding: utf-8 -*- |
|
1 | ## -*- coding: utf-8 -*- | |
2 | <%inherit file="/base/base.html"/> |
|
2 | <%inherit file="/base/base.html"/> | |
3 |
|
3 | |||
4 | <%def name="title()"> |
|
4 | <%def name="title()"> | |
5 | ${_('Edit repos group')} ${c.repos_group.name} - ${c.rhodecode_name} |
|
5 | ${_('Edit repos group')} ${c.repos_group.name} - ${c.rhodecode_name} | |
6 | </%def> |
|
6 | </%def> | |
7 | <%def name="breadcrumbs_links()"> |
|
7 | <%def name="breadcrumbs_links()"> | |
8 | ${h.link_to(_('Admin'),h.url('admin_home'))} |
|
8 | ${h.link_to(_('Admin'),h.url('admin_home'))} | |
9 | » |
|
9 | » | |
10 | ${h.link_to(_('Repos groups'),h.url('repos_groups'))} |
|
10 | ${h.link_to(_('Repos groups'),h.url('repos_groups'))} | |
11 | » |
|
11 | » | |
12 | ${_('edit repos group')} "${c.repos_group.name}" |
|
12 | ${_('edit repos group')} "${c.repos_group.name}" | |
13 | </%def> |
|
13 | </%def> | |
14 |
|
14 | |||
15 | <%def name="page_nav()"> |
|
15 | <%def name="page_nav()"> | |
16 | ${self.menu('admin')} |
|
16 | ${self.menu('admin')} | |
17 | </%def> |
|
17 | </%def> | |
18 |
|
18 | |||
19 | <%def name="main()"> |
|
19 | <%def name="main()"> | |
20 | <div class="box"> |
|
20 | <div class="box"> | |
21 | <!-- box / title --> |
|
21 | <!-- box / title --> | |
22 | <div class="title"> |
|
22 | <div class="title"> | |
23 | ${self.breadcrumbs()} |
|
23 | ${self.breadcrumbs()} | |
24 | </div> |
|
24 | </div> | |
25 | <!-- end box / title --> |
|
25 | <!-- end box / title --> | |
26 | ${h.form(url('repos_group',id=c.repos_group.group_id),method='put')} |
|
26 | ${h.form(url('repos_group',id=c.repos_group.group_id),method='put')} | |
27 | <div class="form"> |
|
27 | <div class="form"> | |
28 | <!-- fields --> |
|
28 | <!-- fields --> | |
29 | <div class="fields"> |
|
29 | <div class="fields"> | |
30 | <div class="field"> |
|
30 | <div class="field"> | |
31 | <div class="label"> |
|
31 | <div class="label"> | |
32 |
<label for=" |
|
32 | <label for="group_name">${_('Group name')}:</label> | |
33 | </div> |
|
33 | </div> | |
34 | <div class="input"> |
|
34 | <div class="input"> | |
35 | ${h.text('group_name',class_='medium')} |
|
35 | ${h.text('group_name',class_='medium')} | |
36 | </div> |
|
36 | </div> | |
37 | </div> |
|
37 | </div> | |
38 |
|
38 | |||
39 | <div class="field"> |
|
39 | <div class="field"> | |
40 | <div class="label label-textarea"> |
|
40 | <div class="label label-textarea"> | |
41 | <label for="description">${_('Description')}:</label> |
|
41 | <label for="group_description">${_('Description')}:</label> | |
42 | </div> |
|
42 | </div> | |
43 | <div class="textarea text-area editor"> |
|
43 | <div class="textarea text-area editor"> | |
44 | ${h.textarea('group_description',cols=23,rows=5,class_="medium")} |
|
44 | ${h.textarea('group_description',cols=23,rows=5,class_="medium")} | |
45 | </div> |
|
45 | </div> | |
46 | </div> |
|
46 | </div> | |
47 |
|
47 | |||
48 | <div class="field"> |
|
48 | <div class="field"> | |
49 | <div class="label"> |
|
49 | <div class="label"> | |
50 |
<label for=" |
|
50 | <label for="group_parent_id">${_('Group parent')}:</label> | |
51 | </div> |
|
51 | </div> | |
52 | <div class="input"> |
|
52 | <div class="input"> | |
53 | ${h.select('group_parent_id','',c.repo_groups,class_="medium")} |
|
53 | ${h.select('group_parent_id','',c.repo_groups,class_="medium")} | |
54 | </div> |
|
54 | </div> | |
55 | </div> |
|
55 | </div> | |
56 |
|
56 | |||
57 | <div class="buttons"> |
|
57 | <div class="buttons"> | |
58 | ${h.submit('save',_('save'),class_="ui-button")} |
|
58 | ${h.submit('save',_('save'),class_="ui-button")} | |
59 | </div> |
|
59 | </div> | |
60 | </div> |
|
60 | </div> | |
61 | </div> |
|
61 | </div> | |
62 | ${h.end_form()} |
|
62 | ${h.end_form()} | |
63 | </div> |
|
63 | </div> | |
64 | </%def> |
|
64 | </%def> |
@@ -1,192 +1,192 b'' | |||||
1 | ## -*- coding: utf-8 -*- |
|
1 | ## -*- coding: utf-8 -*- | |
2 | <%inherit file="/base/base.html"/> |
|
2 | <%inherit file="/base/base.html"/> | |
3 |
|
3 | |||
4 | <%def name="title()"> |
|
4 | <%def name="title()"> | |
5 | ${_('Settings administration')} - ${c.rhodecode_name} |
|
5 | ${_('Settings administration')} - ${c.rhodecode_name} | |
6 | </%def> |
|
6 | </%def> | |
7 |
|
7 | |||
8 | <%def name="breadcrumbs_links()"> |
|
8 | <%def name="breadcrumbs_links()"> | |
9 | ${h.link_to(_('Admin'),h.url('admin_home'))} » ${_('Settings')} |
|
9 | ${h.link_to(_('Admin'),h.url('admin_home'))} » ${_('Settings')} | |
10 | </%def> |
|
10 | </%def> | |
11 |
|
11 | |||
12 | <%def name="page_nav()"> |
|
12 | <%def name="page_nav()"> | |
13 | ${self.menu('admin')} |
|
13 | ${self.menu('admin')} | |
14 | </%def> |
|
14 | </%def> | |
15 |
|
15 | |||
16 | <%def name="main()"> |
|
16 | <%def name="main()"> | |
17 | <div class="box"> |
|
17 | <div class="box"> | |
18 | <!-- box / title --> |
|
18 | <!-- box / title --> | |
19 | <div class="title"> |
|
19 | <div class="title"> | |
20 | ${self.breadcrumbs()} |
|
20 | ${self.breadcrumbs()} | |
21 | </div> |
|
21 | </div> | |
22 | <!-- end box / title --> |
|
22 | <!-- end box / title --> | |
23 |
|
23 | |||
24 | <h3>${_('Remap and rescan repositories')}</h3> |
|
24 | <h3>${_('Remap and rescan repositories')}</h3> | |
25 | ${h.form(url('admin_setting', setting_id='mapping'),method='put')} |
|
25 | ${h.form(url('admin_setting', setting_id='mapping'),method='put')} | |
26 | <div class="form"> |
|
26 | <div class="form"> | |
27 | <!-- fields --> |
|
27 | <!-- fields --> | |
28 |
|
28 | |||
29 | <div class="fields"> |
|
29 | <div class="fields"> | |
30 | <div class="field"> |
|
30 | <div class="field"> | |
31 | <div class="label label-checkbox"> |
|
31 | <div class="label label-checkbox"> | |
32 | <label for="destroy">${_('rescan option')}:</label> |
|
32 | <label for="destroy">${_('rescan option')}:</label> | |
33 | </div> |
|
33 | </div> | |
34 | <div class="checkboxes"> |
|
34 | <div class="checkboxes"> | |
35 | <div class="checkbox"> |
|
35 | <div class="checkbox"> | |
36 | ${h.checkbox('destroy',True)} |
|
36 | ${h.checkbox('destroy',True)} | |
37 |
<label for=" |
|
37 | <label for="destroy"> | |
38 | <span class="tooltip" title="${h.tooltip(_('In case a repository was deleted from filesystem and there are leftovers in the database check this option to scan obsolete data in database and remove it.'))}"> |
|
38 | <span class="tooltip" title="${h.tooltip(_('In case a repository was deleted from filesystem and there are leftovers in the database check this option to scan obsolete data in database and remove it.'))}"> | |
39 | ${_('destroy old data')}</span> </label> |
|
39 | ${_('destroy old data')}</span> </label> | |
40 | </div> |
|
40 | </div> | |
41 | </div> |
|
41 | </div> | |
42 | </div> |
|
42 | </div> | |
43 |
|
43 | |||
44 | <div class="buttons"> |
|
44 | <div class="buttons"> | |
45 | ${h.submit('rescan',_('Rescan repositories'),class_="ui-button")} |
|
45 | ${h.submit('rescan',_('Rescan repositories'),class_="ui-button")} | |
46 | </div> |
|
46 | </div> | |
47 | </div> |
|
47 | </div> | |
48 | </div> |
|
48 | </div> | |
49 | ${h.end_form()} |
|
49 | ${h.end_form()} | |
50 |
|
50 | |||
51 | <h3>${_('Whoosh indexing')}</h3> |
|
51 | <h3>${_('Whoosh indexing')}</h3> | |
52 | ${h.form(url('admin_setting', setting_id='whoosh'),method='put')} |
|
52 | ${h.form(url('admin_setting', setting_id='whoosh'),method='put')} | |
53 | <div class="form"> |
|
53 | <div class="form"> | |
54 | <!-- fields --> |
|
54 | <!-- fields --> | |
55 |
|
55 | |||
56 | <div class="fields"> |
|
56 | <div class="fields"> | |
57 | <div class="field"> |
|
57 | <div class="field"> | |
58 | <div class="label label-checkbox"> |
|
58 | <div class="label label-checkbox"> | |
59 |
<label |
|
59 | <label>${_('index build option')}:</label> | |
60 | </div> |
|
60 | </div> | |
61 | <div class="checkboxes"> |
|
61 | <div class="checkboxes"> | |
62 | <div class="checkbox"> |
|
62 | <div class="checkbox"> | |
63 | ${h.checkbox('full_index',True)} |
|
63 | ${h.checkbox('full_index',True)} | |
64 |
<label for=" |
|
64 | <label for="full_index">${_('build from scratch')}</label> | |
65 | </div> |
|
65 | </div> | |
66 | </div> |
|
66 | </div> | |
67 | </div> |
|
67 | </div> | |
68 |
|
68 | |||
69 | <div class="buttons"> |
|
69 | <div class="buttons"> | |
70 | ${h.submit('reindex',_('Reindex'),class_="ui-button")} |
|
70 | ${h.submit('reindex',_('Reindex'),class_="ui-button")} | |
71 | </div> |
|
71 | </div> | |
72 | </div> |
|
72 | </div> | |
73 | </div> |
|
73 | </div> | |
74 | ${h.end_form()} |
|
74 | ${h.end_form()} | |
75 |
|
75 | |||
76 | <h3>${_('Global application settings')}</h3> |
|
76 | <h3>${_('Global application settings')}</h3> | |
77 | ${h.form(url('admin_setting', setting_id='global'),method='put')} |
|
77 | ${h.form(url('admin_setting', setting_id='global'),method='put')} | |
78 | <div class="form"> |
|
78 | <div class="form"> | |
79 | <!-- fields --> |
|
79 | <!-- fields --> | |
80 |
|
80 | |||
81 | <div class="fields"> |
|
81 | <div class="fields"> | |
82 |
|
82 | |||
83 | <div class="field"> |
|
83 | <div class="field"> | |
84 | <div class="label"> |
|
84 | <div class="label"> | |
85 | <label for="rhodecode_title">${_('Application name')}:</label> |
|
85 | <label for="rhodecode_title">${_('Application name')}:</label> | |
86 | </div> |
|
86 | </div> | |
87 | <div class="input"> |
|
87 | <div class="input"> | |
88 | ${h.text('rhodecode_title',size=30)} |
|
88 | ${h.text('rhodecode_title',size=30)} | |
89 | </div> |
|
89 | </div> | |
90 | </div> |
|
90 | </div> | |
91 |
|
91 | |||
92 | <div class="field"> |
|
92 | <div class="field"> | |
93 | <div class="label"> |
|
93 | <div class="label"> | |
94 | <label for="rhodecode_realm">${_('Realm text')}:</label> |
|
94 | <label for="rhodecode_realm">${_('Realm text')}:</label> | |
95 | </div> |
|
95 | </div> | |
96 | <div class="input"> |
|
96 | <div class="input"> | |
97 | ${h.text('rhodecode_realm',size=30)} |
|
97 | ${h.text('rhodecode_realm',size=30)} | |
98 | </div> |
|
98 | </div> | |
99 | </div> |
|
99 | </div> | |
100 |
|
100 | |||
101 | <div class="field"> |
|
101 | <div class="field"> | |
102 | <div class="label"> |
|
102 | <div class="label"> | |
103 | <label for="ga_code">${_('GA code')}:</label> |
|
103 | <label for="rhodecode_ga_code">${_('GA code')}:</label> | |
104 | </div> |
|
104 | </div> | |
105 | <div class="input"> |
|
105 | <div class="input"> | |
106 | ${h.text('rhodecode_ga_code',size=30)} |
|
106 | ${h.text('rhodecode_ga_code',size=30)} | |
107 | </div> |
|
107 | </div> | |
108 | </div> |
|
108 | </div> | |
109 |
|
109 | |||
110 | <div class="buttons"> |
|
110 | <div class="buttons"> | |
111 | ${h.submit('save',_('Save settings'),class_="ui-button")} |
|
111 | ${h.submit('save',_('Save settings'),class_="ui-button")} | |
112 | ${h.reset('reset',_('Reset'),class_="ui-button")} |
|
112 | ${h.reset('reset',_('Reset'),class_="ui-button")} | |
113 | </div> |
|
113 | </div> | |
114 | </div> |
|
114 | </div> | |
115 | </div> |
|
115 | </div> | |
116 | ${h.end_form()} |
|
116 | ${h.end_form()} | |
117 |
|
117 | |||
118 | <h3>${_('Mercurial settings')}</h3> |
|
118 | <h3>${_('Mercurial settings')}</h3> | |
119 | ${h.form(url('admin_setting', setting_id='mercurial'),method='put')} |
|
119 | ${h.form(url('admin_setting', setting_id='mercurial'),method='put')} | |
120 | <div class="form"> |
|
120 | <div class="form"> | |
121 | <!-- fields --> |
|
121 | <!-- fields --> | |
122 |
|
122 | |||
123 | <div class="fields"> |
|
123 | <div class="fields"> | |
124 |
|
124 | |||
125 | <div class="field"> |
|
125 | <div class="field"> | |
126 | <div class="label label-checkbox"> |
|
126 | <div class="label label-checkbox"> | |
127 |
<label |
|
127 | <label>${_('Web')}:</label> | |
128 | </div> |
|
128 | </div> | |
129 | <div class="checkboxes"> |
|
129 | <div class="checkboxes"> | |
130 | <div class="checkbox"> |
|
130 | <div class="checkbox"> | |
131 | ${h.checkbox('web_push_ssl','true')} |
|
131 | ${h.checkbox('web_push_ssl','true')} | |
132 | <label for="web_push_ssl">${_('require ssl for pushing')}</label> |
|
132 | <label for="web_push_ssl">${_('require ssl for pushing')}</label> | |
133 | </div> |
|
133 | </div> | |
134 | </div> |
|
134 | </div> | |
135 | </div> |
|
135 | </div> | |
136 |
|
136 | |||
137 | <div class="field"> |
|
137 | <div class="field"> | |
138 | <div class="label label-checkbox"> |
|
138 | <div class="label label-checkbox"> | |
139 |
<label |
|
139 | <label>${_('Hooks')}:</label> | |
140 | </div> |
|
140 | </div> | |
141 | <div class="input"> |
|
141 | <div class="input"> | |
142 | ${h.link_to(_('advanced setup'),url('admin_edit_setting',setting_id='hooks'))} |
|
142 | ${h.link_to(_('advanced setup'),url('admin_edit_setting',setting_id='hooks'))} | |
143 | </div> |
|
143 | </div> | |
144 | <div class="checkboxes"> |
|
144 | <div class="checkboxes"> | |
145 | <div class="checkbox"> |
|
145 | <div class="checkbox"> | |
146 | ${h.checkbox('hooks_changegroup_update','True')} |
|
146 | ${h.checkbox('hooks_changegroup_update','True')} | |
147 | <label for="hooks_changegroup_update">${_('Update repository after push (hg update)')}</label> |
|
147 | <label for="hooks_changegroup_update">${_('Update repository after push (hg update)')}</label> | |
148 | </div> |
|
148 | </div> | |
149 | <div class="checkbox"> |
|
149 | <div class="checkbox"> | |
150 | ${h.checkbox('hooks_changegroup_repo_size','True')} |
|
150 | ${h.checkbox('hooks_changegroup_repo_size','True')} | |
151 | <label for="hooks_changegroup_repo_size">${_('Show repository size after push')}</label> |
|
151 | <label for="hooks_changegroup_repo_size">${_('Show repository size after push')}</label> | |
152 | </div> |
|
152 | </div> | |
153 | <div class="checkbox"> |
|
153 | <div class="checkbox"> | |
154 | ${h.checkbox('hooks_pretxnchangegroup_push_logger','True')} |
|
154 | ${h.checkbox('hooks_pretxnchangegroup_push_logger','True')} | |
155 | <label for="hooks_pretxnchangegroup_push_logger">${_('Log user push commands')}</label> |
|
155 | <label for="hooks_pretxnchangegroup_push_logger">${_('Log user push commands')}</label> | |
156 | </div> |
|
156 | </div> | |
157 | <div class="checkbox"> |
|
157 | <div class="checkbox"> | |
158 | ${h.checkbox('hooks_preoutgoing_pull_logger','True')} |
|
158 | ${h.checkbox('hooks_preoutgoing_pull_logger','True')} | |
159 | <label for="hooks_preoutgoing_pull_logger">${_('Log user pull commands')}</label> |
|
159 | <label for="hooks_preoutgoing_pull_logger">${_('Log user pull commands')}</label> | |
160 | </div> |
|
160 | </div> | |
161 | </div> |
|
161 | </div> | |
162 | </div> |
|
162 | </div> | |
163 |
|
163 | |||
164 | <div class="field"> |
|
164 | <div class="field"> | |
165 | <div class="label"> |
|
165 | <div class="label"> | |
166 | <label for="paths_root_path">${_('Repositories location')}:</label> |
|
166 | <label for="paths_root_path">${_('Repositories location')}:</label> | |
167 | </div> |
|
167 | </div> | |
168 | <div class="input"> |
|
168 | <div class="input"> | |
169 | ${h.text('paths_root_path',size=30,readonly="readonly")} |
|
169 | ${h.text('paths_root_path',size=30,readonly="readonly")} | |
170 | <span id="path_unlock" class="tooltip" |
|
170 | <span id="path_unlock" class="tooltip" | |
171 | title="${h.tooltip(_('This a crucial application setting. If you are really sure you need to change this, you must restart application in order to make this setting take effect. Click this label to unlock.'))}"> |
|
171 | title="${h.tooltip(_('This a crucial application setting. If you are really sure you need to change this, you must restart application in order to make this setting take effect. Click this label to unlock.'))}"> | |
172 | ${_('unlock')}</span> |
|
172 | ${_('unlock')}</span> | |
173 | </div> |
|
173 | </div> | |
174 | </div> |
|
174 | </div> | |
175 |
|
175 | |||
176 | <div class="buttons"> |
|
176 | <div class="buttons"> | |
177 | ${h.submit('save',_('Save settings'),class_="ui-button")} |
|
177 | ${h.submit('save',_('Save settings'),class_="ui-button")} | |
178 | ${h.reset('reset',_('Reset'),class_="ui-button")} |
|
178 | ${h.reset('reset',_('Reset'),class_="ui-button")} | |
179 | </div> |
|
179 | </div> | |
180 | </div> |
|
180 | </div> | |
181 | </div> |
|
181 | </div> | |
182 | ${h.end_form()} |
|
182 | ${h.end_form()} | |
183 |
|
183 | |||
184 | <script type="text/javascript"> |
|
184 | <script type="text/javascript"> | |
185 | YAHOO.util.Event.onDOMReady(function(){ |
|
185 | YAHOO.util.Event.onDOMReady(function(){ | |
186 | YAHOO.util.Event.addListener('path_unlock','click',function(){ |
|
186 | YAHOO.util.Event.addListener('path_unlock','click',function(){ | |
187 | YAHOO.util.Dom.get('paths_root_path').removeAttribute('readonly'); |
|
187 | YAHOO.util.Dom.get('paths_root_path').removeAttribute('readonly'); | |
188 | }); |
|
188 | }); | |
189 | }); |
|
189 | }); | |
190 | </script> |
|
190 | </script> | |
191 | </div> |
|
191 | </div> | |
192 | </%def> |
|
192 | </%def> |
@@ -1,91 +1,100 b'' | |||||
1 | ## -*- coding: utf-8 -*- |
|
1 | ## -*- coding: utf-8 -*- | |
2 | <%inherit file="/base/base.html"/> |
|
2 | <%inherit file="/base/base.html"/> | |
3 |
|
3 | |||
4 | <%def name="title()"> |
|
4 | <%def name="title()"> | |
5 | ${_('Add user')} - ${c.rhodecode_name} |
|
5 | ${_('Add user')} - ${c.rhodecode_name} | |
6 | </%def> |
|
6 | </%def> | |
7 | <%def name="breadcrumbs_links()"> |
|
7 | <%def name="breadcrumbs_links()"> | |
8 | ${h.link_to(_('Admin'),h.url('admin_home'))} |
|
8 | ${h.link_to(_('Admin'),h.url('admin_home'))} | |
9 | » |
|
9 | » | |
10 | ${h.link_to(_('Users'),h.url('users'))} |
|
10 | ${h.link_to(_('Users'),h.url('users'))} | |
11 | » |
|
11 | » | |
12 | ${_('add new user')} |
|
12 | ${_('add new user')} | |
13 | </%def> |
|
13 | </%def> | |
14 |
|
14 | |||
15 | <%def name="page_nav()"> |
|
15 | <%def name="page_nav()"> | |
16 | ${self.menu('admin')} |
|
16 | ${self.menu('admin')} | |
17 | </%def> |
|
17 | </%def> | |
18 |
|
18 | |||
19 | <%def name="main()"> |
|
19 | <%def name="main()"> | |
20 | <div class="box"> |
|
20 | <div class="box"> | |
21 | <!-- box / title --> |
|
21 | <!-- box / title --> | |
22 | <div class="title"> |
|
22 | <div class="title"> | |
23 | ${self.breadcrumbs()} |
|
23 | ${self.breadcrumbs()} | |
24 | </div> |
|
24 | </div> | |
25 | <!-- end box / title --> |
|
25 | <!-- end box / title --> | |
26 | ${h.form(url('users'))} |
|
26 | ${h.form(url('users'))} | |
27 | <div class="form"> |
|
27 | <div class="form"> | |
28 | <!-- fields --> |
|
28 | <!-- fields --> | |
29 | <div class="fields"> |
|
29 | <div class="fields"> | |
30 | <div class="field"> |
|
30 | <div class="field"> | |
31 | <div class="label"> |
|
31 | <div class="label"> | |
32 | <label for="username">${_('Username')}:</label> |
|
32 | <label for="username">${_('Username')}:</label> | |
33 | </div> |
|
33 | </div> | |
34 | <div class="input"> |
|
34 | <div class="input"> | |
35 | ${h.text('username',class_='small')} |
|
35 | ${h.text('username',class_='small')} | |
36 | </div> |
|
36 | </div> | |
37 | </div> |
|
37 | </div> | |
38 |
|
38 | |||
39 | <div class="field"> |
|
39 | <div class="field"> | |
40 | <div class="label"> |
|
40 | <div class="label"> | |
41 | <label for="password">${_('Password')}:</label> |
|
41 | <label for="password">${_('Password')}:</label> | |
42 | </div> |
|
42 | </div> | |
43 | <div class="input"> |
|
43 | <div class="input"> | |
44 | ${h.password('password',class_='small')} |
|
44 | ${h.password('password',class_='small')} | |
45 | </div> |
|
45 | </div> | |
46 | </div> |
|
46 | </div> | |
47 |
|
47 | |||
|
48 | <div class="field"> | |||
|
49 | <div class="label"> | |||
|
50 | <label for="password_confirmation">${_('Password confirmation')}:</label> | |||
|
51 | </div> | |||
|
52 | <div class="input"> | |||
|
53 | ${h.password('password_confirmation',class_="small",autocomplete="off")} | |||
|
54 | </div> | |||
|
55 | </div> | |||
|
56 | ||||
48 | <div class="field"> |
|
57 | <div class="field"> | |
49 | <div class="label"> |
|
58 | <div class="label"> | |
50 | <label for="name">${_('First Name')}:</label> |
|
59 | <label for="name">${_('First Name')}:</label> | |
51 | </div> |
|
60 | </div> | |
52 | <div class="input"> |
|
61 | <div class="input"> | |
53 | ${h.text('name',class_='small')} |
|
62 | ${h.text('name',class_='small')} | |
54 | </div> |
|
63 | </div> | |
55 | </div> |
|
64 | </div> | |
56 |
|
65 | |||
57 | <div class="field"> |
|
66 | <div class="field"> | |
58 | <div class="label"> |
|
67 | <div class="label"> | |
59 | <label for="lastname">${_('Last Name')}:</label> |
|
68 | <label for="lastname">${_('Last Name')}:</label> | |
60 | </div> |
|
69 | </div> | |
61 | <div class="input"> |
|
70 | <div class="input"> | |
62 | ${h.text('lastname',class_='small')} |
|
71 | ${h.text('lastname',class_='small')} | |
63 | </div> |
|
72 | </div> | |
64 | </div> |
|
73 | </div> | |
65 |
|
74 | |||
66 | <div class="field"> |
|
75 | <div class="field"> | |
67 | <div class="label"> |
|
76 | <div class="label"> | |
68 | <label for="email">${_('Email')}:</label> |
|
77 | <label for="email">${_('Email')}:</label> | |
69 | </div> |
|
78 | </div> | |
70 | <div class="input"> |
|
79 | <div class="input"> | |
71 | ${h.text('email',class_='small')} |
|
80 | ${h.text('email',class_='small')} | |
72 | </div> |
|
81 | </div> | |
73 | </div> |
|
82 | </div> | |
74 |
|
83 | |||
75 | <div class="field"> |
|
84 | <div class="field"> | |
76 | <div class="label label-checkbox"> |
|
85 | <div class="label label-checkbox"> | |
77 | <label for="active">${_('Active')}:</label> |
|
86 | <label for="active">${_('Active')}:</label> | |
78 | </div> |
|
87 | </div> | |
79 | <div class="checkboxes"> |
|
88 | <div class="checkboxes"> | |
80 | ${h.checkbox('active',value=True)} |
|
89 | ${h.checkbox('active',value=True)} | |
81 | </div> |
|
90 | </div> | |
82 | </div> |
|
91 | </div> | |
83 |
|
92 | |||
84 | <div class="buttons"> |
|
93 | <div class="buttons"> | |
85 | ${h.submit('save',_('save'),class_="ui-button")} |
|
94 | ${h.submit('save',_('save'),class_="ui-button")} | |
86 | </div> |
|
95 | </div> | |
87 | </div> |
|
96 | </div> | |
88 | </div> |
|
97 | </div> | |
89 | ${h.end_form()} |
|
98 | ${h.end_form()} | |
90 | </div> |
|
99 | </div> | |
91 | </%def> |
|
100 | </%def> |
@@ -1,149 +1,158 b'' | |||||
1 | ## -*- coding: utf-8 -*- |
|
1 | ## -*- coding: utf-8 -*- | |
2 | <%inherit file="/base/base.html"/> |
|
2 | <%inherit file="/base/base.html"/> | |
3 |
|
3 | |||
4 | <%def name="title()"> |
|
4 | <%def name="title()"> | |
5 | ${_('Edit user')} ${c.user.username} - ${c.rhodecode_name} |
|
5 | ${_('Edit user')} ${c.user.username} - ${c.rhodecode_name} | |
6 | </%def> |
|
6 | </%def> | |
7 |
|
7 | |||
8 | <%def name="breadcrumbs_links()"> |
|
8 | <%def name="breadcrumbs_links()"> | |
9 | ${h.link_to(_('Admin'),h.url('admin_home'))} |
|
9 | ${h.link_to(_('Admin'),h.url('admin_home'))} | |
10 | » |
|
10 | » | |
11 | ${h.link_to(_('Users'),h.url('users'))} |
|
11 | ${h.link_to(_('Users'),h.url('users'))} | |
12 | » |
|
12 | » | |
13 | ${_('edit')} "${c.user.username}" |
|
13 | ${_('edit')} "${c.user.username}" | |
14 | </%def> |
|
14 | </%def> | |
15 |
|
15 | |||
16 | <%def name="page_nav()"> |
|
16 | <%def name="page_nav()"> | |
17 | ${self.menu('admin')} |
|
17 | ${self.menu('admin')} | |
18 | </%def> |
|
18 | </%def> | |
19 |
|
19 | |||
20 | <%def name="main()"> |
|
20 | <%def name="main()"> | |
21 | <div class="box box-left"> |
|
21 | <div class="box box-left"> | |
22 | <!-- box / title --> |
|
22 | <!-- box / title --> | |
23 | <div class="title"> |
|
23 | <div class="title"> | |
24 | ${self.breadcrumbs()} |
|
24 | ${self.breadcrumbs()} | |
25 | </div> |
|
25 | </div> | |
26 | <!-- end box / title --> |
|
26 | <!-- end box / title --> | |
27 | ${h.form(url('update_user', id=c.user.user_id),method='put')} |
|
27 | ${h.form(url('update_user', id=c.user.user_id),method='put')} | |
28 | <div class="form"> |
|
28 | <div class="form"> | |
29 | <div class="field"> |
|
29 | <div class="field"> | |
30 | <div class="gravatar_box"> |
|
30 | <div class="gravatar_box"> | |
31 | <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(c.user.email)}"/></div> |
|
31 | <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(c.user.email)}"/></div> | |
32 | <p> |
|
32 | <p> | |
33 | <strong>${_('Change your avatar at')} <a href="http://gravatar.com">gravatar.com</a></strong><br/> |
|
33 | <strong>${_('Change your avatar at')} <a href="http://gravatar.com">gravatar.com</a></strong><br/> | |
34 | ${_('Using')} ${c.user.email} |
|
34 | ${_('Using')} ${c.user.email} | |
35 | </p> |
|
35 | </p> | |
36 | </div> |
|
36 | </div> | |
37 | </div> |
|
37 | </div> | |
38 | <div class="field"> |
|
38 | <div class="field"> | |
39 | <div class="label"> |
|
39 | <div class="label"> | |
40 | <label>${_('API key')}</label> ${c.user.api_key} |
|
40 | <label>${_('API key')}</label> ${c.user.api_key} | |
41 | </div> |
|
41 | </div> | |
42 | </div> |
|
42 | </div> | |
43 |
|
43 | |||
44 | <div class="fields"> |
|
44 | <div class="fields"> | |
45 | <div class="field"> |
|
45 | <div class="field"> | |
46 | <div class="label"> |
|
46 | <div class="label"> | |
47 | <label for="username">${_('Username')}:</label> |
|
47 | <label for="username">${_('Username')}:</label> | |
48 | </div> |
|
48 | </div> | |
49 | <div class="input"> |
|
49 | <div class="input"> | |
50 | ${h.text('username',class_='medium')} |
|
50 | ${h.text('username',class_='medium')} | |
51 | </div> |
|
51 | </div> | |
52 | </div> |
|
52 | </div> | |
53 |
|
53 | |||
54 | <div class="field"> |
|
54 | <div class="field"> | |
55 | <div class="label"> |
|
55 | <div class="label"> | |
56 | <label for="ldap_dn">${_('LDAP DN')}:</label> |
|
56 | <label for="ldap_dn">${_('LDAP DN')}:</label> | |
57 | </div> |
|
57 | </div> | |
58 | <div class="input"> |
|
58 | <div class="input"> | |
59 | ${h.text('ldap_dn',class_='medium')} |
|
59 | ${h.text('ldap_dn',class_='medium')} | |
60 | </div> |
|
60 | </div> | |
61 | </div> |
|
61 | </div> | |
62 |
|
62 | |||
63 | <div class="field"> |
|
63 | <div class="field"> | |
64 | <div class="label"> |
|
64 | <div class="label"> | |
65 | <label for="new_password">${_('New password')}:</label> |
|
65 | <label for="new_password">${_('New password')}:</label> | |
66 | </div> |
|
66 | </div> | |
67 | <div class="input"> |
|
67 | <div class="input"> | |
68 | ${h.password('new_password',class_='medium',autocomplete="off")} |
|
68 | ${h.password('new_password',class_='medium',autocomplete="off")} | |
69 | </div> |
|
69 | </div> | |
70 | </div> |
|
70 | </div> | |
71 |
|
71 | |||
|
72 | <div class="field"> | |||
|
73 | <div class="label"> | |||
|
74 | <label for="password_confirmation">${_('New password confirmation')}:</label> | |||
|
75 | </div> | |||
|
76 | <div class="input"> | |||
|
77 | ${h.password('password_confirmation',class_="medium",autocomplete="off")} | |||
|
78 | </div> | |||
|
79 | </div> | |||
|
80 | ||||
72 | <div class="field"> |
|
81 | <div class="field"> | |
73 | <div class="label"> |
|
82 | <div class="label"> | |
74 | <label for="name">${_('First Name')}:</label> |
|
83 | <label for="name">${_('First Name')}:</label> | |
75 | </div> |
|
84 | </div> | |
76 | <div class="input"> |
|
85 | <div class="input"> | |
77 | ${h.text('name',class_='medium')} |
|
86 | ${h.text('name',class_='medium')} | |
78 | </div> |
|
87 | </div> | |
79 | </div> |
|
88 | </div> | |
80 |
|
89 | |||
81 | <div class="field"> |
|
90 | <div class="field"> | |
82 | <div class="label"> |
|
91 | <div class="label"> | |
83 | <label for="lastname">${_('Last Name')}:</label> |
|
92 | <label for="lastname">${_('Last Name')}:</label> | |
84 | </div> |
|
93 | </div> | |
85 | <div class="input"> |
|
94 | <div class="input"> | |
86 | ${h.text('lastname',class_='medium')} |
|
95 | ${h.text('lastname',class_='medium')} | |
87 | </div> |
|
96 | </div> | |
88 | </div> |
|
97 | </div> | |
89 |
|
98 | |||
90 | <div class="field"> |
|
99 | <div class="field"> | |
91 | <div class="label"> |
|
100 | <div class="label"> | |
92 | <label for="email">${_('Email')}:</label> |
|
101 | <label for="email">${_('Email')}:</label> | |
93 | </div> |
|
102 | </div> | |
94 | <div class="input"> |
|
103 | <div class="input"> | |
95 | ${h.text('email',class_='medium')} |
|
104 | ${h.text('email',class_='medium')} | |
96 | </div> |
|
105 | </div> | |
97 | </div> |
|
106 | </div> | |
98 |
|
107 | |||
99 | <div class="field"> |
|
108 | <div class="field"> | |
100 | <div class="label label-checkbox"> |
|
109 | <div class="label label-checkbox"> | |
101 | <label for="active">${_('Active')}:</label> |
|
110 | <label for="active">${_('Active')}:</label> | |
102 | </div> |
|
111 | </div> | |
103 | <div class="checkboxes"> |
|
112 | <div class="checkboxes"> | |
104 | ${h.checkbox('active',value=True)} |
|
113 | ${h.checkbox('active',value=True)} | |
105 | </div> |
|
114 | </div> | |
106 | </div> |
|
115 | </div> | |
107 |
|
116 | |||
108 | <div class="field"> |
|
117 | <div class="field"> | |
109 | <div class="label label-checkbox"> |
|
118 | <div class="label label-checkbox"> | |
110 | <label for="admin">${_('Admin')}:</label> |
|
119 | <label for="admin">${_('Admin')}:</label> | |
111 | </div> |
|
120 | </div> | |
112 | <div class="checkboxes"> |
|
121 | <div class="checkboxes"> | |
113 | ${h.checkbox('admin',value=True)} |
|
122 | ${h.checkbox('admin',value=True)} | |
114 | </div> |
|
123 | </div> | |
115 | </div> |
|
124 | </div> | |
116 | <div class="buttons"> |
|
125 | <div class="buttons"> | |
117 | ${h.submit('save',_('Save'),class_="ui-button")} |
|
126 | ${h.submit('save',_('Save'),class_="ui-button")} | |
118 | ${h.reset('reset',_('Reset'),class_="ui-button")} |
|
127 | ${h.reset('reset',_('Reset'),class_="ui-button")} | |
119 | </div> |
|
128 | </div> | |
120 | </div> |
|
129 | </div> | |
121 | </div> |
|
130 | </div> | |
122 | ${h.end_form()} |
|
131 | ${h.end_form()} | |
123 | </div> |
|
132 | </div> | |
124 | <div class="box box-right"> |
|
133 | <div class="box box-right"> | |
125 | <!-- box / title --> |
|
134 | <!-- box / title --> | |
126 | <div class="title"> |
|
135 | <div class="title"> | |
127 | <h5>${_('Permissions')}</h5> |
|
136 | <h5>${_('Permissions')}</h5> | |
128 | </div> |
|
137 | </div> | |
129 | ${h.form(url('user_perm', id=c.user.user_id),method='put')} |
|
138 | ${h.form(url('user_perm', id=c.user.user_id),method='put')} | |
130 | <div class="form"> |
|
139 | <div class="form"> | |
131 | <!-- fields --> |
|
140 | <!-- fields --> | |
132 | <div class="fields"> |
|
141 | <div class="fields"> | |
133 | <div class="field"> |
|
142 | <div class="field"> | |
134 | <div class="label label-checkbox"> |
|
143 | <div class="label label-checkbox"> | |
135 | <label for="">${_('Create repositories')}:</label> |
|
144 | <label for="create_repo_perm">${_('Create repositories')}:</label> | |
136 | </div> |
|
145 | </div> | |
137 | <div class="checkboxes"> |
|
146 | <div class="checkboxes"> | |
138 | ${h.checkbox('create_repo_perm',value=True)} |
|
147 | ${h.checkbox('create_repo_perm',value=True)} | |
139 | </div> |
|
148 | </div> | |
140 | </div> |
|
149 | </div> | |
141 | <div class="buttons"> |
|
150 | <div class="buttons"> | |
142 | ${h.submit('save',_('Save'),class_="ui-button")} |
|
151 | ${h.submit('save',_('Save'),class_="ui-button")} | |
143 | ${h.reset('reset',_('Reset'),class_="ui-button")} |
|
152 | ${h.reset('reset',_('Reset'),class_="ui-button")} | |
144 | </div> |
|
153 | </div> | |
145 | </div> |
|
154 | </div> | |
146 | </div> |
|
155 | </div> | |
147 | ${h.end_form()} |
|
156 | ${h.end_form()} | |
148 | </div> |
|
157 | </div> | |
149 | </%def> |
|
158 | </%def> |
@@ -1,211 +1,222 b'' | |||||
1 | ## -*- coding: utf-8 -*- |
|
1 | ## -*- coding: utf-8 -*- | |
2 | <%inherit file="/base/base.html"/> |
|
2 | <%inherit file="/base/base.html"/> | |
3 |
|
3 | |||
4 | <%def name="title()"> |
|
4 | <%def name="title()"> | |
5 | ${_('My account')} ${c.rhodecode_user.username} - ${c.rhodecode_name} |
|
5 | ${_('My account')} ${c.rhodecode_user.username} - ${c.rhodecode_name} | |
6 | </%def> |
|
6 | </%def> | |
7 |
|
7 | |||
8 | <%def name="breadcrumbs_links()"> |
|
8 | <%def name="breadcrumbs_links()"> | |
9 | ${_('My Account')} |
|
9 | ${_('My Account')} | |
10 | </%def> |
|
10 | </%def> | |
11 |
|
11 | |||
12 | <%def name="page_nav()"> |
|
12 | <%def name="page_nav()"> | |
13 | ${self.menu('admin')} |
|
13 | ${self.menu('admin')} | |
14 | </%def> |
|
14 | </%def> | |
15 |
|
15 | |||
16 | <%def name="main()"> |
|
16 | <%def name="main()"> | |
17 |
|
17 | |||
18 | <div class="box box-left"> |
|
18 | <div class="box box-left"> | |
19 | <!-- box / title --> |
|
19 | <!-- box / title --> | |
20 | <div class="title"> |
|
20 | <div class="title"> | |
21 | ${self.breadcrumbs()} |
|
21 | ${self.breadcrumbs()} | |
22 | </div> |
|
22 | </div> | |
23 | <!-- end box / title --> |
|
23 | <!-- end box / title --> | |
24 | <div> |
|
24 | <div> | |
25 | ${h.form(url('admin_settings_my_account_update'),method='put')} |
|
25 | ${h.form(url('admin_settings_my_account_update'),method='put')} | |
26 | <div class="form"> |
|
26 | <div class="form"> | |
27 |
|
27 | |||
28 | <div class="field"> |
|
28 | <div class="field"> | |
29 | <div class="gravatar_box"> |
|
29 | <div class="gravatar_box"> | |
30 | <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(c.user.email)}"/></div> |
|
30 | <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(c.user.email)}"/></div> | |
31 | <p> |
|
31 | <p> | |
32 | <strong>${_('Change your avatar at')} <a href="http://gravatar.com">gravatar.com</a></strong><br/> |
|
32 | <strong>${_('Change your avatar at')} <a href="http://gravatar.com">gravatar.com</a></strong><br/> | |
33 | ${_('Using')} ${c.user.email} |
|
33 | ${_('Using')} ${c.user.email} | |
34 | </p> |
|
34 | </p> | |
35 | </div> |
|
35 | </div> | |
36 | </div> |
|
36 | </div> | |
37 | <div class="field"> |
|
37 | <div class="field"> | |
38 | <div class="label"> |
|
38 | <div class="label"> | |
39 | <label>${_('API key')}</label> ${c.user.api_key} |
|
39 | <label>${_('API key')}</label> ${c.user.api_key} | |
40 | </div> |
|
40 | </div> | |
41 | </div> |
|
41 | </div> | |
42 | <div class="fields"> |
|
42 | <div class="fields"> | |
43 | <div class="field"> |
|
43 | <div class="field"> | |
44 | <div class="label"> |
|
44 | <div class="label"> | |
45 | <label for="username">${_('Username')}:</label> |
|
45 | <label for="username">${_('Username')}:</label> | |
46 | </div> |
|
46 | </div> | |
47 | <div class="input"> |
|
47 | <div class="input"> | |
48 | ${h.text('username',class_="medium")} |
|
48 | ${h.text('username',class_="medium")} | |
49 | </div> |
|
49 | </div> | |
50 | </div> |
|
50 | </div> | |
51 |
|
51 | |||
52 | <div class="field"> |
|
52 | <div class="field"> | |
53 | <div class="label"> |
|
53 | <div class="label"> | |
54 | <label for="new_password">${_('New password')}:</label> |
|
54 | <label for="new_password">${_('New password')}:</label> | |
55 | </div> |
|
55 | </div> | |
56 | <div class="input"> |
|
56 | <div class="input"> | |
57 | ${h.password('new_password',class_="medium",autocomplete="off")} |
|
57 | ${h.password('new_password',class_="medium",autocomplete="off")} | |
58 | </div> |
|
58 | </div> | |
59 | </div> |
|
59 | </div> | |
60 |
|
|
60 | ||
|
61 | <div class="field"> | |||
|
62 | <div class="label"> | |||
|
63 | <label for="password_confirmation">${_('New password confirmation')}:</label> | |||
|
64 | </div> | |||
|
65 | <div class="input"> | |||
|
66 | ${h.password('password_confirmation',class_="medium",autocomplete="off")} | |||
|
67 | </div> | |||
|
68 | </div> | |||
|
69 | ||||
61 | <div class="field"> |
|
70 | <div class="field"> | |
62 | <div class="label"> |
|
71 | <div class="label"> | |
63 | <label for="name">${_('First Name')}:</label> |
|
72 | <label for="name">${_('First Name')}:</label> | |
64 | </div> |
|
73 | </div> | |
65 | <div class="input"> |
|
74 | <div class="input"> | |
66 | ${h.text('name',class_="medium")} |
|
75 | ${h.text('name',class_="medium")} | |
67 | </div> |
|
76 | </div> | |
68 | </div> |
|
77 | </div> | |
69 |
|
78 | |||
70 | <div class="field"> |
|
79 | <div class="field"> | |
71 | <div class="label"> |
|
80 | <div class="label"> | |
72 | <label for="lastname">${_('Last Name')}:</label> |
|
81 | <label for="lastname">${_('Last Name')}:</label> | |
73 | </div> |
|
82 | </div> | |
74 | <div class="input"> |
|
83 | <div class="input"> | |
75 | ${h.text('lastname',class_="medium")} |
|
84 | ${h.text('lastname',class_="medium")} | |
76 | </div> |
|
85 | </div> | |
77 | </div> |
|
86 | </div> | |
78 |
|
87 | |||
79 | <div class="field"> |
|
88 | <div class="field"> | |
80 | <div class="label"> |
|
89 | <div class="label"> | |
81 | <label for="email">${_('Email')}:</label> |
|
90 | <label for="email">${_('Email')}:</label> | |
82 | </div> |
|
91 | </div> | |
83 | <div class="input"> |
|
92 | <div class="input"> | |
84 | ${h.text('email',class_="medium")} |
|
93 | ${h.text('email',class_="medium")} | |
85 | </div> |
|
94 | </div> | |
86 | </div> |
|
95 | </div> | |
87 |
|
96 | |||
88 | <div class="buttons"> |
|
97 | <div class="buttons"> | |
89 | ${h.submit('save',_('Save'),class_="ui-button")} |
|
98 | ${h.submit('save',_('Save'),class_="ui-button")} | |
90 | ${h.reset('reset',_('Reset'),class_="ui-button")} |
|
99 | ${h.reset('reset',_('Reset'),class_="ui-button")} | |
91 | </div> |
|
100 | </div> | |
92 | </div> |
|
101 | </div> | |
93 | </div> |
|
102 | </div> | |
94 | ${h.end_form()} |
|
103 | ${h.end_form()} | |
95 | </div> |
|
104 | </div> | |
96 | </div> |
|
105 | </div> | |
97 |
|
106 | |||
98 | <div class="box box-right"> |
|
107 | <div class="box box-right"> | |
99 | <!-- box / title --> |
|
108 | <!-- box / title --> | |
100 | <div class="title"> |
|
109 | <div class="title"> | |
101 | <h5>${_('My repositories')} |
|
110 | <h5>${_('My repositories')} | |
102 | <input class="top-right-rounded-corner top-left-rounded-corner bottom-left-rounded-corner bottom-right-rounded-corner" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}"/> |
|
111 | <input class="top-right-rounded-corner top-left-rounded-corner bottom-left-rounded-corner bottom-right-rounded-corner" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}"/> | |
103 | </h5> |
|
112 | </h5> | |
104 | %if h.HasPermissionAny('hg.admin','hg.create.repository')(): |
|
113 | %if h.HasPermissionAny('hg.admin','hg.create.repository')(): | |
105 | <ul class="links"> |
|
114 | <ul class="links"> | |
106 | <li> |
|
115 | <li> | |
107 | <span>${h.link_to(_('ADD REPOSITORY'),h.url('admin_settings_create_repository'))}</span> |
|
116 | <span>${h.link_to(_('ADD REPOSITORY'),h.url('admin_settings_create_repository'))}</span> | |
108 | </li> |
|
117 | </li> | |
109 | </ul> |
|
118 | </ul> | |
110 | %endif |
|
119 | %endif | |
111 | </div> |
|
120 | </div> | |
112 | <!-- end box / title --> |
|
121 | <!-- end box / title --> | |
113 | <div class="table"> |
|
122 | <div class="table"> | |
114 | <table> |
|
123 | <table> | |
115 | <thead> |
|
124 | <thead> | |
116 | <tr> |
|
125 | <tr> | |
117 | <th class="left">${_('Name')}</th> |
|
126 | <th class="left">${_('Name')}</th> | |
118 | <th class="left">${_('revision')}</th> |
|
127 | <th class="left">${_('revision')}</th> | |
119 | <th colspan="2" class="left">${_('action')}</th> |
|
128 | <th colspan="2" class="left">${_('action')}</th> | |
120 | </thead> |
|
129 | </thead> | |
121 | <tbody> |
|
130 | <tbody> | |
122 | %if c.user_repos: |
|
131 | %if c.user_repos: | |
123 | %for repo in c.user_repos: |
|
132 | %for repo in c.user_repos: | |
124 | <tr> |
|
133 | <tr> | |
125 | <td> |
|
134 | <td> | |
126 | %if repo['dbrepo']['repo_type'] =='hg': |
|
135 | %if repo['dbrepo']['repo_type'] =='hg': | |
127 | <img class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url("/images/icons/hgicon.png")}"/> |
|
136 | <img class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url("/images/icons/hgicon.png")}"/> | |
128 | %elif repo['dbrepo']['repo_type'] =='git': |
|
137 | %elif repo['dbrepo']['repo_type'] =='git': | |
129 | <img class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url("/images/icons/giticon.png")}"/> |
|
138 | <img class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url("/images/icons/giticon.png")}"/> | |
130 | %else: |
|
139 | %else: | |
131 |
|
140 | |||
132 | %endif |
|
141 | %endif | |
133 | %if repo['dbrepo']['private']: |
|
142 | %if repo['dbrepo']['private']: | |
134 | <img class="icon" alt="${_('private')}" src="${h.url("/images/icons/lock.png")}"/> |
|
143 | <img class="icon" alt="${_('private')}" src="${h.url("/images/icons/lock.png")}"/> | |
135 | %else: |
|
144 | %else: | |
136 | <img class="icon" alt="${_('public')}" src="${h.url("/images/icons/lock_open.png")}"/> |
|
145 | <img class="icon" alt="${_('public')}" src="${h.url("/images/icons/lock_open.png")}"/> | |
137 | %endif |
|
146 | %endif | |
138 |
|
147 | |||
139 | ${h.link_to(repo['name'], h.url('summary_home',repo_name=repo['name']),class_="repo_name")} |
|
148 | ${h.link_to(repo['name'], h.url('summary_home',repo_name=repo['name']),class_="repo_name")} | |
140 | %if repo['dbrepo_fork']: |
|
149 | %if repo['dbrepo_fork']: | |
141 | <a href="${h.url('summary_home',repo_name=repo['dbrepo_fork']['repo_name'])}"> |
|
150 | <a href="${h.url('summary_home',repo_name=repo['dbrepo_fork']['repo_name'])}"> | |
142 | <img class="icon" alt="${_('public')}" |
|
151 | <img class="icon" alt="${_('public')}" | |
143 | title="${_('Fork of')} ${repo['dbrepo_fork']['repo_name']}" |
|
152 | title="${_('Fork of')} ${repo['dbrepo_fork']['repo_name']}" | |
144 | src="${h.url('/images/icons/arrow_divide.png')}"/></a> |
|
153 | src="${h.url('/images/icons/arrow_divide.png')}"/></a> | |
145 | %endif |
|
154 | %endif | |
146 | </td> |
|
155 | </td> | |
147 | <td><span class="tooltip" title="${repo['last_change']}">${("r%s:%s") % (repo['rev'],h.short_id(repo['tip']))}</span></td> |
|
156 | <td><span class="tooltip" title="${repo['last_change']}">${("r%s:%s") % (repo['rev'],h.short_id(repo['tip']))}</span></td> | |
148 | <td><a href="${h.url('repo_settings_home',repo_name=repo['name'])}" title="${_('edit')}"><img class="icon" alt="${_('private')}" src="${h.url('/images/icons/application_form_edit.png')}"/></a></td> |
|
157 | <td><a href="${h.url('repo_settings_home',repo_name=repo['name'])}" title="${_('edit')}"><img class="icon" alt="${_('private')}" src="${h.url('/images/icons/application_form_edit.png')}"/></a></td> | |
149 | <td> |
|
158 | <td> | |
150 | ${h.form(url('repo_settings_delete', repo_name=repo['name']),method='delete')} |
|
159 | ${h.form(url('repo_settings_delete', repo_name=repo['name']),method='delete')} | |
151 | ${h.submit('remove_%s' % repo['name'],'',class_="delete_icon action_button",onclick="return confirm('Confirm to delete this repository');")} |
|
160 | ${h.submit('remove_%s' % repo['name'],'',class_="delete_icon action_button",onclick="return confirm('Confirm to delete this repository');")} | |
152 | ${h.end_form()} |
|
161 | ${h.end_form()} | |
153 | </td> |
|
162 | </td> | |
154 | </tr> |
|
163 | </tr> | |
155 | %endfor |
|
164 | %endfor | |
156 | %else: |
|
165 | %else: | |
|
166 | <div style="padding:5px 0px 10px 0px;"> | |||
157 | ${_('No repositories yet')} |
|
167 | ${_('No repositories yet')} | |
158 | %if h.HasPermissionAny('hg.admin','hg.create.repository')(): |
|
168 | %if h.HasPermissionAny('hg.admin','hg.create.repository')(): | |
159 | ${h.link_to(_('create one now'),h.url('admin_settings_create_repository'))} |
|
169 | ${h.link_to(_('create one now'),h.url('admin_settings_create_repository'),class_="ui-button-small")} | |
160 | %endif |
|
170 | %endif | |
|
171 | </div> | |||
161 | %endif |
|
172 | %endif | |
162 | </tbody> |
|
173 | </tbody> | |
163 | </table> |
|
174 | </table> | |
164 | </div> |
|
175 | </div> | |
165 |
|
176 | |||
166 | </div> |
|
177 | </div> | |
167 | <script type="text/javascript"> |
|
178 | <script type="text/javascript"> | |
168 | var D = YAHOO.util.Dom; |
|
179 | var D = YAHOO.util.Dom; | |
169 | var E = YAHOO.util.Event; |
|
180 | var E = YAHOO.util.Event; | |
170 | var S = YAHOO.util.Selector; |
|
181 | var S = YAHOO.util.Selector; | |
171 |
|
182 | |||
172 | var q_filter = D.get('q_filter'); |
|
183 | var q_filter = D.get('q_filter'); | |
173 | var F = YAHOO.namespace('q_filter'); |
|
184 | var F = YAHOO.namespace('q_filter'); | |
174 |
|
185 | |||
175 | E.on(q_filter,'click',function(){ |
|
186 | E.on(q_filter,'click',function(){ | |
176 | q_filter.value = ''; |
|
187 | q_filter.value = ''; | |
177 | }); |
|
188 | }); | |
178 |
|
189 | |||
179 | F.filterTimeout = null; |
|
190 | F.filterTimeout = null; | |
180 |
|
191 | |||
181 | F.updateFilter = function() { |
|
192 | F.updateFilter = function() { | |
182 | // Reset timeout |
|
193 | // Reset timeout | |
183 | F.filterTimeout = null; |
|
194 | F.filterTimeout = null; | |
184 |
|
195 | |||
185 | var obsolete = []; |
|
196 | var obsolete = []; | |
186 | var nodes = S.query('div.table tr td a.repo_name'); |
|
197 | var nodes = S.query('div.table tr td a.repo_name'); | |
187 | var req = q_filter.value.toLowerCase(); |
|
198 | var req = q_filter.value.toLowerCase(); | |
188 | for (n in nodes){ |
|
199 | for (n in nodes){ | |
189 | D.setStyle(nodes[n].parentNode.parentNode,'display','') |
|
200 | D.setStyle(nodes[n].parentNode.parentNode,'display','') | |
190 | } |
|
201 | } | |
191 | if (req){ |
|
202 | if (req){ | |
192 | for (n in nodes){ |
|
203 | for (n in nodes){ | |
193 | if (nodes[n].innerHTML.toLowerCase().indexOf(req) == -1) { |
|
204 | if (nodes[n].innerHTML.toLowerCase().indexOf(req) == -1) { | |
194 | obsolete.push(nodes[n]); |
|
205 | obsolete.push(nodes[n]); | |
195 | } |
|
206 | } | |
196 | } |
|
207 | } | |
197 | if(obsolete){ |
|
208 | if(obsolete){ | |
198 | for (n in obsolete){ |
|
209 | for (n in obsolete){ | |
199 | D.setStyle(obsolete[n].parentNode.parentNode,'display','none'); |
|
210 | D.setStyle(obsolete[n].parentNode.parentNode,'display','none'); | |
200 | } |
|
211 | } | |
201 | } |
|
212 | } | |
202 | } |
|
213 | } | |
203 | } |
|
214 | } | |
204 |
|
215 | |||
205 | E.on(q_filter,'keyup',function(e){ |
|
216 | E.on(q_filter,'keyup',function(e){ | |
206 | clearTimeout(F.filterTimeout); |
|
217 | clearTimeout(F.filterTimeout); | |
207 | F.filterTimeout = setTimeout(F.updateFilter,600); |
|
218 | F.filterTimeout = setTimeout(F.updateFilter,600); | |
208 | }); |
|
219 | }); | |
209 |
|
220 | |||
210 | </script> |
|
221 | </script> | |
211 | </%def> |
|
222 | </%def> |
@@ -1,270 +1,270 b'' | |||||
1 | ## -*- coding: utf-8 -*- |
|
1 | ## -*- coding: utf-8 -*- | |
2 | <%inherit file="/base/base.html"/> |
|
2 | <%inherit file="/base/base.html"/> | |
3 |
|
3 | |||
4 | <%def name="title()"> |
|
4 | <%def name="title()"> | |
5 | ${_('Edit users group')} ${c.users_group.users_group_name} - ${c.rhodecode_name} |
|
5 | ${_('Edit users group')} ${c.users_group.users_group_name} - ${c.rhodecode_name} | |
6 | </%def> |
|
6 | </%def> | |
7 |
|
7 | |||
8 | <%def name="breadcrumbs_links()"> |
|
8 | <%def name="breadcrumbs_links()"> | |
9 | ${h.link_to(_('Admin'),h.url('admin_home'))} |
|
9 | ${h.link_to(_('Admin'),h.url('admin_home'))} | |
10 | » |
|
10 | » | |
11 | ${h.link_to(_('UsersGroups'),h.url('users_groups'))} |
|
11 | ${h.link_to(_('UsersGroups'),h.url('users_groups'))} | |
12 | » |
|
12 | » | |
13 | ${_('edit')} "${c.users_group.users_group_name}" |
|
13 | ${_('edit')} "${c.users_group.users_group_name}" | |
14 | </%def> |
|
14 | </%def> | |
15 |
|
15 | |||
16 | <%def name="page_nav()"> |
|
16 | <%def name="page_nav()"> | |
17 | ${self.menu('admin')} |
|
17 | ${self.menu('admin')} | |
18 | </%def> |
|
18 | </%def> | |
19 |
|
19 | |||
20 | <%def name="main()"> |
|
20 | <%def name="main()"> | |
21 | <div class="box box-left"> |
|
21 | <div class="box box-left"> | |
22 | <!-- box / title --> |
|
22 | <!-- box / title --> | |
23 | <div class="title"> |
|
23 | <div class="title"> | |
24 | ${self.breadcrumbs()} |
|
24 | ${self.breadcrumbs()} | |
25 | </div> |
|
25 | </div> | |
26 | <!-- end box / title --> |
|
26 | <!-- end box / title --> | |
27 | ${h.form(url('users_group', id=c.users_group.users_group_id),method='put', id='edit_users_group')} |
|
27 | ${h.form(url('users_group', id=c.users_group.users_group_id),method='put', id='edit_users_group')} | |
28 | <div class="form"> |
|
28 | <div class="form"> | |
29 | <!-- fields --> |
|
29 | <!-- fields --> | |
30 | <div class="fields"> |
|
30 | <div class="fields"> | |
31 | <div class="field"> |
|
31 | <div class="field"> | |
32 | <div class="label"> |
|
32 | <div class="label"> | |
33 | <label for="users_group_name">${_('Group name')}:</label> |
|
33 | <label for="users_group_name">${_('Group name')}:</label> | |
34 | </div> |
|
34 | </div> | |
35 | <div class="input"> |
|
35 | <div class="input"> | |
36 | ${h.text('users_group_name',class_='small')} |
|
36 | ${h.text('users_group_name',class_='small')} | |
37 | </div> |
|
37 | </div> | |
38 | </div> |
|
38 | </div> | |
39 |
|
39 | |||
40 | <div class="field"> |
|
40 | <div class="field"> | |
41 | <div class="label label-checkbox"> |
|
41 | <div class="label label-checkbox"> | |
42 | <label for="users_group_active">${_('Active')}:</label> |
|
42 | <label for="users_group_active">${_('Active')}:</label> | |
43 | </div> |
|
43 | </div> | |
44 | <div class="checkboxes"> |
|
44 | <div class="checkboxes"> | |
45 | ${h.checkbox('users_group_active',value=True)} |
|
45 | ${h.checkbox('users_group_active',value=True)} | |
46 | </div> |
|
46 | </div> | |
47 | </div> |
|
47 | </div> | |
48 | <div class="field"> |
|
48 | <div class="field"> | |
49 | <div class="label"> |
|
49 | <div class="label"> | |
50 | <label for="users_group_active">${_('Members')}:</label> |
|
50 | <label for="users_group_active">${_('Members')}:</label> | |
51 | </div> |
|
51 | </div> | |
52 | <div class="select"> |
|
52 | <div class="select"> | |
53 | <table> |
|
53 | <table> | |
54 | <tr> |
|
54 | <tr> | |
55 | <td> |
|
55 | <td> | |
56 | <div> |
|
56 | <div> | |
57 | <div style="float:left"> |
|
57 | <div style="float:left"> | |
58 | <div class="text" style="padding: 0px 0px 6px;">${_('Choosen group members')}</div> |
|
58 | <div class="text" style="padding: 0px 0px 6px;">${_('Choosen group members')}</div> | |
59 | ${h.select('users_group_members',[x[0] for x in c.group_members],c.group_members,multiple=True,size=8,style="min-width:210px")} |
|
59 | ${h.select('users_group_members',[x[0] for x in c.group_members],c.group_members,multiple=True,size=8,style="min-width:210px")} | |
60 | <div id="remove_all_elements" style="cursor:pointer;text-align:center"> |
|
60 | <div id="remove_all_elements" style="cursor:pointer;text-align:center"> | |
61 | ${_('Remove all elements')} |
|
61 | ${_('Remove all elements')} | |
62 | <img alt="remove" style="vertical-align:text-bottom" src="${h.url("/images/icons/arrow_right.png")}"/> |
|
62 | <img alt="remove" style="vertical-align:text-bottom" src="${h.url("/images/icons/arrow_right.png")}"/> | |
63 | </div> |
|
63 | </div> | |
64 | </div> |
|
64 | </div> | |
65 | <div style="float:left;width:20px;padding-top:50px"> |
|
65 | <div style="float:left;width:20px;padding-top:50px"> | |
66 | <img alt="add" id="add_element" |
|
66 | <img alt="add" id="add_element" | |
67 | style="padding:2px;cursor:pointer" |
|
67 | style="padding:2px;cursor:pointer" | |
68 | src="${h.url("/images/icons/arrow_left.png")}"/> |
|
68 | src="${h.url("/images/icons/arrow_left.png")}"/> | |
69 | <br /> |
|
69 | <br /> | |
70 | <img alt="remove" id="remove_element" |
|
70 | <img alt="remove" id="remove_element" | |
71 | style="padding:2px;cursor:pointer" |
|
71 | style="padding:2px;cursor:pointer" | |
72 | src="${h.url("/images/icons/arrow_right.png")}"/> |
|
72 | src="${h.url("/images/icons/arrow_right.png")}"/> | |
73 | </div> |
|
73 | </div> | |
74 | <div style="float:left"> |
|
74 | <div style="float:left"> | |
75 | <div class="text" style="padding: 0px 0px 6px;">${_('Available members')}</div> |
|
75 | <div class="text" style="padding: 0px 0px 6px;">${_('Available members')}</div> | |
76 | ${h.select('available_members',[],c.available_members,multiple=True,size=8,style="min-width:210px")} |
|
76 | ${h.select('available_members',[],c.available_members,multiple=True,size=8,style="min-width:210px")} | |
77 | <div id="add_all_elements" style="cursor:pointer;text-align:center"> |
|
77 | <div id="add_all_elements" style="cursor:pointer;text-align:center"> | |
78 | <img alt="add" style="vertical-align:text-bottom" src="${h.url("/images/icons/arrow_left.png")}"/> |
|
78 | <img alt="add" style="vertical-align:text-bottom" src="${h.url("/images/icons/arrow_left.png")}"/> | |
79 | ${_('Add all elements')} |
|
79 | ${_('Add all elements')} | |
80 | </div> |
|
80 | </div> | |
81 | </div> |
|
81 | </div> | |
82 | </div> |
|
82 | </div> | |
83 | </td> |
|
83 | </td> | |
84 | </tr> |
|
84 | </tr> | |
85 | </table> |
|
85 | </table> | |
86 | </div> |
|
86 | </div> | |
87 |
|
87 | |||
88 | </div> |
|
88 | </div> | |
89 | <div class="buttons"> |
|
89 | <div class="buttons"> | |
90 | ${h.submit('save',_('save'),class_="ui-button")} |
|
90 | ${h.submit('save',_('save'),class_="ui-button")} | |
91 | </div> |
|
91 | </div> | |
92 | </div> |
|
92 | </div> | |
93 | </div> |
|
93 | </div> | |
94 | ${h.end_form()} |
|
94 | ${h.end_form()} | |
95 | </div> |
|
95 | </div> | |
96 |
|
96 | |||
97 | <script type="text/javascript"> |
|
97 | <script type="text/javascript"> | |
98 | YAHOO.util.Event.onDOMReady(function(){ |
|
98 | YAHOO.util.Event.onDOMReady(function(){ | |
99 | var D = YAHOO.util.Dom; |
|
99 | var D = YAHOO.util.Dom; | |
100 | var E = YAHOO.util.Event; |
|
100 | var E = YAHOO.util.Event; | |
101 |
|
101 | |||
102 | //definition of containers ID's |
|
102 | //definition of containers ID's | |
103 | var available_container = 'available_members'; |
|
103 | var available_container = 'available_members'; | |
104 | var selected_container = 'users_group_members'; |
|
104 | var selected_container = 'users_group_members'; | |
105 |
|
105 | |||
106 | //form containing containers id |
|
106 | //form containing containers id | |
107 | var form_id = 'edit_users_group'; |
|
107 | var form_id = 'edit_users_group'; | |
108 |
|
108 | |||
109 | //temp container for selected storage. |
|
109 | //temp container for selected storage. | |
110 | var cache = new Array(); |
|
110 | var cache = new Array(); | |
111 | var av_cache = new Array(); |
|
111 | var av_cache = new Array(); | |
112 | var c = D.get(selected_container); |
|
112 | var c = D.get(selected_container); | |
113 | var ac = D.get(available_container); |
|
113 | var ac = D.get(available_container); | |
114 |
|
114 | |||
115 | //get only selected options for further fullfilment |
|
115 | //get only selected options for further fullfilment | |
116 | for(var i = 0;node =c.options[i];i++){ |
|
116 | for(var i = 0;node =c.options[i];i++){ | |
117 | if(node.selected){ |
|
117 | if(node.selected){ | |
118 | //push selected to my temp storage left overs :) |
|
118 | //push selected to my temp storage left overs :) | |
119 | cache.push(node); |
|
119 | cache.push(node); | |
120 | } |
|
120 | } | |
121 | } |
|
121 | } | |
122 |
|
122 | |||
123 | //clear 'selected' select |
|
123 | //clear 'selected' select | |
124 | //c.options.length = 0; |
|
124 | //c.options.length = 0; | |
125 |
|
125 | |||
126 | //fill it with remembered options |
|
126 | //fill it with remembered options | |
127 | //for(var i = 0;node = cache[i];i++){ |
|
127 | //for(var i = 0;node = cache[i];i++){ | |
128 | // c.options[i]=new Option(node.text, node.value, false, false); |
|
128 | // c.options[i]=new Option(node.text, node.value, false, false); | |
129 | //} |
|
129 | //} | |
130 |
|
130 | |||
131 |
|
131 | |||
132 | //get all available options to cache |
|
132 | //get all available options to cache | |
133 | for(var i = 0;node =ac.options[i];i++){ |
|
133 | for(var i = 0;node =ac.options[i];i++){ | |
134 | //push selected to my temp storage left overs :) |
|
134 | //push selected to my temp storage left overs :) | |
135 | av_cache.push(node); |
|
135 | av_cache.push(node); | |
136 | } |
|
136 | } | |
137 |
|
137 | |||
138 | //fill available only with those not in choosen |
|
138 | //fill available only with those not in choosen | |
139 | ac.options.length=0; |
|
139 | ac.options.length=0; | |
140 | tmp_cache = new Array(); |
|
140 | tmp_cache = new Array(); | |
141 |
|
141 | |||
142 | for(var i = 0;node = av_cache[i];i++){ |
|
142 | for(var i = 0;node = av_cache[i];i++){ | |
143 | var add = true; |
|
143 | var add = true; | |
144 | for(var i2 = 0;node_2 = cache[i2];i2++){ |
|
144 | for(var i2 = 0;node_2 = cache[i2];i2++){ | |
145 | if(node.value == node_2.value){ |
|
145 | if(node.value == node_2.value){ | |
146 | add=false; |
|
146 | add=false; | |
147 | break; |
|
147 | break; | |
148 | } |
|
148 | } | |
149 | } |
|
149 | } | |
150 | if(add){ |
|
150 | if(add){ | |
151 | tmp_cache.push(new Option(node.text, node.value, false, false)); |
|
151 | tmp_cache.push(new Option(node.text, node.value, false, false)); | |
152 | } |
|
152 | } | |
153 | } |
|
153 | } | |
154 |
|
154 | |||
155 | for(var i = 0;node = tmp_cache[i];i++){ |
|
155 | for(var i = 0;node = tmp_cache[i];i++){ | |
156 | ac.options[i] = node; |
|
156 | ac.options[i] = node; | |
157 | } |
|
157 | } | |
158 |
|
158 | |||
159 | function prompts_action_callback(e){ |
|
159 | function prompts_action_callback(e){ | |
160 |
|
160 | |||
161 | var choosen = D.get(selected_container); |
|
161 | var choosen = D.get(selected_container); | |
162 | var available = D.get(available_container); |
|
162 | var available = D.get(available_container); | |
163 |
|
163 | |||
164 | //get checked and unchecked options from field |
|
164 | //get checked and unchecked options from field | |
165 | function get_checked(from_field){ |
|
165 | function get_checked(from_field){ | |
166 | //temp container for storage. |
|
166 | //temp container for storage. | |
167 | var sel_cache = new Array(); |
|
167 | var sel_cache = new Array(); | |
168 | var oth_cache = new Array(); |
|
168 | var oth_cache = new Array(); | |
169 |
|
169 | |||
170 | for(var i = 0;node = from_field.options[i];i++){ |
|
170 | for(var i = 0;node = from_field.options[i];i++){ | |
171 | if(node.selected){ |
|
171 | if(node.selected){ | |
172 | //push selected fields :) |
|
172 | //push selected fields :) | |
173 | sel_cache.push(node); |
|
173 | sel_cache.push(node); | |
174 | } |
|
174 | } | |
175 | else{ |
|
175 | else{ | |
176 | oth_cache.push(node) |
|
176 | oth_cache.push(node) | |
177 | } |
|
177 | } | |
178 | } |
|
178 | } | |
179 |
|
179 | |||
180 | return [sel_cache,oth_cache] |
|
180 | return [sel_cache,oth_cache] | |
181 | } |
|
181 | } | |
182 |
|
182 | |||
183 | //fill the field with given options |
|
183 | //fill the field with given options | |
184 | function fill_with(field,options){ |
|
184 | function fill_with(field,options){ | |
185 | //clear firtst |
|
185 | //clear firtst | |
186 | field.options.length=0; |
|
186 | field.options.length=0; | |
187 | for(var i = 0;node = options[i];i++){ |
|
187 | for(var i = 0;node = options[i];i++){ | |
188 | field.options[i]=new Option(node.text, node.value, |
|
188 | field.options[i]=new Option(node.text, node.value, | |
189 | false, false); |
|
189 | false, false); | |
190 | } |
|
190 | } | |
191 |
|
191 | |||
192 | } |
|
192 | } | |
193 | //adds to current field |
|
193 | //adds to current field | |
194 | function add_to(field,options){ |
|
194 | function add_to(field,options){ | |
195 | for(var i = 0;node = options[i];i++){ |
|
195 | for(var i = 0;node = options[i];i++){ | |
196 | field.appendChild(new Option(node.text, node.value, |
|
196 | field.appendChild(new Option(node.text, node.value, | |
197 | false, false)); |
|
197 | false, false)); | |
198 | } |
|
198 | } | |
199 | } |
|
199 | } | |
200 |
|
200 | |||
201 | // add action |
|
201 | // add action | |
202 | if (this.id=='add_element'){ |
|
202 | if (this.id=='add_element'){ | |
203 | var c = get_checked(available); |
|
203 | var c = get_checked(available); | |
204 | add_to(choosen,c[0]); |
|
204 | add_to(choosen,c[0]); | |
205 | fill_with(available,c[1]); |
|
205 | fill_with(available,c[1]); | |
206 | } |
|
206 | } | |
207 | // remove action |
|
207 | // remove action | |
208 | if (this.id=='remove_element'){ |
|
208 | if (this.id=='remove_element'){ | |
209 | var c = get_checked(choosen); |
|
209 | var c = get_checked(choosen); | |
210 | add_to(available,c[0]); |
|
210 | add_to(available,c[0]); | |
211 | fill_with(choosen,c[1]); |
|
211 | fill_with(choosen,c[1]); | |
212 | } |
|
212 | } | |
213 | // add all elements |
|
213 | // add all elements | |
214 | if(this.id=='add_all_elements'){ |
|
214 | if(this.id=='add_all_elements'){ | |
215 | for(var i=0; node = available.options[i];i++){ |
|
215 | for(var i=0; node = available.options[i];i++){ | |
216 | choosen.appendChild(new Option(node.text, |
|
216 | choosen.appendChild(new Option(node.text, | |
217 | node.value, false, false)); |
|
217 | node.value, false, false)); | |
218 | } |
|
218 | } | |
219 | available.options.length = 0; |
|
219 | available.options.length = 0; | |
220 | } |
|
220 | } | |
221 | //remove all elements |
|
221 | //remove all elements | |
222 | if(this.id=='remove_all_elements'){ |
|
222 | if(this.id=='remove_all_elements'){ | |
223 | for(var i=0; node = choosen.options[i];i++){ |
|
223 | for(var i=0; node = choosen.options[i];i++){ | |
224 | available.appendChild(new Option(node.text, |
|
224 | available.appendChild(new Option(node.text, | |
225 | node.value, false, false)); |
|
225 | node.value, false, false)); | |
226 | } |
|
226 | } | |
227 | choosen.options.length = 0; |
|
227 | choosen.options.length = 0; | |
228 | } |
|
228 | } | |
229 |
|
229 | |||
230 | } |
|
230 | } | |
231 |
|
231 | |||
232 |
|
232 | |||
233 | E.addListener(['add_element','remove_element', |
|
233 | E.addListener(['add_element','remove_element', | |
234 | 'add_all_elements','remove_all_elements'],'click', |
|
234 | 'add_all_elements','remove_all_elements'],'click', | |
235 | prompts_action_callback) |
|
235 | prompts_action_callback) | |
236 |
|
236 | |||
237 | E.addListener(form_id,'submit',function(){ |
|
237 | E.addListener(form_id,'submit',function(){ | |
238 | var choosen = D.get(selected_container); |
|
238 | var choosen = D.get(selected_container); | |
239 | for (var i = 0; i < choosen.options.length; i++) { |
|
239 | for (var i = 0; i < choosen.options.length; i++) { | |
240 | choosen.options[i].selected = 'selected'; |
|
240 | choosen.options[i].selected = 'selected'; | |
241 | } |
|
241 | } | |
242 | }) |
|
242 | }) | |
243 | }); |
|
243 | }); | |
244 | </script> |
|
244 | </script> | |
245 | <div class="box box-right"> |
|
245 | <div class="box box-right"> | |
246 | <!-- box / title --> |
|
246 | <!-- box / title --> | |
247 | <div class="title"> |
|
247 | <div class="title"> | |
248 | <h5>${_('Permissions')}</h5> |
|
248 | <h5>${_('Permissions')}</h5> | |
249 | </div> |
|
249 | </div> | |
250 | ${h.form(url('users_group_perm', id=c.users_group.users_group_id), method='put')} |
|
250 | ${h.form(url('users_group_perm', id=c.users_group.users_group_id), method='put')} | |
251 | <div class="form"> |
|
251 | <div class="form"> | |
252 | <!-- fields --> |
|
252 | <!-- fields --> | |
253 | <div class="fields"> |
|
253 | <div class="fields"> | |
254 | <div class="field"> |
|
254 | <div class="field"> | |
255 | <div class="label label-checkbox"> |
|
255 | <div class="label label-checkbox"> | |
256 | <label for="">${_('Create repositories')}:</label> |
|
256 | <label for="create_repo_perm">${_('Create repositories')}:</label> | |
257 | </div> |
|
257 | </div> | |
258 | <div class="checkboxes"> |
|
258 | <div class="checkboxes"> | |
259 | ${h.checkbox('create_repo_perm',value=True)} |
|
259 | ${h.checkbox('create_repo_perm',value=True)} | |
260 | </div> |
|
260 | </div> | |
261 | </div> |
|
261 | </div> | |
262 | <div class="buttons"> |
|
262 | <div class="buttons"> | |
263 | ${h.submit('save',_('Save'),class_="ui-button")} |
|
263 | ${h.submit('save',_('Save'),class_="ui-button")} | |
264 | ${h.reset('reset',_('Reset'),class_="ui-button")} |
|
264 | ${h.reset('reset',_('Reset'),class_="ui-button")} | |
265 | </div> |
|
265 | </div> | |
266 | </div> |
|
266 | </div> | |
267 | </div> |
|
267 | </div> | |
268 | ${h.end_form()} |
|
268 | ${h.end_form()} | |
269 | </div> |
|
269 | </div> | |
270 | </%def> |
|
270 | </%def> |
@@ -1,75 +1,82 b'' | |||||
1 | ## -*- coding: utf-8 -*- |
|
1 | ## -*- coding: utf-8 -*- | |
2 | <%inherit file="/base/base.html"/> |
|
2 | <%inherit file="/base/base.html"/> | |
3 |
|
3 | |||
4 | <%def name="title()"> |
|
4 | <%def name="title()"> | |
5 | ${c.repo_name} ${_('Settings')} - ${c.rhodecode_name} |
|
5 | ${c.repo_name} ${_('Settings')} - ${c.rhodecode_name} | |
6 | </%def> |
|
6 | </%def> | |
7 |
|
7 | |||
8 | <%def name="breadcrumbs_links()"> |
|
8 | <%def name="breadcrumbs_links()"> | |
9 | ${h.link_to(u'Home',h.url('/'))} |
|
9 | ${h.link_to(u'Home',h.url('/'))} | |
10 | » |
|
10 | » | |
11 | ${h.link_to(c.repo_info.repo_name,h.url('summary_home',repo_name=c.repo_info.repo_name))} |
|
11 | ${h.link_to(c.repo_info.repo_name,h.url('summary_home',repo_name=c.repo_info.repo_name))} | |
12 | » |
|
12 | » | |
13 | ${_('Settings')} |
|
13 | ${_('Settings')} | |
14 | </%def> |
|
14 | </%def> | |
15 |
|
15 | |||
16 | <%def name="page_nav()"> |
|
16 | <%def name="page_nav()"> | |
17 | ${self.menu('settings')} |
|
17 | ${self.menu('settings')} | |
18 | </%def> |
|
18 | </%def> | |
19 | <%def name="main()"> |
|
19 | <%def name="main()"> | |
20 | <div class="box"> |
|
20 | <div class="box"> | |
21 | <!-- box / title --> |
|
21 | <!-- box / title --> | |
22 | <div class="title"> |
|
22 | <div class="title"> | |
23 | ${self.breadcrumbs()} |
|
23 | ${self.breadcrumbs()} | |
24 | </div> |
|
24 | </div> | |
25 | ${h.form(url('repo_settings_update', repo_name=c.repo_info.repo_name),method='put')} |
|
25 | ${h.form(url('repo_settings_update', repo_name=c.repo_info.repo_name),method='put')} | |
26 | <div class="form"> |
|
26 | <div class="form"> | |
27 | <!-- fields --> |
|
27 | <!-- fields --> | |
28 | <div class="fields"> |
|
28 | <div class="fields"> | |
29 | <div class="field"> |
|
29 | <div class="field"> | |
30 | <div class="label"> |
|
30 | <div class="label"> | |
31 | <label for="repo_name">${_('Name')}:</label> |
|
31 | <label for="repo_name">${_('Name')}:</label> | |
32 | </div> |
|
32 | </div> | |
33 | <div class="input input-medium"> |
|
33 | <div class="input input-medium"> | |
34 | ${h.text('repo_name',class_="small")} |
|
34 | ${h.text('repo_name',class_="small")} | |
35 | </div> |
|
35 | </div> | |
36 | </div> |
|
36 | </div> | |
37 |
|
37 | <div class="field"> | ||
|
38 | <div class="label"> | |||
|
39 | <label for="repo_group">${_('Repository group')}:</label> | |||
|
40 | </div> | |||
|
41 | <div class="input"> | |||
|
42 | ${h.select('repo_group','',c.repo_groups,class_="medium")} | |||
|
43 | </div> | |||
|
44 | </div> | |||
38 | <div class="field"> |
|
45 | <div class="field"> | |
39 | <div class="label label-textarea"> |
|
46 | <div class="label label-textarea"> | |
40 | <label for="description">${_('Description')}:</label> |
|
47 | <label for="description">${_('Description')}:</label> | |
41 | </div> |
|
48 | </div> | |
42 | <div class="textarea text-area editor"> |
|
49 | <div class="textarea text-area editor"> | |
43 | ${h.textarea('description',cols=23,rows=5)} |
|
50 | ${h.textarea('description',cols=23,rows=5)} | |
44 | </div> |
|
51 | </div> | |
45 | </div> |
|
52 | </div> | |
46 |
|
53 | |||
47 | <div class="field"> |
|
54 | <div class="field"> | |
48 | <div class="label label-checkbox"> |
|
55 | <div class="label label-checkbox"> | |
49 | <label for="private">${_('Private')}:</label> |
|
56 | <label for="private">${_('Private')}:</label> | |
50 | </div> |
|
57 | </div> | |
51 | <div class="checkboxes"> |
|
58 | <div class="checkboxes"> | |
52 | ${h.checkbox('private',value="True")} |
|
59 | ${h.checkbox('private',value="True")} | |
53 | </div> |
|
60 | </div> | |
54 | </div> |
|
61 | </div> | |
55 |
|
62 | |||
56 | <div class="field"> |
|
63 | <div class="field"> | |
57 | <div class="label"> |
|
64 | <div class="label"> | |
58 | <label for="">${_('Permissions')}:</label> |
|
65 | <label for="">${_('Permissions')}:</label> | |
59 | </div> |
|
66 | </div> | |
60 | <div class="input"> |
|
67 | <div class="input"> | |
61 | <%include file="../admin/repos/repo_edit_perms.html"/> |
|
68 | <%include file="../admin/repos/repo_edit_perms.html"/> | |
62 | </div> |
|
69 | </div> | |
63 |
|
70 | |||
64 | <div class="buttons"> |
|
71 | <div class="buttons"> | |
65 | ${h.submit('save','Save',class_="ui-button")} |
|
72 | ${h.submit('save','Save',class_="ui-button")} | |
66 | ${h.reset('reset','Reset',class_="ui-button")} |
|
73 | ${h.reset('reset','Reset',class_="ui-button")} | |
67 | </div> |
|
74 | </div> | |
68 | </div> |
|
75 | </div> | |
69 | </div> |
|
76 | </div> | |
70 | ${h.end_form()} |
|
77 | ${h.end_form()} | |
71 | </div> |
|
78 | </div> | |
72 | </div> |
|
79 | </div> | |
73 | </%def> |
|
80 | </%def> | |
74 |
|
81 | |||
75 |
|
82 |
@@ -1,85 +1,83 b'' | |||||
1 | """Pylons application test package |
|
1 | """Pylons application test package | |
2 |
|
2 | |||
3 | This package assumes the Pylons environment is already loaded, such as |
|
3 | This package assumes the Pylons environment is already loaded, such as | |
4 | when this script is imported from the `nosetests --with-pylons=test.ini` |
|
4 | when this script is imported from the `nosetests --with-pylons=test.ini` | |
5 | command. |
|
5 | command. | |
6 |
|
6 | |||
7 | This module initializes the application via ``websetup`` (`paster |
|
7 | This module initializes the application via ``websetup`` (`paster | |
8 | setup-app`) and provides the base testing objects. |
|
8 | setup-app`) and provides the base testing objects. | |
9 | """ |
|
9 | """ | |
10 | import os |
|
10 | import os | |
11 | from os.path import join as jn |
|
11 | from os.path import join as jn | |
12 |
|
12 | |||
13 | from unittest import TestCase |
|
13 | from unittest import TestCase | |
14 |
|
14 | |||
15 | from paste.deploy import loadapp |
|
15 | from paste.deploy import loadapp | |
16 | from paste.script.appinstall import SetupCommand |
|
16 | from paste.script.appinstall import SetupCommand | |
17 | from pylons import config, url |
|
17 | from pylons import config, url | |
18 | from routes.util import URLGenerator |
|
18 | from routes.util import URLGenerator | |
19 | from webtest import TestApp |
|
19 | from webtest import TestApp | |
20 |
|
20 | |||
21 | from rhodecode.model import meta |
|
21 | from rhodecode.model import meta | |
22 | import logging |
|
22 | import logging | |
23 |
|
23 | |||
24 |
|
24 | |||
25 | log = logging.getLogger(__name__) |
|
25 | log = logging.getLogger(__name__) | |
26 |
|
26 | |||
27 | import pylons.test |
|
27 | import pylons.test | |
28 |
|
28 | |||
29 | __all__ = ['environ', 'url', 'TestController', 'TESTS_TMP_PATH', 'HG_REPO', |
|
29 | __all__ = ['environ', 'url', 'TestController', 'TESTS_TMP_PATH', 'HG_REPO', | |
30 | 'GIT_REPO', 'NEW_HG_REPO', 'NEW_GIT_REPO', 'HG_FORK', 'GIT_FORK', |
|
30 | 'GIT_REPO', 'NEW_HG_REPO', 'NEW_GIT_REPO', 'HG_FORK', 'GIT_FORK', | |
31 | 'TEST_USER_ADMIN_LOGIN', 'TEST_USER_ADMIN_PASS' ] |
|
31 | 'TEST_USER_ADMIN_LOGIN', 'TEST_USER_ADMIN_PASS' ] | |
32 |
|
32 | |||
33 | # Invoke websetup with the current config file |
|
33 | # Invoke websetup with the current config file | |
34 | #SetupCommand('setup-app').run([config_file]) |
|
34 | #SetupCommand('setup-app').run([config_file]) | |
35 |
|
35 | |||
36 | ##RUNNING DESIRED TESTS |
|
36 | ##RUNNING DESIRED TESTS | |
37 | # nosetests -x rhodecode.tests.functional.test_admin_settings:TestSettingsController.test_my_account |
|
37 | # nosetests -x rhodecode.tests.functional.test_admin_settings:TestSettingsController.test_my_account | |
38 | # nosetests --pdb --pdb-failures |
|
38 | # nosetests --pdb --pdb-failures | |
39 | environ = {} |
|
39 | environ = {} | |
40 |
|
40 | |||
41 | #SOME GLOBALS FOR TESTS |
|
41 | #SOME GLOBALS FOR TESTS | |
42 | from tempfile import _RandomNameSequence |
|
42 | from tempfile import _RandomNameSequence | |
43 | TESTS_TMP_PATH = jn('/', 'tmp', 'rc_test_%s' % _RandomNameSequence().next()) |
|
43 | TESTS_TMP_PATH = jn('/', 'tmp', 'rc_test_%s' % _RandomNameSequence().next()) | |
44 | TEST_USER_ADMIN_LOGIN = 'test_admin' |
|
44 | TEST_USER_ADMIN_LOGIN = 'test_admin' | |
45 | TEST_USER_ADMIN_PASS = 'test12' |
|
45 | TEST_USER_ADMIN_PASS = 'test12' | |
46 | HG_REPO = 'vcs_test_hg' |
|
46 | HG_REPO = 'vcs_test_hg' | |
47 | GIT_REPO = 'vcs_test_git' |
|
47 | GIT_REPO = 'vcs_test_git' | |
48 |
|
48 | |||
49 | NEW_HG_REPO = 'vcs_test_hg_new' |
|
49 | NEW_HG_REPO = 'vcs_test_hg_new' | |
50 | NEW_GIT_REPO = 'vcs_test_git_new' |
|
50 | NEW_GIT_REPO = 'vcs_test_git_new' | |
51 |
|
51 | |||
52 | HG_FORK = 'vcs_test_hg_fork' |
|
52 | HG_FORK = 'vcs_test_hg_fork' | |
53 | GIT_FORK = 'vcs_test_git_fork' |
|
53 | GIT_FORK = 'vcs_test_git_fork' | |
54 |
|
54 | |||
55 | class TestController(TestCase): |
|
55 | class TestController(TestCase): | |
56 |
|
56 | |||
57 | def __init__(self, *args, **kwargs): |
|
57 | def __init__(self, *args, **kwargs): | |
58 | wsgiapp = pylons.test.pylonsapp |
|
58 | wsgiapp = pylons.test.pylonsapp | |
59 | config = wsgiapp.config |
|
59 | config = wsgiapp.config | |
60 |
|
60 | |||
61 | self.app = TestApp(wsgiapp) |
|
61 | self.app = TestApp(wsgiapp) | |
62 | url._push_object(URLGenerator(config['routes.map'], environ)) |
|
62 | url._push_object(URLGenerator(config['routes.map'], environ)) | |
63 | self.sa = meta.Session |
|
63 | self.sa = meta.Session | |
64 | self.index_location = config['app_conf']['index_dir'] |
|
64 | self.index_location = config['app_conf']['index_dir'] | |
65 | TestCase.__init__(self, *args, **kwargs) |
|
65 | TestCase.__init__(self, *args, **kwargs) | |
66 |
|
66 | |||
67 | def log_user(self, username=TEST_USER_ADMIN_LOGIN, |
|
67 | def log_user(self, username=TEST_USER_ADMIN_LOGIN, | |
68 | password=TEST_USER_ADMIN_PASS): |
|
68 | password=TEST_USER_ADMIN_PASS): | |
69 | response = self.app.post(url(controller='login', action='index'), |
|
69 | response = self.app.post(url(controller='login', action='index'), | |
70 | {'username':username, |
|
70 | {'username':username, | |
71 | 'password':password}) |
|
71 | 'password':password}) | |
72 |
|
72 | |||
73 | if 'invalid user name' in response.body: |
|
73 | if 'invalid user name' in response.body: | |
74 | self.fail('could not login using %s %s' % (username, password)) |
|
74 | self.fail('could not login using %s %s' % (username, password)) | |
75 |
|
75 | |||
76 | self.assertEqual(response.status, '302 Found') |
|
76 | self.assertEqual(response.status, '302 Found') | |
77 | self.assertEqual(response.session['rhodecode_user'].username, username) |
|
77 | self.assertEqual(response.session['rhodecode_user'].username, username) | |
78 | return response.follow() |
|
78 | return response.follow() | |
79 |
|
79 | |||
80 |
|
||||
81 |
|
||||
82 | def checkSessionFlash(self, response, msg): |
|
80 | def checkSessionFlash(self, response, msg): | |
83 | self.assertTrue('flash' in response.session) |
|
81 | self.assertTrue('flash' in response.session) | |
84 | self.assertTrue(msg in response.session['flash'][0][1]) |
|
82 | self.assertTrue(msg in response.session['flash'][0][1]) | |
85 |
|
83 |
@@ -1,208 +1,212 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 |
|
2 | |||
3 | from rhodecode.lib.auth import get_crypt_password, check_password |
|
3 | from rhodecode.lib.auth import get_crypt_password, check_password | |
4 | from rhodecode.model.db import User, RhodeCodeSettings |
|
4 | from rhodecode.model.db import User, RhodeCodeSettings | |
5 | from rhodecode.tests import * |
|
5 | from rhodecode.tests import * | |
6 |
|
6 | |||
7 | class TestAdminSettingsController(TestController): |
|
7 | class TestAdminSettingsController(TestController): | |
8 |
|
8 | |||
9 | def test_index(self): |
|
9 | def test_index(self): | |
10 | response = self.app.get(url('admin_settings')) |
|
10 | response = self.app.get(url('admin_settings')) | |
11 | # Test response... |
|
11 | # Test response... | |
12 |
|
12 | |||
13 | def test_index_as_xml(self): |
|
13 | def test_index_as_xml(self): | |
14 | response = self.app.get(url('formatted_admin_settings', format='xml')) |
|
14 | response = self.app.get(url('formatted_admin_settings', format='xml')) | |
15 |
|
15 | |||
16 | def test_create(self): |
|
16 | def test_create(self): | |
17 | response = self.app.post(url('admin_settings')) |
|
17 | response = self.app.post(url('admin_settings')) | |
18 |
|
18 | |||
19 | def test_new(self): |
|
19 | def test_new(self): | |
20 | response = self.app.get(url('admin_new_setting')) |
|
20 | response = self.app.get(url('admin_new_setting')) | |
21 |
|
21 | |||
22 | def test_new_as_xml(self): |
|
22 | def test_new_as_xml(self): | |
23 | response = self.app.get(url('formatted_admin_new_setting', format='xml')) |
|
23 | response = self.app.get(url('formatted_admin_new_setting', format='xml')) | |
24 |
|
24 | |||
25 | def test_update(self): |
|
25 | def test_update(self): | |
26 | response = self.app.put(url('admin_setting', setting_id=1)) |
|
26 | response = self.app.put(url('admin_setting', setting_id=1)) | |
27 |
|
27 | |||
28 | def test_update_browser_fakeout(self): |
|
28 | def test_update_browser_fakeout(self): | |
29 | response = self.app.post(url('admin_setting', setting_id=1), params=dict(_method='put')) |
|
29 | response = self.app.post(url('admin_setting', setting_id=1), params=dict(_method='put')) | |
30 |
|
30 | |||
31 | def test_delete(self): |
|
31 | def test_delete(self): | |
32 | response = self.app.delete(url('admin_setting', setting_id=1)) |
|
32 | response = self.app.delete(url('admin_setting', setting_id=1)) | |
33 |
|
33 | |||
34 | def test_delete_browser_fakeout(self): |
|
34 | def test_delete_browser_fakeout(self): | |
35 | response = self.app.post(url('admin_setting', setting_id=1), params=dict(_method='delete')) |
|
35 | response = self.app.post(url('admin_setting', setting_id=1), params=dict(_method='delete')) | |
36 |
|
36 | |||
37 | def test_show(self): |
|
37 | def test_show(self): | |
38 | response = self.app.get(url('admin_setting', setting_id=1)) |
|
38 | response = self.app.get(url('admin_setting', setting_id=1)) | |
39 |
|
39 | |||
40 | def test_show_as_xml(self): |
|
40 | def test_show_as_xml(self): | |
41 | response = self.app.get(url('formatted_admin_setting', setting_id=1, format='xml')) |
|
41 | response = self.app.get(url('formatted_admin_setting', setting_id=1, format='xml')) | |
42 |
|
42 | |||
43 | def test_edit(self): |
|
43 | def test_edit(self): | |
44 | response = self.app.get(url('admin_edit_setting', setting_id=1)) |
|
44 | response = self.app.get(url('admin_edit_setting', setting_id=1)) | |
45 |
|
45 | |||
46 | def test_edit_as_xml(self): |
|
46 | def test_edit_as_xml(self): | |
47 | response = self.app.get(url('formatted_admin_edit_setting', |
|
47 | response = self.app.get(url('formatted_admin_edit_setting', | |
48 | setting_id=1, format='xml')) |
|
48 | setting_id=1, format='xml')) | |
49 |
|
49 | |||
50 |
|
50 | |||
51 | def test_ga_code_active(self): |
|
51 | def test_ga_code_active(self): | |
52 | self.log_user() |
|
52 | self.log_user() | |
53 | old_title = 'RhodeCode' |
|
53 | old_title = 'RhodeCode' | |
54 | old_realm = 'RhodeCode authentication' |
|
54 | old_realm = 'RhodeCode authentication' | |
55 | new_ga_code = 'ga-test-123456789' |
|
55 | new_ga_code = 'ga-test-123456789' | |
56 | response = self.app.post(url('admin_setting', setting_id='global'), |
|
56 | response = self.app.post(url('admin_setting', setting_id='global'), | |
57 | params=dict( |
|
57 | params=dict( | |
58 | _method='put', |
|
58 | _method='put', | |
59 | rhodecode_title=old_title, |
|
59 | rhodecode_title=old_title, | |
60 | rhodecode_realm=old_realm, |
|
60 | rhodecode_realm=old_realm, | |
61 | rhodecode_ga_code=new_ga_code |
|
61 | rhodecode_ga_code=new_ga_code | |
62 | )) |
|
62 | )) | |
63 |
|
63 | |||
64 | self.checkSessionFlash(response, 'Updated application settings') |
|
64 | self.checkSessionFlash(response, 'Updated application settings') | |
65 |
|
65 | |||
66 | self.assertEqual(RhodeCodeSettings |
|
66 | self.assertEqual(RhodeCodeSettings | |
67 | .get_app_settings()['rhodecode_ga_code'], new_ga_code) |
|
67 | .get_app_settings()['rhodecode_ga_code'], new_ga_code) | |
68 |
|
68 | |||
69 | response = response.follow() |
|
69 | response = response.follow() | |
70 | self.assertTrue("""_gaq.push(['_setAccount', '%s']);""" % new_ga_code |
|
70 | self.assertTrue("""_gaq.push(['_setAccount', '%s']);""" % new_ga_code | |
71 | in response.body) |
|
71 | in response.body) | |
72 |
|
72 | |||
73 | def test_ga_code_inactive(self): |
|
73 | def test_ga_code_inactive(self): | |
74 | self.log_user() |
|
74 | self.log_user() | |
75 | old_title = 'RhodeCode' |
|
75 | old_title = 'RhodeCode' | |
76 | old_realm = 'RhodeCode authentication' |
|
76 | old_realm = 'RhodeCode authentication' | |
77 | new_ga_code = '' |
|
77 | new_ga_code = '' | |
78 | response = self.app.post(url('admin_setting', setting_id='global'), |
|
78 | response = self.app.post(url('admin_setting', setting_id='global'), | |
79 | params=dict( |
|
79 | params=dict( | |
80 | _method='put', |
|
80 | _method='put', | |
81 | rhodecode_title=old_title, |
|
81 | rhodecode_title=old_title, | |
82 | rhodecode_realm=old_realm, |
|
82 | rhodecode_realm=old_realm, | |
83 | rhodecode_ga_code=new_ga_code |
|
83 | rhodecode_ga_code=new_ga_code | |
84 | )) |
|
84 | )) | |
85 |
|
85 | |||
86 | self.assertTrue('Updated application settings' in |
|
86 | self.assertTrue('Updated application settings' in | |
87 | response.session['flash'][0][1]) |
|
87 | response.session['flash'][0][1]) | |
88 | self.assertEqual(RhodeCodeSettings |
|
88 | self.assertEqual(RhodeCodeSettings | |
89 | .get_app_settings()['rhodecode_ga_code'], new_ga_code) |
|
89 | .get_app_settings()['rhodecode_ga_code'], new_ga_code) | |
90 |
|
90 | |||
91 | response = response.follow() |
|
91 | response = response.follow() | |
92 | self.assertTrue("""_gaq.push(['_setAccount', '%s']);""" % new_ga_code |
|
92 | self.assertTrue("""_gaq.push(['_setAccount', '%s']);""" % new_ga_code | |
93 | not in response.body) |
|
93 | not in response.body) | |
94 |
|
94 | |||
95 |
|
95 | |||
96 | def test_title_change(self): |
|
96 | def test_title_change(self): | |
97 | self.log_user() |
|
97 | self.log_user() | |
98 | old_title = 'RhodeCode' |
|
98 | old_title = 'RhodeCode' | |
99 | new_title = old_title + '_changed' |
|
99 | new_title = old_title + '_changed' | |
100 | old_realm = 'RhodeCode authentication' |
|
100 | old_realm = 'RhodeCode authentication' | |
101 |
|
101 | |||
102 | for new_title in ['Changed', 'Żółwik', old_title]: |
|
102 | for new_title in ['Changed', 'Żółwik', old_title]: | |
103 | response = self.app.post(url('admin_setting', setting_id='global'), |
|
103 | response = self.app.post(url('admin_setting', setting_id='global'), | |
104 | params=dict( |
|
104 | params=dict( | |
105 | _method='put', |
|
105 | _method='put', | |
106 | rhodecode_title=new_title, |
|
106 | rhodecode_title=new_title, | |
107 | rhodecode_realm=old_realm, |
|
107 | rhodecode_realm=old_realm, | |
108 | rhodecode_ga_code='' |
|
108 | rhodecode_ga_code='' | |
109 | )) |
|
109 | )) | |
110 |
|
110 | |||
111 | self.checkSessionFlash(response, 'Updated application settings') |
|
111 | self.checkSessionFlash(response, 'Updated application settings') | |
112 | self.assertEqual(RhodeCodeSettings |
|
112 | self.assertEqual(RhodeCodeSettings | |
113 | .get_app_settings()['rhodecode_title'], |
|
113 | .get_app_settings()['rhodecode_title'], | |
114 | new_title.decode('utf-8')) |
|
114 | new_title.decode('utf-8')) | |
115 |
|
115 | |||
116 | response = response.follow() |
|
116 | response = response.follow() | |
117 | self.assertTrue("""<h1><a href="/">%s</a></h1>""" % new_title |
|
117 | self.assertTrue("""<h1><a href="/">%s</a></h1>""" % new_title | |
118 | in response.body) |
|
118 | in response.body) | |
119 |
|
119 | |||
120 |
|
120 | |||
121 | def test_my_account(self): |
|
121 | def test_my_account(self): | |
122 | self.log_user() |
|
122 | self.log_user() | |
123 | response = self.app.get(url('admin_settings_my_account')) |
|
123 | response = self.app.get(url('admin_settings_my_account')) | |
124 |
|
124 | |||
125 | self.assertTrue('value="test_admin' in response.body) |
|
125 | self.assertTrue('value="test_admin' in response.body) | |
126 |
|
126 | |||
127 | def test_my_account_update(self): |
|
127 | def test_my_account_update(self): | |
128 | self.log_user() |
|
128 | self.log_user() | |
129 |
|
129 | |||
130 | new_email = 'new@mail.pl' |
|
130 | new_email = 'new@mail.pl' | |
131 | new_name = 'NewName' |
|
131 | new_name = 'NewName' | |
132 | new_lastname = 'NewLastname' |
|
132 | new_lastname = 'NewLastname' | |
133 | new_password = 'test123' |
|
133 | new_password = 'test123' | |
134 |
|
134 | |||
135 |
|
135 | |||
136 | response = self.app.post(url('admin_settings_my_account_update'), |
|
136 | response = self.app.post(url('admin_settings_my_account_update'), | |
137 | params=dict(_method='put', |
|
137 | params=dict(_method='put', | |
138 | username='test_admin', |
|
138 | username='test_admin', | |
139 | new_password=new_password, |
|
139 | new_password=new_password, | |
|
140 | password_confirmation = new_password, | |||
140 | password='', |
|
141 | password='', | |
141 | name=new_name, |
|
142 | name=new_name, | |
142 | lastname=new_lastname, |
|
143 | lastname=new_lastname, | |
143 | email=new_email,)) |
|
144 | email=new_email,)) | |
144 | response.follow() |
|
145 | response.follow() | |
145 |
|
146 | |||
146 | assert 'Your account was updated successfully' in response.session['flash'][0][1], 'no flash message about success of change' |
|
147 | assert 'Your account was updated successfully' in response.session['flash'][0][1], 'no flash message about success of change' | |
147 | user = self.sa.query(User).filter(User.username == 'test_admin').one() |
|
148 | user = self.sa.query(User).filter(User.username == 'test_admin').one() | |
148 | assert user.email == new_email , 'incorrect user email after update got %s vs %s' % (user.email, new_email) |
|
149 | assert user.email == new_email , 'incorrect user email after update got %s vs %s' % (user.email, new_email) | |
149 | assert user.name == new_name, 'updated field mismatch %s vs %s' % (user.name, new_name) |
|
150 | assert user.name == new_name, 'updated field mismatch %s vs %s' % (user.name, new_name) | |
150 | assert user.lastname == new_lastname, 'updated field mismatch %s vs %s' % (user.lastname, new_lastname) |
|
151 | assert user.lastname == new_lastname, 'updated field mismatch %s vs %s' % (user.lastname, new_lastname) | |
151 | assert check_password(new_password, user.password) is True, 'password field mismatch %s vs %s' % (user.password, new_password) |
|
152 | assert check_password(new_password, user.password) is True, 'password field mismatch %s vs %s' % (user.password, new_password) | |
152 |
|
153 | |||
153 | #bring back the admin settings |
|
154 | #bring back the admin settings | |
154 | old_email = 'test_admin@mail.com' |
|
155 | old_email = 'test_admin@mail.com' | |
155 | old_name = 'RhodeCode' |
|
156 | old_name = 'RhodeCode' | |
156 | old_lastname = 'Admin' |
|
157 | old_lastname = 'Admin' | |
157 | old_password = 'test12' |
|
158 | old_password = 'test12' | |
158 |
|
159 | |||
159 | response = self.app.post(url('admin_settings_my_account_update'), params=dict( |
|
160 | response = self.app.post(url('admin_settings_my_account_update'), params=dict( | |
160 | _method='put', |
|
161 | _method='put', | |
161 | username='test_admin', |
|
162 | username='test_admin', | |
162 | new_password=old_password, |
|
163 | new_password=old_password, | |
|
164 | password_confirmation = old_password, | |||
163 | password='', |
|
165 | password='', | |
164 | name=old_name, |
|
166 | name=old_name, | |
165 | lastname=old_lastname, |
|
167 | lastname=old_lastname, | |
166 | email=old_email,)) |
|
168 | email=old_email,)) | |
167 |
|
169 | |||
168 | response.follow() |
|
170 | response.follow() | |
169 | self.checkSessionFlash(response, |
|
171 | self.checkSessionFlash(response, | |
170 | 'Your account was updated successfully') |
|
172 | 'Your account was updated successfully') | |
171 |
|
173 | |||
172 | user = self.sa.query(User).filter(User.username == 'test_admin').one() |
|
174 | user = self.sa.query(User).filter(User.username == 'test_admin').one() | |
173 | assert user.email == old_email , 'incorrect user email after update got %s vs %s' % (user.email, old_email) |
|
175 | assert user.email == old_email , 'incorrect user email after update got %s vs %s' % (user.email, old_email) | |
174 |
|
176 | |||
175 | assert user.email == old_email , 'incorrect user email after update got %s vs %s' % (user.email, old_email) |
|
177 | assert user.email == old_email , 'incorrect user email after update got %s vs %s' % (user.email, old_email) | |
176 | assert user.name == old_name, 'updated field mismatch %s vs %s' % (user.name, old_name) |
|
178 | assert user.name == old_name, 'updated field mismatch %s vs %s' % (user.name, old_name) | |
177 | assert user.lastname == old_lastname, 'updated field mismatch %s vs %s' % (user.lastname, old_lastname) |
|
179 | assert user.lastname == old_lastname, 'updated field mismatch %s vs %s' % (user.lastname, old_lastname) | |
178 | assert check_password(old_password, user.password) is True , 'password updated field mismatch %s vs %s' % (user.password, old_password) |
|
180 | assert check_password(old_password, user.password) is True , 'password updated field mismatch %s vs %s' % (user.password, old_password) | |
179 |
|
181 | |||
180 |
|
182 | |||
181 | def test_my_account_update_err_email_exists(self): |
|
183 | def test_my_account_update_err_email_exists(self): | |
182 | self.log_user() |
|
184 | self.log_user() | |
183 |
|
185 | |||
184 | new_email = 'test_regular@mail.com'#already exisitn email |
|
186 | new_email = 'test_regular@mail.com'#already exisitn email | |
185 | response = self.app.post(url('admin_settings_my_account_update'), params=dict( |
|
187 | response = self.app.post(url('admin_settings_my_account_update'), params=dict( | |
186 | _method='put', |
|
188 | _method='put', | |
187 | username='test_admin', |
|
189 | username='test_admin', | |
188 | new_password='test12', |
|
190 | new_password='test12', | |
|
191 | password_confirmation = 'test122', | |||
189 | name='NewName', |
|
192 | name='NewName', | |
190 | lastname='NewLastname', |
|
193 | lastname='NewLastname', | |
191 | email=new_email,)) |
|
194 | email=new_email,)) | |
192 |
|
195 | |||
193 | assert 'This e-mail address is already taken' in response.body, 'Missing error message about existing email' |
|
196 | assert 'This e-mail address is already taken' in response.body, 'Missing error message about existing email' | |
194 |
|
197 | |||
195 |
|
198 | |||
196 | def test_my_account_update_err(self): |
|
199 | def test_my_account_update_err(self): | |
197 | self.log_user('test_regular2', 'test12') |
|
200 | self.log_user('test_regular2', 'test12') | |
198 |
|
201 | |||
199 | new_email = 'newmail.pl' |
|
202 | new_email = 'newmail.pl' | |
200 | response = self.app.post(url('admin_settings_my_account_update'), params=dict( |
|
203 | response = self.app.post(url('admin_settings_my_account_update'), params=dict( | |
201 | _method='put', |
|
204 | _method='put', | |
202 | username='test_admin', |
|
205 | username='test_admin', | |
203 | new_password='test12', |
|
206 | new_password='test12', | |
|
207 | password_confirmation = 'test122', | |||
204 | name='NewName', |
|
208 | name='NewName', | |
205 | lastname='NewLastname', |
|
209 | lastname='NewLastname', | |
206 | email=new_email,)) |
|
210 | email=new_email,)) | |
207 | assert 'An email address must contain a single @' in response.body, 'Missing error message about wrong email' |
|
211 | assert 'An email address must contain a single @' in response.body, 'Missing error message about wrong email' | |
208 | assert 'This username already exists' in response.body, 'Missing error message about existing user' |
|
212 | assert 'This username already exists' in response.body, 'Missing error message about existing user' |
@@ -1,119 +1,122 b'' | |||||
1 | from rhodecode.tests import * |
|
1 | from rhodecode.tests import * | |
2 | from rhodecode.model.db import User |
|
2 | from rhodecode.model.db import User | |
3 | from rhodecode.lib.auth import check_password |
|
3 | from rhodecode.lib.auth import check_password | |
4 | from sqlalchemy.orm.exc import NoResultFound |
|
4 | from sqlalchemy.orm.exc import NoResultFound | |
5 |
|
5 | |||
6 | class TestAdminUsersController(TestController): |
|
6 | class TestAdminUsersController(TestController): | |
7 |
|
7 | |||
8 | def test_index(self): |
|
8 | def test_index(self): | |
9 | response = self.app.get(url('users')) |
|
9 | response = self.app.get(url('users')) | |
10 | # Test response... |
|
10 | # Test response... | |
11 |
|
11 | |||
12 | def test_index_as_xml(self): |
|
12 | def test_index_as_xml(self): | |
13 | response = self.app.get(url('formatted_users', format='xml')) |
|
13 | response = self.app.get(url('formatted_users', format='xml')) | |
14 |
|
14 | |||
15 | def test_create(self): |
|
15 | def test_create(self): | |
16 | self.log_user() |
|
16 | self.log_user() | |
17 | username = 'newtestuser' |
|
17 | username = 'newtestuser' | |
18 | password = 'test12' |
|
18 | password = 'test12' | |
|
19 | password_confirmation = password | |||
19 | name = 'name' |
|
20 | name = 'name' | |
20 | lastname = 'lastname' |
|
21 | lastname = 'lastname' | |
21 | email = 'mail@mail.com' |
|
22 | email = 'mail@mail.com' | |
22 |
|
23 | |||
23 | response = self.app.post(url('users'), {'username':username, |
|
24 | response = self.app.post(url('users'), {'username':username, | |
24 | 'password':password, |
|
25 | 'password':password, | |
|
26 | 'password_confirmation':password_confirmation, | |||
25 | 'name':name, |
|
27 | 'name':name, | |
26 | 'active':True, |
|
28 | 'active':True, | |
27 | 'lastname':lastname, |
|
29 | 'lastname':lastname, | |
28 | 'email':email}) |
|
30 | 'email':email}) | |
29 |
|
31 | |||
30 |
|
32 | |||
31 | assert '''created user %s''' % (username) in response.session['flash'][0], 'No flash message about new user' |
|
33 | assert '''created user %s''' % (username) in response.session['flash'][0], 'No flash message about new user' | |
32 |
|
34 | |||
33 | new_user = self.sa.query(User).filter(User.username == username).one() |
|
35 | new_user = self.sa.query(User).filter(User.username == username).one() | |
34 |
|
36 | |||
35 |
|
37 | |||
36 | assert new_user.username == username, 'wrong info about username' |
|
38 | assert new_user.username == username, 'wrong info about username' | |
37 | assert check_password(password, new_user.password) == True , 'wrong info about password' |
|
39 | assert check_password(password, new_user.password) == True , 'wrong info about password' | |
38 | assert new_user.name == name, 'wrong info about name' |
|
40 | assert new_user.name == name, 'wrong info about name' | |
39 | assert new_user.lastname == lastname, 'wrong info about lastname' |
|
41 | assert new_user.lastname == lastname, 'wrong info about lastname' | |
40 | assert new_user.email == email, 'wrong info about email' |
|
42 | assert new_user.email == email, 'wrong info about email' | |
41 |
|
43 | |||
42 |
|
44 | |||
43 | response.follow() |
|
45 | response.follow() | |
44 | response = response.follow() |
|
46 | response = response.follow() | |
45 | assert """edit">newtestuser</a>""" in response.body |
|
47 | assert """edit">newtestuser</a>""" in response.body | |
46 |
|
48 | |||
47 | def test_create_err(self): |
|
49 | def test_create_err(self): | |
48 | self.log_user() |
|
50 | self.log_user() | |
49 | username = 'new_user' |
|
51 | username = 'new_user' | |
50 | password = '' |
|
52 | password = '' | |
51 | name = 'name' |
|
53 | name = 'name' | |
52 | lastname = 'lastname' |
|
54 | lastname = 'lastname' | |
53 | email = 'errmail.com' |
|
55 | email = 'errmail.com' | |
54 |
|
56 | |||
55 | response = self.app.post(url('users'), {'username':username, |
|
57 | response = self.app.post(url('users'), {'username':username, | |
56 | 'password':password, |
|
58 | 'password':password, | |
57 | 'name':name, |
|
59 | 'name':name, | |
58 | 'active':False, |
|
60 | 'active':False, | |
59 | 'lastname':lastname, |
|
61 | 'lastname':lastname, | |
60 | 'email':email}) |
|
62 | 'email':email}) | |
61 |
|
63 | |||
62 | assert """<span class="error-message">Invalid username</span>""" in response.body |
|
64 | assert """<span class="error-message">Invalid username</span>""" in response.body | |
63 | assert """<span class="error-message">Please enter a value</span>""" in response.body |
|
65 | assert """<span class="error-message">Please enter a value</span>""" in response.body | |
64 | assert """<span class="error-message">An email address must contain a single @</span>""" in response.body |
|
66 | assert """<span class="error-message">An email address must contain a single @</span>""" in response.body | |
65 |
|
67 | |||
66 | def get_user(): |
|
68 | def get_user(): | |
67 | self.sa.query(User).filter(User.username == username).one() |
|
69 | self.sa.query(User).filter(User.username == username).one() | |
68 |
|
70 | |||
69 | self.assertRaises(NoResultFound, get_user), 'found user in database' |
|
71 | self.assertRaises(NoResultFound, get_user), 'found user in database' | |
70 |
|
72 | |||
71 | def test_new(self): |
|
73 | def test_new(self): | |
72 | response = self.app.get(url('new_user')) |
|
74 | response = self.app.get(url('new_user')) | |
73 |
|
75 | |||
74 | def test_new_as_xml(self): |
|
76 | def test_new_as_xml(self): | |
75 | response = self.app.get(url('formatted_new_user', format='xml')) |
|
77 | response = self.app.get(url('formatted_new_user', format='xml')) | |
76 |
|
78 | |||
77 | def test_update(self): |
|
79 | def test_update(self): | |
78 | response = self.app.put(url('user', id=1)) |
|
80 | response = self.app.put(url('user', id=1)) | |
79 |
|
81 | |||
80 | def test_update_browser_fakeout(self): |
|
82 | def test_update_browser_fakeout(self): | |
81 | response = self.app.post(url('user', id=1), params=dict(_method='put')) |
|
83 | response = self.app.post(url('user', id=1), params=dict(_method='put')) | |
82 |
|
84 | |||
83 | def test_delete(self): |
|
85 | def test_delete(self): | |
84 | self.log_user() |
|
86 | self.log_user() | |
85 | username = 'newtestuserdeleteme' |
|
87 | username = 'newtestuserdeleteme' | |
86 | password = 'test12' |
|
88 | password = 'test12' | |
87 | name = 'name' |
|
89 | name = 'name' | |
88 | lastname = 'lastname' |
|
90 | lastname = 'lastname' | |
89 | email = 'todeletemail@mail.com' |
|
91 | email = 'todeletemail@mail.com' | |
90 |
|
92 | |||
91 | response = self.app.post(url('users'), {'username':username, |
|
93 | response = self.app.post(url('users'), {'username':username, | |
92 | 'password':password, |
|
94 | 'password':password, | |
|
95 | 'password_confirmation':password, | |||
93 | 'name':name, |
|
96 | 'name':name, | |
94 | 'active':True, |
|
97 | 'active':True, | |
95 | 'lastname':lastname, |
|
98 | 'lastname':lastname, | |
96 | 'email':email}) |
|
99 | 'email':email}) | |
97 |
|
100 | |||
98 | response = response.follow() |
|
101 | response = response.follow() | |
99 |
|
102 | |||
100 | new_user = self.sa.query(User).filter(User.username == username).one() |
|
103 | new_user = self.sa.query(User).filter(User.username == username).one() | |
101 | response = self.app.delete(url('user', id=new_user.user_id)) |
|
104 | response = self.app.delete(url('user', id=new_user.user_id)) | |
102 |
|
105 | |||
103 | assert """successfully deleted user""" in response.session['flash'][0], 'No info about user deletion' |
|
106 | assert """successfully deleted user""" in response.session['flash'][0], 'No info about user deletion' | |
104 |
|
107 | |||
105 |
|
108 | |||
106 | def test_delete_browser_fakeout(self): |
|
109 | def test_delete_browser_fakeout(self): | |
107 | response = self.app.post(url('user', id=1), params=dict(_method='delete')) |
|
110 | response = self.app.post(url('user', id=1), params=dict(_method='delete')) | |
108 |
|
111 | |||
109 | def test_show(self): |
|
112 | def test_show(self): | |
110 | response = self.app.get(url('user', id=1)) |
|
113 | response = self.app.get(url('user', id=1)) | |
111 |
|
114 | |||
112 | def test_show_as_xml(self): |
|
115 | def test_show_as_xml(self): | |
113 | response = self.app.get(url('formatted_user', id=1, format='xml')) |
|
116 | response = self.app.get(url('formatted_user', id=1, format='xml')) | |
114 |
|
117 | |||
115 | def test_edit(self): |
|
118 | def test_edit(self): | |
116 | response = self.app.get(url('edit_user', id=1)) |
|
119 | response = self.app.get(url('edit_user', id=1)) | |
117 |
|
120 | |||
118 | def test_edit_as_xml(self): |
|
121 | def test_edit_as_xml(self): | |
119 | response = self.app.get(url('formatted_edit_user', id=1, format='xml')) |
|
122 | response = self.app.get(url('formatted_edit_user', id=1, format='xml')) |
@@ -1,412 +1,401 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """ |
|
2 | """ | |
3 | rhodecode.tests.test_hg_operations |
|
3 | rhodecode.tests.test_hg_operations | |
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
5 |
|
5 | |||
6 | Test suite for making push/pull operations |
|
6 | Test suite for making push/pull operations | |
7 |
|
7 | |||
8 | :created_on: Dec 30, 2010 |
|
8 | :created_on: Dec 30, 2010 | |
9 | :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> |
|
9 | :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> | |
10 | :license: GPLv3, see COPYING for more details. |
|
10 | :license: GPLv3, see COPYING for more details. | |
11 | """ |
|
11 | """ | |
12 | # This program is free software: you can redistribute it and/or modify |
|
12 | # This program is free software: you can redistribute it and/or modify | |
13 | # it under the terms of the GNU General Public License as published by |
|
13 | # it under the terms of the GNU General Public License as published by | |
14 | # the Free Software Foundation, either version 3 of the License, or |
|
14 | # the Free Software Foundation, either version 3 of the License, or | |
15 | # (at your option) any later version. |
|
15 | # (at your option) any later version. | |
16 | # |
|
16 | # | |
17 | # This program is distributed in the hope that it will be useful, |
|
17 | # This program is distributed in the hope that it will be useful, | |
18 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
18 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
19 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | # GNU General Public License for more details. |
|
20 | # GNU General Public License for more details. | |
21 | # |
|
21 | # | |
22 | # You should have received a copy of the GNU General Public License |
|
22 | # You should have received a copy of the GNU General Public License | |
23 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
23 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
24 |
|
24 | |||
25 | import os |
|
25 | import os | |
26 | import time |
|
26 | import time | |
27 | import sys |
|
27 | import sys | |
28 | import shutil |
|
28 | import shutil | |
29 | import logging |
|
29 | import logging | |
30 |
|
30 | |||
31 | from os.path import join as jn |
|
31 | from os.path import join as jn | |
32 | from os.path import dirname as dn |
|
32 | from os.path import dirname as dn | |
33 |
|
33 | |||
34 | from tempfile import _RandomNameSequence |
|
34 | from tempfile import _RandomNameSequence | |
35 | from subprocess import Popen, PIPE |
|
35 | from subprocess import Popen, PIPE | |
36 |
|
36 | |||
37 | from paste.deploy import appconfig |
|
37 | from paste.deploy import appconfig | |
38 | from pylons import config |
|
38 | from pylons import config | |
39 | from sqlalchemy import engine_from_config |
|
39 | from sqlalchemy import engine_from_config | |
40 |
|
40 | |||
41 | from rhodecode.lib.utils import add_cache |
|
41 | from rhodecode.lib.utils import add_cache | |
42 | from rhodecode.model import init_model |
|
42 | from rhodecode.model import init_model | |
43 | from rhodecode.model import meta |
|
43 | from rhodecode.model import meta | |
44 | from rhodecode.model.db import User, Repository, UserLog |
|
44 | from rhodecode.model.db import User, Repository, UserLog | |
45 | from rhodecode.lib.auth import get_crypt_password |
|
45 | from rhodecode.lib.auth import get_crypt_password | |
46 |
|
46 | |||
47 | from rhodecode.tests import TESTS_TMP_PATH, NEW_HG_REPO, HG_REPO |
|
47 | from rhodecode.tests import TESTS_TMP_PATH, NEW_HG_REPO, HG_REPO | |
48 | from rhodecode.config.environment import load_environment |
|
48 | from rhodecode.config.environment import load_environment | |
49 |
|
49 | |||
50 | rel_path = dn(dn(dn(os.path.abspath(__file__)))) |
|
50 | rel_path = dn(dn(dn(os.path.abspath(__file__)))) | |
51 | conf = appconfig('config:development.ini', relative_to=rel_path) |
|
51 | ||
|
52 | conf = appconfig('config:%s' % sys.argv[1], relative_to=rel_path) | |||
52 | load_environment(conf.global_conf, conf.local_conf) |
|
53 | load_environment(conf.global_conf, conf.local_conf) | |
53 |
|
54 | |||
54 | add_cache(conf) |
|
55 | add_cache(conf) | |
55 |
|
56 | |||
56 | USER = 'test_admin' |
|
57 | USER = 'test_admin' | |
57 | PASS = 'test12' |
|
58 | PASS = 'test12' | |
58 | HOST = '127.0.0.1:5000' |
|
59 | HOST = '127.0.0.1:5000' | |
59 | DEBUG = True if sys.argv[1:] else False |
|
60 | DEBUG = False | |
60 | print 'DEBUG:', DEBUG |
|
61 | print 'DEBUG:', DEBUG | |
61 | log = logging.getLogger(__name__) |
|
62 | log = logging.getLogger(__name__) | |
62 |
|
63 | |||
|
64 | engine = engine_from_config(conf, 'sqlalchemy.db1.') | |||
|
65 | init_model(engine) | |||
|
66 | sa = meta.Session | |||
63 |
|
67 | |||
64 | class Command(object): |
|
68 | class Command(object): | |
65 |
|
69 | |||
66 | def __init__(self, cwd): |
|
70 | def __init__(self, cwd): | |
67 | self.cwd = cwd |
|
71 | self.cwd = cwd | |
68 |
|
72 | |||
69 | def execute(self, cmd, *args): |
|
73 | def execute(self, cmd, *args): | |
70 | """Runs command on the system with given ``args``. |
|
74 | """Runs command on the system with given ``args``. | |
71 | """ |
|
75 | """ | |
72 |
|
76 | |||
73 | command = cmd + ' ' + ' '.join(args) |
|
77 | command = cmd + ' ' + ' '.join(args) | |
74 | log.debug('Executing %s' % command) |
|
78 | log.debug('Executing %s' % command) | |
75 | if DEBUG: |
|
79 | if DEBUG: | |
76 | print command |
|
80 | print command | |
77 | p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, cwd=self.cwd) |
|
81 | p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, cwd=self.cwd) | |
78 | stdout, stderr = p.communicate() |
|
82 | stdout, stderr = p.communicate() | |
79 | if DEBUG: |
|
83 | if DEBUG: | |
80 | print stdout, stderr |
|
84 | print stdout, stderr | |
81 | return stdout, stderr |
|
85 | return stdout, stderr | |
82 |
|
86 | |||
83 |
|
87 | |||
84 | def test_wrapp(func): |
|
88 | def test_wrapp(func): | |
85 |
|
89 | |||
86 | def __wrapp(*args, **kwargs): |
|
90 | def __wrapp(*args, **kwargs): | |
87 | print '>>>%s' % func.__name__ |
|
91 | print '>>>%s' % func.__name__ | |
88 | try: |
|
92 | try: | |
89 | res = func(*args, **kwargs) |
|
93 | res = func(*args, **kwargs) | |
90 | except Exception, e: |
|
94 | except Exception, e: | |
91 | print ('###############\n-' |
|
95 | print ('###############\n-' | |
92 | '--%s failed %s--\n' |
|
96 | '--%s failed %s--\n' | |
93 | '###############\n' % (func.__name__, e)) |
|
97 | '###############\n' % (func.__name__, e)) | |
94 | sys.exit() |
|
98 | sys.exit() | |
95 | print '++OK++' |
|
99 | print '++OK++' | |
96 | return res |
|
100 | return res | |
97 | return __wrapp |
|
101 | return __wrapp | |
98 |
|
102 | |||
99 | def get_session(): |
|
|||
100 | engine = engine_from_config(conf, 'sqlalchemy.db1.') |
|
|||
101 | init_model(engine) |
|
|||
102 | sa = meta.Session |
|
|||
103 | return sa |
|
|||
104 |
|
||||
105 |
|
103 | |||
106 | def create_test_user(force=True): |
|
104 | def create_test_user(force=True): | |
107 | print '\tcreating test user' |
|
105 | print '\tcreating test user' | |
108 | sa = get_session() |
|
|||
109 |
|
106 | |||
110 | user = sa.query(User).filter(User.username == USER).scalar() |
|
107 | user = User.get_by_username(USER) | |
111 |
|
108 | |||
112 | if force and user is not None: |
|
109 | if force and user is not None: | |
113 | print '\tremoving current user' |
|
110 | print '\tremoving current user' | |
114 |
for repo in |
|
111 | for repo in Repository.query().filter(Repository.user == user).all(): | |
115 | sa.delete(repo) |
|
112 | sa.delete(repo) | |
116 | sa.delete(user) |
|
113 | sa.delete(user) | |
117 | sa.commit() |
|
114 | sa.commit() | |
118 |
|
115 | |||
119 | if user is None or force: |
|
116 | if user is None or force: | |
120 | print '\tcreating new one' |
|
117 | print '\tcreating new one' | |
121 | new_usr = User() |
|
118 | new_usr = User() | |
122 | new_usr.username = USER |
|
119 | new_usr.username = USER | |
123 | new_usr.password = get_crypt_password(PASS) |
|
120 | new_usr.password = get_crypt_password(PASS) | |
124 | new_usr.email = 'mail@mail.com' |
|
121 | new_usr.email = 'mail@mail.com' | |
125 | new_usr.name = 'test' |
|
122 | new_usr.name = 'test' | |
126 | new_usr.lastname = 'lasttestname' |
|
123 | new_usr.lastname = 'lasttestname' | |
127 | new_usr.active = True |
|
124 | new_usr.active = True | |
128 | new_usr.admin = True |
|
125 | new_usr.admin = True | |
129 | sa.add(new_usr) |
|
126 | sa.add(new_usr) | |
130 | sa.commit() |
|
127 | sa.commit() | |
131 |
|
128 | |||
132 | print '\tdone' |
|
129 | print '\tdone' | |
133 |
|
130 | |||
134 |
|
131 | |||
135 | def create_test_repo(force=True): |
|
132 | def create_test_repo(force=True): | |
136 | from rhodecode.model.repo import RepoModel |
|
133 | from rhodecode.model.repo import RepoModel | |
137 | sa = get_session() |
|
|||
138 |
|
134 | |||
139 | user = sa.query(User).filter(User.username == USER).scalar() |
|
135 | user = User.get_by_username(USER) | |
140 | if user is None: |
|
136 | if user is None: | |
141 | raise Exception('user not found') |
|
137 | raise Exception('user not found') | |
142 |
|
138 | |||
143 |
|
139 | |||
144 | repo = sa.query(Repository).filter(Repository.repo_name == HG_REPO).scalar() |
|
140 | repo = sa.query(Repository).filter(Repository.repo_name == HG_REPO).scalar() | |
145 |
|
141 | |||
146 | if repo is None: |
|
142 | if repo is None: | |
147 | print '\trepo not found creating' |
|
143 | print '\trepo not found creating' | |
148 |
|
144 | |||
149 | form_data = {'repo_name':HG_REPO, |
|
145 | form_data = {'repo_name':HG_REPO, | |
150 | 'repo_type':'hg', |
|
146 | 'repo_type':'hg', | |
151 | 'private':False, |
|
147 | 'private':False, | |
152 | 'clone_uri':'' } |
|
148 | 'clone_uri':'' } | |
153 | rm = RepoModel(sa) |
|
149 | rm = RepoModel(sa) | |
154 | rm.base_path = '/home/hg' |
|
150 | rm.base_path = '/home/hg' | |
155 | rm.create(form_data, user) |
|
151 | rm.create(form_data, user) | |
156 |
|
152 | |||
157 |
|
153 | |||
158 | def set_anonymous_access(enable=True): |
|
154 | def set_anonymous_access(enable=True): | |
159 | sa = get_session() |
|
155 | user = User.get_by_username('default') | |
160 | user = sa.query(User).filter(User.username == 'default').one() |
|
|||
161 | sa.expire(user) |
|
|||
162 | user.active = enable |
|
156 | user.active = enable | |
163 | sa.add(user) |
|
157 | sa.add(user) | |
164 | sa.commit() |
|
158 | sa.commit() | |
165 | sa.remove() |
|
|||
166 | import time;time.sleep(3) |
|
|||
167 | print '\tanonymous access is now:', enable |
|
159 | print '\tanonymous access is now:', enable | |
168 |
|
160 | if enable != User.get_by_username('default').active: | ||
|
161 | raise Exception('Cannot set anonymous access') | |||
169 |
|
162 | |||
170 | def get_anonymous_access(): |
|
163 | def get_anonymous_access(): | |
171 | sa = get_session() |
|
164 | user = User.get_by_username('default') | |
172 | obj1 = sa.query(User).filter(User.username == 'default').one() |
|
165 | return user.active | |
173 | sa.expire(obj1) |
|
|||
174 | return obj1.active |
|
|||
175 |
|
166 | |||
176 |
|
167 | |||
177 | #============================================================================== |
|
168 | #============================================================================== | |
178 | # TESTS |
|
169 | # TESTS | |
179 | #============================================================================== |
|
170 | #============================================================================== | |
180 | @test_wrapp |
|
171 | @test_wrapp | |
181 | def test_clone_with_credentials(no_errors=False): |
|
172 | def test_clone_with_credentials(no_errors=False): | |
182 | cwd = path = jn(TESTS_TMP_PATH, HG_REPO) |
|
173 | cwd = path = jn(TESTS_TMP_PATH, HG_REPO) | |
183 |
|
174 | |||
184 | try: |
|
175 | try: | |
185 | shutil.rmtree(path, ignore_errors=True) |
|
176 | shutil.rmtree(path, ignore_errors=True) | |
186 | os.makedirs(path) |
|
177 | os.makedirs(path) | |
187 | #print 'made dirs %s' % jn(path) |
|
178 | #print 'made dirs %s' % jn(path) | |
188 | except OSError: |
|
179 | except OSError: | |
189 | raise |
|
180 | raise | |
190 |
|
181 | |||
191 | print '\tchecking if anonymous access is enabled' |
|
182 | print '\tchecking if anonymous access is enabled' | |
192 | anonymous_access = get_anonymous_access() |
|
183 | anonymous_access = get_anonymous_access() | |
193 | if anonymous_access: |
|
184 | if anonymous_access: | |
194 | print '\tenabled, disabling it ' |
|
185 | print '\tenabled, disabling it ' | |
195 | set_anonymous_access(enable=False) |
|
186 | set_anonymous_access(enable=False) | |
196 | time.sleep(1) |
|
187 | time.sleep(1) | |
197 |
|
188 | |||
198 | clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \ |
|
189 | clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \ | |
199 | {'user':USER, |
|
190 | {'user':USER, | |
200 | 'pass':PASS, |
|
191 | 'pass':PASS, | |
201 | 'host':HOST, |
|
192 | 'host':HOST, | |
202 | 'cloned_repo':HG_REPO, |
|
193 | 'cloned_repo':HG_REPO, | |
203 | 'dest':path} |
|
194 | 'dest':path} | |
204 |
|
195 | |||
205 | stdout, stderr = Command(cwd).execute('hg clone', clone_url) |
|
196 | stdout, stderr = Command(cwd).execute('hg clone', clone_url) | |
206 |
|
197 | |||
207 | if no_errors is False: |
|
198 | if no_errors is False: | |
208 | assert """adding file changes""" in stdout, 'no messages about cloning' |
|
199 | assert """adding file changes""" in stdout, 'no messages about cloning' | |
209 | assert """abort""" not in stderr , 'got error from clone' |
|
200 | assert """abort""" not in stderr , 'got error from clone' | |
210 |
|
201 | |||
211 |
|
202 | |||
212 | @test_wrapp |
|
203 | @test_wrapp | |
213 | def test_clone_anonymous(): |
|
204 | def test_clone_anonymous(): | |
214 | cwd = path = jn(TESTS_TMP_PATH, HG_REPO) |
|
205 | cwd = path = jn(TESTS_TMP_PATH, HG_REPO) | |
215 |
|
206 | |||
216 | try: |
|
207 | try: | |
217 | shutil.rmtree(path, ignore_errors=True) |
|
208 | shutil.rmtree(path, ignore_errors=True) | |
218 | os.makedirs(path) |
|
209 | os.makedirs(path) | |
219 | #print 'made dirs %s' % jn(path) |
|
210 | #print 'made dirs %s' % jn(path) | |
220 | except OSError: |
|
211 | except OSError: | |
221 | raise |
|
212 | raise | |
222 |
|
213 | |||
223 |
|
214 | |||
224 | print '\tchecking if anonymous access is enabled' |
|
215 | print '\tchecking if anonymous access is enabled' | |
225 | anonymous_access = get_anonymous_access() |
|
216 | anonymous_access = get_anonymous_access() | |
226 | if not anonymous_access: |
|
217 | if not anonymous_access: | |
227 | print '\tnot enabled, enabling it ' |
|
218 | print '\tnot enabled, enabling it ' | |
228 | set_anonymous_access(enable=True) |
|
219 | set_anonymous_access(enable=True) | |
229 | time.sleep(1) |
|
220 | time.sleep(1) | |
230 |
|
221 | |||
231 | clone_url = 'http://%(host)s/%(cloned_repo)s %(dest)s' % \ |
|
222 | clone_url = 'http://%(host)s/%(cloned_repo)s %(dest)s' % \ | |
232 | {'user':USER, |
|
223 | {'user':USER, | |
233 | 'pass':PASS, |
|
224 | 'pass':PASS, | |
234 | 'host':HOST, |
|
225 | 'host':HOST, | |
235 | 'cloned_repo':HG_REPO, |
|
226 | 'cloned_repo':HG_REPO, | |
236 | 'dest':path} |
|
227 | 'dest':path} | |
237 |
|
228 | |||
238 | stdout, stderr = Command(cwd).execute('hg clone', clone_url) |
|
229 | stdout, stderr = Command(cwd).execute('hg clone', clone_url) | |
239 |
|
230 | |||
240 | assert """adding file changes""" in stdout, 'no messages about cloning' |
|
231 | assert """adding file changes""" in stdout, 'no messages about cloning' | |
241 | assert """abort""" not in stderr , 'got error from clone' |
|
232 | assert """abort""" not in stderr , 'got error from clone' | |
242 |
|
233 | |||
243 | #disable if it was enabled |
|
234 | #disable if it was enabled | |
244 | if not anonymous_access: |
|
235 | if not anonymous_access: | |
245 | print '\tdisabling anonymous access' |
|
236 | print '\tdisabling anonymous access' | |
246 | set_anonymous_access(enable=False) |
|
237 | set_anonymous_access(enable=False) | |
247 |
|
238 | |||
248 | @test_wrapp |
|
239 | @test_wrapp | |
249 | def test_clone_wrong_credentials(): |
|
240 | def test_clone_wrong_credentials(): | |
250 | cwd = path = jn(TESTS_TMP_PATH, HG_REPO) |
|
241 | cwd = path = jn(TESTS_TMP_PATH, HG_REPO) | |
251 |
|
242 | |||
252 | try: |
|
243 | try: | |
253 | shutil.rmtree(path, ignore_errors=True) |
|
244 | shutil.rmtree(path, ignore_errors=True) | |
254 | os.makedirs(path) |
|
245 | os.makedirs(path) | |
255 | #print 'made dirs %s' % jn(path) |
|
246 | #print 'made dirs %s' % jn(path) | |
256 | except OSError: |
|
247 | except OSError: | |
257 | raise |
|
248 | raise | |
258 |
|
249 | |||
259 | print '\tchecking if anonymous access is enabled' |
|
250 | print '\tchecking if anonymous access is enabled' | |
260 | anonymous_access = get_anonymous_access() |
|
251 | anonymous_access = get_anonymous_access() | |
261 | if anonymous_access: |
|
252 | if anonymous_access: | |
262 | print '\tenabled, disabling it ' |
|
253 | print '\tenabled, disabling it ' | |
263 | set_anonymous_access(enable=False) |
|
254 | set_anonymous_access(enable=False) | |
264 |
|
255 | |||
265 | clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \ |
|
256 | clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \ | |
266 | {'user':USER + 'error', |
|
257 | {'user':USER + 'error', | |
267 | 'pass':PASS, |
|
258 | 'pass':PASS, | |
268 | 'host':HOST, |
|
259 | 'host':HOST, | |
269 | 'cloned_repo':HG_REPO, |
|
260 | 'cloned_repo':HG_REPO, | |
270 | 'dest':path} |
|
261 | 'dest':path} | |
271 |
|
262 | |||
272 | stdout, stderr = Command(cwd).execute('hg clone', clone_url) |
|
263 | stdout, stderr = Command(cwd).execute('hg clone', clone_url) | |
273 |
|
264 | |||
274 | if not """abort: authorization failed""" in stderr: |
|
265 | if not """abort: authorization failed""" in stderr: | |
275 | raise Exception('Failure') |
|
266 | raise Exception('Failure') | |
276 |
|
267 | |||
277 | @test_wrapp |
|
268 | @test_wrapp | |
278 | def test_pull(): |
|
269 | def test_pull(): | |
279 | pass |
|
270 | pass | |
280 |
|
271 | |||
281 | @test_wrapp |
|
272 | @test_wrapp | |
282 | def test_push_modify_file(f_name='setup.py'): |
|
273 | def test_push_modify_file(f_name='setup.py'): | |
283 | cwd = path = jn(TESTS_TMP_PATH, HG_REPO) |
|
274 | cwd = path = jn(TESTS_TMP_PATH, HG_REPO) | |
284 | modified_file = jn(TESTS_TMP_PATH, HG_REPO, f_name) |
|
275 | modified_file = jn(TESTS_TMP_PATH, HG_REPO, f_name) | |
285 | for i in xrange(5): |
|
276 | for i in xrange(5): | |
286 | cmd = """echo 'added_line%s' >> %s""" % (i, modified_file) |
|
277 | cmd = """echo 'added_line%s' >> %s""" % (i, modified_file) | |
287 | Command(cwd).execute(cmd) |
|
278 | Command(cwd).execute(cmd) | |
288 |
|
279 | |||
289 | cmd = """hg ci -m 'changed file %s' %s """ % (i, modified_file) |
|
280 | cmd = """hg ci -m 'changed file %s' %s """ % (i, modified_file) | |
290 | Command(cwd).execute(cmd) |
|
281 | Command(cwd).execute(cmd) | |
291 |
|
282 | |||
292 | Command(cwd).execute('hg push %s' % jn(TESTS_TMP_PATH, HG_REPO)) |
|
283 | Command(cwd).execute('hg push %s' % jn(TESTS_TMP_PATH, HG_REPO)) | |
293 |
|
284 | |||
294 | @test_wrapp |
|
285 | @test_wrapp | |
295 | def test_push_new_file(commits=15, with_clone=True): |
|
286 | def test_push_new_file(commits=15, with_clone=True): | |
296 |
|
287 | |||
297 | if with_clone: |
|
288 | if with_clone: | |
298 | test_clone_with_credentials(no_errors=True) |
|
289 | test_clone_with_credentials(no_errors=True) | |
299 |
|
290 | |||
300 | cwd = path = jn(TESTS_TMP_PATH, HG_REPO) |
|
291 | cwd = path = jn(TESTS_TMP_PATH, HG_REPO) | |
301 | added_file = jn(path, '%ssetupążźć.py' % _RandomNameSequence().next()) |
|
292 | added_file = jn(path, '%ssetupążźć.py' % _RandomNameSequence().next()) | |
302 |
|
293 | |||
303 | Command(cwd).execute('touch %s' % added_file) |
|
294 | Command(cwd).execute('touch %s' % added_file) | |
304 |
|
295 | |||
305 | Command(cwd).execute('hg add %s' % added_file) |
|
296 | Command(cwd).execute('hg add %s' % added_file) | |
306 |
|
297 | |||
307 | for i in xrange(commits): |
|
298 | for i in xrange(commits): | |
308 | cmd = """echo 'added_line%s' >> %s""" % (i, added_file) |
|
299 | cmd = """echo 'added_line%s' >> %s""" % (i, added_file) | |
309 | Command(cwd).execute(cmd) |
|
300 | Command(cwd).execute(cmd) | |
310 |
|
301 | |||
311 | cmd = """hg ci -m 'commited new %s' -u '%s' %s """ % (i, |
|
302 | cmd = """hg ci -m 'commited new %s' -u '%s' %s """ % (i, | |
312 | 'Marcin Kuźminski <marcin@python-blog.com>', |
|
303 | 'Marcin Kuźminski <marcin@python-blog.com>', | |
313 | added_file) |
|
304 | added_file) | |
314 | Command(cwd).execute(cmd) |
|
305 | Command(cwd).execute(cmd) | |
315 |
|
306 | |||
316 | push_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \ |
|
307 | push_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \ | |
317 | {'user':USER, |
|
308 | {'user':USER, | |
318 | 'pass':PASS, |
|
309 | 'pass':PASS, | |
319 | 'host':HOST, |
|
310 | 'host':HOST, | |
320 | 'cloned_repo':HG_REPO, |
|
311 | 'cloned_repo':HG_REPO, | |
321 | 'dest':jn(TESTS_TMP_PATH, HG_REPO)} |
|
312 | 'dest':jn(TESTS_TMP_PATH, HG_REPO)} | |
322 |
|
313 | |||
323 | Command(cwd).execute('hg push --verbose --debug %s' % push_url) |
|
314 | Command(cwd).execute('hg push --verbose --debug %s' % push_url) | |
324 |
|
315 | |||
325 | @test_wrapp |
|
316 | @test_wrapp | |
326 | def test_push_wrong_credentials(): |
|
317 | def test_push_wrong_credentials(): | |
327 | cwd = path = jn(TESTS_TMP_PATH, HG_REPO) |
|
318 | cwd = path = jn(TESTS_TMP_PATH, HG_REPO) | |
328 | clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \ |
|
319 | clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \ | |
329 | {'user':USER + 'xxx', |
|
320 | {'user':USER + 'xxx', | |
330 | 'pass':PASS, |
|
321 | 'pass':PASS, | |
331 | 'host':HOST, |
|
322 | 'host':HOST, | |
332 | 'cloned_repo':HG_REPO, |
|
323 | 'cloned_repo':HG_REPO, | |
333 | 'dest':jn(TESTS_TMP_PATH, HG_REPO)} |
|
324 | 'dest':jn(TESTS_TMP_PATH, HG_REPO)} | |
334 |
|
325 | |||
335 | modified_file = jn(TESTS_TMP_PATH, HG_REPO, 'setup.py') |
|
326 | modified_file = jn(TESTS_TMP_PATH, HG_REPO, 'setup.py') | |
336 | for i in xrange(5): |
|
327 | for i in xrange(5): | |
337 | cmd = """echo 'added_line%s' >> %s""" % (i, modified_file) |
|
328 | cmd = """echo 'added_line%s' >> %s""" % (i, modified_file) | |
338 | Command(cwd).execute(cmd) |
|
329 | Command(cwd).execute(cmd) | |
339 |
|
330 | |||
340 | cmd = """hg ci -m 'commited %s' %s """ % (i, modified_file) |
|
331 | cmd = """hg ci -m 'commited %s' %s """ % (i, modified_file) | |
341 | Command(cwd).execute(cmd) |
|
332 | Command(cwd).execute(cmd) | |
342 |
|
333 | |||
343 | Command(cwd).execute('hg push %s' % clone_url) |
|
334 | Command(cwd).execute('hg push %s' % clone_url) | |
344 |
|
335 | |||
345 | @test_wrapp |
|
336 | @test_wrapp | |
346 | def test_push_wrong_path(): |
|
337 | def test_push_wrong_path(): | |
347 | cwd = path = jn(TESTS_TMP_PATH, HG_REPO) |
|
338 | cwd = path = jn(TESTS_TMP_PATH, HG_REPO) | |
348 | added_file = jn(path, 'somefile.py') |
|
339 | added_file = jn(path, 'somefile.py') | |
349 |
|
340 | |||
350 | try: |
|
341 | try: | |
351 | shutil.rmtree(path, ignore_errors=True) |
|
342 | shutil.rmtree(path, ignore_errors=True) | |
352 | os.makedirs(path) |
|
343 | os.makedirs(path) | |
353 | print '\tmade dirs %s' % jn(path) |
|
344 | print '\tmade dirs %s' % jn(path) | |
354 | except OSError: |
|
345 | except OSError: | |
355 | raise |
|
346 | raise | |
356 |
|
347 | |||
357 | Command(cwd).execute("""echo '' > %s""" % added_file) |
|
348 | Command(cwd).execute("""echo '' > %s""" % added_file) | |
358 | Command(cwd).execute("""hg init %s""" % path) |
|
349 | Command(cwd).execute("""hg init %s""" % path) | |
359 | Command(cwd).execute("""hg add %s""" % added_file) |
|
350 | Command(cwd).execute("""hg add %s""" % added_file) | |
360 |
|
351 | |||
361 | for i in xrange(2): |
|
352 | for i in xrange(2): | |
362 | cmd = """echo 'added_line%s' >> %s""" % (i, added_file) |
|
353 | cmd = """echo 'added_line%s' >> %s""" % (i, added_file) | |
363 | Command(cwd).execute(cmd) |
|
354 | Command(cwd).execute(cmd) | |
364 |
|
355 | |||
365 | cmd = """hg ci -m 'commited new %s' %s """ % (i, added_file) |
|
356 | cmd = """hg ci -m 'commited new %s' %s """ % (i, added_file) | |
366 | Command(cwd).execute(cmd) |
|
357 | Command(cwd).execute(cmd) | |
367 |
|
358 | |||
368 | clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \ |
|
359 | clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \ | |
369 | {'user':USER, |
|
360 | {'user':USER, | |
370 | 'pass':PASS, |
|
361 | 'pass':PASS, | |
371 | 'host':HOST, |
|
362 | 'host':HOST, | |
372 | 'cloned_repo':HG_REPO + '_error', |
|
363 | 'cloned_repo':HG_REPO + '_error', | |
373 | 'dest':jn(TESTS_TMP_PATH, HG_REPO)} |
|
364 | 'dest':jn(TESTS_TMP_PATH, HG_REPO)} | |
374 |
|
365 | |||
375 | stdout, stderr = Command(cwd).execute('hg push %s' % clone_url) |
|
366 | stdout, stderr = Command(cwd).execute('hg push %s' % clone_url) | |
376 | if not """abort: HTTP Error 403: Forbidden""" in stderr: |
|
367 | if not """abort: HTTP Error 403: Forbidden""" in stderr: | |
377 | raise Exception('Failure') |
|
368 | raise Exception('Failure') | |
378 |
|
369 | |||
379 | @test_wrapp |
|
370 | @test_wrapp | |
380 | def get_logs(): |
|
371 | def get_logs(): | |
381 | sa = get_session() |
|
372 | return UserLog.query().all() | |
382 | return len(sa.query(UserLog).all()) |
|
|||
383 |
|
373 | |||
384 | @test_wrapp |
|
374 | @test_wrapp | |
385 | def test_logs(initial): |
|
375 | def test_logs(initial): | |
386 | sa = get_session() |
|
376 | logs = UserLog.query().all() | |
387 | logs = sa.query(UserLog).all() |
|
377 | operations = 4 | |
388 | operations = 7 |
|
378 | if len(initial) + operations != len(logs): | |
389 | if initial + operations != len(logs): |
|
379 | raise Exception("missing number of logs initial:%s vs current:%s" % \ | |
390 | raise Exception("missing number of logs %s vs %s" % (initial, len(logs))) |
|
380 | (len(initial), len(logs))) | |
391 |
|
381 | |||
392 |
|
382 | |||
393 | if __name__ == '__main__': |
|
383 | if __name__ == '__main__': | |
394 | create_test_user(force=False) |
|
384 | create_test_user(force=False) | |
395 | create_test_repo() |
|
385 | create_test_repo() | |
396 |
|
386 | |||
397 | initial_logs = get_logs() |
|
387 | initial_logs = get_logs() | |
|
388 | print 'initial activity logs: %s' % len(initial_logs) | |||
398 |
|
389 | |||
399 |
|
|
390 | #test_push_modify_file() | |
400 | test_clone_with_credentials() |
|
391 | test_clone_with_credentials() | |
401 | test_clone_wrong_credentials() |
|
392 | test_clone_wrong_credentials() | |
402 |
|
393 | |||
403 |
|
||||
404 | test_push_new_file(commits=2, with_clone=True) |
|
394 | test_push_new_file(commits=2, with_clone=True) | |
405 |
|
395 | |||
406 | test_clone_anonymous() |
|
396 | test_clone_anonymous() | |
407 | test_push_wrong_path() |
|
397 | test_push_wrong_path() | |
408 |
|
398 | |||
409 |
|
||||
410 | test_push_wrong_credentials() |
|
399 | test_push_wrong_credentials() | |
411 |
|
400 | |||
412 | test_logs(initial_logs) |
|
401 | test_logs(initial_logs) |
General Comments 0
You need to be logged in to leave comments.
Login now