##// END OF EJS Templates
Change git & hg hooks to post. They shouldn't block as they are used just for logging actions. Futhermore post hooks have access to changesets, so it's much better flexible
marcink -
r2407:8a68e029 beta
parent child Browse files
Show More
@@ -1,257 +1,257 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.hooks
3 rhodecode.lib.hooks
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Hooks runned by rhodecode
6 Hooks runned by rhodecode
7
7
8 :created_on: Aug 6, 2010
8 :created_on: Aug 6, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 import os
25 import os
26 import sys
26 import sys
27 import binascii
27 import binascii
28 from inspect import isfunction
28 from inspect import isfunction
29
29
30 from mercurial.scmutil import revrange
30 from mercurial.scmutil import revrange
31 from mercurial.node import nullrev
31 from mercurial.node import nullrev
32
32
33 from rhodecode.lib import helpers as h
33 from rhodecode.lib import helpers as h
34 from rhodecode.lib.utils import action_logger
34 from rhodecode.lib.utils import action_logger
35 from rhodecode.lib.vcs.backends.base import EmptyChangeset
35 from rhodecode.lib.vcs.backends.base import EmptyChangeset
36
36
37
37
38 def _get_scm_size(alias, root_path):
38 def _get_scm_size(alias, root_path):
39
39
40 if not alias.startswith('.'):
40 if not alias.startswith('.'):
41 alias += '.'
41 alias += '.'
42
42
43 size_scm, size_root = 0, 0
43 size_scm, size_root = 0, 0
44 for path, dirs, files in os.walk(root_path):
44 for path, dirs, files in os.walk(root_path):
45 if path.find(alias) != -1:
45 if path.find(alias) != -1:
46 for f in files:
46 for f in files:
47 try:
47 try:
48 size_scm += os.path.getsize(os.path.join(path, f))
48 size_scm += os.path.getsize(os.path.join(path, f))
49 except OSError:
49 except OSError:
50 pass
50 pass
51 else:
51 else:
52 for f in files:
52 for f in files:
53 try:
53 try:
54 size_root += os.path.getsize(os.path.join(path, f))
54 size_root += os.path.getsize(os.path.join(path, f))
55 except OSError:
55 except OSError:
56 pass
56 pass
57
57
58 size_scm_f = h.format_byte_size(size_scm)
58 size_scm_f = h.format_byte_size(size_scm)
59 size_root_f = h.format_byte_size(size_root)
59 size_root_f = h.format_byte_size(size_root)
60 size_total_f = h.format_byte_size(size_root + size_scm)
60 size_total_f = h.format_byte_size(size_root + size_scm)
61
61
62 return size_scm_f, size_root_f, size_total_f
62 return size_scm_f, size_root_f, size_total_f
63
63
64
64
65 def repo_size(ui, repo, hooktype=None, **kwargs):
65 def repo_size(ui, repo, hooktype=None, **kwargs):
66 """
66 """
67 Presents size of repository after push
67 Presents size of repository after push
68
68
69 :param ui:
69 :param ui:
70 :param repo:
70 :param repo:
71 :param hooktype:
71 :param hooktype:
72 """
72 """
73
73
74 size_hg_f, size_root_f, size_total_f = _get_scm_size('.hg', repo.root)
74 size_hg_f, size_root_f, size_total_f = _get_scm_size('.hg', repo.root)
75
75
76 last_cs = repo[len(repo) - 1]
76 last_cs = repo[len(repo) - 1]
77
77
78 msg = ('Repository size .hg:%s repo:%s total:%s\n'
78 msg = ('Repository size .hg:%s repo:%s total:%s\n'
79 'Last revision is now r%s:%s\n') % (
79 'Last revision is now r%s:%s\n') % (
80 size_hg_f, size_root_f, size_total_f, last_cs.rev(), last_cs.hex()[:12]
80 size_hg_f, size_root_f, size_total_f, last_cs.rev(), last_cs.hex()[:12]
81 )
81 )
82
82
83 sys.stdout.write(msg)
83 sys.stdout.write(msg)
84
84
85
85
86 def log_pull_action(ui, repo, **kwargs):
86 def log_pull_action(ui, repo, **kwargs):
87 """
87 """
88 Logs user last pull action
88 Logs user last pull action
89
89
90 :param ui:
90 :param ui:
91 :param repo:
91 :param repo:
92 """
92 """
93
93
94 extras = dict(repo.ui.configitems('rhodecode_extras'))
94 extras = dict(repo.ui.configitems('rhodecode_extras'))
95 username = extras['username']
95 username = extras['username']
96 repository = extras['repository']
96 repository = extras['repository']
97 scm = extras['scm']
97 scm = extras['scm']
98 action = 'pull'
98 action = 'pull'
99
99
100 action_logger(username, action, repository, extras['ip'], commit=True)
100 action_logger(username, action, repository, extras['ip'], commit=True)
101 # extension hook call
101 # extension hook call
102 from rhodecode import EXTENSIONS
102 from rhodecode import EXTENSIONS
103 callback = getattr(EXTENSIONS, 'PULL_HOOK', None)
103 callback = getattr(EXTENSIONS, 'PULL_HOOK', None)
104
104
105 if isfunction(callback):
105 if isfunction(callback):
106 kw = {}
106 kw = {}
107 kw.update(extras)
107 kw.update(extras)
108 callback(**kw)
108 callback(**kw)
109 return 0
109 return 0
110
110
111
111
112 def log_push_action(ui, repo, **kwargs):
112 def log_push_action(ui, repo, **kwargs):
113 """
113 """
114 Maps user last push action to new changeset id, from mercurial
114 Maps user last push action to new changeset id, from mercurial
115
115
116 :param ui:
116 :param ui:
117 :param repo: repo object containing the `ui` object
117 :param repo: repo object containing the `ui` object
118 """
118 """
119
119
120 extras = dict(repo.ui.configitems('rhodecode_extras'))
120 extras = dict(repo.ui.configitems('rhodecode_extras'))
121 username = extras['username']
121 username = extras['username']
122 repository = extras['repository']
122 repository = extras['repository']
123 action = extras['action'] + ':%s'
123 action = extras['action'] + ':%s'
124 scm = extras['scm']
124 scm = extras['scm']
125
125
126 if scm == 'hg':
126 if scm == 'hg':
127 node = kwargs['node']
127 node = kwargs['node']
128
128
129 def get_revs(repo, rev_opt):
129 def get_revs(repo, rev_opt):
130 if rev_opt:
130 if rev_opt:
131 revs = revrange(repo, rev_opt)
131 revs = revrange(repo, rev_opt)
132
132
133 if len(revs) == 0:
133 if len(revs) == 0:
134 return (nullrev, nullrev)
134 return (nullrev, nullrev)
135 return (max(revs), min(revs))
135 return (max(revs), min(revs))
136 else:
136 else:
137 return (len(repo) - 1, 0)
137 return (len(repo) - 1, 0)
138
138
139 stop, start = get_revs(repo, [node + ':'])
139 stop, start = get_revs(repo, [node + ':'])
140 h = binascii.hexlify
140 h = binascii.hexlify
141 revs = (h(repo[r].node()) for r in xrange(start, stop + 1))
141 revs = [h(repo[r].node()) for r in xrange(start, stop + 1)]
142 elif scm == 'git':
142 elif scm == 'git':
143 revs = kwargs.get('_git_revs', [])
143 revs = kwargs.get('_git_revs', [])
144 if '_git_revs' in kwargs:
144 if '_git_revs' in kwargs:
145 kwargs.pop('_git_revs')
145 kwargs.pop('_git_revs')
146
146
147 action = action % ','.join(revs)
147 action = action % ','.join(revs)
148
148
149 action_logger(username, action, repository, extras['ip'], commit=True)
149 action_logger(username, action, repository, extras['ip'], commit=True)
150
150
151 # extension hook call
151 # extension hook call
152 from rhodecode import EXTENSIONS
152 from rhodecode import EXTENSIONS
153 callback = getattr(EXTENSIONS, 'PUSH_HOOK', None)
153 callback = getattr(EXTENSIONS, 'PUSH_HOOK', None)
154 if isfunction(callback):
154 if isfunction(callback):
155 kw = {'pushed_revs': revs}
155 kw = {'pushed_revs': revs}
156 kw.update(extras)
156 kw.update(extras)
157 callback(**kw)
157 callback(**kw)
158 return 0
158 return 0
159
159
160
160
161 def log_create_repository(repository_dict, created_by, **kwargs):
161 def log_create_repository(repository_dict, created_by, **kwargs):
162 """
162 """
163 Post create repository Hook. This is a dummy function for admins to re-use
163 Post create repository Hook. This is a dummy function for admins to re-use
164 if needed. It's taken from rhodecode-extensions module and executed
164 if needed. It's taken from rhodecode-extensions module and executed
165 if present
165 if present
166
166
167 :param repository: dict dump of repository object
167 :param repository: dict dump of repository object
168 :param created_by: username who created repository
168 :param created_by: username who created repository
169 :param created_date: date of creation
169 :param created_date: date of creation
170
170
171 available keys of repository_dict:
171 available keys of repository_dict:
172
172
173 'repo_type',
173 'repo_type',
174 'description',
174 'description',
175 'private',
175 'private',
176 'created_on',
176 'created_on',
177 'enable_downloads',
177 'enable_downloads',
178 'repo_id',
178 'repo_id',
179 'user_id',
179 'user_id',
180 'enable_statistics',
180 'enable_statistics',
181 'clone_uri',
181 'clone_uri',
182 'fork_id',
182 'fork_id',
183 'group_id',
183 'group_id',
184 'repo_name'
184 'repo_name'
185
185
186 """
186 """
187 from rhodecode import EXTENSIONS
187 from rhodecode import EXTENSIONS
188 callback = getattr(EXTENSIONS, 'CREATE_REPO_HOOK', None)
188 callback = getattr(EXTENSIONS, 'CREATE_REPO_HOOK', None)
189 if isfunction(callback):
189 if isfunction(callback):
190 kw = {}
190 kw = {}
191 kw.update(repository_dict)
191 kw.update(repository_dict)
192 kw.update({'created_by': created_by})
192 kw.update({'created_by': created_by})
193 kw.update(kwargs)
193 kw.update(kwargs)
194 return callback(**kw)
194 return callback(**kw)
195
195
196 return 0
196 return 0
197
197
198
198
199 def handle_git_post_receive(repo_path, revs, env):
199 def handle_git_post_receive(repo_path, revs, env):
200 """
200 """
201 A really hacky method that is runned by git pre-receive hook and logs
201 A really hacky method that is runned by git post-receive hook and logs
202 an push action together with pushed revisions. It's runned by subprocess
202 an push action together with pushed revisions. It's runned by subprocess
203 thus needs all info to be able to create a temp pylons enviroment, connect
203 thus needs all info to be able to create a temp pylons enviroment, connect
204 to database and run the logging code. Hacky as sh**t but works. ps.
204 to database and run the logging code. Hacky as sh**t but works. ps.
205 GIT SUCKS
205 GIT SUCKS
206
206
207 :param repo_path:
207 :param repo_path:
208 :type repo_path:
208 :type repo_path:
209 :param revs:
209 :param revs:
210 :type revs:
210 :type revs:
211 :param env:
211 :param env:
212 :type env:
212 :type env:
213 """
213 """
214 from paste.deploy import appconfig
214 from paste.deploy import appconfig
215 from sqlalchemy import engine_from_config
215 from sqlalchemy import engine_from_config
216 from rhodecode.config.environment import load_environment
216 from rhodecode.config.environment import load_environment
217 from rhodecode.model import init_model
217 from rhodecode.model import init_model
218 from rhodecode.model.db import RhodeCodeUi
218 from rhodecode.model.db import RhodeCodeUi
219 from rhodecode.lib.utils import make_ui
219 from rhodecode.lib.utils import make_ui
220 from rhodecode.model.db import Repository
220 from rhodecode.model.db import Repository
221
221
222 path, ini_name = os.path.split(env['RHODECODE_CONFIG_FILE'])
222 path, ini_name = os.path.split(env['RHODECODE_CONFIG_FILE'])
223 conf = appconfig('config:%s' % ini_name, relative_to=path)
223 conf = appconfig('config:%s' % ini_name, relative_to=path)
224 load_environment(conf.global_conf, conf.local_conf)
224 load_environment(conf.global_conf, conf.local_conf)
225
225
226 engine = engine_from_config(conf, 'sqlalchemy.db1.')
226 engine = engine_from_config(conf, 'sqlalchemy.db1.')
227 init_model(engine)
227 init_model(engine)
228
228
229 baseui = make_ui('db')
229 baseui = make_ui('db')
230 repo = Repository.get_by_full_path(repo_path)
230 repo = Repository.get_by_full_path(repo_path)
231
231
232 _hooks = dict(baseui.configitems('hooks')) or {}
232 _hooks = dict(baseui.configitems('hooks')) or {}
233 # if push hook is enabled via web interface
233 # if push hook is enabled via web interface
234 if _hooks.get(RhodeCodeUi.HOOK_PUSH):
234 if _hooks.get(RhodeCodeUi.HOOK_PUSH):
235
235
236 extras = {
236 extras = {
237 'username': env['RHODECODE_USER'],
237 'username': env['RHODECODE_USER'],
238 'repository': repo.repo_name,
238 'repository': repo.repo_name,
239 'scm': 'git',
239 'scm': 'git',
240 'action': 'push',
240 'action': 'push',
241 'ip': env['RHODECODE_CONFIG_IP'],
241 'ip': env['RHODECODE_CONFIG_IP'],
242 }
242 }
243 for k, v in extras.items():
243 for k, v in extras.items():
244 baseui.setconfig('rhodecode_extras', k, v)
244 baseui.setconfig('rhodecode_extras', k, v)
245 repo = repo.scm_instance
245 repo = repo.scm_instance
246 repo.ui = baseui
246 repo.ui = baseui
247 old_rev, new_rev, ref = revs
247 old_rev, new_rev, ref = revs
248 if old_rev == EmptyChangeset().raw_id:
248 if old_rev == EmptyChangeset().raw_id:
249 cmd = "for-each-ref --format='%(refname)' 'refs/heads/*'"
249 cmd = "for-each-ref --format='%(refname)' 'refs/heads/*'"
250 heads = repo.run_git_command(cmd)[0]
250 heads = repo.run_git_command(cmd)[0]
251 heads = heads.replace(ref, '')
251 heads = heads.replace(ref, '')
252 cmd = 'log ' + new_rev + ' --reverse --pretty=format:"%H" --not ' + heads
252 cmd = 'log ' + new_rev + ' --reverse --pretty=format:"%H" --not ' + heads
253 else:
253 else:
254 cmd = 'log ' + old_rev + '..' + new_rev + ' --reverse --pretty=format:"%H"'
254 cmd = 'log ' + old_rev + '..' + new_rev + ' --reverse --pretty=format:"%H"'
255 git_revs = repo.run_git_command(cmd)[0].splitlines()
255 git_revs = repo.run_git_command(cmd)[0].splitlines()
256
256
257 log_push_action(baseui, repo, _git_revs=git_revs)
257 log_push_action(baseui, repo, _git_revs=git_revs)
@@ -1,304 +1,304 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) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2010-2012 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 re
28 import re
29 import logging
29 import logging
30 import traceback
30 import traceback
31
31
32 from dulwich import server as dulserver
32 from dulwich import server as dulserver
33
33
34
34
35 class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
35 class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
36
36
37 def handle(self):
37 def handle(self):
38 write = lambda x: self.proto.write_sideband(1, x)
38 write = lambda x: self.proto.write_sideband(1, x)
39
39
40 graph_walker = dulserver.ProtocolGraphWalker(self,
40 graph_walker = dulserver.ProtocolGraphWalker(self,
41 self.repo.object_store,
41 self.repo.object_store,
42 self.repo.get_peeled)
42 self.repo.get_peeled)
43 objects_iter = self.repo.fetch_objects(
43 objects_iter = self.repo.fetch_objects(
44 graph_walker.determine_wants, graph_walker, self.progress,
44 graph_walker.determine_wants, graph_walker, self.progress,
45 get_tagged=self.get_tagged)
45 get_tagged=self.get_tagged)
46
46
47 # Did the process short-circuit (e.g. in a stateless RPC call)? Note
47 # Did the process short-circuit (e.g. in a stateless RPC call)? Note
48 # that the client still expects a 0-object pack in most cases.
48 # that the client still expects a 0-object pack in most cases.
49 if objects_iter is None:
49 if objects_iter is None:
50 return
50 return
51
51
52 self.progress("counting objects: %d, done.\n" % len(objects_iter))
52 self.progress("counting objects: %d, done.\n" % len(objects_iter))
53 dulserver.write_pack_objects(dulserver.ProtocolFile(None, write),
53 dulserver.write_pack_objects(dulserver.ProtocolFile(None, write),
54 objects_iter)
54 objects_iter)
55 messages = []
55 messages = []
56 messages.append('thank you for using rhodecode')
56 messages.append('thank you for using rhodecode')
57
57
58 for msg in messages:
58 for msg in messages:
59 self.progress(msg + "\n")
59 self.progress(msg + "\n")
60 # we are done
60 # we are done
61 self.proto.write("0000")
61 self.proto.write("0000")
62
62
63
63
64 dulserver.DEFAULT_HANDLERS = {
64 dulserver.DEFAULT_HANDLERS = {
65 #git-ls-remote, git-clone, git-fetch and git-pull
65 #git-ls-remote, git-clone, git-fetch and git-pull
66 'git-upload-pack': SimpleGitUploadPackHandler,
66 'git-upload-pack': SimpleGitUploadPackHandler,
67 #git-push
67 #git-push
68 'git-receive-pack': dulserver.ReceivePackHandler,
68 'git-receive-pack': dulserver.ReceivePackHandler,
69 }
69 }
70
70
71 # not used for now until dulwich get's fixed
71 # not used for now until dulwich get's fixed
72 #from dulwich.repo import Repo
72 #from dulwich.repo import Repo
73 #from dulwich.web import make_wsgi_chain
73 #from dulwich.web import make_wsgi_chain
74
74
75 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
75 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
76
76
77 from rhodecode.lib.utils2 import safe_str
77 from rhodecode.lib.utils2 import safe_str
78 from rhodecode.lib.base import BaseVCSController
78 from rhodecode.lib.base import BaseVCSController
79 from rhodecode.lib.auth import get_container_username
79 from rhodecode.lib.auth import get_container_username
80 from rhodecode.lib.utils import is_valid_repo, make_ui
80 from rhodecode.lib.utils import is_valid_repo, make_ui
81 from rhodecode.model.db import User, RhodeCodeUi
81 from rhodecode.model.db import User, RhodeCodeUi
82
82
83 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
83 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
84
84
85 log = logging.getLogger(__name__)
85 log = logging.getLogger(__name__)
86
86
87
87
88 GIT_PROTO_PAT = re.compile(r'^/(.+)/(info/refs|git-upload-pack|git-receive-pack)')
88 GIT_PROTO_PAT = re.compile(r'^/(.+)/(info/refs|git-upload-pack|git-receive-pack)')
89
89
90
90
91 def is_git(environ):
91 def is_git(environ):
92 path_info = environ['PATH_INFO']
92 path_info = environ['PATH_INFO']
93 isgit_path = GIT_PROTO_PAT.match(path_info)
93 isgit_path = GIT_PROTO_PAT.match(path_info)
94 log.debug('pathinfo: %s detected as GIT %s' % (
94 log.debug('pathinfo: %s detected as GIT %s' % (
95 path_info, isgit_path != None)
95 path_info, isgit_path != None)
96 )
96 )
97 return isgit_path
97 return isgit_path
98
98
99
99
100 class SimpleGit(BaseVCSController):
100 class SimpleGit(BaseVCSController):
101
101
102 def _handle_request(self, environ, start_response):
102 def _handle_request(self, environ, start_response):
103
103
104 if not is_git(environ):
104 if not is_git(environ):
105 return self.application(environ, start_response)
105 return self.application(environ, start_response)
106
106
107 ipaddr = self._get_ip_addr(environ)
107 ipaddr = self._get_ip_addr(environ)
108 username = None
108 username = None
109 self._git_first_op = False
109 self._git_first_op = False
110 # skip passing error to error controller
110 # skip passing error to error controller
111 environ['pylons.status_code_redirect'] = True
111 environ['pylons.status_code_redirect'] = True
112
112
113 #======================================================================
113 #======================================================================
114 # EXTRACT REPOSITORY NAME FROM ENV
114 # EXTRACT REPOSITORY NAME FROM ENV
115 #======================================================================
115 #======================================================================
116 try:
116 try:
117 repo_name = self.__get_repository(environ)
117 repo_name = self.__get_repository(environ)
118 log.debug('Extracted repo name is %s' % repo_name)
118 log.debug('Extracted repo name is %s' % repo_name)
119 except:
119 except:
120 return HTTPInternalServerError()(environ, start_response)
120 return HTTPInternalServerError()(environ, start_response)
121
121
122 # quick check if that dir exists...
122 # quick check if that dir exists...
123 if is_valid_repo(repo_name, self.basepath) is False:
123 if is_valid_repo(repo_name, self.basepath) is False:
124 return HTTPNotFound()(environ, start_response)
124 return HTTPNotFound()(environ, start_response)
125
125
126 #======================================================================
126 #======================================================================
127 # GET ACTION PULL or PUSH
127 # GET ACTION PULL or PUSH
128 #======================================================================
128 #======================================================================
129 action = self.__get_action(environ)
129 action = self.__get_action(environ)
130
130
131 #======================================================================
131 #======================================================================
132 # CHECK ANONYMOUS PERMISSION
132 # CHECK ANONYMOUS PERMISSION
133 #======================================================================
133 #======================================================================
134 if action in ['pull', 'push']:
134 if action in ['pull', 'push']:
135 anonymous_user = self.__get_user('default')
135 anonymous_user = self.__get_user('default')
136 username = anonymous_user.username
136 username = anonymous_user.username
137 anonymous_perm = self._check_permission(action, anonymous_user,
137 anonymous_perm = self._check_permission(action, anonymous_user,
138 repo_name)
138 repo_name)
139
139
140 if anonymous_perm is not True or anonymous_user.active is False:
140 if anonymous_perm is not True or anonymous_user.active is False:
141 if anonymous_perm is not True:
141 if anonymous_perm is not True:
142 log.debug('Not enough credentials to access this '
142 log.debug('Not enough credentials to access this '
143 'repository as anonymous user')
143 'repository as anonymous user')
144 if anonymous_user.active is False:
144 if anonymous_user.active is False:
145 log.debug('Anonymous access is disabled, running '
145 log.debug('Anonymous access is disabled, running '
146 'authentication')
146 'authentication')
147 #==============================================================
147 #==============================================================
148 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
148 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
149 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
149 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
150 #==============================================================
150 #==============================================================
151
151
152 # Attempting to retrieve username from the container
152 # Attempting to retrieve username from the container
153 username = get_container_username(environ, self.config)
153 username = get_container_username(environ, self.config)
154
154
155 # If not authenticated by the container, running basic auth
155 # If not authenticated by the container, running basic auth
156 if not username:
156 if not username:
157 self.authenticate.realm = \
157 self.authenticate.realm = \
158 safe_str(self.config['rhodecode_realm'])
158 safe_str(self.config['rhodecode_realm'])
159 result = self.authenticate(environ)
159 result = self.authenticate(environ)
160 if isinstance(result, str):
160 if isinstance(result, str):
161 AUTH_TYPE.update(environ, 'basic')
161 AUTH_TYPE.update(environ, 'basic')
162 REMOTE_USER.update(environ, result)
162 REMOTE_USER.update(environ, result)
163 username = result
163 username = result
164 else:
164 else:
165 return result.wsgi_application(environ, start_response)
165 return result.wsgi_application(environ, start_response)
166
166
167 #==============================================================
167 #==============================================================
168 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
168 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
169 #==============================================================
169 #==============================================================
170 if action in ['pull', 'push']:
170 if action in ['pull', 'push']:
171 try:
171 try:
172 user = self.__get_user(username)
172 user = self.__get_user(username)
173 if user is None or not user.active:
173 if user is None or not user.active:
174 return HTTPForbidden()(environ, start_response)
174 return HTTPForbidden()(environ, start_response)
175 username = user.username
175 username = user.username
176 except:
176 except:
177 log.error(traceback.format_exc())
177 log.error(traceback.format_exc())
178 return HTTPInternalServerError()(environ,
178 return HTTPInternalServerError()(environ,
179 start_response)
179 start_response)
180
180
181 #check permissions for this repository
181 #check permissions for this repository
182 perm = self._check_permission(action, user, repo_name)
182 perm = self._check_permission(action, user, repo_name)
183 if perm is not True:
183 if perm is not True:
184 return HTTPForbidden()(environ, start_response)
184 return HTTPForbidden()(environ, start_response)
185 extras = {
185 extras = {
186 'ip': ipaddr,
186 'ip': ipaddr,
187 'username': username,
187 'username': username,
188 'action': action,
188 'action': action,
189 'repository': repo_name,
189 'repository': repo_name,
190 'scm': 'git',
190 'scm': 'git',
191 }
191 }
192
192
193 #===================================================================
193 #===================================================================
194 # GIT REQUEST HANDLING
194 # GIT REQUEST HANDLING
195 #===================================================================
195 #===================================================================
196 repo_path = os.path.join(safe_str(self.basepath), safe_str(repo_name))
196 repo_path = os.path.join(safe_str(self.basepath), safe_str(repo_name))
197 log.debug('Repository path is %s' % repo_path)
197 log.debug('Repository path is %s' % repo_path)
198
198
199 baseui = make_ui('db')
199 baseui = make_ui('db')
200 self.__inject_extras(repo_path, baseui, extras)
200 self.__inject_extras(repo_path, baseui, extras)
201
201
202 try:
202 try:
203 # invalidate cache on push
203 # invalidate cache on push
204 if action == 'push':
204 if action == 'push':
205 self._invalidate_cache(repo_name)
205 self._invalidate_cache(repo_name)
206 self._handle_githooks(repo_name, action, baseui, environ)
206 self._handle_githooks(repo_name, action, baseui, environ)
207
207
208 log.info('%s action on GIT repo "%s"' % (action, repo_name))
208 log.info('%s action on GIT repo "%s"' % (action, repo_name))
209 app = self.__make_app(repo_name, repo_path, username)
209 app = self.__make_app(repo_name, repo_path, username)
210 return app(environ, start_response)
210 return app(environ, start_response)
211 except Exception:
211 except Exception:
212 log.error(traceback.format_exc())
212 log.error(traceback.format_exc())
213 return HTTPInternalServerError()(environ, start_response)
213 return HTTPInternalServerError()(environ, start_response)
214
214
215 def __make_app(self, repo_name, repo_path, username):
215 def __make_app(self, repo_name, repo_path, username):
216 """
216 """
217 Make an wsgi application using dulserver
217 Make an wsgi application using dulserver
218
218
219 :param repo_name: name of the repository
219 :param repo_name: name of the repository
220 :param repo_path: full path to the repository
220 :param repo_path: full path to the repository
221 """
221 """
222
222
223 from rhodecode.lib.middleware.pygrack import make_wsgi_app
223 from rhodecode.lib.middleware.pygrack import make_wsgi_app
224 app = make_wsgi_app(
224 app = make_wsgi_app(
225 repo_root=os.path.dirname(repo_path),
225 repo_root=os.path.dirname(repo_path),
226 repo_name=repo_name,
226 repo_name=repo_name,
227 username=username,
227 username=username,
228 )
228 )
229 return app
229 return app
230
230
231 def __get_repository(self, environ):
231 def __get_repository(self, environ):
232 """
232 """
233 Get's repository name out of PATH_INFO header
233 Get's repository name out of PATH_INFO header
234
234
235 :param environ: environ where PATH_INFO is stored
235 :param environ: environ where PATH_INFO is stored
236 """
236 """
237 try:
237 try:
238 environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
238 environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
239 repo_name = GIT_PROTO_PAT.match(environ['PATH_INFO']).group(1)
239 repo_name = GIT_PROTO_PAT.match(environ['PATH_INFO']).group(1)
240 except:
240 except:
241 log.error(traceback.format_exc())
241 log.error(traceback.format_exc())
242 raise
242 raise
243
243
244 return repo_name
244 return repo_name
245
245
246 def __get_user(self, username):
246 def __get_user(self, username):
247 return User.get_by_username(username)
247 return User.get_by_username(username)
248
248
249 def __get_action(self, environ):
249 def __get_action(self, environ):
250 """
250 """
251 Maps git request commands into a pull or push command.
251 Maps git request commands into a pull or push command.
252
252
253 :param environ:
253 :param environ:
254 """
254 """
255 service = environ['QUERY_STRING'].split('=')
255 service = environ['QUERY_STRING'].split('=')
256
256
257 if len(service) > 1:
257 if len(service) > 1:
258 service_cmd = service[1]
258 service_cmd = service[1]
259 mapping = {
259 mapping = {
260 'git-receive-pack': 'push',
260 'git-receive-pack': 'push',
261 'git-upload-pack': 'pull',
261 'git-upload-pack': 'pull',
262 }
262 }
263 op = mapping[service_cmd]
263 op = mapping[service_cmd]
264 self._git_stored_op = op
264 self._git_stored_op = op
265 return op
265 return op
266 else:
266 else:
267 # try to fallback to stored variable as we don't know if the last
267 # try to fallback to stored variable as we don't know if the last
268 # operation is pull/push
268 # operation is pull/push
269 op = getattr(self, '_git_stored_op', 'pull')
269 op = getattr(self, '_git_stored_op', 'pull')
270 return op
270 return op
271
271
272 def _handle_githooks(self, repo_name, action, baseui, environ):
272 def _handle_githooks(self, repo_name, action, baseui, environ):
273 """
273 """
274 Handles pull action, push is handled by pre-receive hook
274 Handles pull action, push is handled by post-receive hook
275 """
275 """
276 from rhodecode.lib.hooks import log_pull_action
276 from rhodecode.lib.hooks import log_pull_action
277 service = environ['QUERY_STRING'].split('=')
277 service = environ['QUERY_STRING'].split('=')
278 if len(service) < 2:
278 if len(service) < 2:
279 return
279 return
280
280
281 from rhodecode.model.db import Repository
281 from rhodecode.model.db import Repository
282 _repo = Repository.get_by_repo_name(repo_name)
282 _repo = Repository.get_by_repo_name(repo_name)
283 _repo = _repo.scm_instance
283 _repo = _repo.scm_instance
284 _repo._repo.ui = baseui
284 _repo._repo.ui = baseui
285
285
286 _hooks = dict(baseui.configitems('hooks')) or {}
286 _hooks = dict(baseui.configitems('hooks')) or {}
287 if action == 'pull' and _hooks.get(RhodeCodeUi.HOOK_PULL):
287 if action == 'pull' and _hooks.get(RhodeCodeUi.HOOK_PULL):
288 log_pull_action(ui=baseui, repo=_repo._repo)
288 log_pull_action(ui=baseui, repo=_repo._repo)
289
289
290 def __inject_extras(self, repo_path, baseui, extras={}):
290 def __inject_extras(self, repo_path, baseui, extras={}):
291 """
291 """
292 Injects some extra params into baseui instance
292 Injects some extra params into baseui instance
293
293
294 :param baseui: baseui instance
294 :param baseui: baseui instance
295 :param extras: dict with extra params to put into baseui
295 :param extras: dict with extra params to put into baseui
296 """
296 """
297
297
298 # make our hgweb quiet so it doesn't print output
298 # make our hgweb quiet so it doesn't print output
299 baseui.setconfig('ui', 'quiet', 'true')
299 baseui.setconfig('ui', 'quiet', 'true')
300
300
301 #inject some additional parameters that will be available in ui
301 #inject some additional parameters that will be available in ui
302 #for hooks
302 #for hooks
303 for k, v in extras.items():
303 for k, v in extras.items():
304 baseui.setconfig('rhodecode_extras', k, v)
304 baseui.setconfig('rhodecode_extras', k, v)
@@ -1,1348 +1,1348 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.db
3 rhodecode.model.db
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 Database Models for RhodeCode
6 Database Models for RhodeCode
7
7
8 :created_on: Apr 08, 2010
8 :created_on: Apr 08, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import logging
27 import logging
28 import datetime
28 import datetime
29 import traceback
29 import traceback
30 import hashlib
30 import hashlib
31 from collections import defaultdict
31 from collections import defaultdict
32
32
33 from sqlalchemy import *
33 from sqlalchemy import *
34 from sqlalchemy.ext.hybrid import hybrid_property
34 from sqlalchemy.ext.hybrid import hybrid_property
35 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
35 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
36 from beaker.cache import cache_region, region_invalidate
36 from beaker.cache import cache_region, region_invalidate
37
37
38 from rhodecode.lib.vcs import get_backend
38 from rhodecode.lib.vcs import get_backend
39 from rhodecode.lib.vcs.utils.helpers import get_scm
39 from rhodecode.lib.vcs.utils.helpers import get_scm
40 from rhodecode.lib.vcs.exceptions import VCSError
40 from rhodecode.lib.vcs.exceptions import VCSError
41 from rhodecode.lib.vcs.utils.lazy import LazyProperty
41 from rhodecode.lib.vcs.utils.lazy import LazyProperty
42
42
43 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
43 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
44 safe_unicode
44 safe_unicode
45 from rhodecode.lib.compat import json
45 from rhodecode.lib.compat import json
46 from rhodecode.lib.caching_query import FromCache
46 from rhodecode.lib.caching_query import FromCache
47 from rhodecode.model.meta import Base, Session
47 from rhodecode.model.meta import Base, Session
48
48
49
49
50 URL_SEP = '/'
50 URL_SEP = '/'
51 log = logging.getLogger(__name__)
51 log = logging.getLogger(__name__)
52
52
53 #==============================================================================
53 #==============================================================================
54 # BASE CLASSES
54 # BASE CLASSES
55 #==============================================================================
55 #==============================================================================
56
56
57 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
57 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
58
58
59
59
60 class ModelSerializer(json.JSONEncoder):
60 class ModelSerializer(json.JSONEncoder):
61 """
61 """
62 Simple Serializer for JSON,
62 Simple Serializer for JSON,
63
63
64 usage::
64 usage::
65
65
66 to make object customized for serialization implement a __json__
66 to make object customized for serialization implement a __json__
67 method that will return a dict for serialization into json
67 method that will return a dict for serialization into json
68
68
69 example::
69 example::
70
70
71 class Task(object):
71 class Task(object):
72
72
73 def __init__(self, name, value):
73 def __init__(self, name, value):
74 self.name = name
74 self.name = name
75 self.value = value
75 self.value = value
76
76
77 def __json__(self):
77 def __json__(self):
78 return dict(name=self.name,
78 return dict(name=self.name,
79 value=self.value)
79 value=self.value)
80
80
81 """
81 """
82
82
83 def default(self, obj):
83 def default(self, obj):
84
84
85 if hasattr(obj, '__json__'):
85 if hasattr(obj, '__json__'):
86 return obj.__json__()
86 return obj.__json__()
87 else:
87 else:
88 return json.JSONEncoder.default(self, obj)
88 return json.JSONEncoder.default(self, obj)
89
89
90
90
91 class BaseModel(object):
91 class BaseModel(object):
92 """
92 """
93 Base Model for all classess
93 Base Model for all classess
94 """
94 """
95
95
96 @classmethod
96 @classmethod
97 def _get_keys(cls):
97 def _get_keys(cls):
98 """return column names for this model """
98 """return column names for this model """
99 return class_mapper(cls).c.keys()
99 return class_mapper(cls).c.keys()
100
100
101 def get_dict(self):
101 def get_dict(self):
102 """
102 """
103 return dict with keys and values corresponding
103 return dict with keys and values corresponding
104 to this model data """
104 to this model data """
105
105
106 d = {}
106 d = {}
107 for k in self._get_keys():
107 for k in self._get_keys():
108 d[k] = getattr(self, k)
108 d[k] = getattr(self, k)
109
109
110 # also use __json__() if present to get additional fields
110 # also use __json__() if present to get additional fields
111 for k, val in getattr(self, '__json__', lambda: {})().iteritems():
111 for k, val in getattr(self, '__json__', lambda: {})().iteritems():
112 d[k] = val
112 d[k] = val
113 return d
113 return d
114
114
115 def get_appstruct(self):
115 def get_appstruct(self):
116 """return list with keys and values tupples corresponding
116 """return list with keys and values tupples corresponding
117 to this model data """
117 to this model data """
118
118
119 l = []
119 l = []
120 for k in self._get_keys():
120 for k in self._get_keys():
121 l.append((k, getattr(self, k),))
121 l.append((k, getattr(self, k),))
122 return l
122 return l
123
123
124 def populate_obj(self, populate_dict):
124 def populate_obj(self, populate_dict):
125 """populate model with data from given populate_dict"""
125 """populate model with data from given populate_dict"""
126
126
127 for k in self._get_keys():
127 for k in self._get_keys():
128 if k in populate_dict:
128 if k in populate_dict:
129 setattr(self, k, populate_dict[k])
129 setattr(self, k, populate_dict[k])
130
130
131 @classmethod
131 @classmethod
132 def query(cls):
132 def query(cls):
133 return Session.query(cls)
133 return Session.query(cls)
134
134
135 @classmethod
135 @classmethod
136 def get(cls, id_):
136 def get(cls, id_):
137 if id_:
137 if id_:
138 return cls.query().get(id_)
138 return cls.query().get(id_)
139
139
140 @classmethod
140 @classmethod
141 def getAll(cls):
141 def getAll(cls):
142 return cls.query().all()
142 return cls.query().all()
143
143
144 @classmethod
144 @classmethod
145 def delete(cls, id_):
145 def delete(cls, id_):
146 obj = cls.query().get(id_)
146 obj = cls.query().get(id_)
147 Session.delete(obj)
147 Session.delete(obj)
148
148
149 def __repr__(self):
149 def __repr__(self):
150 if hasattr(self, '__unicode__'):
150 if hasattr(self, '__unicode__'):
151 # python repr needs to return str
151 # python repr needs to return str
152 return safe_str(self.__unicode__())
152 return safe_str(self.__unicode__())
153 return '<DB:%s>' % (self.__class__.__name__)
153 return '<DB:%s>' % (self.__class__.__name__)
154
154
155
155
156 class RhodeCodeSetting(Base, BaseModel):
156 class RhodeCodeSetting(Base, BaseModel):
157 __tablename__ = 'rhodecode_settings'
157 __tablename__ = 'rhodecode_settings'
158 __table_args__ = (
158 __table_args__ = (
159 UniqueConstraint('app_settings_name'),
159 UniqueConstraint('app_settings_name'),
160 {'extend_existing': True, 'mysql_engine': 'InnoDB',
160 {'extend_existing': True, 'mysql_engine': 'InnoDB',
161 'mysql_charset': 'utf8'}
161 'mysql_charset': 'utf8'}
162 )
162 )
163 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
163 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
164 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
164 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
165 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
165 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
166
166
167 def __init__(self, k='', v=''):
167 def __init__(self, k='', v=''):
168 self.app_settings_name = k
168 self.app_settings_name = k
169 self.app_settings_value = v
169 self.app_settings_value = v
170
170
171 @validates('_app_settings_value')
171 @validates('_app_settings_value')
172 def validate_settings_value(self, key, val):
172 def validate_settings_value(self, key, val):
173 assert type(val) == unicode
173 assert type(val) == unicode
174 return val
174 return val
175
175
176 @hybrid_property
176 @hybrid_property
177 def app_settings_value(self):
177 def app_settings_value(self):
178 v = self._app_settings_value
178 v = self._app_settings_value
179 if self.app_settings_name == 'ldap_active':
179 if self.app_settings_name == 'ldap_active':
180 v = str2bool(v)
180 v = str2bool(v)
181 return v
181 return v
182
182
183 @app_settings_value.setter
183 @app_settings_value.setter
184 def app_settings_value(self, val):
184 def app_settings_value(self, val):
185 """
185 """
186 Setter that will always make sure we use unicode in app_settings_value
186 Setter that will always make sure we use unicode in app_settings_value
187
187
188 :param val:
188 :param val:
189 """
189 """
190 self._app_settings_value = safe_unicode(val)
190 self._app_settings_value = safe_unicode(val)
191
191
192 def __unicode__(self):
192 def __unicode__(self):
193 return u"<%s('%s:%s')>" % (
193 return u"<%s('%s:%s')>" % (
194 self.__class__.__name__,
194 self.__class__.__name__,
195 self.app_settings_name, self.app_settings_value
195 self.app_settings_name, self.app_settings_value
196 )
196 )
197
197
198 @classmethod
198 @classmethod
199 def get_by_name(cls, ldap_key):
199 def get_by_name(cls, ldap_key):
200 return cls.query()\
200 return cls.query()\
201 .filter(cls.app_settings_name == ldap_key).scalar()
201 .filter(cls.app_settings_name == ldap_key).scalar()
202
202
203 @classmethod
203 @classmethod
204 def get_app_settings(cls, cache=False):
204 def get_app_settings(cls, cache=False):
205
205
206 ret = cls.query()
206 ret = cls.query()
207
207
208 if cache:
208 if cache:
209 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
209 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
210
210
211 if not ret:
211 if not ret:
212 raise Exception('Could not get application settings !')
212 raise Exception('Could not get application settings !')
213 settings = {}
213 settings = {}
214 for each in ret:
214 for each in ret:
215 settings['rhodecode_' + each.app_settings_name] = \
215 settings['rhodecode_' + each.app_settings_name] = \
216 each.app_settings_value
216 each.app_settings_value
217
217
218 return settings
218 return settings
219
219
220 @classmethod
220 @classmethod
221 def get_ldap_settings(cls, cache=False):
221 def get_ldap_settings(cls, cache=False):
222 ret = cls.query()\
222 ret = cls.query()\
223 .filter(cls.app_settings_name.startswith('ldap_')).all()
223 .filter(cls.app_settings_name.startswith('ldap_')).all()
224 fd = {}
224 fd = {}
225 for row in ret:
225 for row in ret:
226 fd.update({row.app_settings_name: row.app_settings_value})
226 fd.update({row.app_settings_name: row.app_settings_value})
227
227
228 return fd
228 return fd
229
229
230
230
231 class RhodeCodeUi(Base, BaseModel):
231 class RhodeCodeUi(Base, BaseModel):
232 __tablename__ = 'rhodecode_ui'
232 __tablename__ = 'rhodecode_ui'
233 __table_args__ = (
233 __table_args__ = (
234 UniqueConstraint('ui_key'),
234 UniqueConstraint('ui_key'),
235 {'extend_existing': True, 'mysql_engine': 'InnoDB',
235 {'extend_existing': True, 'mysql_engine': 'InnoDB',
236 'mysql_charset': 'utf8'}
236 'mysql_charset': 'utf8'}
237 )
237 )
238
238
239 HOOK_UPDATE = 'changegroup.update'
239 HOOK_UPDATE = 'changegroup.update'
240 HOOK_REPO_SIZE = 'changegroup.repo_size'
240 HOOK_REPO_SIZE = 'changegroup.repo_size'
241 HOOK_PUSH = 'pretxnchangegroup.push_logger'
241 HOOK_PUSH = 'changegroup.push_logger'
242 HOOK_PULL = 'preoutgoing.pull_logger'
242 HOOK_PULL = 'preoutgoing.pull_logger'
243
243
244 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
244 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
245 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
245 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
246 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
246 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
247 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
247 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
248 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
248 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
249
249
250 @classmethod
250 @classmethod
251 def get_by_key(cls, key):
251 def get_by_key(cls, key):
252 return cls.query().filter(cls.ui_key == key)
252 return cls.query().filter(cls.ui_key == key)
253
253
254 @classmethod
254 @classmethod
255 def get_builtin_hooks(cls):
255 def get_builtin_hooks(cls):
256 q = cls.query()
256 q = cls.query()
257 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
257 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
258 cls.HOOK_REPO_SIZE,
258 cls.HOOK_REPO_SIZE,
259 cls.HOOK_PUSH, cls.HOOK_PULL]))
259 cls.HOOK_PUSH, cls.HOOK_PULL]))
260 return q.all()
260 return q.all()
261
261
262 @classmethod
262 @classmethod
263 def get_custom_hooks(cls):
263 def get_custom_hooks(cls):
264 q = cls.query()
264 q = cls.query()
265 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
265 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
266 cls.HOOK_REPO_SIZE,
266 cls.HOOK_REPO_SIZE,
267 cls.HOOK_PUSH, cls.HOOK_PULL]))
267 cls.HOOK_PUSH, cls.HOOK_PULL]))
268 q = q.filter(cls.ui_section == 'hooks')
268 q = q.filter(cls.ui_section == 'hooks')
269 return q.all()
269 return q.all()
270
270
271 @classmethod
271 @classmethod
272 def get_repos_location(cls):
272 def get_repos_location(cls):
273 return cls.get_by_key('/').one().ui_value
273 return cls.get_by_key('/').one().ui_value
274
274
275 @classmethod
275 @classmethod
276 def create_or_update_hook(cls, key, val):
276 def create_or_update_hook(cls, key, val):
277 new_ui = cls.get_by_key(key).scalar() or cls()
277 new_ui = cls.get_by_key(key).scalar() or cls()
278 new_ui.ui_section = 'hooks'
278 new_ui.ui_section = 'hooks'
279 new_ui.ui_active = True
279 new_ui.ui_active = True
280 new_ui.ui_key = key
280 new_ui.ui_key = key
281 new_ui.ui_value = val
281 new_ui.ui_value = val
282
282
283 Session.add(new_ui)
283 Session.add(new_ui)
284
284
285
285
286 class User(Base, BaseModel):
286 class User(Base, BaseModel):
287 __tablename__ = 'users'
287 __tablename__ = 'users'
288 __table_args__ = (
288 __table_args__ = (
289 UniqueConstraint('username'), UniqueConstraint('email'),
289 UniqueConstraint('username'), UniqueConstraint('email'),
290 {'extend_existing': True, 'mysql_engine': 'InnoDB',
290 {'extend_existing': True, 'mysql_engine': 'InnoDB',
291 'mysql_charset': 'utf8'}
291 'mysql_charset': 'utf8'}
292 )
292 )
293 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
293 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
294 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
294 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
295 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
295 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
296 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
296 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
297 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
297 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
298 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
298 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
299 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
299 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
300 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
300 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
301 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
301 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
302 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
302 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
303 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
303 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
304
304
305 user_log = relationship('UserLog', cascade='all')
305 user_log = relationship('UserLog', cascade='all')
306 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
306 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
307
307
308 repositories = relationship('Repository')
308 repositories = relationship('Repository')
309 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
309 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
310 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
310 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
311 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
311 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
312
312
313 group_member = relationship('UsersGroupMember', cascade='all')
313 group_member = relationship('UsersGroupMember', cascade='all')
314
314
315 notifications = relationship('UserNotification', cascade='all')
315 notifications = relationship('UserNotification', cascade='all')
316 # notifications assigned to this user
316 # notifications assigned to this user
317 user_created_notifications = relationship('Notification', cascade='all')
317 user_created_notifications = relationship('Notification', cascade='all')
318 # comments created by this user
318 # comments created by this user
319 user_comments = relationship('ChangesetComment', cascade='all')
319 user_comments = relationship('ChangesetComment', cascade='all')
320
320
321 @hybrid_property
321 @hybrid_property
322 def email(self):
322 def email(self):
323 return self._email
323 return self._email
324
324
325 @email.setter
325 @email.setter
326 def email(self, val):
326 def email(self, val):
327 self._email = val.lower() if val else None
327 self._email = val.lower() if val else None
328
328
329 @property
329 @property
330 def full_name(self):
330 def full_name(self):
331 return '%s %s' % (self.name, self.lastname)
331 return '%s %s' % (self.name, self.lastname)
332
332
333 @property
333 @property
334 def full_name_or_username(self):
334 def full_name_or_username(self):
335 return ('%s %s' % (self.name, self.lastname)
335 return ('%s %s' % (self.name, self.lastname)
336 if (self.name and self.lastname) else self.username)
336 if (self.name and self.lastname) else self.username)
337
337
338 @property
338 @property
339 def full_contact(self):
339 def full_contact(self):
340 return '%s %s <%s>' % (self.name, self.lastname, self.email)
340 return '%s %s <%s>' % (self.name, self.lastname, self.email)
341
341
342 @property
342 @property
343 def short_contact(self):
343 def short_contact(self):
344 return '%s %s' % (self.name, self.lastname)
344 return '%s %s' % (self.name, self.lastname)
345
345
346 @property
346 @property
347 def is_admin(self):
347 def is_admin(self):
348 return self.admin
348 return self.admin
349
349
350 def __unicode__(self):
350 def __unicode__(self):
351 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
351 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
352 self.user_id, self.username)
352 self.user_id, self.username)
353
353
354 @classmethod
354 @classmethod
355 def get_by_username(cls, username, case_insensitive=False, cache=False):
355 def get_by_username(cls, username, case_insensitive=False, cache=False):
356 if case_insensitive:
356 if case_insensitive:
357 q = cls.query().filter(cls.username.ilike(username))
357 q = cls.query().filter(cls.username.ilike(username))
358 else:
358 else:
359 q = cls.query().filter(cls.username == username)
359 q = cls.query().filter(cls.username == username)
360
360
361 if cache:
361 if cache:
362 q = q.options(FromCache(
362 q = q.options(FromCache(
363 "sql_cache_short",
363 "sql_cache_short",
364 "get_user_%s" % _hash_key(username)
364 "get_user_%s" % _hash_key(username)
365 )
365 )
366 )
366 )
367 return q.scalar()
367 return q.scalar()
368
368
369 @classmethod
369 @classmethod
370 def get_by_api_key(cls, api_key, cache=False):
370 def get_by_api_key(cls, api_key, cache=False):
371 q = cls.query().filter(cls.api_key == api_key)
371 q = cls.query().filter(cls.api_key == api_key)
372
372
373 if cache:
373 if cache:
374 q = q.options(FromCache("sql_cache_short",
374 q = q.options(FromCache("sql_cache_short",
375 "get_api_key_%s" % api_key))
375 "get_api_key_%s" % api_key))
376 return q.scalar()
376 return q.scalar()
377
377
378 @classmethod
378 @classmethod
379 def get_by_email(cls, email, case_insensitive=False, cache=False):
379 def get_by_email(cls, email, case_insensitive=False, cache=False):
380 if case_insensitive:
380 if case_insensitive:
381 q = cls.query().filter(cls.email.ilike(email))
381 q = cls.query().filter(cls.email.ilike(email))
382 else:
382 else:
383 q = cls.query().filter(cls.email == email)
383 q = cls.query().filter(cls.email == email)
384
384
385 if cache:
385 if cache:
386 q = q.options(FromCache("sql_cache_short",
386 q = q.options(FromCache("sql_cache_short",
387 "get_api_key_%s" % email))
387 "get_api_key_%s" % email))
388 return q.scalar()
388 return q.scalar()
389
389
390 def update_lastlogin(self):
390 def update_lastlogin(self):
391 """Update user lastlogin"""
391 """Update user lastlogin"""
392 self.last_login = datetime.datetime.now()
392 self.last_login = datetime.datetime.now()
393 Session.add(self)
393 Session.add(self)
394 log.debug('updated user %s lastlogin' % self.username)
394 log.debug('updated user %s lastlogin' % self.username)
395
395
396 def __json__(self):
396 def __json__(self):
397 return dict(
397 return dict(
398 user_id=self.user_id,
398 user_id=self.user_id,
399 first_name=self.name,
399 first_name=self.name,
400 last_name=self.lastname,
400 last_name=self.lastname,
401 email=self.email,
401 email=self.email,
402 full_name=self.full_name,
402 full_name=self.full_name,
403 full_name_or_username=self.full_name_or_username,
403 full_name_or_username=self.full_name_or_username,
404 short_contact=self.short_contact,
404 short_contact=self.short_contact,
405 full_contact=self.full_contact
405 full_contact=self.full_contact
406 )
406 )
407
407
408
408
409 class UserLog(Base, BaseModel):
409 class UserLog(Base, BaseModel):
410 __tablename__ = 'user_logs'
410 __tablename__ = 'user_logs'
411 __table_args__ = (
411 __table_args__ = (
412 {'extend_existing': True, 'mysql_engine': 'InnoDB',
412 {'extend_existing': True, 'mysql_engine': 'InnoDB',
413 'mysql_charset': 'utf8'},
413 'mysql_charset': 'utf8'},
414 )
414 )
415 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
415 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
416 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
416 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
417 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
417 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
418 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
418 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
419 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
419 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
420 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
420 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
421 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
421 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
422
422
423 @property
423 @property
424 def action_as_day(self):
424 def action_as_day(self):
425 return datetime.date(*self.action_date.timetuple()[:3])
425 return datetime.date(*self.action_date.timetuple()[:3])
426
426
427 user = relationship('User')
427 user = relationship('User')
428 repository = relationship('Repository', cascade='')
428 repository = relationship('Repository', cascade='')
429
429
430
430
431 class UsersGroup(Base, BaseModel):
431 class UsersGroup(Base, BaseModel):
432 __tablename__ = 'users_groups'
432 __tablename__ = 'users_groups'
433 __table_args__ = (
433 __table_args__ = (
434 {'extend_existing': True, 'mysql_engine': 'InnoDB',
434 {'extend_existing': True, 'mysql_engine': 'InnoDB',
435 'mysql_charset': 'utf8'},
435 'mysql_charset': 'utf8'},
436 )
436 )
437
437
438 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
438 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
439 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
439 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
440 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
440 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
441
441
442 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
442 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
443 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
443 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
444 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
444 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
445
445
446 def __unicode__(self):
446 def __unicode__(self):
447 return u'<userGroup(%s)>' % (self.users_group_name)
447 return u'<userGroup(%s)>' % (self.users_group_name)
448
448
449 @classmethod
449 @classmethod
450 def get_by_group_name(cls, group_name, cache=False,
450 def get_by_group_name(cls, group_name, cache=False,
451 case_insensitive=False):
451 case_insensitive=False):
452 if case_insensitive:
452 if case_insensitive:
453 q = cls.query().filter(cls.users_group_name.ilike(group_name))
453 q = cls.query().filter(cls.users_group_name.ilike(group_name))
454 else:
454 else:
455 q = cls.query().filter(cls.users_group_name == group_name)
455 q = cls.query().filter(cls.users_group_name == group_name)
456 if cache:
456 if cache:
457 q = q.options(FromCache(
457 q = q.options(FromCache(
458 "sql_cache_short",
458 "sql_cache_short",
459 "get_user_%s" % _hash_key(group_name)
459 "get_user_%s" % _hash_key(group_name)
460 )
460 )
461 )
461 )
462 return q.scalar()
462 return q.scalar()
463
463
464 @classmethod
464 @classmethod
465 def get(cls, users_group_id, cache=False):
465 def get(cls, users_group_id, cache=False):
466 users_group = cls.query()
466 users_group = cls.query()
467 if cache:
467 if cache:
468 users_group = users_group.options(FromCache("sql_cache_short",
468 users_group = users_group.options(FromCache("sql_cache_short",
469 "get_users_group_%s" % users_group_id))
469 "get_users_group_%s" % users_group_id))
470 return users_group.get(users_group_id)
470 return users_group.get(users_group_id)
471
471
472
472
473 class UsersGroupMember(Base, BaseModel):
473 class UsersGroupMember(Base, BaseModel):
474 __tablename__ = 'users_groups_members'
474 __tablename__ = 'users_groups_members'
475 __table_args__ = (
475 __table_args__ = (
476 {'extend_existing': True, 'mysql_engine': 'InnoDB',
476 {'extend_existing': True, 'mysql_engine': 'InnoDB',
477 'mysql_charset': 'utf8'},
477 'mysql_charset': 'utf8'},
478 )
478 )
479
479
480 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
480 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
481 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
481 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
482 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
482 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
483
483
484 user = relationship('User', lazy='joined')
484 user = relationship('User', lazy='joined')
485 users_group = relationship('UsersGroup')
485 users_group = relationship('UsersGroup')
486
486
487 def __init__(self, gr_id='', u_id=''):
487 def __init__(self, gr_id='', u_id=''):
488 self.users_group_id = gr_id
488 self.users_group_id = gr_id
489 self.user_id = u_id
489 self.user_id = u_id
490
490
491
491
492 class Repository(Base, BaseModel):
492 class Repository(Base, BaseModel):
493 __tablename__ = 'repositories'
493 __tablename__ = 'repositories'
494 __table_args__ = (
494 __table_args__ = (
495 UniqueConstraint('repo_name'),
495 UniqueConstraint('repo_name'),
496 {'extend_existing': True, 'mysql_engine': 'InnoDB',
496 {'extend_existing': True, 'mysql_engine': 'InnoDB',
497 'mysql_charset': 'utf8'},
497 'mysql_charset': 'utf8'},
498 )
498 )
499
499
500 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
500 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
501 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
501 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
502 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
502 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
503 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
503 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
504 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
504 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
505 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
505 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
506 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
506 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
507 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
507 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
508 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
508 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
509 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
509 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
510
510
511 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
511 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
512 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
512 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
513
513
514 user = relationship('User')
514 user = relationship('User')
515 fork = relationship('Repository', remote_side=repo_id)
515 fork = relationship('Repository', remote_side=repo_id)
516 group = relationship('RepoGroup')
516 group = relationship('RepoGroup')
517 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
517 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
518 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
518 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
519 stats = relationship('Statistics', cascade='all', uselist=False)
519 stats = relationship('Statistics', cascade='all', uselist=False)
520
520
521 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
521 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
522
522
523 logs = relationship('UserLog')
523 logs = relationship('UserLog')
524
524
525 def __unicode__(self):
525 def __unicode__(self):
526 return u"<%s('%s:%s')>" % (self.__class__.__name__,self.repo_id,
526 return u"<%s('%s:%s')>" % (self.__class__.__name__,self.repo_id,
527 self.repo_name)
527 self.repo_name)
528
528
529 @classmethod
529 @classmethod
530 def url_sep(cls):
530 def url_sep(cls):
531 return URL_SEP
531 return URL_SEP
532
532
533 @classmethod
533 @classmethod
534 def get_by_repo_name(cls, repo_name):
534 def get_by_repo_name(cls, repo_name):
535 q = Session.query(cls).filter(cls.repo_name == repo_name)
535 q = Session.query(cls).filter(cls.repo_name == repo_name)
536 q = q.options(joinedload(Repository.fork))\
536 q = q.options(joinedload(Repository.fork))\
537 .options(joinedload(Repository.user))\
537 .options(joinedload(Repository.user))\
538 .options(joinedload(Repository.group))
538 .options(joinedload(Repository.group))
539 return q.scalar()
539 return q.scalar()
540
540
541 @classmethod
541 @classmethod
542 def get_by_full_path(cls, repo_full_path):
542 def get_by_full_path(cls, repo_full_path):
543 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
543 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
544 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
544 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
545
545
546 @classmethod
546 @classmethod
547 def get_repo_forks(cls, repo_id):
547 def get_repo_forks(cls, repo_id):
548 return cls.query().filter(Repository.fork_id == repo_id)
548 return cls.query().filter(Repository.fork_id == repo_id)
549
549
550 @classmethod
550 @classmethod
551 def base_path(cls):
551 def base_path(cls):
552 """
552 """
553 Returns base path when all repos are stored
553 Returns base path when all repos are stored
554
554
555 :param cls:
555 :param cls:
556 """
556 """
557 q = Session.query(RhodeCodeUi)\
557 q = Session.query(RhodeCodeUi)\
558 .filter(RhodeCodeUi.ui_key == cls.url_sep())
558 .filter(RhodeCodeUi.ui_key == cls.url_sep())
559 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
559 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
560 return q.one().ui_value
560 return q.one().ui_value
561
561
562 @property
562 @property
563 def just_name(self):
563 def just_name(self):
564 return self.repo_name.split(Repository.url_sep())[-1]
564 return self.repo_name.split(Repository.url_sep())[-1]
565
565
566 @property
566 @property
567 def groups_with_parents(self):
567 def groups_with_parents(self):
568 groups = []
568 groups = []
569 if self.group is None:
569 if self.group is None:
570 return groups
570 return groups
571
571
572 cur_gr = self.group
572 cur_gr = self.group
573 groups.insert(0, cur_gr)
573 groups.insert(0, cur_gr)
574 while 1:
574 while 1:
575 gr = getattr(cur_gr, 'parent_group', None)
575 gr = getattr(cur_gr, 'parent_group', None)
576 cur_gr = cur_gr.parent_group
576 cur_gr = cur_gr.parent_group
577 if gr is None:
577 if gr is None:
578 break
578 break
579 groups.insert(0, gr)
579 groups.insert(0, gr)
580
580
581 return groups
581 return groups
582
582
583 @property
583 @property
584 def groups_and_repo(self):
584 def groups_and_repo(self):
585 return self.groups_with_parents, self.just_name
585 return self.groups_with_parents, self.just_name
586
586
587 @LazyProperty
587 @LazyProperty
588 def repo_path(self):
588 def repo_path(self):
589 """
589 """
590 Returns base full path for that repository means where it actually
590 Returns base full path for that repository means where it actually
591 exists on a filesystem
591 exists on a filesystem
592 """
592 """
593 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
593 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
594 Repository.url_sep())
594 Repository.url_sep())
595 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
595 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
596 return q.one().ui_value
596 return q.one().ui_value
597
597
598 @property
598 @property
599 def repo_full_path(self):
599 def repo_full_path(self):
600 p = [self.repo_path]
600 p = [self.repo_path]
601 # we need to split the name by / since this is how we store the
601 # we need to split the name by / since this is how we store the
602 # names in the database, but that eventually needs to be converted
602 # names in the database, but that eventually needs to be converted
603 # into a valid system path
603 # into a valid system path
604 p += self.repo_name.split(Repository.url_sep())
604 p += self.repo_name.split(Repository.url_sep())
605 return os.path.join(*p)
605 return os.path.join(*p)
606
606
607 def get_new_name(self, repo_name):
607 def get_new_name(self, repo_name):
608 """
608 """
609 returns new full repository name based on assigned group and new new
609 returns new full repository name based on assigned group and new new
610
610
611 :param group_name:
611 :param group_name:
612 """
612 """
613 path_prefix = self.group.full_path_splitted if self.group else []
613 path_prefix = self.group.full_path_splitted if self.group else []
614 return Repository.url_sep().join(path_prefix + [repo_name])
614 return Repository.url_sep().join(path_prefix + [repo_name])
615
615
616 @property
616 @property
617 def _ui(self):
617 def _ui(self):
618 """
618 """
619 Creates an db based ui object for this repository
619 Creates an db based ui object for this repository
620 """
620 """
621 from mercurial import ui
621 from mercurial import ui
622 from mercurial import config
622 from mercurial import config
623 baseui = ui.ui()
623 baseui = ui.ui()
624
624
625 #clean the baseui object
625 #clean the baseui object
626 baseui._ocfg = config.config()
626 baseui._ocfg = config.config()
627 baseui._ucfg = config.config()
627 baseui._ucfg = config.config()
628 baseui._tcfg = config.config()
628 baseui._tcfg = config.config()
629
629
630 ret = RhodeCodeUi.query()\
630 ret = RhodeCodeUi.query()\
631 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
631 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
632
632
633 hg_ui = ret
633 hg_ui = ret
634 for ui_ in hg_ui:
634 for ui_ in hg_ui:
635 if ui_.ui_active:
635 if ui_.ui_active:
636 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
636 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
637 ui_.ui_key, ui_.ui_value)
637 ui_.ui_key, ui_.ui_value)
638 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
638 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
639
639
640 return baseui
640 return baseui
641
641
642 @classmethod
642 @classmethod
643 def is_valid(cls, repo_name):
643 def is_valid(cls, repo_name):
644 """
644 """
645 returns True if given repo name is a valid filesystem repository
645 returns True if given repo name is a valid filesystem repository
646
646
647 :param cls:
647 :param cls:
648 :param repo_name:
648 :param repo_name:
649 """
649 """
650 from rhodecode.lib.utils import is_valid_repo
650 from rhodecode.lib.utils import is_valid_repo
651
651
652 return is_valid_repo(repo_name, cls.base_path())
652 return is_valid_repo(repo_name, cls.base_path())
653
653
654 #==========================================================================
654 #==========================================================================
655 # SCM PROPERTIES
655 # SCM PROPERTIES
656 #==========================================================================
656 #==========================================================================
657
657
658 def get_changeset(self, rev=None):
658 def get_changeset(self, rev=None):
659 return get_changeset_safe(self.scm_instance, rev)
659 return get_changeset_safe(self.scm_instance, rev)
660
660
661 @property
661 @property
662 def tip(self):
662 def tip(self):
663 return self.get_changeset('tip')
663 return self.get_changeset('tip')
664
664
665 @property
665 @property
666 def author(self):
666 def author(self):
667 return self.tip.author
667 return self.tip.author
668
668
669 @property
669 @property
670 def last_change(self):
670 def last_change(self):
671 return self.scm_instance.last_change
671 return self.scm_instance.last_change
672
672
673 def comments(self, revisions=None):
673 def comments(self, revisions=None):
674 """
674 """
675 Returns comments for this repository grouped by revisions
675 Returns comments for this repository grouped by revisions
676
676
677 :param revisions: filter query by revisions only
677 :param revisions: filter query by revisions only
678 """
678 """
679 cmts = ChangesetComment.query()\
679 cmts = ChangesetComment.query()\
680 .filter(ChangesetComment.repo == self)
680 .filter(ChangesetComment.repo == self)
681 if revisions:
681 if revisions:
682 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
682 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
683 grouped = defaultdict(list)
683 grouped = defaultdict(list)
684 for cmt in cmts.all():
684 for cmt in cmts.all():
685 grouped[cmt.revision].append(cmt)
685 grouped[cmt.revision].append(cmt)
686 return grouped
686 return grouped
687
687
688 #==========================================================================
688 #==========================================================================
689 # SCM CACHE INSTANCE
689 # SCM CACHE INSTANCE
690 #==========================================================================
690 #==========================================================================
691
691
692 @property
692 @property
693 def invalidate(self):
693 def invalidate(self):
694 return CacheInvalidation.invalidate(self.repo_name)
694 return CacheInvalidation.invalidate(self.repo_name)
695
695
696 def set_invalidate(self):
696 def set_invalidate(self):
697 """
697 """
698 set a cache for invalidation for this instance
698 set a cache for invalidation for this instance
699 """
699 """
700 CacheInvalidation.set_invalidate(self.repo_name)
700 CacheInvalidation.set_invalidate(self.repo_name)
701
701
702 @LazyProperty
702 @LazyProperty
703 def scm_instance(self):
703 def scm_instance(self):
704 return self.__get_instance()
704 return self.__get_instance()
705
705
706 def scm_instance_cached(self, cache_map=None):
706 def scm_instance_cached(self, cache_map=None):
707 @cache_region('long_term')
707 @cache_region('long_term')
708 def _c(repo_name):
708 def _c(repo_name):
709 return self.__get_instance()
709 return self.__get_instance()
710 rn = self.repo_name
710 rn = self.repo_name
711 log.debug('Getting cached instance of repo')
711 log.debug('Getting cached instance of repo')
712
712
713 if cache_map:
713 if cache_map:
714 # get using prefilled cache_map
714 # get using prefilled cache_map
715 invalidate_repo = cache_map[self.repo_name]
715 invalidate_repo = cache_map[self.repo_name]
716 if invalidate_repo:
716 if invalidate_repo:
717 invalidate_repo = (None if invalidate_repo.cache_active
717 invalidate_repo = (None if invalidate_repo.cache_active
718 else invalidate_repo)
718 else invalidate_repo)
719 else:
719 else:
720 # get from invalidate
720 # get from invalidate
721 invalidate_repo = self.invalidate
721 invalidate_repo = self.invalidate
722
722
723 if invalidate_repo is not None:
723 if invalidate_repo is not None:
724 region_invalidate(_c, None, rn)
724 region_invalidate(_c, None, rn)
725 # update our cache
725 # update our cache
726 CacheInvalidation.set_valid(invalidate_repo.cache_key)
726 CacheInvalidation.set_valid(invalidate_repo.cache_key)
727 return _c(rn)
727 return _c(rn)
728
728
729 def __get_instance(self):
729 def __get_instance(self):
730 repo_full_path = self.repo_full_path
730 repo_full_path = self.repo_full_path
731 try:
731 try:
732 alias = get_scm(repo_full_path)[0]
732 alias = get_scm(repo_full_path)[0]
733 log.debug('Creating instance of %s repository' % alias)
733 log.debug('Creating instance of %s repository' % alias)
734 backend = get_backend(alias)
734 backend = get_backend(alias)
735 except VCSError:
735 except VCSError:
736 log.error(traceback.format_exc())
736 log.error(traceback.format_exc())
737 log.error('Perhaps this repository is in db and not in '
737 log.error('Perhaps this repository is in db and not in '
738 'filesystem run rescan repositories with '
738 'filesystem run rescan repositories with '
739 '"destroy old data " option from admin panel')
739 '"destroy old data " option from admin panel')
740 return
740 return
741
741
742 if alias == 'hg':
742 if alias == 'hg':
743
743
744 repo = backend(safe_str(repo_full_path), create=False,
744 repo = backend(safe_str(repo_full_path), create=False,
745 baseui=self._ui)
745 baseui=self._ui)
746 # skip hidden web repository
746 # skip hidden web repository
747 if repo._get_hidden():
747 if repo._get_hidden():
748 return
748 return
749 else:
749 else:
750 repo = backend(repo_full_path, create=False)
750 repo = backend(repo_full_path, create=False)
751
751
752 return repo
752 return repo
753
753
754
754
755 class RepoGroup(Base, BaseModel):
755 class RepoGroup(Base, BaseModel):
756 __tablename__ = 'groups'
756 __tablename__ = 'groups'
757 __table_args__ = (
757 __table_args__ = (
758 UniqueConstraint('group_name', 'group_parent_id'),
758 UniqueConstraint('group_name', 'group_parent_id'),
759 CheckConstraint('group_id != group_parent_id'),
759 CheckConstraint('group_id != group_parent_id'),
760 {'extend_existing': True, 'mysql_engine': 'InnoDB',
760 {'extend_existing': True, 'mysql_engine': 'InnoDB',
761 'mysql_charset': 'utf8'},
761 'mysql_charset': 'utf8'},
762 )
762 )
763 __mapper_args__ = {'order_by': 'group_name'}
763 __mapper_args__ = {'order_by': 'group_name'}
764
764
765 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
765 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
766 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
766 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
767 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
767 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
768 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
768 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
769
769
770 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
770 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
771 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
771 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
772
772
773 parent_group = relationship('RepoGroup', remote_side=group_id)
773 parent_group = relationship('RepoGroup', remote_side=group_id)
774
774
775 def __init__(self, group_name='', parent_group=None):
775 def __init__(self, group_name='', parent_group=None):
776 self.group_name = group_name
776 self.group_name = group_name
777 self.parent_group = parent_group
777 self.parent_group = parent_group
778
778
779 def __unicode__(self):
779 def __unicode__(self):
780 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
780 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
781 self.group_name)
781 self.group_name)
782
782
783 @classmethod
783 @classmethod
784 def groups_choices(cls):
784 def groups_choices(cls):
785 from webhelpers.html import literal as _literal
785 from webhelpers.html import literal as _literal
786 repo_groups = [('', '')]
786 repo_groups = [('', '')]
787 sep = ' &raquo; '
787 sep = ' &raquo; '
788 _name = lambda k: _literal(sep.join(k))
788 _name = lambda k: _literal(sep.join(k))
789
789
790 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
790 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
791 for x in cls.query().all()])
791 for x in cls.query().all()])
792
792
793 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
793 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
794 return repo_groups
794 return repo_groups
795
795
796 @classmethod
796 @classmethod
797 def url_sep(cls):
797 def url_sep(cls):
798 return URL_SEP
798 return URL_SEP
799
799
800 @classmethod
800 @classmethod
801 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
801 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
802 if case_insensitive:
802 if case_insensitive:
803 gr = cls.query()\
803 gr = cls.query()\
804 .filter(cls.group_name.ilike(group_name))
804 .filter(cls.group_name.ilike(group_name))
805 else:
805 else:
806 gr = cls.query()\
806 gr = cls.query()\
807 .filter(cls.group_name == group_name)
807 .filter(cls.group_name == group_name)
808 if cache:
808 if cache:
809 gr = gr.options(FromCache(
809 gr = gr.options(FromCache(
810 "sql_cache_short",
810 "sql_cache_short",
811 "get_group_%s" % _hash_key(group_name)
811 "get_group_%s" % _hash_key(group_name)
812 )
812 )
813 )
813 )
814 return gr.scalar()
814 return gr.scalar()
815
815
816 @property
816 @property
817 def parents(self):
817 def parents(self):
818 parents_recursion_limit = 5
818 parents_recursion_limit = 5
819 groups = []
819 groups = []
820 if self.parent_group is None:
820 if self.parent_group is None:
821 return groups
821 return groups
822 cur_gr = self.parent_group
822 cur_gr = self.parent_group
823 groups.insert(0, cur_gr)
823 groups.insert(0, cur_gr)
824 cnt = 0
824 cnt = 0
825 while 1:
825 while 1:
826 cnt += 1
826 cnt += 1
827 gr = getattr(cur_gr, 'parent_group', None)
827 gr = getattr(cur_gr, 'parent_group', None)
828 cur_gr = cur_gr.parent_group
828 cur_gr = cur_gr.parent_group
829 if gr is None:
829 if gr is None:
830 break
830 break
831 if cnt == parents_recursion_limit:
831 if cnt == parents_recursion_limit:
832 # this will prevent accidental infinit loops
832 # this will prevent accidental infinit loops
833 log.error('group nested more than %s' %
833 log.error('group nested more than %s' %
834 parents_recursion_limit)
834 parents_recursion_limit)
835 break
835 break
836
836
837 groups.insert(0, gr)
837 groups.insert(0, gr)
838 return groups
838 return groups
839
839
840 @property
840 @property
841 def children(self):
841 def children(self):
842 return RepoGroup.query().filter(RepoGroup.parent_group == self)
842 return RepoGroup.query().filter(RepoGroup.parent_group == self)
843
843
844 @property
844 @property
845 def name(self):
845 def name(self):
846 return self.group_name.split(RepoGroup.url_sep())[-1]
846 return self.group_name.split(RepoGroup.url_sep())[-1]
847
847
848 @property
848 @property
849 def full_path(self):
849 def full_path(self):
850 return self.group_name
850 return self.group_name
851
851
852 @property
852 @property
853 def full_path_splitted(self):
853 def full_path_splitted(self):
854 return self.group_name.split(RepoGroup.url_sep())
854 return self.group_name.split(RepoGroup.url_sep())
855
855
856 @property
856 @property
857 def repositories(self):
857 def repositories(self):
858 return Repository.query()\
858 return Repository.query()\
859 .filter(Repository.group == self)\
859 .filter(Repository.group == self)\
860 .order_by(Repository.repo_name)
860 .order_by(Repository.repo_name)
861
861
862 @property
862 @property
863 def repositories_recursive_count(self):
863 def repositories_recursive_count(self):
864 cnt = self.repositories.count()
864 cnt = self.repositories.count()
865
865
866 def children_count(group):
866 def children_count(group):
867 cnt = 0
867 cnt = 0
868 for child in group.children:
868 for child in group.children:
869 cnt += child.repositories.count()
869 cnt += child.repositories.count()
870 cnt += children_count(child)
870 cnt += children_count(child)
871 return cnt
871 return cnt
872
872
873 return cnt + children_count(self)
873 return cnt + children_count(self)
874
874
875 def get_new_name(self, group_name):
875 def get_new_name(self, group_name):
876 """
876 """
877 returns new full group name based on parent and new name
877 returns new full group name based on parent and new name
878
878
879 :param group_name:
879 :param group_name:
880 """
880 """
881 path_prefix = (self.parent_group.full_path_splitted if
881 path_prefix = (self.parent_group.full_path_splitted if
882 self.parent_group else [])
882 self.parent_group else [])
883 return RepoGroup.url_sep().join(path_prefix + [group_name])
883 return RepoGroup.url_sep().join(path_prefix + [group_name])
884
884
885
885
886 class Permission(Base, BaseModel):
886 class Permission(Base, BaseModel):
887 __tablename__ = 'permissions'
887 __tablename__ = 'permissions'
888 __table_args__ = (
888 __table_args__ = (
889 {'extend_existing': True, 'mysql_engine': 'InnoDB',
889 {'extend_existing': True, 'mysql_engine': 'InnoDB',
890 'mysql_charset': 'utf8'},
890 'mysql_charset': 'utf8'},
891 )
891 )
892 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
892 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
893 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
893 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
894 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
894 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
895
895
896 def __unicode__(self):
896 def __unicode__(self):
897 return u"<%s('%s:%s')>" % (
897 return u"<%s('%s:%s')>" % (
898 self.__class__.__name__, self.permission_id, self.permission_name
898 self.__class__.__name__, self.permission_id, self.permission_name
899 )
899 )
900
900
901 @classmethod
901 @classmethod
902 def get_by_key(cls, key):
902 def get_by_key(cls, key):
903 return cls.query().filter(cls.permission_name == key).scalar()
903 return cls.query().filter(cls.permission_name == key).scalar()
904
904
905 @classmethod
905 @classmethod
906 def get_default_perms(cls, default_user_id):
906 def get_default_perms(cls, default_user_id):
907 q = Session.query(UserRepoToPerm, Repository, cls)\
907 q = Session.query(UserRepoToPerm, Repository, cls)\
908 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
908 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
909 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
909 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
910 .filter(UserRepoToPerm.user_id == default_user_id)
910 .filter(UserRepoToPerm.user_id == default_user_id)
911
911
912 return q.all()
912 return q.all()
913
913
914 @classmethod
914 @classmethod
915 def get_default_group_perms(cls, default_user_id):
915 def get_default_group_perms(cls, default_user_id):
916 q = Session.query(UserRepoGroupToPerm, RepoGroup, cls)\
916 q = Session.query(UserRepoGroupToPerm, RepoGroup, cls)\
917 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
917 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
918 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
918 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
919 .filter(UserRepoGroupToPerm.user_id == default_user_id)
919 .filter(UserRepoGroupToPerm.user_id == default_user_id)
920
920
921 return q.all()
921 return q.all()
922
922
923
923
924 class UserRepoToPerm(Base, BaseModel):
924 class UserRepoToPerm(Base, BaseModel):
925 __tablename__ = 'repo_to_perm'
925 __tablename__ = 'repo_to_perm'
926 __table_args__ = (
926 __table_args__ = (
927 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
927 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
928 {'extend_existing': True, 'mysql_engine': 'InnoDB',
928 {'extend_existing': True, 'mysql_engine': 'InnoDB',
929 'mysql_charset': 'utf8'}
929 'mysql_charset': 'utf8'}
930 )
930 )
931 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
931 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
932 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
932 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
933 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
933 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
934 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
934 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
935
935
936 user = relationship('User')
936 user = relationship('User')
937 repository = relationship('Repository')
937 repository = relationship('Repository')
938 permission = relationship('Permission')
938 permission = relationship('Permission')
939
939
940 @classmethod
940 @classmethod
941 def create(cls, user, repository, permission):
941 def create(cls, user, repository, permission):
942 n = cls()
942 n = cls()
943 n.user = user
943 n.user = user
944 n.repository = repository
944 n.repository = repository
945 n.permission = permission
945 n.permission = permission
946 Session.add(n)
946 Session.add(n)
947 return n
947 return n
948
948
949 def __unicode__(self):
949 def __unicode__(self):
950 return u'<user:%s => %s >' % (self.user, self.repository)
950 return u'<user:%s => %s >' % (self.user, self.repository)
951
951
952
952
953 class UserToPerm(Base, BaseModel):
953 class UserToPerm(Base, BaseModel):
954 __tablename__ = 'user_to_perm'
954 __tablename__ = 'user_to_perm'
955 __table_args__ = (
955 __table_args__ = (
956 UniqueConstraint('user_id', 'permission_id'),
956 UniqueConstraint('user_id', 'permission_id'),
957 {'extend_existing': True, 'mysql_engine': 'InnoDB',
957 {'extend_existing': True, 'mysql_engine': 'InnoDB',
958 'mysql_charset': 'utf8'}
958 'mysql_charset': 'utf8'}
959 )
959 )
960 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
960 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
961 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
961 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
962 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
962 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
963
963
964 user = relationship('User')
964 user = relationship('User')
965 permission = relationship('Permission', lazy='joined')
965 permission = relationship('Permission', lazy='joined')
966
966
967
967
968 class UsersGroupRepoToPerm(Base, BaseModel):
968 class UsersGroupRepoToPerm(Base, BaseModel):
969 __tablename__ = 'users_group_repo_to_perm'
969 __tablename__ = 'users_group_repo_to_perm'
970 __table_args__ = (
970 __table_args__ = (
971 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
971 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
972 {'extend_existing': True, 'mysql_engine': 'InnoDB',
972 {'extend_existing': True, 'mysql_engine': 'InnoDB',
973 'mysql_charset': 'utf8'}
973 'mysql_charset': 'utf8'}
974 )
974 )
975 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
975 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
976 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
976 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
977 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
977 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
978 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
978 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
979
979
980 users_group = relationship('UsersGroup')
980 users_group = relationship('UsersGroup')
981 permission = relationship('Permission')
981 permission = relationship('Permission')
982 repository = relationship('Repository')
982 repository = relationship('Repository')
983
983
984 @classmethod
984 @classmethod
985 def create(cls, users_group, repository, permission):
985 def create(cls, users_group, repository, permission):
986 n = cls()
986 n = cls()
987 n.users_group = users_group
987 n.users_group = users_group
988 n.repository = repository
988 n.repository = repository
989 n.permission = permission
989 n.permission = permission
990 Session.add(n)
990 Session.add(n)
991 return n
991 return n
992
992
993 def __unicode__(self):
993 def __unicode__(self):
994 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
994 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
995
995
996
996
997 class UsersGroupToPerm(Base, BaseModel):
997 class UsersGroupToPerm(Base, BaseModel):
998 __tablename__ = 'users_group_to_perm'
998 __tablename__ = 'users_group_to_perm'
999 __table_args__ = (
999 __table_args__ = (
1000 UniqueConstraint('users_group_id', 'permission_id',),
1000 UniqueConstraint('users_group_id', 'permission_id',),
1001 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1001 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1002 'mysql_charset': 'utf8'}
1002 'mysql_charset': 'utf8'}
1003 )
1003 )
1004 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1004 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1005 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1005 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1006 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1006 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1007
1007
1008 users_group = relationship('UsersGroup')
1008 users_group = relationship('UsersGroup')
1009 permission = relationship('Permission')
1009 permission = relationship('Permission')
1010
1010
1011
1011
1012 class UserRepoGroupToPerm(Base, BaseModel):
1012 class UserRepoGroupToPerm(Base, BaseModel):
1013 __tablename__ = 'user_repo_group_to_perm'
1013 __tablename__ = 'user_repo_group_to_perm'
1014 __table_args__ = (
1014 __table_args__ = (
1015 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1015 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1016 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1016 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1017 'mysql_charset': 'utf8'}
1017 'mysql_charset': 'utf8'}
1018 )
1018 )
1019
1019
1020 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1020 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1021 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1021 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1022 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1022 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1023 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1023 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1024
1024
1025 user = relationship('User')
1025 user = relationship('User')
1026 group = relationship('RepoGroup')
1026 group = relationship('RepoGroup')
1027 permission = relationship('Permission')
1027 permission = relationship('Permission')
1028
1028
1029
1029
1030 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1030 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1031 __tablename__ = 'users_group_repo_group_to_perm'
1031 __tablename__ = 'users_group_repo_group_to_perm'
1032 __table_args__ = (
1032 __table_args__ = (
1033 UniqueConstraint('users_group_id', 'group_id'),
1033 UniqueConstraint('users_group_id', 'group_id'),
1034 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1034 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1035 'mysql_charset': 'utf8'}
1035 'mysql_charset': 'utf8'}
1036 )
1036 )
1037
1037
1038 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1038 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1039 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1039 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1040 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1040 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1041 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1041 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1042
1042
1043 users_group = relationship('UsersGroup')
1043 users_group = relationship('UsersGroup')
1044 permission = relationship('Permission')
1044 permission = relationship('Permission')
1045 group = relationship('RepoGroup')
1045 group = relationship('RepoGroup')
1046
1046
1047
1047
1048 class Statistics(Base, BaseModel):
1048 class Statistics(Base, BaseModel):
1049 __tablename__ = 'statistics'
1049 __tablename__ = 'statistics'
1050 __table_args__ = (
1050 __table_args__ = (
1051 UniqueConstraint('repository_id'),
1051 UniqueConstraint('repository_id'),
1052 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1052 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1053 'mysql_charset': 'utf8'}
1053 'mysql_charset': 'utf8'}
1054 )
1054 )
1055 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1055 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1056 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1056 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1057 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1057 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1058 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1058 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1059 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1059 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1060 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1060 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1061
1061
1062 repository = relationship('Repository', single_parent=True)
1062 repository = relationship('Repository', single_parent=True)
1063
1063
1064
1064
1065 class UserFollowing(Base, BaseModel):
1065 class UserFollowing(Base, BaseModel):
1066 __tablename__ = 'user_followings'
1066 __tablename__ = 'user_followings'
1067 __table_args__ = (
1067 __table_args__ = (
1068 UniqueConstraint('user_id', 'follows_repository_id'),
1068 UniqueConstraint('user_id', 'follows_repository_id'),
1069 UniqueConstraint('user_id', 'follows_user_id'),
1069 UniqueConstraint('user_id', 'follows_user_id'),
1070 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1070 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1071 'mysql_charset': 'utf8'}
1071 'mysql_charset': 'utf8'}
1072 )
1072 )
1073
1073
1074 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1074 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1075 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1075 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1076 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1076 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1077 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1077 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1078 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1078 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1079
1079
1080 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1080 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1081
1081
1082 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1082 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1083 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1083 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1084
1084
1085 @classmethod
1085 @classmethod
1086 def get_repo_followers(cls, repo_id):
1086 def get_repo_followers(cls, repo_id):
1087 return cls.query().filter(cls.follows_repo_id == repo_id)
1087 return cls.query().filter(cls.follows_repo_id == repo_id)
1088
1088
1089
1089
1090 class CacheInvalidation(Base, BaseModel):
1090 class CacheInvalidation(Base, BaseModel):
1091 __tablename__ = 'cache_invalidation'
1091 __tablename__ = 'cache_invalidation'
1092 __table_args__ = (
1092 __table_args__ = (
1093 UniqueConstraint('cache_key'),
1093 UniqueConstraint('cache_key'),
1094 Index('key_idx', 'cache_key'),
1094 Index('key_idx', 'cache_key'),
1095 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1095 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1096 'mysql_charset': 'utf8'},
1096 'mysql_charset': 'utf8'},
1097 )
1097 )
1098 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1098 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1099 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1099 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1100 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1100 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1101 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1101 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1102
1102
1103 def __init__(self, cache_key, cache_args=''):
1103 def __init__(self, cache_key, cache_args=''):
1104 self.cache_key = cache_key
1104 self.cache_key = cache_key
1105 self.cache_args = cache_args
1105 self.cache_args = cache_args
1106 self.cache_active = False
1106 self.cache_active = False
1107
1107
1108 def __unicode__(self):
1108 def __unicode__(self):
1109 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1109 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1110 self.cache_id, self.cache_key)
1110 self.cache_id, self.cache_key)
1111
1111
1112 @classmethod
1112 @classmethod
1113 def clear_cache(cls):
1113 def clear_cache(cls):
1114 cls.query().delete()
1114 cls.query().delete()
1115
1115
1116 @classmethod
1116 @classmethod
1117 def _get_key(cls, key):
1117 def _get_key(cls, key):
1118 """
1118 """
1119 Wrapper for generating a key, together with a prefix
1119 Wrapper for generating a key, together with a prefix
1120
1120
1121 :param key:
1121 :param key:
1122 """
1122 """
1123 import rhodecode
1123 import rhodecode
1124 prefix = ''
1124 prefix = ''
1125 iid = rhodecode.CONFIG.get('instance_id')
1125 iid = rhodecode.CONFIG.get('instance_id')
1126 if iid:
1126 if iid:
1127 prefix = iid
1127 prefix = iid
1128 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1128 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1129
1129
1130 @classmethod
1130 @classmethod
1131 def get_by_key(cls, key):
1131 def get_by_key(cls, key):
1132 return cls.query().filter(cls.cache_key == key).scalar()
1132 return cls.query().filter(cls.cache_key == key).scalar()
1133
1133
1134 @classmethod
1134 @classmethod
1135 def _get_or_create_key(cls, key, prefix, org_key):
1135 def _get_or_create_key(cls, key, prefix, org_key):
1136 inv_obj = Session.query(cls).filter(cls.cache_key == key).scalar()
1136 inv_obj = Session.query(cls).filter(cls.cache_key == key).scalar()
1137 if not inv_obj:
1137 if not inv_obj:
1138 try:
1138 try:
1139 inv_obj = CacheInvalidation(key, org_key)
1139 inv_obj = CacheInvalidation(key, org_key)
1140 Session.add(inv_obj)
1140 Session.add(inv_obj)
1141 Session.commit()
1141 Session.commit()
1142 except Exception:
1142 except Exception:
1143 log.error(traceback.format_exc())
1143 log.error(traceback.format_exc())
1144 Session.rollback()
1144 Session.rollback()
1145 return inv_obj
1145 return inv_obj
1146
1146
1147 @classmethod
1147 @classmethod
1148 def invalidate(cls, key):
1148 def invalidate(cls, key):
1149 """
1149 """
1150 Returns Invalidation object if this given key should be invalidated
1150 Returns Invalidation object if this given key should be invalidated
1151 None otherwise. `cache_active = False` means that this cache
1151 None otherwise. `cache_active = False` means that this cache
1152 state is not valid and needs to be invalidated
1152 state is not valid and needs to be invalidated
1153
1153
1154 :param key:
1154 :param key:
1155 """
1155 """
1156
1156
1157 key, _prefix, _org_key = cls._get_key(key)
1157 key, _prefix, _org_key = cls._get_key(key)
1158 inv = cls._get_or_create_key(key, _prefix, _org_key)
1158 inv = cls._get_or_create_key(key, _prefix, _org_key)
1159
1159
1160 if inv and inv.cache_active is False:
1160 if inv and inv.cache_active is False:
1161 return inv
1161 return inv
1162
1162
1163 @classmethod
1163 @classmethod
1164 def set_invalidate(cls, key):
1164 def set_invalidate(cls, key):
1165 """
1165 """
1166 Mark this Cache key for invalidation
1166 Mark this Cache key for invalidation
1167
1167
1168 :param key:
1168 :param key:
1169 """
1169 """
1170
1170
1171 key, _prefix, _org_key = cls._get_key(key)
1171 key, _prefix, _org_key = cls._get_key(key)
1172 inv_objs = Session.query(cls).filter(cls.cache_args == _org_key).all()
1172 inv_objs = Session.query(cls).filter(cls.cache_args == _org_key).all()
1173 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1173 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1174 _org_key))
1174 _org_key))
1175 try:
1175 try:
1176 for inv_obj in inv_objs:
1176 for inv_obj in inv_objs:
1177 if inv_obj:
1177 if inv_obj:
1178 inv_obj.cache_active = False
1178 inv_obj.cache_active = False
1179
1179
1180 Session.add(inv_obj)
1180 Session.add(inv_obj)
1181 Session.commit()
1181 Session.commit()
1182 except Exception:
1182 except Exception:
1183 log.error(traceback.format_exc())
1183 log.error(traceback.format_exc())
1184 Session.rollback()
1184 Session.rollback()
1185
1185
1186 @classmethod
1186 @classmethod
1187 def set_valid(cls, key):
1187 def set_valid(cls, key):
1188 """
1188 """
1189 Mark this cache key as active and currently cached
1189 Mark this cache key as active and currently cached
1190
1190
1191 :param key:
1191 :param key:
1192 """
1192 """
1193 inv_obj = cls.get_by_key(key)
1193 inv_obj = cls.get_by_key(key)
1194 inv_obj.cache_active = True
1194 inv_obj.cache_active = True
1195 Session.add(inv_obj)
1195 Session.add(inv_obj)
1196 Session.commit()
1196 Session.commit()
1197
1197
1198 @classmethod
1198 @classmethod
1199 def get_cache_map(cls):
1199 def get_cache_map(cls):
1200
1200
1201 class cachemapdict(dict):
1201 class cachemapdict(dict):
1202
1202
1203 def __init__(self, *args, **kwargs):
1203 def __init__(self, *args, **kwargs):
1204 fixkey = kwargs.get('fixkey')
1204 fixkey = kwargs.get('fixkey')
1205 if fixkey:
1205 if fixkey:
1206 del kwargs['fixkey']
1206 del kwargs['fixkey']
1207 self.fixkey = fixkey
1207 self.fixkey = fixkey
1208 super(cachemapdict, self).__init__(*args, **kwargs)
1208 super(cachemapdict, self).__init__(*args, **kwargs)
1209
1209
1210 def __getattr__(self, name):
1210 def __getattr__(self, name):
1211 key = name
1211 key = name
1212 if self.fixkey:
1212 if self.fixkey:
1213 key, _prefix, _org_key = cls._get_key(key)
1213 key, _prefix, _org_key = cls._get_key(key)
1214 if key in self.__dict__:
1214 if key in self.__dict__:
1215 return self.__dict__[key]
1215 return self.__dict__[key]
1216 else:
1216 else:
1217 return self[key]
1217 return self[key]
1218
1218
1219 def __getitem__(self, key):
1219 def __getitem__(self, key):
1220 if self.fixkey:
1220 if self.fixkey:
1221 key, _prefix, _org_key = cls._get_key(key)
1221 key, _prefix, _org_key = cls._get_key(key)
1222 try:
1222 try:
1223 return super(cachemapdict, self).__getitem__(key)
1223 return super(cachemapdict, self).__getitem__(key)
1224 except KeyError:
1224 except KeyError:
1225 return
1225 return
1226
1226
1227 cache_map = cachemapdict(fixkey=True)
1227 cache_map = cachemapdict(fixkey=True)
1228 for obj in cls.query().all():
1228 for obj in cls.query().all():
1229 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1229 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1230 return cache_map
1230 return cache_map
1231
1231
1232
1232
1233 class ChangesetComment(Base, BaseModel):
1233 class ChangesetComment(Base, BaseModel):
1234 __tablename__ = 'changeset_comments'
1234 __tablename__ = 'changeset_comments'
1235 __table_args__ = (
1235 __table_args__ = (
1236 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1236 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1237 'mysql_charset': 'utf8'},
1237 'mysql_charset': 'utf8'},
1238 )
1238 )
1239 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1239 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1240 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1240 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1241 revision = Column('revision', String(40), nullable=False)
1241 revision = Column('revision', String(40), nullable=False)
1242 line_no = Column('line_no', Unicode(10), nullable=True)
1242 line_no = Column('line_no', Unicode(10), nullable=True)
1243 f_path = Column('f_path', Unicode(1000), nullable=True)
1243 f_path = Column('f_path', Unicode(1000), nullable=True)
1244 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1244 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1245 text = Column('text', Unicode(25000), nullable=False)
1245 text = Column('text', Unicode(25000), nullable=False)
1246 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1246 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1247
1247
1248 author = relationship('User', lazy='joined')
1248 author = relationship('User', lazy='joined')
1249 repo = relationship('Repository')
1249 repo = relationship('Repository')
1250
1250
1251 @classmethod
1251 @classmethod
1252 def get_users(cls, revision):
1252 def get_users(cls, revision):
1253 """
1253 """
1254 Returns user associated with this changesetComment. ie those
1254 Returns user associated with this changesetComment. ie those
1255 who actually commented
1255 who actually commented
1256
1256
1257 :param cls:
1257 :param cls:
1258 :param revision:
1258 :param revision:
1259 """
1259 """
1260 return Session.query(User)\
1260 return Session.query(User)\
1261 .filter(cls.revision == revision)\
1261 .filter(cls.revision == revision)\
1262 .join(ChangesetComment.author).all()
1262 .join(ChangesetComment.author).all()
1263
1263
1264
1264
1265 class Notification(Base, BaseModel):
1265 class Notification(Base, BaseModel):
1266 __tablename__ = 'notifications'
1266 __tablename__ = 'notifications'
1267 __table_args__ = (
1267 __table_args__ = (
1268 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1268 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1269 'mysql_charset': 'utf8'},
1269 'mysql_charset': 'utf8'},
1270 )
1270 )
1271
1271
1272 TYPE_CHANGESET_COMMENT = u'cs_comment'
1272 TYPE_CHANGESET_COMMENT = u'cs_comment'
1273 TYPE_MESSAGE = u'message'
1273 TYPE_MESSAGE = u'message'
1274 TYPE_MENTION = u'mention'
1274 TYPE_MENTION = u'mention'
1275 TYPE_REGISTRATION = u'registration'
1275 TYPE_REGISTRATION = u'registration'
1276
1276
1277 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1277 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1278 subject = Column('subject', Unicode(512), nullable=True)
1278 subject = Column('subject', Unicode(512), nullable=True)
1279 body = Column('body', Unicode(50000), nullable=True)
1279 body = Column('body', Unicode(50000), nullable=True)
1280 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1280 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1281 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1281 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1282 type_ = Column('type', Unicode(256))
1282 type_ = Column('type', Unicode(256))
1283
1283
1284 created_by_user = relationship('User')
1284 created_by_user = relationship('User')
1285 notifications_to_users = relationship('UserNotification', lazy='joined',
1285 notifications_to_users = relationship('UserNotification', lazy='joined',
1286 cascade="all, delete, delete-orphan")
1286 cascade="all, delete, delete-orphan")
1287
1287
1288 @property
1288 @property
1289 def recipients(self):
1289 def recipients(self):
1290 return [x.user for x in UserNotification.query()\
1290 return [x.user for x in UserNotification.query()\
1291 .filter(UserNotification.notification == self)\
1291 .filter(UserNotification.notification == self)\
1292 .order_by(UserNotification.user).all()]
1292 .order_by(UserNotification.user).all()]
1293
1293
1294 @classmethod
1294 @classmethod
1295 def create(cls, created_by, subject, body, recipients, type_=None):
1295 def create(cls, created_by, subject, body, recipients, type_=None):
1296 if type_ is None:
1296 if type_ is None:
1297 type_ = Notification.TYPE_MESSAGE
1297 type_ = Notification.TYPE_MESSAGE
1298
1298
1299 notification = cls()
1299 notification = cls()
1300 notification.created_by_user = created_by
1300 notification.created_by_user = created_by
1301 notification.subject = subject
1301 notification.subject = subject
1302 notification.body = body
1302 notification.body = body
1303 notification.type_ = type_
1303 notification.type_ = type_
1304 notification.created_on = datetime.datetime.now()
1304 notification.created_on = datetime.datetime.now()
1305
1305
1306 for u in recipients:
1306 for u in recipients:
1307 assoc = UserNotification()
1307 assoc = UserNotification()
1308 assoc.notification = notification
1308 assoc.notification = notification
1309 u.notifications.append(assoc)
1309 u.notifications.append(assoc)
1310 Session.add(notification)
1310 Session.add(notification)
1311 return notification
1311 return notification
1312
1312
1313 @property
1313 @property
1314 def description(self):
1314 def description(self):
1315 from rhodecode.model.notification import NotificationModel
1315 from rhodecode.model.notification import NotificationModel
1316 return NotificationModel().make_description(self)
1316 return NotificationModel().make_description(self)
1317
1317
1318
1318
1319 class UserNotification(Base, BaseModel):
1319 class UserNotification(Base, BaseModel):
1320 __tablename__ = 'user_to_notification'
1320 __tablename__ = 'user_to_notification'
1321 __table_args__ = (
1321 __table_args__ = (
1322 UniqueConstraint('user_id', 'notification_id'),
1322 UniqueConstraint('user_id', 'notification_id'),
1323 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1323 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1324 'mysql_charset': 'utf8'}
1324 'mysql_charset': 'utf8'}
1325 )
1325 )
1326 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1326 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1327 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1327 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1328 read = Column('read', Boolean, default=False)
1328 read = Column('read', Boolean, default=False)
1329 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1329 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1330
1330
1331 user = relationship('User', lazy="joined")
1331 user = relationship('User', lazy="joined")
1332 notification = relationship('Notification', lazy="joined",
1332 notification = relationship('Notification', lazy="joined",
1333 order_by=lambda: Notification.created_on.desc(),)
1333 order_by=lambda: Notification.created_on.desc(),)
1334
1334
1335 def mark_as_read(self):
1335 def mark_as_read(self):
1336 self.read = True
1336 self.read = True
1337 Session.add(self)
1337 Session.add(self)
1338
1338
1339
1339
1340 class DbMigrateVersion(Base, BaseModel):
1340 class DbMigrateVersion(Base, BaseModel):
1341 __tablename__ = 'db_migrate_version'
1341 __tablename__ = 'db_migrate_version'
1342 __table_args__ = (
1342 __table_args__ = (
1343 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1343 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1344 'mysql_charset': 'utf8'},
1344 'mysql_charset': 'utf8'},
1345 )
1345 )
1346 repository_id = Column('repository_id', String(250), primary_key=True)
1346 repository_id = Column('repository_id', String(250), primary_key=True)
1347 repository_path = Column('repository_path', Text)
1347 repository_path = Column('repository_path', Text)
1348 version = Column('version', Integer)
1348 version = Column('version', Integer)
@@ -1,773 +1,773 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.db import User, UsersGroup, RepoGroup, Repository
39 from rhodecode.model.db import User, UsersGroup, RepoGroup, Repository
40 from rhodecode import BACKENDS
40 from rhodecode import BACKENDS
41
41
42 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
43
43
44
44
45 #this is needed to translate the messages using _() in validators
45 #this is needed to translate the messages using _() in validators
46 class State_obj(object):
46 class State_obj(object):
47 _ = staticmethod(_)
47 _ = staticmethod(_)
48
48
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(
59 raise formencode.Invalid(
60 self.message('invalid_token',
60 self.message('invalid_token',
61 state, search_number=value),
61 state, search_number=value),
62 value,
62 value,
63 state
63 state
64 )
64 )
65
65
66
66
67 def ValidUsername(edit, old_data):
67 def ValidUsername(edit, old_data):
68 class _ValidUsername(formencode.validators.FancyValidator):
68 class _ValidUsername(formencode.validators.FancyValidator):
69
69
70 def validate_python(self, value, state):
70 def validate_python(self, value, state):
71 if value in ['default', 'new_user']:
71 if value in ['default', 'new_user']:
72 raise formencode.Invalid(_('Invalid username'), value, state)
72 raise formencode.Invalid(_('Invalid username'), value, state)
73 #check if user is unique
73 #check if user is unique
74 old_un = None
74 old_un = None
75 if edit:
75 if edit:
76 old_un = User.get(old_data.get('user_id')).username
76 old_un = User.get(old_data.get('user_id')).username
77
77
78 if old_un != value or not edit:
78 if old_un != value or not edit:
79 if User.get_by_username(value, case_insensitive=True):
79 if User.get_by_username(value, case_insensitive=True):
80 raise formencode.Invalid(_('This username already '
80 raise formencode.Invalid(_('This username already '
81 'exists') , value, state)
81 'exists') , value, state)
82
82
83 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
83 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
84 raise formencode.Invalid(
84 raise formencode.Invalid(
85 _('Username may only contain alphanumeric characters '
85 _('Username may only contain alphanumeric characters '
86 'underscores, periods or dashes and must begin with '
86 'underscores, periods or dashes and must begin with '
87 'alphanumeric character'),
87 'alphanumeric character'),
88 value,
88 value,
89 state
89 state
90 )
90 )
91
91
92 return _ValidUsername
92 return _ValidUsername
93
93
94
94
95 def ValidUsersGroup(edit, old_data):
95 def ValidUsersGroup(edit, old_data):
96
96
97 class _ValidUsersGroup(formencode.validators.FancyValidator):
97 class _ValidUsersGroup(formencode.validators.FancyValidator):
98
98
99 def validate_python(self, value, state):
99 def validate_python(self, value, state):
100 if value in ['default']:
100 if value in ['default']:
101 raise formencode.Invalid(_('Invalid group name'), value, state)
101 raise formencode.Invalid(_('Invalid group name'), value, state)
102 #check if group is unique
102 #check if group is unique
103 old_ugname = None
103 old_ugname = None
104 if edit:
104 if edit:
105 old_ugname = UsersGroup.get(
105 old_ugname = UsersGroup.get(
106 old_data.get('users_group_id')).users_group_name
106 old_data.get('users_group_id')).users_group_name
107
107
108 if old_ugname != value or not edit:
108 if old_ugname != value or not edit:
109 if UsersGroup.get_by_group_name(value, cache=False,
109 if UsersGroup.get_by_group_name(value, cache=False,
110 case_insensitive=True):
110 case_insensitive=True):
111 raise formencode.Invalid(_('This users group '
111 raise formencode.Invalid(_('This users group '
112 'already exists'), value,
112 'already exists'), value,
113 state)
113 state)
114
114
115 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
115 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
116 raise formencode.Invalid(
116 raise formencode.Invalid(
117 _('RepoGroup name may only contain alphanumeric characters '
117 _('RepoGroup name may only contain alphanumeric characters '
118 'underscores, periods or dashes and must begin with '
118 'underscores, periods or dashes and must begin with '
119 'alphanumeric character'),
119 'alphanumeric character'),
120 value,
120 value,
121 state
121 state
122 )
122 )
123
123
124 return _ValidUsersGroup
124 return _ValidUsersGroup
125
125
126
126
127 def ValidReposGroup(edit, old_data):
127 def ValidReposGroup(edit, old_data):
128 class _ValidReposGroup(formencode.validators.FancyValidator):
128 class _ValidReposGroup(formencode.validators.FancyValidator):
129
129
130 def validate_python(self, value, state):
130 def validate_python(self, value, state):
131 # TODO WRITE VALIDATIONS
131 # TODO WRITE VALIDATIONS
132 group_name = value.get('group_name')
132 group_name = value.get('group_name')
133 group_parent_id = value.get('group_parent_id')
133 group_parent_id = value.get('group_parent_id')
134
134
135 # slugify repo group just in case :)
135 # slugify repo group just in case :)
136 slug = repo_name_slug(group_name)
136 slug = repo_name_slug(group_name)
137
137
138 # check for parent of self
138 # check for parent of self
139 parent_of_self = lambda: (
139 parent_of_self = lambda: (
140 old_data['group_id'] == int(group_parent_id)
140 old_data['group_id'] == int(group_parent_id)
141 if group_parent_id else False
141 if group_parent_id else False
142 )
142 )
143 if edit and parent_of_self():
143 if edit and parent_of_self():
144 e_dict = {
144 e_dict = {
145 'group_parent_id': _('Cannot assign this group as parent')
145 'group_parent_id': _('Cannot assign this group as parent')
146 }
146 }
147 raise formencode.Invalid('', value, state,
147 raise formencode.Invalid('', value, state,
148 error_dict=e_dict)
148 error_dict=e_dict)
149
149
150 old_gname = None
150 old_gname = None
151 if edit:
151 if edit:
152 old_gname = RepoGroup.get(old_data.get('group_id')).group_name
152 old_gname = RepoGroup.get(old_data.get('group_id')).group_name
153
153
154 if old_gname != group_name or not edit:
154 if old_gname != group_name or not edit:
155
155
156 # check group
156 # check group
157 gr = RepoGroup.query()\
157 gr = RepoGroup.query()\
158 .filter(RepoGroup.group_name == slug)\
158 .filter(RepoGroup.group_name == slug)\
159 .filter(RepoGroup.group_parent_id == group_parent_id)\
159 .filter(RepoGroup.group_parent_id == group_parent_id)\
160 .scalar()
160 .scalar()
161
161
162 if gr:
162 if gr:
163 e_dict = {
163 e_dict = {
164 'group_name': _('This group already exists')
164 'group_name': _('This group already exists')
165 }
165 }
166 raise formencode.Invalid('', value, state,
166 raise formencode.Invalid('', value, state,
167 error_dict=e_dict)
167 error_dict=e_dict)
168
168
169 # check for same repo
169 # check for same repo
170 repo = Repository.query()\
170 repo = Repository.query()\
171 .filter(Repository.repo_name == slug)\
171 .filter(Repository.repo_name == slug)\
172 .scalar()
172 .scalar()
173
173
174 if repo:
174 if repo:
175 e_dict = {
175 e_dict = {
176 'group_name': _('Repository with this name already exists')
176 'group_name': _('Repository with this name already exists')
177 }
177 }
178 raise formencode.Invalid('', value, state,
178 raise formencode.Invalid('', value, state,
179 error_dict=e_dict)
179 error_dict=e_dict)
180
180
181 return _ValidReposGroup
181 return _ValidReposGroup
182
182
183
183
184 class ValidPassword(formencode.validators.FancyValidator):
184 class ValidPassword(formencode.validators.FancyValidator):
185
185
186 def to_python(self, value, state):
186 def to_python(self, value, state):
187
187
188 if not value:
188 if not value:
189 return
189 return
190
190
191 if value.get('password'):
191 if value.get('password'):
192 try:
192 try:
193 value['password'] = get_crypt_password(value['password'])
193 value['password'] = get_crypt_password(value['password'])
194 except UnicodeEncodeError:
194 except UnicodeEncodeError:
195 e_dict = {'password': _('Invalid characters in password')}
195 e_dict = {'password': _('Invalid characters in password')}
196 raise formencode.Invalid('', value, state, error_dict=e_dict)
196 raise formencode.Invalid('', value, state, error_dict=e_dict)
197
197
198 if value.get('password_confirmation'):
198 if value.get('password_confirmation'):
199 try:
199 try:
200 value['password_confirmation'] = \
200 value['password_confirmation'] = \
201 get_crypt_password(value['password_confirmation'])
201 get_crypt_password(value['password_confirmation'])
202 except UnicodeEncodeError:
202 except UnicodeEncodeError:
203 e_dict = {
203 e_dict = {
204 'password_confirmation': _('Invalid characters in password')
204 'password_confirmation': _('Invalid characters in password')
205 }
205 }
206 raise formencode.Invalid('', value, state, error_dict=e_dict)
206 raise formencode.Invalid('', value, state, error_dict=e_dict)
207
207
208 if value.get('new_password'):
208 if value.get('new_password'):
209 try:
209 try:
210 value['new_password'] = \
210 value['new_password'] = \
211 get_crypt_password(value['new_password'])
211 get_crypt_password(value['new_password'])
212 except UnicodeEncodeError:
212 except UnicodeEncodeError:
213 e_dict = {'new_password': _('Invalid characters in password')}
213 e_dict = {'new_password': _('Invalid characters in password')}
214 raise formencode.Invalid('', value, state, error_dict=e_dict)
214 raise formencode.Invalid('', value, state, error_dict=e_dict)
215
215
216 return value
216 return value
217
217
218
218
219 class ValidPasswordsMatch(formencode.validators.FancyValidator):
219 class ValidPasswordsMatch(formencode.validators.FancyValidator):
220
220
221 def validate_python(self, value, state):
221 def validate_python(self, value, state):
222
222
223 pass_val = value.get('password') or value.get('new_password')
223 pass_val = value.get('password') or value.get('new_password')
224 if pass_val != value['password_confirmation']:
224 if pass_val != value['password_confirmation']:
225 e_dict = {'password_confirmation':
225 e_dict = {'password_confirmation':
226 _('Passwords do not match')}
226 _('Passwords do not match')}
227 raise formencode.Invalid('', value, state, error_dict=e_dict)
227 raise formencode.Invalid('', value, state, error_dict=e_dict)
228
228
229
229
230 class ValidAuth(formencode.validators.FancyValidator):
230 class ValidAuth(formencode.validators.FancyValidator):
231 messages = {
231 messages = {
232 'invalid_password':_('invalid password'),
232 'invalid_password':_('invalid password'),
233 'invalid_login':_('invalid user name'),
233 'invalid_login':_('invalid user name'),
234 'disabled_account':_('Your account is disabled')
234 'disabled_account':_('Your account is disabled')
235 }
235 }
236
236
237 # error mapping
237 # error mapping
238 e_dict = {'username': messages['invalid_login'],
238 e_dict = {'username': messages['invalid_login'],
239 'password': messages['invalid_password']}
239 'password': messages['invalid_password']}
240 e_dict_disable = {'username': messages['disabled_account']}
240 e_dict_disable = {'username': messages['disabled_account']}
241
241
242 def validate_python(self, value, state):
242 def validate_python(self, value, state):
243 password = value['password']
243 password = value['password']
244 username = value['username']
244 username = value['username']
245 user = User.get_by_username(username)
245 user = User.get_by_username(username)
246
246
247 if authenticate(username, password):
247 if authenticate(username, password):
248 return value
248 return value
249 else:
249 else:
250 if user and user.active is False:
250 if user and user.active is False:
251 log.warning('user %s is disabled' % username)
251 log.warning('user %s is disabled' % username)
252 raise formencode.Invalid(
252 raise formencode.Invalid(
253 self.message('disabled_account',
253 self.message('disabled_account',
254 state=State_obj),
254 state=State_obj),
255 value, state,
255 value, state,
256 error_dict=self.e_dict_disable
256 error_dict=self.e_dict_disable
257 )
257 )
258 else:
258 else:
259 log.warning('user %s failed to authenticate' % username)
259 log.warning('user %s failed to authenticate' % username)
260 raise formencode.Invalid(
260 raise formencode.Invalid(
261 self.message('invalid_password',
261 self.message('invalid_password',
262 state=State_obj), value, state,
262 state=State_obj), value, state,
263 error_dict=self.e_dict
263 error_dict=self.e_dict
264 )
264 )
265
265
266
266
267 class ValidRepoUser(formencode.validators.FancyValidator):
267 class ValidRepoUser(formencode.validators.FancyValidator):
268
268
269 def to_python(self, value, state):
269 def to_python(self, value, state):
270 try:
270 try:
271 User.query().filter(User.active == True)\
271 User.query().filter(User.active == True)\
272 .filter(User.username == value).one()
272 .filter(User.username == value).one()
273 except Exception:
273 except Exception:
274 raise formencode.Invalid(_('This username is not valid'),
274 raise formencode.Invalid(_('This username is not valid'),
275 value, state)
275 value, state)
276 return value
276 return value
277
277
278
278
279 def ValidRepoName(edit, old_data):
279 def ValidRepoName(edit, old_data):
280 class _ValidRepoName(formencode.validators.FancyValidator):
280 class _ValidRepoName(formencode.validators.FancyValidator):
281 def to_python(self, value, state):
281 def to_python(self, value, state):
282
282
283 repo_name = value.get('repo_name')
283 repo_name = value.get('repo_name')
284
284
285 slug = repo_name_slug(repo_name)
285 slug = repo_name_slug(repo_name)
286 if slug in [ADMIN_PREFIX, '']:
286 if slug in [ADMIN_PREFIX, '']:
287 e_dict = {'repo_name': _('This repository name is disallowed')}
287 e_dict = {'repo_name': _('This repository name is disallowed')}
288 raise formencode.Invalid('', value, state, error_dict=e_dict)
288 raise formencode.Invalid('', value, state, error_dict=e_dict)
289
289
290 if value.get('repo_group'):
290 if value.get('repo_group'):
291 gr = RepoGroup.get(value.get('repo_group'))
291 gr = RepoGroup.get(value.get('repo_group'))
292 group_path = gr.full_path
292 group_path = gr.full_path
293 # value needs to be aware of group name in order to check
293 # value needs to be aware of group name in order to check
294 # db key This is an actual just the name to store in the
294 # db key This is an actual just the name to store in the
295 # database
295 # database
296 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
296 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
297
297
298 else:
298 else:
299 group_path = ''
299 group_path = ''
300 repo_name_full = repo_name
300 repo_name_full = repo_name
301
301
302 value['repo_name_full'] = repo_name_full
302 value['repo_name_full'] = repo_name_full
303 rename = old_data.get('repo_name') != repo_name_full
303 rename = old_data.get('repo_name') != repo_name_full
304 create = not edit
304 create = not edit
305 if rename or create:
305 if rename or create:
306
306
307 if group_path != '':
307 if group_path != '':
308 if Repository.get_by_repo_name(repo_name_full):
308 if Repository.get_by_repo_name(repo_name_full):
309 e_dict = {
309 e_dict = {
310 'repo_name': _('This repository already exists in '
310 'repo_name': _('This repository already exists in '
311 'a group "%s"') % gr.group_name
311 'a group "%s"') % gr.group_name
312 }
312 }
313 raise formencode.Invalid('', value, state,
313 raise formencode.Invalid('', value, state,
314 error_dict=e_dict)
314 error_dict=e_dict)
315 elif RepoGroup.get_by_group_name(repo_name_full):
315 elif RepoGroup.get_by_group_name(repo_name_full):
316 e_dict = {
316 e_dict = {
317 'repo_name': _('There is a group with this name '
317 'repo_name': _('There is a group with this name '
318 'already "%s"') % repo_name_full
318 'already "%s"') % repo_name_full
319 }
319 }
320 raise formencode.Invalid('', value, state,
320 raise formencode.Invalid('', value, state,
321 error_dict=e_dict)
321 error_dict=e_dict)
322
322
323 elif Repository.get_by_repo_name(repo_name_full):
323 elif Repository.get_by_repo_name(repo_name_full):
324 e_dict = {'repo_name': _('This repository '
324 e_dict = {'repo_name': _('This repository '
325 'already exists')}
325 'already exists')}
326 raise formencode.Invalid('', value, state,
326 raise formencode.Invalid('', value, state,
327 error_dict=e_dict)
327 error_dict=e_dict)
328
328
329 return value
329 return value
330
330
331 return _ValidRepoName
331 return _ValidRepoName
332
332
333
333
334 def ValidForkName(*args, **kwargs):
334 def ValidForkName(*args, **kwargs):
335 return ValidRepoName(*args, **kwargs)
335 return ValidRepoName(*args, **kwargs)
336
336
337
337
338 def SlugifyName():
338 def SlugifyName():
339 class _SlugifyName(formencode.validators.FancyValidator):
339 class _SlugifyName(formencode.validators.FancyValidator):
340
340
341 def to_python(self, value, state):
341 def to_python(self, value, state):
342 return repo_name_slug(value)
342 return repo_name_slug(value)
343
343
344 return _SlugifyName
344 return _SlugifyName
345
345
346
346
347 def ValidCloneUri():
347 def ValidCloneUri():
348 from rhodecode.lib.utils import make_ui
348 from rhodecode.lib.utils import make_ui
349
349
350 def url_handler(repo_type, url, proto, ui=None):
350 def url_handler(repo_type, url, proto, ui=None):
351 if repo_type == 'hg':
351 if repo_type == 'hg':
352 from mercurial.httprepo import httprepository, httpsrepository
352 from mercurial.httprepo import httprepository, httpsrepository
353 if proto == 'https':
353 if proto == 'https':
354 httpsrepository(make_ui('db'), url).capabilities
354 httpsrepository(make_ui('db'), url).capabilities
355 elif proto == 'http':
355 elif proto == 'http':
356 httprepository(make_ui('db'), url).capabilities
356 httprepository(make_ui('db'), url).capabilities
357 elif repo_type == 'git':
357 elif repo_type == 'git':
358 #TODO: write a git url validator
358 #TODO: write a git url validator
359 pass
359 pass
360
360
361 class _ValidCloneUri(formencode.validators.FancyValidator):
361 class _ValidCloneUri(formencode.validators.FancyValidator):
362
362
363 def to_python(self, value, state):
363 def to_python(self, value, state):
364
364
365 repo_type = value.get('repo_type')
365 repo_type = value.get('repo_type')
366 url = value.get('clone_uri')
366 url = value.get('clone_uri')
367 e_dict = {'clone_uri': _('invalid clone url')}
367 e_dict = {'clone_uri': _('invalid clone url')}
368
368
369 if not url:
369 if not url:
370 pass
370 pass
371 elif url.startswith('https'):
371 elif url.startswith('https'):
372 try:
372 try:
373 url_handler(repo_type, url, 'https', make_ui('db'))
373 url_handler(repo_type, url, 'https', make_ui('db'))
374 except Exception:
374 except Exception:
375 log.error(traceback.format_exc())
375 log.error(traceback.format_exc())
376 raise formencode.Invalid('', value, state, error_dict=e_dict)
376 raise formencode.Invalid('', value, state, error_dict=e_dict)
377 elif url.startswith('http'):
377 elif url.startswith('http'):
378 try:
378 try:
379 url_handler(repo_type, url, 'http', make_ui('db'))
379 url_handler(repo_type, url, 'http', make_ui('db'))
380 except Exception:
380 except Exception:
381 log.error(traceback.format_exc())
381 log.error(traceback.format_exc())
382 raise formencode.Invalid('', value, state, error_dict=e_dict)
382 raise formencode.Invalid('', value, state, error_dict=e_dict)
383 else:
383 else:
384 e_dict = {'clone_uri': _('Invalid clone url, provide a '
384 e_dict = {'clone_uri': _('Invalid clone url, provide a '
385 'valid clone http\s url')}
385 'valid clone http\s url')}
386 raise formencode.Invalid('', value, state, error_dict=e_dict)
386 raise formencode.Invalid('', value, state, error_dict=e_dict)
387
387
388 return value
388 return value
389
389
390 return _ValidCloneUri
390 return _ValidCloneUri
391
391
392
392
393 def ValidForkType(old_data):
393 def ValidForkType(old_data):
394 class _ValidForkType(formencode.validators.FancyValidator):
394 class _ValidForkType(formencode.validators.FancyValidator):
395
395
396 def to_python(self, value, state):
396 def to_python(self, value, state):
397 if old_data['repo_type'] != value:
397 if old_data['repo_type'] != value:
398 raise formencode.Invalid(_('Fork have to be the same '
398 raise formencode.Invalid(_('Fork have to be the same '
399 'type as original'), value, state)
399 'type as original'), value, state)
400
400
401 return value
401 return value
402 return _ValidForkType
402 return _ValidForkType
403
403
404
404
405 def ValidPerms(type_='repo'):
405 def ValidPerms(type_='repo'):
406 if type_ == 'group':
406 if type_ == 'group':
407 EMPTY_PERM = 'group.none'
407 EMPTY_PERM = 'group.none'
408 elif type_ == 'repo':
408 elif type_ == 'repo':
409 EMPTY_PERM = 'repository.none'
409 EMPTY_PERM = 'repository.none'
410
410
411 class _ValidPerms(formencode.validators.FancyValidator):
411 class _ValidPerms(formencode.validators.FancyValidator):
412 messages = {
412 messages = {
413 'perm_new_member_name':
413 'perm_new_member_name':
414 _('This username or users group name is not valid')
414 _('This username or users group name is not valid')
415 }
415 }
416
416
417 def to_python(self, value, state):
417 def to_python(self, value, state):
418 perms_update = []
418 perms_update = []
419 perms_new = []
419 perms_new = []
420 # build a list of permission to update and new permission to create
420 # build a list of permission to update and new permission to create
421 for k, v in value.items():
421 for k, v in value.items():
422 # means new added member to permissions
422 # means new added member to permissions
423 if k.startswith('perm_new_member'):
423 if k.startswith('perm_new_member'):
424 new_perm = value.get('perm_new_member', False)
424 new_perm = value.get('perm_new_member', False)
425 new_member = value.get('perm_new_member_name', False)
425 new_member = value.get('perm_new_member_name', False)
426 new_type = value.get('perm_new_member_type')
426 new_type = value.get('perm_new_member_type')
427
427
428 if new_member and new_perm:
428 if new_member and new_perm:
429 if (new_member, new_perm, new_type) not in perms_new:
429 if (new_member, new_perm, new_type) not in perms_new:
430 perms_new.append((new_member, new_perm, new_type))
430 perms_new.append((new_member, new_perm, new_type))
431 elif k.startswith('u_perm_') or k.startswith('g_perm_'):
431 elif k.startswith('u_perm_') or k.startswith('g_perm_'):
432 member = k[7:]
432 member = k[7:]
433 t = {'u': 'user',
433 t = {'u': 'user',
434 'g': 'users_group'
434 'g': 'users_group'
435 }[k[0]]
435 }[k[0]]
436 if member == 'default':
436 if member == 'default':
437 if value.get('private'):
437 if value.get('private'):
438 # set none for default when updating to private repo
438 # set none for default when updating to private repo
439 v = EMPTY_PERM
439 v = EMPTY_PERM
440 perms_update.append((member, v, t))
440 perms_update.append((member, v, t))
441
441
442 value['perms_updates'] = perms_update
442 value['perms_updates'] = perms_update
443 value['perms_new'] = perms_new
443 value['perms_new'] = perms_new
444
444
445 # update permissions
445 # update permissions
446 for k, v, t in perms_new:
446 for k, v, t in perms_new:
447 try:
447 try:
448 if t is 'user':
448 if t is 'user':
449 self.user_db = User.query()\
449 self.user_db = User.query()\
450 .filter(User.active == True)\
450 .filter(User.active == True)\
451 .filter(User.username == k).one()
451 .filter(User.username == k).one()
452 if t is 'users_group':
452 if t is 'users_group':
453 self.user_db = UsersGroup.query()\
453 self.user_db = UsersGroup.query()\
454 .filter(UsersGroup.users_group_active == True)\
454 .filter(UsersGroup.users_group_active == True)\
455 .filter(UsersGroup.users_group_name == k).one()
455 .filter(UsersGroup.users_group_name == k).one()
456
456
457 except Exception:
457 except Exception:
458 msg = self.message('perm_new_member_name',
458 msg = self.message('perm_new_member_name',
459 state=State_obj)
459 state=State_obj)
460 raise formencode.Invalid(
460 raise formencode.Invalid(
461 msg, value, state, error_dict={'perm_new_member_name': msg}
461 msg, value, state, error_dict={'perm_new_member_name': msg}
462 )
462 )
463 return value
463 return value
464 return _ValidPerms
464 return _ValidPerms
465
465
466
466
467 class ValidSettings(formencode.validators.FancyValidator):
467 class ValidSettings(formencode.validators.FancyValidator):
468
468
469 def to_python(self, value, state):
469 def to_python(self, value, state):
470 # settings form can't edit user
470 # settings form can't edit user
471 if 'user' in value:
471 if 'user' in value:
472 del['value']['user']
472 del['value']['user']
473 return value
473 return value
474
474
475
475
476 class ValidPath(formencode.validators.FancyValidator):
476 class ValidPath(formencode.validators.FancyValidator):
477 def to_python(self, value, state):
477 def to_python(self, value, state):
478
478
479 if not os.path.isdir(value):
479 if not os.path.isdir(value):
480 msg = _('This is not a valid path')
480 msg = _('This is not a valid path')
481 raise formencode.Invalid(msg, value, state,
481 raise formencode.Invalid(msg, value, state,
482 error_dict={'paths_root_path': msg})
482 error_dict={'paths_root_path': msg})
483 return value
483 return value
484
484
485
485
486 def UniqSystemEmail(old_data):
486 def UniqSystemEmail(old_data):
487 class _UniqSystemEmail(formencode.validators.FancyValidator):
487 class _UniqSystemEmail(formencode.validators.FancyValidator):
488 def to_python(self, value, state):
488 def to_python(self, value, state):
489 value = value.lower()
489 value = value.lower()
490 if (old_data.get('email') or '').lower() != value:
490 if (old_data.get('email') or '').lower() != value:
491 user = User.get_by_email(value, case_insensitive=True)
491 user = User.get_by_email(value, case_insensitive=True)
492 if user:
492 if user:
493 raise formencode.Invalid(
493 raise formencode.Invalid(
494 _("This e-mail address is already taken"), value, state
494 _("This e-mail address is already taken"), value, state
495 )
495 )
496 return value
496 return value
497
497
498 return _UniqSystemEmail
498 return _UniqSystemEmail
499
499
500
500
501 class ValidSystemEmail(formencode.validators.FancyValidator):
501 class ValidSystemEmail(formencode.validators.FancyValidator):
502 def to_python(self, value, state):
502 def to_python(self, value, state):
503 value = value.lower()
503 value = value.lower()
504 user = User.get_by_email(value, case_insensitive=True)
504 user = User.get_by_email(value, case_insensitive=True)
505 if user is None:
505 if user is None:
506 raise formencode.Invalid(
506 raise formencode.Invalid(
507 _("This e-mail address doesn't exist."), value, state
507 _("This e-mail address doesn't exist."), value, state
508 )
508 )
509
509
510 return value
510 return value
511
511
512
512
513 class LdapLibValidator(formencode.validators.FancyValidator):
513 class LdapLibValidator(formencode.validators.FancyValidator):
514
514
515 def to_python(self, value, state):
515 def to_python(self, value, state):
516
516
517 try:
517 try:
518 import ldap
518 import ldap
519 except ImportError:
519 except ImportError:
520 raise LdapImportError
520 raise LdapImportError
521 return value
521 return value
522
522
523
523
524 class AttrLoginValidator(formencode.validators.FancyValidator):
524 class AttrLoginValidator(formencode.validators.FancyValidator):
525
525
526 def to_python(self, value, state):
526 def to_python(self, value, state):
527
527
528 if not value or not isinstance(value, (str, unicode)):
528 if not value or not isinstance(value, (str, unicode)):
529 raise formencode.Invalid(
529 raise formencode.Invalid(
530 _("The LDAP Login attribute of the CN must be specified - "
530 _("The LDAP Login attribute of the CN must be specified - "
531 "this is the name of the attribute that is equivalent "
531 "this is the name of the attribute that is equivalent "
532 "to 'username'"), value, state
532 "to 'username'"), value, state
533 )
533 )
534
534
535 return value
535 return value
536
536
537
537
538 #==============================================================================
538 #==============================================================================
539 # FORMS
539 # FORMS
540 #==============================================================================
540 #==============================================================================
541 class LoginForm(formencode.Schema):
541 class LoginForm(formencode.Schema):
542 allow_extra_fields = True
542 allow_extra_fields = True
543 filter_extra_fields = True
543 filter_extra_fields = True
544 username = UnicodeString(
544 username = UnicodeString(
545 strip=True,
545 strip=True,
546 min=1,
546 min=1,
547 not_empty=True,
547 not_empty=True,
548 messages={
548 messages={
549 'empty': _('Please enter a login'),
549 'empty': _('Please enter a login'),
550 'tooShort': _('Enter a value %(min)i characters long or more')}
550 'tooShort': _('Enter a value %(min)i characters long or more')}
551 )
551 )
552
552
553 password = UnicodeString(
553 password = UnicodeString(
554 strip=False,
554 strip=False,
555 min=3,
555 min=3,
556 not_empty=True,
556 not_empty=True,
557 messages={
557 messages={
558 'empty': _('Please enter a password'),
558 'empty': _('Please enter a password'),
559 'tooShort': _('Enter %(min)i characters or more')}
559 'tooShort': _('Enter %(min)i characters or more')}
560 )
560 )
561
561
562 remember = StringBoolean(if_missing=False)
562 remember = StringBoolean(if_missing=False)
563
563
564 chained_validators = [ValidAuth]
564 chained_validators = [ValidAuth]
565
565
566
566
567 def UserForm(edit=False, old_data={}):
567 def UserForm(edit=False, old_data={}):
568 class _UserForm(formencode.Schema):
568 class _UserForm(formencode.Schema):
569 allow_extra_fields = True
569 allow_extra_fields = True
570 filter_extra_fields = True
570 filter_extra_fields = True
571 username = All(UnicodeString(strip=True, min=1, not_empty=True),
571 username = All(UnicodeString(strip=True, min=1, not_empty=True),
572 ValidUsername(edit, old_data))
572 ValidUsername(edit, old_data))
573 if edit:
573 if edit:
574 new_password = All(UnicodeString(strip=False, min=6, not_empty=False))
574 new_password = All(UnicodeString(strip=False, min=6, not_empty=False))
575 password_confirmation = All(UnicodeString(strip=False, min=6,
575 password_confirmation = All(UnicodeString(strip=False, min=6,
576 not_empty=False))
576 not_empty=False))
577 admin = StringBoolean(if_missing=False)
577 admin = StringBoolean(if_missing=False)
578 else:
578 else:
579 password = All(UnicodeString(strip=False, min=6, not_empty=True))
579 password = All(UnicodeString(strip=False, min=6, not_empty=True))
580 password_confirmation = All(UnicodeString(strip=False, min=6,
580 password_confirmation = All(UnicodeString(strip=False, min=6,
581 not_empty=False))
581 not_empty=False))
582
582
583 active = StringBoolean(if_missing=False)
583 active = StringBoolean(if_missing=False)
584 name = UnicodeString(strip=True, min=1, not_empty=False)
584 name = UnicodeString(strip=True, min=1, not_empty=False)
585 lastname = UnicodeString(strip=True, min=1, not_empty=False)
585 lastname = UnicodeString(strip=True, min=1, not_empty=False)
586 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
586 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
587
587
588 chained_validators = [ValidPasswordsMatch, ValidPassword]
588 chained_validators = [ValidPasswordsMatch, ValidPassword]
589
589
590 return _UserForm
590 return _UserForm
591
591
592
592
593 def UsersGroupForm(edit=False, old_data={}, available_members=[]):
593 def UsersGroupForm(edit=False, old_data={}, available_members=[]):
594 class _UsersGroupForm(formencode.Schema):
594 class _UsersGroupForm(formencode.Schema):
595 allow_extra_fields = True
595 allow_extra_fields = True
596 filter_extra_fields = True
596 filter_extra_fields = True
597
597
598 users_group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
598 users_group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
599 ValidUsersGroup(edit, old_data))
599 ValidUsersGroup(edit, old_data))
600
600
601 users_group_active = StringBoolean(if_missing=False)
601 users_group_active = StringBoolean(if_missing=False)
602
602
603 if edit:
603 if edit:
604 users_group_members = OneOf(available_members, hideList=False,
604 users_group_members = OneOf(available_members, hideList=False,
605 testValueList=True,
605 testValueList=True,
606 if_missing=None, not_empty=False)
606 if_missing=None, not_empty=False)
607
607
608 return _UsersGroupForm
608 return _UsersGroupForm
609
609
610
610
611 def ReposGroupForm(edit=False, old_data={}, available_groups=[]):
611 def ReposGroupForm(edit=False, old_data={}, available_groups=[]):
612 class _ReposGroupForm(formencode.Schema):
612 class _ReposGroupForm(formencode.Schema):
613 allow_extra_fields = True
613 allow_extra_fields = True
614 filter_extra_fields = False
614 filter_extra_fields = False
615
615
616 group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
616 group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
617 SlugifyName())
617 SlugifyName())
618 group_description = UnicodeString(strip=True, min=1,
618 group_description = UnicodeString(strip=True, min=1,
619 not_empty=True)
619 not_empty=True)
620 group_parent_id = OneOf(available_groups, hideList=False,
620 group_parent_id = OneOf(available_groups, hideList=False,
621 testValueList=True,
621 testValueList=True,
622 if_missing=None, not_empty=False)
622 if_missing=None, not_empty=False)
623
623
624 chained_validators = [ValidReposGroup(edit, old_data), ValidPerms('group')]
624 chained_validators = [ValidReposGroup(edit, old_data), ValidPerms('group')]
625
625
626 return _ReposGroupForm
626 return _ReposGroupForm
627
627
628
628
629 def RegisterForm(edit=False, old_data={}):
629 def RegisterForm(edit=False, old_data={}):
630 class _RegisterForm(formencode.Schema):
630 class _RegisterForm(formencode.Schema):
631 allow_extra_fields = True
631 allow_extra_fields = True
632 filter_extra_fields = True
632 filter_extra_fields = True
633 username = All(ValidUsername(edit, old_data),
633 username = All(ValidUsername(edit, old_data),
634 UnicodeString(strip=True, min=1, not_empty=True))
634 UnicodeString(strip=True, min=1, not_empty=True))
635 password = All(UnicodeString(strip=False, min=6, not_empty=True))
635 password = All(UnicodeString(strip=False, min=6, not_empty=True))
636 password_confirmation = All(UnicodeString(strip=False, min=6, not_empty=True))
636 password_confirmation = All(UnicodeString(strip=False, min=6, not_empty=True))
637 active = StringBoolean(if_missing=False)
637 active = StringBoolean(if_missing=False)
638 name = UnicodeString(strip=True, min=1, not_empty=False)
638 name = UnicodeString(strip=True, min=1, not_empty=False)
639 lastname = UnicodeString(strip=True, min=1, not_empty=False)
639 lastname = UnicodeString(strip=True, min=1, not_empty=False)
640 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
640 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
641
641
642 chained_validators = [ValidPasswordsMatch, ValidPassword]
642 chained_validators = [ValidPasswordsMatch, ValidPassword]
643
643
644 return _RegisterForm
644 return _RegisterForm
645
645
646
646
647 def PasswordResetForm():
647 def PasswordResetForm():
648 class _PasswordResetForm(formencode.Schema):
648 class _PasswordResetForm(formencode.Schema):
649 allow_extra_fields = True
649 allow_extra_fields = True
650 filter_extra_fields = True
650 filter_extra_fields = True
651 email = All(ValidSystemEmail(), Email(not_empty=True))
651 email = All(ValidSystemEmail(), Email(not_empty=True))
652 return _PasswordResetForm
652 return _PasswordResetForm
653
653
654
654
655 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
655 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
656 repo_groups=[]):
656 repo_groups=[]):
657 class _RepoForm(formencode.Schema):
657 class _RepoForm(formencode.Schema):
658 allow_extra_fields = True
658 allow_extra_fields = True
659 filter_extra_fields = False
659 filter_extra_fields = False
660 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
660 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
661 SlugifyName())
661 SlugifyName())
662 clone_uri = All(UnicodeString(strip=True, min=1, not_empty=False))
662 clone_uri = All(UnicodeString(strip=True, min=1, not_empty=False))
663 repo_group = OneOf(repo_groups, hideList=True)
663 repo_group = OneOf(repo_groups, hideList=True)
664 repo_type = OneOf(supported_backends)
664 repo_type = OneOf(supported_backends)
665 description = UnicodeString(strip=True, min=1, not_empty=True)
665 description = UnicodeString(strip=True, min=1, not_empty=True)
666 private = StringBoolean(if_missing=False)
666 private = StringBoolean(if_missing=False)
667 enable_statistics = StringBoolean(if_missing=False)
667 enable_statistics = StringBoolean(if_missing=False)
668 enable_downloads = StringBoolean(if_missing=False)
668 enable_downloads = StringBoolean(if_missing=False)
669
669
670 if edit:
670 if edit:
671 #this is repo owner
671 #this is repo owner
672 user = All(UnicodeString(not_empty=True), ValidRepoUser)
672 user = All(UnicodeString(not_empty=True), ValidRepoUser)
673
673
674 chained_validators = [ValidCloneUri()(),
674 chained_validators = [ValidCloneUri()(),
675 ValidRepoName(edit, old_data),
675 ValidRepoName(edit, old_data),
676 ValidPerms()]
676 ValidPerms()]
677 return _RepoForm
677 return _RepoForm
678
678
679
679
680 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
680 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
681 repo_groups=[]):
681 repo_groups=[]):
682 class _RepoForkForm(formencode.Schema):
682 class _RepoForkForm(formencode.Schema):
683 allow_extra_fields = True
683 allow_extra_fields = True
684 filter_extra_fields = False
684 filter_extra_fields = False
685 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
685 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
686 SlugifyName())
686 SlugifyName())
687 repo_group = OneOf(repo_groups, hideList=True)
687 repo_group = OneOf(repo_groups, hideList=True)
688 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
688 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
689 description = UnicodeString(strip=True, min=1, not_empty=True)
689 description = UnicodeString(strip=True, min=1, not_empty=True)
690 private = StringBoolean(if_missing=False)
690 private = StringBoolean(if_missing=False)
691 copy_permissions = StringBoolean(if_missing=False)
691 copy_permissions = StringBoolean(if_missing=False)
692 update_after_clone = StringBoolean(if_missing=False)
692 update_after_clone = StringBoolean(if_missing=False)
693 fork_parent_id = UnicodeString()
693 fork_parent_id = UnicodeString()
694 chained_validators = [ValidForkName(edit, old_data)]
694 chained_validators = [ValidForkName(edit, old_data)]
695
695
696 return _RepoForkForm
696 return _RepoForkForm
697
697
698
698
699 def RepoSettingsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
699 def RepoSettingsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
700 repo_groups=[]):
700 repo_groups=[]):
701 class _RepoForm(formencode.Schema):
701 class _RepoForm(formencode.Schema):
702 allow_extra_fields = True
702 allow_extra_fields = True
703 filter_extra_fields = False
703 filter_extra_fields = False
704 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
704 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
705 SlugifyName())
705 SlugifyName())
706 description = UnicodeString(strip=True, min=1, not_empty=True)
706 description = UnicodeString(strip=True, min=1, not_empty=True)
707 repo_group = OneOf(repo_groups, hideList=True)
707 repo_group = OneOf(repo_groups, hideList=True)
708 private = StringBoolean(if_missing=False)
708 private = StringBoolean(if_missing=False)
709
709
710 chained_validators = [ValidRepoName(edit, old_data), ValidPerms(),
710 chained_validators = [ValidRepoName(edit, old_data), ValidPerms(),
711 ValidSettings]
711 ValidSettings]
712 return _RepoForm
712 return _RepoForm
713
713
714
714
715 def ApplicationSettingsForm():
715 def ApplicationSettingsForm():
716 class _ApplicationSettingsForm(formencode.Schema):
716 class _ApplicationSettingsForm(formencode.Schema):
717 allow_extra_fields = True
717 allow_extra_fields = True
718 filter_extra_fields = False
718 filter_extra_fields = False
719 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
719 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
720 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
720 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
721 rhodecode_ga_code = UnicodeString(strip=True, min=1, not_empty=False)
721 rhodecode_ga_code = UnicodeString(strip=True, min=1, not_empty=False)
722
722
723 return _ApplicationSettingsForm
723 return _ApplicationSettingsForm
724
724
725
725
726 def ApplicationUiSettingsForm():
726 def ApplicationUiSettingsForm():
727 class _ApplicationUiSettingsForm(formencode.Schema):
727 class _ApplicationUiSettingsForm(formencode.Schema):
728 allow_extra_fields = True
728 allow_extra_fields = True
729 filter_extra_fields = False
729 filter_extra_fields = False
730 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
730 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
731 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
731 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
732 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
732 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
733 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
733 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
734 hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
734 hooks_changegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
735 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
735 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
736
736
737 return _ApplicationUiSettingsForm
737 return _ApplicationUiSettingsForm
738
738
739
739
740 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
740 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
741 class _DefaultPermissionsForm(formencode.Schema):
741 class _DefaultPermissionsForm(formencode.Schema):
742 allow_extra_fields = True
742 allow_extra_fields = True
743 filter_extra_fields = True
743 filter_extra_fields = True
744 overwrite_default = StringBoolean(if_missing=False)
744 overwrite_default = StringBoolean(if_missing=False)
745 anonymous = OneOf(['True', 'False'], if_missing=False)
745 anonymous = OneOf(['True', 'False'], if_missing=False)
746 default_perm = OneOf(perms_choices)
746 default_perm = OneOf(perms_choices)
747 default_register = OneOf(register_choices)
747 default_register = OneOf(register_choices)
748 default_create = OneOf(create_choices)
748 default_create = OneOf(create_choices)
749
749
750 return _DefaultPermissionsForm
750 return _DefaultPermissionsForm
751
751
752
752
753 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices, tls_kind_choices):
753 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices, tls_kind_choices):
754 class _LdapSettingsForm(formencode.Schema):
754 class _LdapSettingsForm(formencode.Schema):
755 allow_extra_fields = True
755 allow_extra_fields = True
756 filter_extra_fields = True
756 filter_extra_fields = True
757 #pre_validators = [LdapLibValidator]
757 #pre_validators = [LdapLibValidator]
758 ldap_active = StringBoolean(if_missing=False)
758 ldap_active = StringBoolean(if_missing=False)
759 ldap_host = UnicodeString(strip=True,)
759 ldap_host = UnicodeString(strip=True,)
760 ldap_port = Number(strip=True,)
760 ldap_port = Number(strip=True,)
761 ldap_tls_kind = OneOf(tls_kind_choices)
761 ldap_tls_kind = OneOf(tls_kind_choices)
762 ldap_tls_reqcert = OneOf(tls_reqcert_choices)
762 ldap_tls_reqcert = OneOf(tls_reqcert_choices)
763 ldap_dn_user = UnicodeString(strip=True,)
763 ldap_dn_user = UnicodeString(strip=True,)
764 ldap_dn_pass = UnicodeString(strip=True,)
764 ldap_dn_pass = UnicodeString(strip=True,)
765 ldap_base_dn = UnicodeString(strip=True,)
765 ldap_base_dn = UnicodeString(strip=True,)
766 ldap_filter = UnicodeString(strip=True,)
766 ldap_filter = UnicodeString(strip=True,)
767 ldap_search_scope = OneOf(search_scope_choices)
767 ldap_search_scope = OneOf(search_scope_choices)
768 ldap_attr_login = All(AttrLoginValidator, UnicodeString(strip=True,))
768 ldap_attr_login = All(AttrLoginValidator, UnicodeString(strip=True,))
769 ldap_attr_firstname = UnicodeString(strip=True,)
769 ldap_attr_firstname = UnicodeString(strip=True,)
770 ldap_attr_lastname = UnicodeString(strip=True,)
770 ldap_attr_lastname = UnicodeString(strip=True,)
771 ldap_attr_email = UnicodeString(strip=True,)
771 ldap_attr_email = UnicodeString(strip=True,)
772
772
773 return _LdapSettingsForm
773 return _LdapSettingsForm
@@ -1,527 +1,527 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) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 from __future__ import with_statement
25 from __future__ import with_statement
26 import os
26 import os
27 import shutil
27 import shutil
28 import logging
28 import logging
29 import traceback
29 import traceback
30 import pkg_resources
30 import pkg_resources
31 from os.path import dirname as dn, join as jn
31 from os.path import dirname as dn, join as jn
32 from datetime import datetime
32 from datetime import datetime
33
33
34 from rhodecode.lib.vcs.backends import get_backend
34 from rhodecode.lib.vcs.backends import get_backend
35 from rhodecode.lib.compat import json
35 from rhodecode.lib.compat import json
36 from rhodecode.lib.utils2 import LazyProperty, safe_str, safe_unicode
36 from rhodecode.lib.utils2 import LazyProperty, safe_str, safe_unicode
37 from rhodecode.lib.caching_query import FromCache
37 from rhodecode.lib.caching_query import FromCache
38 from rhodecode.lib.hooks import log_create_repository
38 from rhodecode.lib.hooks import log_create_repository
39
39
40 from rhodecode.model import BaseModel
40 from rhodecode.model import BaseModel
41 from rhodecode.model.db import Repository, UserRepoToPerm, User, Permission, \
41 from rhodecode.model.db import Repository, UserRepoToPerm, User, Permission, \
42 Statistics, UsersGroup, UsersGroupRepoToPerm, RhodeCodeUi, RepoGroup
42 Statistics, UsersGroup, UsersGroupRepoToPerm, RhodeCodeUi, RepoGroup
43 from rhodecode.lib import helpers as h
43 from rhodecode.lib import helpers as h
44
44
45
45
46 log = logging.getLogger(__name__)
46 log = logging.getLogger(__name__)
47
47
48
48
49 class RepoModel(BaseModel):
49 class RepoModel(BaseModel):
50
50
51 def __get_user(self, user):
51 def __get_user(self, user):
52 return self._get_instance(User, user, callback=User.get_by_username)
52 return self._get_instance(User, user, callback=User.get_by_username)
53
53
54 def __get_users_group(self, users_group):
54 def __get_users_group(self, users_group):
55 return self._get_instance(UsersGroup, users_group,
55 return self._get_instance(UsersGroup, users_group,
56 callback=UsersGroup.get_by_group_name)
56 callback=UsersGroup.get_by_group_name)
57
57
58 def __get_repos_group(self, repos_group):
58 def __get_repos_group(self, repos_group):
59 return self._get_instance(RepoGroup, repos_group,
59 return self._get_instance(RepoGroup, repos_group,
60 callback=RepoGroup.get_by_group_name)
60 callback=RepoGroup.get_by_group_name)
61
61
62 def __get_repo(self, repository):
62 def __get_repo(self, repository):
63 return self._get_instance(Repository, repository,
63 return self._get_instance(Repository, repository,
64 callback=Repository.get_by_repo_name)
64 callback=Repository.get_by_repo_name)
65
65
66 def __get_perm(self, permission):
66 def __get_perm(self, permission):
67 return self._get_instance(Permission, permission,
67 return self._get_instance(Permission, permission,
68 callback=Permission.get_by_key)
68 callback=Permission.get_by_key)
69
69
70 @LazyProperty
70 @LazyProperty
71 def repos_path(self):
71 def repos_path(self):
72 """
72 """
73 Get's the repositories root path from database
73 Get's the repositories root path from database
74 """
74 """
75
75
76 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
76 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
77 return q.ui_value
77 return q.ui_value
78
78
79 def get(self, repo_id, cache=False):
79 def get(self, repo_id, cache=False):
80 repo = self.sa.query(Repository)\
80 repo = self.sa.query(Repository)\
81 .filter(Repository.repo_id == repo_id)
81 .filter(Repository.repo_id == repo_id)
82
82
83 if cache:
83 if cache:
84 repo = repo.options(FromCache("sql_cache_short",
84 repo = repo.options(FromCache("sql_cache_short",
85 "get_repo_%s" % repo_id))
85 "get_repo_%s" % repo_id))
86 return repo.scalar()
86 return repo.scalar()
87
87
88 def get_repo(self, repository):
88 def get_repo(self, repository):
89 return self.__get_repo(repository)
89 return self.__get_repo(repository)
90
90
91 def get_by_repo_name(self, repo_name, cache=False):
91 def get_by_repo_name(self, repo_name, cache=False):
92 repo = self.sa.query(Repository)\
92 repo = self.sa.query(Repository)\
93 .filter(Repository.repo_name == repo_name)
93 .filter(Repository.repo_name == repo_name)
94
94
95 if cache:
95 if cache:
96 repo = repo.options(FromCache("sql_cache_short",
96 repo = repo.options(FromCache("sql_cache_short",
97 "get_repo_%s" % repo_name))
97 "get_repo_%s" % repo_name))
98 return repo.scalar()
98 return repo.scalar()
99
99
100 def get_users_js(self):
100 def get_users_js(self):
101 users = self.sa.query(User).filter(User.active == True).all()
101 users = self.sa.query(User).filter(User.active == True).all()
102 return json.dumps([
102 return json.dumps([
103 {
103 {
104 'id': u.user_id,
104 'id': u.user_id,
105 'fname': u.name,
105 'fname': u.name,
106 'lname': u.lastname,
106 'lname': u.lastname,
107 'nname': u.username,
107 'nname': u.username,
108 'gravatar_lnk': h.gravatar_url(u.email, 14)
108 'gravatar_lnk': h.gravatar_url(u.email, 14)
109 } for u in users]
109 } for u in users]
110 )
110 )
111
111
112 def get_users_groups_js(self):
112 def get_users_groups_js(self):
113 users_groups = self.sa.query(UsersGroup)\
113 users_groups = self.sa.query(UsersGroup)\
114 .filter(UsersGroup.users_group_active == True).all()
114 .filter(UsersGroup.users_group_active == True).all()
115
115
116 return json.dumps([
116 return json.dumps([
117 {
117 {
118 'id': gr.users_group_id,
118 'id': gr.users_group_id,
119 'grname': gr.users_group_name,
119 'grname': gr.users_group_name,
120 'grmembers': len(gr.members),
120 'grmembers': len(gr.members),
121 } for gr in users_groups]
121 } for gr in users_groups]
122 )
122 )
123
123
124 def _get_defaults(self, repo_name):
124 def _get_defaults(self, repo_name):
125 """
125 """
126 Get's information about repository, and returns a dict for
126 Get's information about repository, and returns a dict for
127 usage in forms
127 usage in forms
128
128
129 :param repo_name:
129 :param repo_name:
130 """
130 """
131
131
132 repo_info = Repository.get_by_repo_name(repo_name)
132 repo_info = Repository.get_by_repo_name(repo_name)
133
133
134 if repo_info is None:
134 if repo_info is None:
135 return None
135 return None
136
136
137 defaults = repo_info.get_dict()
137 defaults = repo_info.get_dict()
138 group, repo_name = repo_info.groups_and_repo
138 group, repo_name = repo_info.groups_and_repo
139 defaults['repo_name'] = repo_name
139 defaults['repo_name'] = repo_name
140 defaults['repo_group'] = getattr(group[-1] if group else None,
140 defaults['repo_group'] = getattr(group[-1] if group else None,
141 'group_id', None)
141 'group_id', None)
142
142
143 # fill owner
143 # fill owner
144 if repo_info.user:
144 if repo_info.user:
145 defaults.update({'user': repo_info.user.username})
145 defaults.update({'user': repo_info.user.username})
146 else:
146 else:
147 replacement_user = User.query().filter(User.admin ==
147 replacement_user = User.query().filter(User.admin ==
148 True).first().username
148 True).first().username
149 defaults.update({'user': replacement_user})
149 defaults.update({'user': replacement_user})
150
150
151 # fill repository users
151 # fill repository users
152 for p in repo_info.repo_to_perm:
152 for p in repo_info.repo_to_perm:
153 defaults.update({'u_perm_%s' % p.user.username:
153 defaults.update({'u_perm_%s' % p.user.username:
154 p.permission.permission_name})
154 p.permission.permission_name})
155
155
156 # fill repository groups
156 # fill repository groups
157 for p in repo_info.users_group_to_perm:
157 for p in repo_info.users_group_to_perm:
158 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
158 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
159 p.permission.permission_name})
159 p.permission.permission_name})
160
160
161 return defaults
161 return defaults
162
162
163 def update(self, repo_name, form_data):
163 def update(self, repo_name, form_data):
164 try:
164 try:
165 cur_repo = self.get_by_repo_name(repo_name, cache=False)
165 cur_repo = self.get_by_repo_name(repo_name, cache=False)
166
166
167 # update permissions
167 # update permissions
168 for member, perm, member_type in form_data['perms_updates']:
168 for member, perm, member_type in form_data['perms_updates']:
169 if member_type == 'user':
169 if member_type == 'user':
170 # this updates existing one
170 # this updates existing one
171 RepoModel().grant_user_permission(
171 RepoModel().grant_user_permission(
172 repo=cur_repo, user=member, perm=perm
172 repo=cur_repo, user=member, perm=perm
173 )
173 )
174 else:
174 else:
175 RepoModel().grant_users_group_permission(
175 RepoModel().grant_users_group_permission(
176 repo=cur_repo, group_name=member, perm=perm
176 repo=cur_repo, group_name=member, perm=perm
177 )
177 )
178 # set new permissions
178 # set new permissions
179 for member, perm, member_type in form_data['perms_new']:
179 for member, perm, member_type in form_data['perms_new']:
180 if member_type == 'user':
180 if member_type == 'user':
181 RepoModel().grant_user_permission(
181 RepoModel().grant_user_permission(
182 repo=cur_repo, user=member, perm=perm
182 repo=cur_repo, user=member, perm=perm
183 )
183 )
184 else:
184 else:
185 RepoModel().grant_users_group_permission(
185 RepoModel().grant_users_group_permission(
186 repo=cur_repo, group_name=member, perm=perm
186 repo=cur_repo, group_name=member, perm=perm
187 )
187 )
188
188
189 # update current repo
189 # update current repo
190 for k, v in form_data.items():
190 for k, v in form_data.items():
191 if k == 'user':
191 if k == 'user':
192 cur_repo.user = User.get_by_username(v)
192 cur_repo.user = User.get_by_username(v)
193 elif k == 'repo_name':
193 elif k == 'repo_name':
194 pass
194 pass
195 elif k == 'repo_group':
195 elif k == 'repo_group':
196 cur_repo.group = RepoGroup.get(v)
196 cur_repo.group = RepoGroup.get(v)
197
197
198 else:
198 else:
199 setattr(cur_repo, k, v)
199 setattr(cur_repo, k, v)
200
200
201 new_name = cur_repo.get_new_name(form_data['repo_name'])
201 new_name = cur_repo.get_new_name(form_data['repo_name'])
202 cur_repo.repo_name = new_name
202 cur_repo.repo_name = new_name
203
203
204 self.sa.add(cur_repo)
204 self.sa.add(cur_repo)
205
205
206 if repo_name != new_name:
206 if repo_name != new_name:
207 # rename repository
207 # rename repository
208 self.__rename_repo(old=repo_name, new=new_name)
208 self.__rename_repo(old=repo_name, new=new_name)
209
209
210 return cur_repo
210 return cur_repo
211 except:
211 except:
212 log.error(traceback.format_exc())
212 log.error(traceback.format_exc())
213 raise
213 raise
214
214
215 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):
216 from rhodecode.model.scm import ScmModel
216 from rhodecode.model.scm import ScmModel
217
217
218 try:
218 try:
219 if fork:
219 if fork:
220 fork_parent_id = form_data['fork_parent_id']
220 fork_parent_id = form_data['fork_parent_id']
221
221
222 # repo name is just a name of repository
222 # repo name is just a name of repository
223 # while repo_name_full is a full qualified name that is combined
223 # while repo_name_full is a full qualified name that is combined
224 # with name and path of group
224 # with name and path of group
225 repo_name = form_data['repo_name']
225 repo_name = form_data['repo_name']
226 repo_name_full = form_data['repo_name_full']
226 repo_name_full = form_data['repo_name_full']
227
227
228 new_repo = Repository()
228 new_repo = Repository()
229 new_repo.enable_statistics = False
229 new_repo.enable_statistics = False
230
230
231 for k, v in form_data.items():
231 for k, v in form_data.items():
232 if k == 'repo_name':
232 if k == 'repo_name':
233 v = repo_name_full
233 v = repo_name_full
234 if k == 'repo_group':
234 if k == 'repo_group':
235 k = 'group_id'
235 k = 'group_id'
236 if k == 'description':
236 if k == 'description':
237 v = v or repo_name
237 v = v or repo_name
238
238
239 setattr(new_repo, k, v)
239 setattr(new_repo, k, v)
240
240
241 if fork:
241 if fork:
242 parent_repo = Repository.get(fork_parent_id)
242 parent_repo = Repository.get(fork_parent_id)
243 new_repo.fork = parent_repo
243 new_repo.fork = parent_repo
244
244
245 new_repo.user_id = cur_user.user_id
245 new_repo.user_id = cur_user.user_id
246 self.sa.add(new_repo)
246 self.sa.add(new_repo)
247
247
248 def _create_default_perms():
248 def _create_default_perms():
249 # create default permission
249 # create default permission
250 repo_to_perm = UserRepoToPerm()
250 repo_to_perm = UserRepoToPerm()
251 default = 'repository.read'
251 default = 'repository.read'
252 for p in User.get_by_username('default').user_perms:
252 for p in User.get_by_username('default').user_perms:
253 if p.permission.permission_name.startswith('repository.'):
253 if p.permission.permission_name.startswith('repository.'):
254 default = p.permission.permission_name
254 default = p.permission.permission_name
255 break
255 break
256
256
257 default_perm = 'repository.none' if form_data['private'] else default
257 default_perm = 'repository.none' if form_data['private'] else default
258
258
259 repo_to_perm.permission_id = self.sa.query(Permission)\
259 repo_to_perm.permission_id = self.sa.query(Permission)\
260 .filter(Permission.permission_name == default_perm)\
260 .filter(Permission.permission_name == default_perm)\
261 .one().permission_id
261 .one().permission_id
262
262
263 repo_to_perm.repository = new_repo
263 repo_to_perm.repository = new_repo
264 repo_to_perm.user_id = User.get_by_username('default').user_id
264 repo_to_perm.user_id = User.get_by_username('default').user_id
265
265
266 self.sa.add(repo_to_perm)
266 self.sa.add(repo_to_perm)
267
267
268 if fork:
268 if fork:
269 if form_data.get('copy_permissions'):
269 if form_data.get('copy_permissions'):
270 repo = Repository.get(fork_parent_id)
270 repo = Repository.get(fork_parent_id)
271 user_perms = UserRepoToPerm.query()\
271 user_perms = UserRepoToPerm.query()\
272 .filter(UserRepoToPerm.repository == repo).all()
272 .filter(UserRepoToPerm.repository == repo).all()
273 group_perms = UsersGroupRepoToPerm.query()\
273 group_perms = UsersGroupRepoToPerm.query()\
274 .filter(UsersGroupRepoToPerm.repository == repo).all()
274 .filter(UsersGroupRepoToPerm.repository == repo).all()
275
275
276 for perm in user_perms:
276 for perm in user_perms:
277 UserRepoToPerm.create(perm.user, new_repo,
277 UserRepoToPerm.create(perm.user, new_repo,
278 perm.permission)
278 perm.permission)
279
279
280 for perm in group_perms:
280 for perm in group_perms:
281 UsersGroupRepoToPerm.create(perm.users_group, new_repo,
281 UsersGroupRepoToPerm.create(perm.users_group, new_repo,
282 perm.permission)
282 perm.permission)
283 else:
283 else:
284 _create_default_perms()
284 _create_default_perms()
285 else:
285 else:
286 _create_default_perms()
286 _create_default_perms()
287
287
288 if not just_db:
288 if not just_db:
289 self.__create_repo(repo_name, form_data['repo_type'],
289 self.__create_repo(repo_name, form_data['repo_type'],
290 form_data['repo_group'],
290 form_data['repo_group'],
291 form_data['clone_uri'])
291 form_data['clone_uri'])
292 log_create_repository(new_repo.get_dict(),
292 log_create_repository(new_repo.get_dict(),
293 created_by=cur_user.username)
293 created_by=cur_user.username)
294
294
295 # now automatically start following this repository as owner
295 # now automatically start following this repository as owner
296 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
296 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
297 cur_user.user_id)
297 cur_user.user_id)
298 return new_repo
298 return new_repo
299 except:
299 except:
300 log.error(traceback.format_exc())
300 log.error(traceback.format_exc())
301 raise
301 raise
302
302
303 def create_fork(self, form_data, cur_user):
303 def create_fork(self, form_data, cur_user):
304 """
304 """
305 Simple wrapper into executing celery task for fork creation
305 Simple wrapper into executing celery task for fork creation
306
306
307 :param form_data:
307 :param form_data:
308 :param cur_user:
308 :param cur_user:
309 """
309 """
310 from rhodecode.lib.celerylib import tasks, run_task
310 from rhodecode.lib.celerylib import tasks, run_task
311 run_task(tasks.create_repo_fork, form_data, cur_user)
311 run_task(tasks.create_repo_fork, form_data, cur_user)
312
312
313 def delete(self, repo):
313 def delete(self, repo):
314 repo = self.__get_repo(repo)
314 repo = self.__get_repo(repo)
315 try:
315 try:
316 self.sa.delete(repo)
316 self.sa.delete(repo)
317 self.__delete_repo(repo)
317 self.__delete_repo(repo)
318 except:
318 except:
319 log.error(traceback.format_exc())
319 log.error(traceback.format_exc())
320 raise
320 raise
321
321
322 def grant_user_permission(self, repo, user, perm):
322 def grant_user_permission(self, repo, user, perm):
323 """
323 """
324 Grant permission for user on given repository, or update existing one
324 Grant permission for user on given repository, or update existing one
325 if found
325 if found
326
326
327 :param repo: Instance of Repository, repository_id, or repository name
327 :param repo: Instance of Repository, repository_id, or repository name
328 :param user: Instance of User, user_id or username
328 :param user: Instance of User, user_id or username
329 :param perm: Instance of Permission, or permission_name
329 :param perm: Instance of Permission, or permission_name
330 """
330 """
331 user = self.__get_user(user)
331 user = self.__get_user(user)
332 repo = self.__get_repo(repo)
332 repo = self.__get_repo(repo)
333 permission = self.__get_perm(perm)
333 permission = self.__get_perm(perm)
334
334
335 # check if we have that permission already
335 # check if we have that permission already
336 obj = self.sa.query(UserRepoToPerm)\
336 obj = self.sa.query(UserRepoToPerm)\
337 .filter(UserRepoToPerm.user == user)\
337 .filter(UserRepoToPerm.user == user)\
338 .filter(UserRepoToPerm.repository == repo)\
338 .filter(UserRepoToPerm.repository == repo)\
339 .scalar()
339 .scalar()
340 if obj is None:
340 if obj is None:
341 # create new !
341 # create new !
342 obj = UserRepoToPerm()
342 obj = UserRepoToPerm()
343 obj.repository = repo
343 obj.repository = repo
344 obj.user = user
344 obj.user = user
345 obj.permission = permission
345 obj.permission = permission
346 self.sa.add(obj)
346 self.sa.add(obj)
347
347
348 def revoke_user_permission(self, repo, user):
348 def revoke_user_permission(self, repo, user):
349 """
349 """
350 Revoke permission for user on given repository
350 Revoke permission for user on given repository
351
351
352 :param repo: Instance of Repository, repository_id, or repository name
352 :param repo: Instance of Repository, repository_id, or repository name
353 :param user: Instance of User, user_id or username
353 :param user: Instance of User, user_id or username
354 """
354 """
355
355
356 user = self.__get_user(user)
356 user = self.__get_user(user)
357 repo = self.__get_repo(repo)
357 repo = self.__get_repo(repo)
358
358
359 obj = self.sa.query(UserRepoToPerm)\
359 obj = self.sa.query(UserRepoToPerm)\
360 .filter(UserRepoToPerm.repository == repo)\
360 .filter(UserRepoToPerm.repository == repo)\
361 .filter(UserRepoToPerm.user == user)\
361 .filter(UserRepoToPerm.user == user)\
362 .one()
362 .one()
363 self.sa.delete(obj)
363 self.sa.delete(obj)
364
364
365 def grant_users_group_permission(self, repo, group_name, perm):
365 def grant_users_group_permission(self, repo, group_name, perm):
366 """
366 """
367 Grant permission for users group on given repository, or update
367 Grant permission for users group on given repository, or update
368 existing one if found
368 existing one if found
369
369
370 :param repo: Instance of Repository, repository_id, or repository name
370 :param repo: Instance of Repository, repository_id, or repository name
371 :param group_name: Instance of UserGroup, users_group_id,
371 :param group_name: Instance of UserGroup, users_group_id,
372 or users group name
372 or users group name
373 :param perm: Instance of Permission, or permission_name
373 :param perm: Instance of Permission, or permission_name
374 """
374 """
375 repo = self.__get_repo(repo)
375 repo = self.__get_repo(repo)
376 group_name = self.__get_users_group(group_name)
376 group_name = self.__get_users_group(group_name)
377 permission = self.__get_perm(perm)
377 permission = self.__get_perm(perm)
378
378
379 # check if we have that permission already
379 # check if we have that permission already
380 obj = self.sa.query(UsersGroupRepoToPerm)\
380 obj = self.sa.query(UsersGroupRepoToPerm)\
381 .filter(UsersGroupRepoToPerm.users_group == group_name)\
381 .filter(UsersGroupRepoToPerm.users_group == group_name)\
382 .filter(UsersGroupRepoToPerm.repository == repo)\
382 .filter(UsersGroupRepoToPerm.repository == repo)\
383 .scalar()
383 .scalar()
384
384
385 if obj is None:
385 if obj is None:
386 # create new
386 # create new
387 obj = UsersGroupRepoToPerm()
387 obj = UsersGroupRepoToPerm()
388
388
389 obj.repository = repo
389 obj.repository = repo
390 obj.users_group = group_name
390 obj.users_group = group_name
391 obj.permission = permission
391 obj.permission = permission
392 self.sa.add(obj)
392 self.sa.add(obj)
393
393
394 def revoke_users_group_permission(self, repo, group_name):
394 def revoke_users_group_permission(self, repo, group_name):
395 """
395 """
396 Revoke permission for users group on given repository
396 Revoke permission for users group on given repository
397
397
398 :param repo: Instance of Repository, repository_id, or repository name
398 :param repo: Instance of Repository, repository_id, or repository name
399 :param group_name: Instance of UserGroup, users_group_id,
399 :param group_name: Instance of UserGroup, users_group_id,
400 or users group name
400 or users group name
401 """
401 """
402 repo = self.__get_repo(repo)
402 repo = self.__get_repo(repo)
403 group_name = self.__get_users_group(group_name)
403 group_name = self.__get_users_group(group_name)
404
404
405 obj = self.sa.query(UsersGroupRepoToPerm)\
405 obj = self.sa.query(UsersGroupRepoToPerm)\
406 .filter(UsersGroupRepoToPerm.repository == repo)\
406 .filter(UsersGroupRepoToPerm.repository == repo)\
407 .filter(UsersGroupRepoToPerm.users_group == group_name)\
407 .filter(UsersGroupRepoToPerm.users_group == group_name)\
408 .one()
408 .one()
409 self.sa.delete(obj)
409 self.sa.delete(obj)
410
410
411 def delete_stats(self, repo_name):
411 def delete_stats(self, repo_name):
412 """
412 """
413 removes stats for given repo
413 removes stats for given repo
414
414
415 :param repo_name:
415 :param repo_name:
416 """
416 """
417 try:
417 try:
418 obj = self.sa.query(Statistics)\
418 obj = self.sa.query(Statistics)\
419 .filter(Statistics.repository ==
419 .filter(Statistics.repository ==
420 self.get_by_repo_name(repo_name))\
420 self.get_by_repo_name(repo_name))\
421 .one()
421 .one()
422 self.sa.delete(obj)
422 self.sa.delete(obj)
423 except:
423 except:
424 log.error(traceback.format_exc())
424 log.error(traceback.format_exc())
425 raise
425 raise
426
426
427 def __create_repo(self, repo_name, alias, new_parent_id, clone_uri=False):
427 def __create_repo(self, repo_name, alias, new_parent_id, clone_uri=False):
428 """
428 """
429 makes repository on filesystem. It's group aware means it'll create
429 makes repository on filesystem. It's group aware means it'll create
430 a repository within a group, and alter the paths accordingly of
430 a repository within a group, and alter the paths accordingly of
431 group location
431 group location
432
432
433 :param repo_name:
433 :param repo_name:
434 :param alias:
434 :param alias:
435 :param parent_id:
435 :param parent_id:
436 :param clone_uri:
436 :param clone_uri:
437 """
437 """
438 from rhodecode.lib.utils import is_valid_repo, is_valid_repos_group
438 from rhodecode.lib.utils import is_valid_repo, is_valid_repos_group
439
439
440 if new_parent_id:
440 if new_parent_id:
441 paths = RepoGroup.get(new_parent_id)\
441 paths = RepoGroup.get(new_parent_id)\
442 .full_path.split(RepoGroup.url_sep())
442 .full_path.split(RepoGroup.url_sep())
443 new_parent_path = os.sep.join(paths)
443 new_parent_path = os.sep.join(paths)
444 else:
444 else:
445 new_parent_path = ''
445 new_parent_path = ''
446
446
447 # we need to make it str for mercurial
447 # we need to make it str for mercurial
448 repo_path = os.path.join(*map(lambda x: safe_str(x),
448 repo_path = os.path.join(*map(lambda x: safe_str(x),
449 [self.repos_path, new_parent_path, repo_name]))
449 [self.repos_path, new_parent_path, repo_name]))
450
450
451 # check if this path is not a repository
451 # check if this path is not a repository
452 if is_valid_repo(repo_path, self.repos_path):
452 if is_valid_repo(repo_path, self.repos_path):
453 raise Exception('This path %s is a valid repository' % repo_path)
453 raise Exception('This path %s is a valid repository' % repo_path)
454
454
455 # check if this path is a group
455 # check if this path is a group
456 if is_valid_repos_group(repo_path, self.repos_path):
456 if is_valid_repos_group(repo_path, self.repos_path):
457 raise Exception('This path %s is a valid group' % repo_path)
457 raise Exception('This path %s is a valid group' % repo_path)
458
458
459 log.info('creating repo %s in %s @ %s' % (
459 log.info('creating repo %s in %s @ %s' % (
460 repo_name, safe_unicode(repo_path), clone_uri
460 repo_name, safe_unicode(repo_path), clone_uri
461 )
461 )
462 )
462 )
463 backend = get_backend(alias)
463 backend = get_backend(alias)
464 if alias == 'hg':
464 if alias == 'hg':
465 backend(repo_path, create=True, src_url=clone_uri)
465 backend(repo_path, create=True, src_url=clone_uri)
466 elif alias == 'git':
466 elif alias == 'git':
467 r = backend(repo_path, create=True, src_url=clone_uri, bare=True)
467 r = backend(repo_path, create=True, src_url=clone_uri, bare=True)
468 # add rhodecode hook into this repo
468 # add rhodecode hook into this repo
469
469
470 loc = jn(r.path, 'hooks')
470 loc = jn(r.path, 'hooks')
471 if not r.bare:
471 if not r.bare:
472 loc = jn(r.path, '.git', 'hooks')
472 loc = jn(r.path, '.git', 'hooks')
473 if not os.path.isdir(loc):
473 if not os.path.isdir(loc):
474 os.makedirs(loc)
474 os.makedirs(loc)
475
475
476 tmpl = pkg_resources.resource_string(
476 tmpl = pkg_resources.resource_string(
477 'rhodecode', jn('config', 'pre_receive_tmpl.py')
477 'rhodecode', jn('config', 'pre_receive_tmpl.py')
478 )
478 )
479 _hook_file = jn(loc, 'pre-receive')
479 _hook_file = jn(loc, 'post-receive')
480 with open(_hook_file, 'wb') as f:
480 with open(_hook_file, 'wb') as f:
481 f.write(tmpl)
481 f.write(tmpl)
482 os.chmod(_hook_file, 0555)
482 os.chmod(_hook_file, 0555)
483
483
484 else:
484 else:
485 raise Exception('Undefined alias %s' % alias)
485 raise Exception('Undefined alias %s' % alias)
486
486
487 def __rename_repo(self, old, new):
487 def __rename_repo(self, old, new):
488 """
488 """
489 renames repository on filesystem
489 renames repository on filesystem
490
490
491 :param old: old name
491 :param old: old name
492 :param new: new name
492 :param new: new name
493 """
493 """
494 log.info('renaming repo from %s to %s' % (old, new))
494 log.info('renaming repo from %s to %s' % (old, new))
495
495
496 old_path = os.path.join(self.repos_path, old)
496 old_path = os.path.join(self.repos_path, old)
497 new_path = os.path.join(self.repos_path, new)
497 new_path = os.path.join(self.repos_path, new)
498 if os.path.isdir(new_path):
498 if os.path.isdir(new_path):
499 raise Exception(
499 raise Exception(
500 'Was trying to rename to already existing dir %s' % new_path
500 'Was trying to rename to already existing dir %s' % new_path
501 )
501 )
502 shutil.move(old_path, new_path)
502 shutil.move(old_path, new_path)
503
503
504 def __delete_repo(self, repo):
504 def __delete_repo(self, repo):
505 """
505 """
506 removes repo from filesystem, the removal is acctually made by
506 removes repo from filesystem, the removal is acctually made by
507 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
507 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
508 repository is no longer valid for rhodecode, can be undeleted later on
508 repository is no longer valid for rhodecode, can be undeleted later on
509 by reverting the renames on this repository
509 by reverting the renames on this repository
510
510
511 :param repo: repo object
511 :param repo: repo object
512 """
512 """
513 rm_path = os.path.join(self.repos_path, repo.repo_name)
513 rm_path = os.path.join(self.repos_path, repo.repo_name)
514 log.info("Removing %s" % (rm_path))
514 log.info("Removing %s" % (rm_path))
515 # disable hg/git internal that it doesn't get detected as repo
515 # disable hg/git internal that it doesn't get detected as repo
516 alias = repo.repo_type
516 alias = repo.repo_type
517
517
518 bare = getattr(repo.scm_instance, 'bare', False)
518 bare = getattr(repo.scm_instance, 'bare', False)
519
519
520 if not bare:
520 if not bare:
521 # skip this for bare git repos
521 # skip this for bare git repos
522 shutil.move(os.path.join(rm_path, '.%s' % alias),
522 shutil.move(os.path.join(rm_path, '.%s' % alias),
523 os.path.join(rm_path, 'rm__.%s' % alias))
523 os.path.join(rm_path, 'rm__.%s' % alias))
524 # disable repo
524 # disable repo
525 _d = 'rm__%s__%s' % (datetime.now().strftime('%Y%m%d_%H%M%S_%f'),
525 _d = 'rm__%s__%s' % (datetime.now().strftime('%Y%m%d_%H%M%S_%f'),
526 repo.repo_name)
526 repo.repo_name)
527 shutil.move(rm_path, os.path.join(self.repos_path, _d))
527 shutil.move(rm_path, os.path.join(self.repos_path, _d))
@@ -1,246 +1,246 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'))} &raquo; ${_('Settings')}
9 ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; ${_('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="destroy">
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>${_('index build option')}:</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="full_index">${_('build from scratch')}</label>
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="rhodecode_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>${_('Web')}:</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>${_('Hooks')}:</label>
139 <label>${_('Hooks')}:</label>
140 </div>
140 </div>
141 <div class="checkboxes">
141 <div class="checkboxes">
142 <div class="checkbox">
142 <div class="checkbox">
143 ${h.checkbox('hooks_changegroup_update','True')}
143 ${h.checkbox('hooks_changegroup_update','True')}
144 <label for="hooks_changegroup_update">${_('Update repository after push (hg update)')}</label>
144 <label for="hooks_changegroup_update">${_('Update repository after push (hg update)')}</label>
145 </div>
145 </div>
146 <div class="checkbox">
146 <div class="checkbox">
147 ${h.checkbox('hooks_changegroup_repo_size','True')}
147 ${h.checkbox('hooks_changegroup_repo_size','True')}
148 <label for="hooks_changegroup_repo_size">${_('Show repository size after push')}</label>
148 <label for="hooks_changegroup_repo_size">${_('Show repository size after push')}</label>
149 </div>
149 </div>
150 <div class="checkbox">
150 <div class="checkbox">
151 ${h.checkbox('hooks_pretxnchangegroup_push_logger','True')}
151 ${h.checkbox('hooks_changegroup_push_logger','True')}
152 <label for="hooks_pretxnchangegroup_push_logger">${_('Log user push commands')}</label>
152 <label for="hooks_changegroup_push_logger">${_('Log user push commands')}</label>
153 </div>
153 </div>
154 <div class="checkbox">
154 <div class="checkbox">
155 ${h.checkbox('hooks_preoutgoing_pull_logger','True')}
155 ${h.checkbox('hooks_preoutgoing_pull_logger','True')}
156 <label for="hooks_preoutgoing_pull_logger">${_('Log user pull commands')}</label>
156 <label for="hooks_preoutgoing_pull_logger">${_('Log user pull commands')}</label>
157 </div>
157 </div>
158 </div>
158 </div>
159 <div class="input" style="margin-top:10px">
159 <div class="input" style="margin-top:10px">
160 ${h.link_to(_('advanced setup'),url('admin_edit_setting',setting_id='hooks'),class_="ui-btn")}
160 ${h.link_to(_('advanced setup'),url('admin_edit_setting',setting_id='hooks'),class_="ui-btn")}
161 </div>
161 </div>
162 </div>
162 </div>
163 <div class="field">
163 <div class="field">
164 <div class="label">
164 <div class="label">
165 <label for="paths_root_path">${_('Repositories location')}:</label>
165 <label for="paths_root_path">${_('Repositories location')}:</label>
166 </div>
166 </div>
167 <div class="input">
167 <div class="input">
168 ${h.text('paths_root_path',size=30,readonly="readonly")}
168 ${h.text('paths_root_path',size=30,readonly="readonly")}
169 <span id="path_unlock" class="tooltip"
169 <span id="path_unlock" class="tooltip"
170 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.'))}">
170 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 ${_('unlock')}</span>
171 ${_('unlock')}</span>
172 </div>
172 </div>
173 </div>
173 </div>
174
174
175 <div class="buttons">
175 <div class="buttons">
176 ${h.submit('save',_('Save settings'),class_="ui-button")}
176 ${h.submit('save',_('Save settings'),class_="ui-button")}
177 ${h.reset('reset',_('Reset'),class_="ui-button")}
177 ${h.reset('reset',_('Reset'),class_="ui-button")}
178 </div>
178 </div>
179 </div>
179 </div>
180 </div>
180 </div>
181 ${h.end_form()}
181 ${h.end_form()}
182
182
183 <script type="text/javascript">
183 <script type="text/javascript">
184 YAHOO.util.Event.onDOMReady(function(){
184 YAHOO.util.Event.onDOMReady(function(){
185 YAHOO.util.Event.addListener('path_unlock','click',function(){
185 YAHOO.util.Event.addListener('path_unlock','click',function(){
186 YAHOO.util.Dom.get('paths_root_path').removeAttribute('readonly');
186 YAHOO.util.Dom.get('paths_root_path').removeAttribute('readonly');
187 });
187 });
188 });
188 });
189 </script>
189 </script>
190
190
191 <h3>${_('Test Email')}</h3>
191 <h3>${_('Test Email')}</h3>
192 ${h.form(url('admin_setting', setting_id='email'),method='put')}
192 ${h.form(url('admin_setting', setting_id='email'),method='put')}
193 <div class="form">
193 <div class="form">
194 <!-- fields -->
194 <!-- fields -->
195
195
196 <div class="fields">
196 <div class="fields">
197 <div class="field">
197 <div class="field">
198 <div class="label">
198 <div class="label">
199 <label for="test_email">${_('Email to')}:</label>
199 <label for="test_email">${_('Email to')}:</label>
200 </div>
200 </div>
201 <div class="input">
201 <div class="input">
202 ${h.text('test_email',size=30)}
202 ${h.text('test_email',size=30)}
203 </div>
203 </div>
204 </div>
204 </div>
205
205
206 <div class="buttons">
206 <div class="buttons">
207 ${h.submit('send',_('Send'),class_="ui-button")}
207 ${h.submit('send',_('Send'),class_="ui-button")}
208 </div>
208 </div>
209 </div>
209 </div>
210 </div>
210 </div>
211 ${h.end_form()}
211 ${h.end_form()}
212
212
213 <h3>${_('System Info and Packages')}</h3>
213 <h3>${_('System Info and Packages')}</h3>
214 <div class="form">
214 <div class="form">
215 <div>
215 <div>
216 <h5 id="expand_modules" style="cursor: pointer">&darr; ${_('show')} &darr;</h5>
216 <h5 id="expand_modules" style="cursor: pointer">&darr; ${_('show')} &darr;</h5>
217 </div>
217 </div>
218 <div id="expand_modules_table" style="display:none">
218 <div id="expand_modules_table" style="display:none">
219 <h5>Python - ${c.py_version}</h5>
219 <h5>Python - ${c.py_version}</h5>
220 <h5>System - ${c.platform}</h5>
220 <h5>System - ${c.platform}</h5>
221
221
222 <table class="table" style="margin:0px 0px 0px 20px">
222 <table class="table" style="margin:0px 0px 0px 20px">
223 <colgroup>
223 <colgroup>
224 <col style="width:220px">
224 <col style="width:220px">
225 </colgroup>
225 </colgroup>
226 <tbody>
226 <tbody>
227 %for key, value in c.modules:
227 %for key, value in c.modules:
228 <tr>
228 <tr>
229 <th style="text-align: right;padding-right:5px;">${key}</th>
229 <th style="text-align: right;padding-right:5px;">${key}</th>
230 <td>${value}</td>
230 <td>${value}</td>
231 </tr>
231 </tr>
232 %endfor
232 %endfor
233 </tbody>
233 </tbody>
234 </table>
234 </table>
235 </div>
235 </div>
236 </div>
236 </div>
237
237
238 <script type="text/javascript">
238 <script type="text/javascript">
239 YUE.on('expand_modules','click',function(e){
239 YUE.on('expand_modules','click',function(e){
240 YUD.setStyle('expand_modules_table','display','');
240 YUD.setStyle('expand_modules_table','display','');
241 YUD.setStyle('expand_modules','display','none');
241 YUD.setStyle('expand_modules','display','none');
242 })
242 })
243 </script>
243 </script>
244
244
245 </div>
245 </div>
246 </%def>
246 </%def>
General Comments 0
You need to be logged in to leave comments. Login now