##// END OF EJS Templates
improved extraction of user from changeset when sending notification....
marcink -
r3185:a665d8cd beta
parent child Browse files
Show More
@@ -1,241 +1,244 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.comment
3 rhodecode.model.comment
4 ~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 comments model for RhodeCode
6 comments model for RhodeCode
7
7
8 :created_on: Nov 11, 2011
8 :created_on: Nov 11, 2011
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28
28
29 from pylons.i18n.translation import _
29 from pylons.i18n.translation import _
30 from sqlalchemy.util.compat import defaultdict
30 from sqlalchemy.util.compat import defaultdict
31
31
32 from rhodecode.lib.utils2 import extract_mentioned_users, safe_unicode
32 from rhodecode.lib.utils2 import extract_mentioned_users, safe_unicode
33 from rhodecode.lib import helpers as h
33 from rhodecode.lib import helpers as h
34 from rhodecode.model import BaseModel
34 from rhodecode.model import BaseModel
35 from rhodecode.model.db import ChangesetComment, User, Repository, \
35 from rhodecode.model.db import ChangesetComment, User, Repository, \
36 Notification, PullRequest
36 Notification, PullRequest
37 from rhodecode.model.notification import NotificationModel
37 from rhodecode.model.notification import NotificationModel
38
38
39 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
40
40
41
41
42 class ChangesetCommentsModel(BaseModel):
42 class ChangesetCommentsModel(BaseModel):
43
43
44 cls = ChangesetComment
44 cls = ChangesetComment
45
45
46 def __get_changeset_comment(self, changeset_comment):
46 def __get_changeset_comment(self, changeset_comment):
47 return self._get_instance(ChangesetComment, changeset_comment)
47 return self._get_instance(ChangesetComment, changeset_comment)
48
48
49 def __get_pull_request(self, pull_request):
49 def __get_pull_request(self, pull_request):
50 return self._get_instance(PullRequest, pull_request)
50 return self._get_instance(PullRequest, pull_request)
51
51
52 def _extract_mentions(self, s):
52 def _extract_mentions(self, s):
53 user_objects = []
53 user_objects = []
54 for username in extract_mentioned_users(s):
54 for username in extract_mentioned_users(s):
55 user_obj = User.get_by_username(username, case_insensitive=True)
55 user_obj = User.get_by_username(username, case_insensitive=True)
56 if user_obj:
56 if user_obj:
57 user_objects.append(user_obj)
57 user_objects.append(user_obj)
58 return user_objects
58 return user_objects
59
59
60 def create(self, text, repo, user, revision=None, pull_request=None,
60 def create(self, text, repo, user, revision=None, pull_request=None,
61 f_path=None, line_no=None, status_change=None):
61 f_path=None, line_no=None, status_change=None):
62 """
62 """
63 Creates new comment for changeset or pull request.
63 Creates new comment for changeset or pull request.
64 IF status_change is not none this comment is associated with a
64 IF status_change is not none this comment is associated with a
65 status change of changeset or changesets associated with pull request
65 status change of changeset or changesets associated with pull request
66
66
67 :param text:
67 :param text:
68 :param repo:
68 :param repo:
69 :param user:
69 :param user:
70 :param revision:
70 :param revision:
71 :param pull_request:
71 :param pull_request:
72 :param f_path:
72 :param f_path:
73 :param line_no:
73 :param line_no:
74 :param status_change:
74 :param status_change:
75 """
75 """
76 if not text:
76 if not text:
77 return
77 return
78
78
79 repo = self._get_repo(repo)
79 repo = self._get_repo(repo)
80 user = self._get_user(user)
80 user = self._get_user(user)
81 comment = ChangesetComment()
81 comment = ChangesetComment()
82 comment.repo = repo
82 comment.repo = repo
83 comment.author = user
83 comment.author = user
84 comment.text = text
84 comment.text = text
85 comment.f_path = f_path
85 comment.f_path = f_path
86 comment.line_no = line_no
86 comment.line_no = line_no
87
87
88 if revision:
88 if revision:
89 cs = repo.scm_instance.get_changeset(revision)
89 cs = repo.scm_instance.get_changeset(revision)
90 desc = "%s - %s" % (cs.short_id, h.shorter(cs.message, 256))
90 desc = "%s - %s" % (cs.short_id, h.shorter(cs.message, 256))
91 author_email = cs.author_email
92 comment.revision = revision
91 comment.revision = revision
93 elif pull_request:
92 elif pull_request:
94 pull_request = self.__get_pull_request(pull_request)
93 pull_request = self.__get_pull_request(pull_request)
95 comment.pull_request = pull_request
94 comment.pull_request = pull_request
96 desc = pull_request.pull_request_id
95 desc = pull_request.pull_request_id
97 else:
96 else:
98 raise Exception('Please specify revision or pull_request_id')
97 raise Exception('Please specify revision or pull_request_id')
99
98
100 self.sa.add(comment)
99 self.sa.add(comment)
101 self.sa.flush()
100 self.sa.flush()
102
101
103 # make notification
102 # make notification
104 line = ''
103 line = ''
105 body = text
104 body = text
106
105
107 #changeset
106 #changeset
108 if revision:
107 if revision:
109 if line_no:
108 if line_no:
110 line = _('on line %s') % line_no
109 line = _('on line %s') % line_no
111 subj = safe_unicode(
110 subj = safe_unicode(
112 h.link_to('Re commit: %(desc)s %(line)s' % \
111 h.link_to('Re commit: %(desc)s %(line)s' % \
113 {'desc': desc, 'line': line},
112 {'desc': desc, 'line': line},
114 h.url('changeset_home', repo_name=repo.repo_name,
113 h.url('changeset_home', repo_name=repo.repo_name,
115 revision=revision,
114 revision=revision,
116 anchor='comment-%s' % comment.comment_id,
115 anchor='comment-%s' % comment.comment_id,
117 qualified=True,
116 qualified=True,
118 )
117 )
119 )
118 )
120 )
119 )
121 notification_type = Notification.TYPE_CHANGESET_COMMENT
120 notification_type = Notification.TYPE_CHANGESET_COMMENT
122 # get the current participants of this changeset
121 # get the current participants of this changeset
123 recipients = ChangesetComment.get_users(revision=revision)
122 recipients = ChangesetComment.get_users(revision=revision)
124 # add changeset author if it's in rhodecode system
123 # add changeset author if it's in rhodecode system
125 recipients += [User.get_by_email(author_email)]
124 cs_author = User.get_from_cs_author(cs.author)
125 if not cs_author:
126 #use repo owner if we cannot extract the author correctly
127 cs_author = repo.user
128 recipients += [cs_author]
126 email_kwargs = {
129 email_kwargs = {
127 'status_change': status_change,
130 'status_change': status_change,
128 }
131 }
129 #pull request
132 #pull request
130 elif pull_request:
133 elif pull_request:
131 _url = h.url('pullrequest_show',
134 _url = h.url('pullrequest_show',
132 repo_name=pull_request.other_repo.repo_name,
135 repo_name=pull_request.other_repo.repo_name,
133 pull_request_id=pull_request.pull_request_id,
136 pull_request_id=pull_request.pull_request_id,
134 anchor='comment-%s' % comment.comment_id,
137 anchor='comment-%s' % comment.comment_id,
135 qualified=True,
138 qualified=True,
136 )
139 )
137 subj = safe_unicode(
140 subj = safe_unicode(
138 h.link_to('Re pull request: %(desc)s %(line)s' % \
141 h.link_to('Re pull request: %(desc)s %(line)s' % \
139 {'desc': desc, 'line': line}, _url)
142 {'desc': desc, 'line': line}, _url)
140 )
143 )
141
144
142 notification_type = Notification.TYPE_PULL_REQUEST_COMMENT
145 notification_type = Notification.TYPE_PULL_REQUEST_COMMENT
143 # get the current participants of this pull request
146 # get the current participants of this pull request
144 recipients = ChangesetComment.get_users(pull_request_id=
147 recipients = ChangesetComment.get_users(pull_request_id=
145 pull_request.pull_request_id)
148 pull_request.pull_request_id)
146 # add pull request author
149 # add pull request author
147 recipients += [pull_request.author]
150 recipients += [pull_request.author]
148
151
149 # add the reviewers to notification
152 # add the reviewers to notification
150 recipients += [x.user for x in pull_request.reviewers]
153 recipients += [x.user for x in pull_request.reviewers]
151
154
152 #set some variables for email notification
155 #set some variables for email notification
153 email_kwargs = {
156 email_kwargs = {
154 'pr_id': pull_request.pull_request_id,
157 'pr_id': pull_request.pull_request_id,
155 'status_change': status_change,
158 'status_change': status_change,
156 'pr_comment_url': _url,
159 'pr_comment_url': _url,
157 'pr_comment_user': h.person(user.email),
160 'pr_comment_user': h.person(user.email),
158 'pr_target_repo': h.url('summary_home',
161 'pr_target_repo': h.url('summary_home',
159 repo_name=pull_request.other_repo.repo_name,
162 repo_name=pull_request.other_repo.repo_name,
160 qualified=True)
163 qualified=True)
161 }
164 }
162 # create notification objects, and emails
165 # create notification objects, and emails
163 NotificationModel().create(
166 NotificationModel().create(
164 created_by=user, subject=subj, body=body,
167 created_by=user, subject=subj, body=body,
165 recipients=recipients, type_=notification_type,
168 recipients=recipients, type_=notification_type,
166 email_kwargs=email_kwargs
169 email_kwargs=email_kwargs
167 )
170 )
168
171
169 mention_recipients = set(self._extract_mentions(body))\
172 mention_recipients = set(self._extract_mentions(body))\
170 .difference(recipients)
173 .difference(recipients)
171 if mention_recipients:
174 if mention_recipients:
172 email_kwargs.update({'pr_mention': True})
175 email_kwargs.update({'pr_mention': True})
173 subj = _('[Mention]') + ' ' + subj
176 subj = _('[Mention]') + ' ' + subj
174 NotificationModel().create(
177 NotificationModel().create(
175 created_by=user, subject=subj, body=body,
178 created_by=user, subject=subj, body=body,
176 recipients=mention_recipients,
179 recipients=mention_recipients,
177 type_=notification_type,
180 type_=notification_type,
178 email_kwargs=email_kwargs
181 email_kwargs=email_kwargs
179 )
182 )
180
183
181 return comment
184 return comment
182
185
183 def delete(self, comment):
186 def delete(self, comment):
184 """
187 """
185 Deletes given comment
188 Deletes given comment
186
189
187 :param comment_id:
190 :param comment_id:
188 """
191 """
189 comment = self.__get_changeset_comment(comment)
192 comment = self.__get_changeset_comment(comment)
190 self.sa.delete(comment)
193 self.sa.delete(comment)
191
194
192 return comment
195 return comment
193
196
194 def get_comments(self, repo_id, revision=None, pull_request=None):
197 def get_comments(self, repo_id, revision=None, pull_request=None):
195 """
198 """
196 Get's main comments based on revision or pull_request_id
199 Get's main comments based on revision or pull_request_id
197
200
198 :param repo_id:
201 :param repo_id:
199 :type repo_id:
202 :type repo_id:
200 :param revision:
203 :param revision:
201 :type revision:
204 :type revision:
202 :param pull_request:
205 :param pull_request:
203 :type pull_request:
206 :type pull_request:
204 """
207 """
205
208
206 q = ChangesetComment.query()\
209 q = ChangesetComment.query()\
207 .filter(ChangesetComment.repo_id == repo_id)\
210 .filter(ChangesetComment.repo_id == repo_id)\
208 .filter(ChangesetComment.line_no == None)\
211 .filter(ChangesetComment.line_no == None)\
209 .filter(ChangesetComment.f_path == None)
212 .filter(ChangesetComment.f_path == None)
210 if revision:
213 if revision:
211 q = q.filter(ChangesetComment.revision == revision)
214 q = q.filter(ChangesetComment.revision == revision)
212 elif pull_request:
215 elif pull_request:
213 pull_request = self.__get_pull_request(pull_request)
216 pull_request = self.__get_pull_request(pull_request)
214 q = q.filter(ChangesetComment.pull_request == pull_request)
217 q = q.filter(ChangesetComment.pull_request == pull_request)
215 else:
218 else:
216 raise Exception('Please specify revision or pull_request')
219 raise Exception('Please specify revision or pull_request')
217 q = q.order_by(ChangesetComment.created_on)
220 q = q.order_by(ChangesetComment.created_on)
218 return q.all()
221 return q.all()
219
222
220 def get_inline_comments(self, repo_id, revision=None, pull_request=None):
223 def get_inline_comments(self, repo_id, revision=None, pull_request=None):
221 q = self.sa.query(ChangesetComment)\
224 q = self.sa.query(ChangesetComment)\
222 .filter(ChangesetComment.repo_id == repo_id)\
225 .filter(ChangesetComment.repo_id == repo_id)\
223 .filter(ChangesetComment.line_no != None)\
226 .filter(ChangesetComment.line_no != None)\
224 .filter(ChangesetComment.f_path != None)\
227 .filter(ChangesetComment.f_path != None)\
225 .order_by(ChangesetComment.comment_id.asc())\
228 .order_by(ChangesetComment.comment_id.asc())\
226
229
227 if revision:
230 if revision:
228 q = q.filter(ChangesetComment.revision == revision)
231 q = q.filter(ChangesetComment.revision == revision)
229 elif pull_request:
232 elif pull_request:
230 pull_request = self.__get_pull_request(pull_request)
233 pull_request = self.__get_pull_request(pull_request)
231 q = q.filter(ChangesetComment.pull_request == pull_request)
234 q = q.filter(ChangesetComment.pull_request == pull_request)
232 else:
235 else:
233 raise Exception('Please specify revision or pull_request_id')
236 raise Exception('Please specify revision or pull_request_id')
234
237
235 comments = q.all()
238 comments = q.all()
236
239
237 paths = defaultdict(lambda: defaultdict(list))
240 paths = defaultdict(lambda: defaultdict(list))
238
241
239 for co in comments:
242 for co in comments:
240 paths[co.f_path][co.line_no].append(co)
243 paths[co.f_path][co.line_no].append(co)
241 return paths.items()
244 return paths.items()
@@ -1,1948 +1,1968 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 import time
31 import time
32 from collections import defaultdict
32 from collections import defaultdict
33
33
34 from sqlalchemy import *
34 from sqlalchemy import *
35 from sqlalchemy.ext.hybrid import hybrid_property
35 from sqlalchemy.ext.hybrid import hybrid_property
36 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
36 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
37 from sqlalchemy.exc import DatabaseError
37 from sqlalchemy.exc import DatabaseError
38 from beaker.cache import cache_region, region_invalidate
38 from beaker.cache import cache_region, region_invalidate
39 from webob.exc import HTTPNotFound
39 from webob.exc import HTTPNotFound
40
40
41 from pylons.i18n.translation import lazy_ugettext as _
41 from pylons.i18n.translation import lazy_ugettext as _
42
42
43 from rhodecode.lib.vcs import get_backend
43 from rhodecode.lib.vcs import get_backend
44 from rhodecode.lib.vcs.utils.helpers import get_scm
44 from rhodecode.lib.vcs.utils.helpers import get_scm
45 from rhodecode.lib.vcs.exceptions import VCSError
45 from rhodecode.lib.vcs.exceptions import VCSError
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
47
47
48 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
48 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
49 safe_unicode, remove_suffix, remove_prefix
49 safe_unicode, remove_suffix, remove_prefix
50 from rhodecode.lib.compat import json
50 from rhodecode.lib.compat import json
51 from rhodecode.lib.caching_query import FromCache
51 from rhodecode.lib.caching_query import FromCache
52
52
53 from rhodecode.model.meta import Base, Session
53 from rhodecode.model.meta import Base, Session
54
54
55 URL_SEP = '/'
55 URL_SEP = '/'
56 log = logging.getLogger(__name__)
56 log = logging.getLogger(__name__)
57
57
58 #==============================================================================
58 #==============================================================================
59 # BASE CLASSES
59 # BASE CLASSES
60 #==============================================================================
60 #==============================================================================
61
61
62 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
62 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
63
63
64
64
65 class BaseModel(object):
65 class BaseModel(object):
66 """
66 """
67 Base Model for all classess
67 Base Model for all classess
68 """
68 """
69
69
70 @classmethod
70 @classmethod
71 def _get_keys(cls):
71 def _get_keys(cls):
72 """return column names for this model """
72 """return column names for this model """
73 return class_mapper(cls).c.keys()
73 return class_mapper(cls).c.keys()
74
74
75 def get_dict(self):
75 def get_dict(self):
76 """
76 """
77 return dict with keys and values corresponding
77 return dict with keys and values corresponding
78 to this model data """
78 to this model data """
79
79
80 d = {}
80 d = {}
81 for k in self._get_keys():
81 for k in self._get_keys():
82 d[k] = getattr(self, k)
82 d[k] = getattr(self, k)
83
83
84 # also use __json__() if present to get additional fields
84 # also use __json__() if present to get additional fields
85 _json_attr = getattr(self, '__json__', None)
85 _json_attr = getattr(self, '__json__', None)
86 if _json_attr:
86 if _json_attr:
87 # update with attributes from __json__
87 # update with attributes from __json__
88 if callable(_json_attr):
88 if callable(_json_attr):
89 _json_attr = _json_attr()
89 _json_attr = _json_attr()
90 for k, val in _json_attr.iteritems():
90 for k, val in _json_attr.iteritems():
91 d[k] = val
91 d[k] = val
92 return d
92 return d
93
93
94 def get_appstruct(self):
94 def get_appstruct(self):
95 """return list with keys and values tupples corresponding
95 """return list with keys and values tupples corresponding
96 to this model data """
96 to this model data """
97
97
98 l = []
98 l = []
99 for k in self._get_keys():
99 for k in self._get_keys():
100 l.append((k, getattr(self, k),))
100 l.append((k, getattr(self, k),))
101 return l
101 return l
102
102
103 def populate_obj(self, populate_dict):
103 def populate_obj(self, populate_dict):
104 """populate model with data from given populate_dict"""
104 """populate model with data from given populate_dict"""
105
105
106 for k in self._get_keys():
106 for k in self._get_keys():
107 if k in populate_dict:
107 if k in populate_dict:
108 setattr(self, k, populate_dict[k])
108 setattr(self, k, populate_dict[k])
109
109
110 @classmethod
110 @classmethod
111 def query(cls):
111 def query(cls):
112 return Session().query(cls)
112 return Session().query(cls)
113
113
114 @classmethod
114 @classmethod
115 def get(cls, id_):
115 def get(cls, id_):
116 if id_:
116 if id_:
117 return cls.query().get(id_)
117 return cls.query().get(id_)
118
118
119 @classmethod
119 @classmethod
120 def get_or_404(cls, id_):
120 def get_or_404(cls, id_):
121 try:
121 try:
122 id_ = int(id_)
122 id_ = int(id_)
123 except (TypeError, ValueError):
123 except (TypeError, ValueError):
124 raise HTTPNotFound
124 raise HTTPNotFound
125
125
126 res = cls.query().get(id_)
126 res = cls.query().get(id_)
127 if not res:
127 if not res:
128 raise HTTPNotFound
128 raise HTTPNotFound
129 return res
129 return res
130
130
131 @classmethod
131 @classmethod
132 def getAll(cls):
132 def getAll(cls):
133 return cls.query().all()
133 return cls.query().all()
134
134
135 @classmethod
135 @classmethod
136 def delete(cls, id_):
136 def delete(cls, id_):
137 obj = cls.query().get(id_)
137 obj = cls.query().get(id_)
138 Session().delete(obj)
138 Session().delete(obj)
139
139
140 def __repr__(self):
140 def __repr__(self):
141 if hasattr(self, '__unicode__'):
141 if hasattr(self, '__unicode__'):
142 # python repr needs to return str
142 # python repr needs to return str
143 return safe_str(self.__unicode__())
143 return safe_str(self.__unicode__())
144 return '<DB:%s>' % (self.__class__.__name__)
144 return '<DB:%s>' % (self.__class__.__name__)
145
145
146
146
147 class RhodeCodeSetting(Base, BaseModel):
147 class RhodeCodeSetting(Base, BaseModel):
148 __tablename__ = 'rhodecode_settings'
148 __tablename__ = 'rhodecode_settings'
149 __table_args__ = (
149 __table_args__ = (
150 UniqueConstraint('app_settings_name'),
150 UniqueConstraint('app_settings_name'),
151 {'extend_existing': True, 'mysql_engine': 'InnoDB',
151 {'extend_existing': True, 'mysql_engine': 'InnoDB',
152 'mysql_charset': 'utf8'}
152 'mysql_charset': 'utf8'}
153 )
153 )
154 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
154 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
155 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
155 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
156 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
156 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
157
157
158 def __init__(self, k='', v=''):
158 def __init__(self, k='', v=''):
159 self.app_settings_name = k
159 self.app_settings_name = k
160 self.app_settings_value = v
160 self.app_settings_value = v
161
161
162 @validates('_app_settings_value')
162 @validates('_app_settings_value')
163 def validate_settings_value(self, key, val):
163 def validate_settings_value(self, key, val):
164 assert type(val) == unicode
164 assert type(val) == unicode
165 return val
165 return val
166
166
167 @hybrid_property
167 @hybrid_property
168 def app_settings_value(self):
168 def app_settings_value(self):
169 v = self._app_settings_value
169 v = self._app_settings_value
170 if self.app_settings_name in ["ldap_active",
170 if self.app_settings_name in ["ldap_active",
171 "default_repo_enable_statistics",
171 "default_repo_enable_statistics",
172 "default_repo_enable_locking",
172 "default_repo_enable_locking",
173 "default_repo_private",
173 "default_repo_private",
174 "default_repo_enable_downloads"]:
174 "default_repo_enable_downloads"]:
175 v = str2bool(v)
175 v = str2bool(v)
176 return v
176 return v
177
177
178 @app_settings_value.setter
178 @app_settings_value.setter
179 def app_settings_value(self, val):
179 def app_settings_value(self, val):
180 """
180 """
181 Setter that will always make sure we use unicode in app_settings_value
181 Setter that will always make sure we use unicode in app_settings_value
182
182
183 :param val:
183 :param val:
184 """
184 """
185 self._app_settings_value = safe_unicode(val)
185 self._app_settings_value = safe_unicode(val)
186
186
187 def __unicode__(self):
187 def __unicode__(self):
188 return u"<%s('%s:%s')>" % (
188 return u"<%s('%s:%s')>" % (
189 self.__class__.__name__,
189 self.__class__.__name__,
190 self.app_settings_name, self.app_settings_value
190 self.app_settings_name, self.app_settings_value
191 )
191 )
192
192
193 @classmethod
193 @classmethod
194 def get_by_name(cls, key):
194 def get_by_name(cls, key):
195 return cls.query()\
195 return cls.query()\
196 .filter(cls.app_settings_name == key).scalar()
196 .filter(cls.app_settings_name == key).scalar()
197
197
198 @classmethod
198 @classmethod
199 def get_by_name_or_create(cls, key):
199 def get_by_name_or_create(cls, key):
200 res = cls.get_by_name(key)
200 res = cls.get_by_name(key)
201 if not res:
201 if not res:
202 res = cls(key)
202 res = cls(key)
203 return res
203 return res
204
204
205 @classmethod
205 @classmethod
206 def get_app_settings(cls, cache=False):
206 def get_app_settings(cls, cache=False):
207
207
208 ret = cls.query()
208 ret = cls.query()
209
209
210 if cache:
210 if cache:
211 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
211 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
212
212
213 if not ret:
213 if not ret:
214 raise Exception('Could not get application settings !')
214 raise Exception('Could not get application settings !')
215 settings = {}
215 settings = {}
216 for each in ret:
216 for each in ret:
217 settings['rhodecode_' + each.app_settings_name] = \
217 settings['rhodecode_' + each.app_settings_name] = \
218 each.app_settings_value
218 each.app_settings_value
219
219
220 return settings
220 return settings
221
221
222 @classmethod
222 @classmethod
223 def get_ldap_settings(cls, cache=False):
223 def get_ldap_settings(cls, cache=False):
224 ret = cls.query()\
224 ret = cls.query()\
225 .filter(cls.app_settings_name.startswith('ldap_')).all()
225 .filter(cls.app_settings_name.startswith('ldap_')).all()
226 fd = {}
226 fd = {}
227 for row in ret:
227 for row in ret:
228 fd.update({row.app_settings_name: row.app_settings_value})
228 fd.update({row.app_settings_name: row.app_settings_value})
229
229
230 return fd
230 return fd
231
231
232 @classmethod
232 @classmethod
233 def get_default_repo_settings(cls, cache=False, strip_prefix=False):
233 def get_default_repo_settings(cls, cache=False, strip_prefix=False):
234 ret = cls.query()\
234 ret = cls.query()\
235 .filter(cls.app_settings_name.startswith('default_')).all()
235 .filter(cls.app_settings_name.startswith('default_')).all()
236 fd = {}
236 fd = {}
237 for row in ret:
237 for row in ret:
238 key = row.app_settings_name
238 key = row.app_settings_name
239 if strip_prefix:
239 if strip_prefix:
240 key = remove_prefix(key, prefix='default_')
240 key = remove_prefix(key, prefix='default_')
241 fd.update({key: row.app_settings_value})
241 fd.update({key: row.app_settings_value})
242
242
243 return fd
243 return fd
244
244
245
245
246 class RhodeCodeUi(Base, BaseModel):
246 class RhodeCodeUi(Base, BaseModel):
247 __tablename__ = 'rhodecode_ui'
247 __tablename__ = 'rhodecode_ui'
248 __table_args__ = (
248 __table_args__ = (
249 UniqueConstraint('ui_key'),
249 UniqueConstraint('ui_key'),
250 {'extend_existing': True, 'mysql_engine': 'InnoDB',
250 {'extend_existing': True, 'mysql_engine': 'InnoDB',
251 'mysql_charset': 'utf8'}
251 'mysql_charset': 'utf8'}
252 )
252 )
253
253
254 HOOK_UPDATE = 'changegroup.update'
254 HOOK_UPDATE = 'changegroup.update'
255 HOOK_REPO_SIZE = 'changegroup.repo_size'
255 HOOK_REPO_SIZE = 'changegroup.repo_size'
256 HOOK_PUSH = 'changegroup.push_logger'
256 HOOK_PUSH = 'changegroup.push_logger'
257 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
257 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
258 HOOK_PULL = 'outgoing.pull_logger'
258 HOOK_PULL = 'outgoing.pull_logger'
259 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
259 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
260
260
261 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
261 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
262 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
262 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
263 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
263 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
264 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
264 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
265 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
265 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
266
266
267 @classmethod
267 @classmethod
268 def get_by_key(cls, key):
268 def get_by_key(cls, key):
269 return cls.query().filter(cls.ui_key == key).scalar()
269 return cls.query().filter(cls.ui_key == key).scalar()
270
270
271 @classmethod
271 @classmethod
272 def get_builtin_hooks(cls):
272 def get_builtin_hooks(cls):
273 q = cls.query()
273 q = cls.query()
274 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
274 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
275 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
275 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
276 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
276 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
277 return q.all()
277 return q.all()
278
278
279 @classmethod
279 @classmethod
280 def get_custom_hooks(cls):
280 def get_custom_hooks(cls):
281 q = cls.query()
281 q = cls.query()
282 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
282 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
283 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
283 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
284 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
284 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
285 q = q.filter(cls.ui_section == 'hooks')
285 q = q.filter(cls.ui_section == 'hooks')
286 return q.all()
286 return q.all()
287
287
288 @classmethod
288 @classmethod
289 def get_repos_location(cls):
289 def get_repos_location(cls):
290 return cls.get_by_key('/').ui_value
290 return cls.get_by_key('/').ui_value
291
291
292 @classmethod
292 @classmethod
293 def create_or_update_hook(cls, key, val):
293 def create_or_update_hook(cls, key, val):
294 new_ui = cls.get_by_key(key) or cls()
294 new_ui = cls.get_by_key(key) or cls()
295 new_ui.ui_section = 'hooks'
295 new_ui.ui_section = 'hooks'
296 new_ui.ui_active = True
296 new_ui.ui_active = True
297 new_ui.ui_key = key
297 new_ui.ui_key = key
298 new_ui.ui_value = val
298 new_ui.ui_value = val
299
299
300 Session().add(new_ui)
300 Session().add(new_ui)
301
301
302 def __repr__(self):
302 def __repr__(self):
303 return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key,
303 return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key,
304 self.ui_value)
304 self.ui_value)
305
305
306
306
307 class User(Base, BaseModel):
307 class User(Base, BaseModel):
308 __tablename__ = 'users'
308 __tablename__ = 'users'
309 __table_args__ = (
309 __table_args__ = (
310 UniqueConstraint('username'), UniqueConstraint('email'),
310 UniqueConstraint('username'), UniqueConstraint('email'),
311 Index('u_username_idx', 'username'),
311 Index('u_username_idx', 'username'),
312 Index('u_email_idx', 'email'),
312 Index('u_email_idx', 'email'),
313 {'extend_existing': True, 'mysql_engine': 'InnoDB',
313 {'extend_existing': True, 'mysql_engine': 'InnoDB',
314 'mysql_charset': 'utf8'}
314 'mysql_charset': 'utf8'}
315 )
315 )
316 DEFAULT_USER = 'default'
316 DEFAULT_USER = 'default'
317 DEFAULT_PERMISSIONS = [
317 DEFAULT_PERMISSIONS = [
318 'hg.register.manual_activate', 'hg.create.repository',
318 'hg.register.manual_activate', 'hg.create.repository',
319 'hg.fork.repository', 'repository.read', 'group.read'
319 'hg.fork.repository', 'repository.read', 'group.read'
320 ]
320 ]
321 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
321 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
322 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
322 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
323 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
323 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
324 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
324 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
325 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
325 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
326 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
326 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
327 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
327 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
328 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
328 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
329 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
329 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
330 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
330 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
331 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
331 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
332 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
332 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
333
333
334 user_log = relationship('UserLog')
334 user_log = relationship('UserLog')
335 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
335 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
336
336
337 repositories = relationship('Repository')
337 repositories = relationship('Repository')
338 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
338 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
339 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
339 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
340
340
341 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
341 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
342 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
342 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
343
343
344 group_member = relationship('UsersGroupMember', cascade='all')
344 group_member = relationship('UsersGroupMember', cascade='all')
345
345
346 notifications = relationship('UserNotification', cascade='all')
346 notifications = relationship('UserNotification', cascade='all')
347 # notifications assigned to this user
347 # notifications assigned to this user
348 user_created_notifications = relationship('Notification', cascade='all')
348 user_created_notifications = relationship('Notification', cascade='all')
349 # comments created by this user
349 # comments created by this user
350 user_comments = relationship('ChangesetComment', cascade='all')
350 user_comments = relationship('ChangesetComment', cascade='all')
351 #extra emails for this user
351 #extra emails for this user
352 user_emails = relationship('UserEmailMap', cascade='all')
352 user_emails = relationship('UserEmailMap', cascade='all')
353
353
354 @hybrid_property
354 @hybrid_property
355 def email(self):
355 def email(self):
356 return self._email
356 return self._email
357
357
358 @email.setter
358 @email.setter
359 def email(self, val):
359 def email(self, val):
360 self._email = val.lower() if val else None
360 self._email = val.lower() if val else None
361
361
362 @property
362 @property
363 def firstname(self):
363 def firstname(self):
364 # alias for future
364 # alias for future
365 return self.name
365 return self.name
366
366
367 @property
367 @property
368 def emails(self):
368 def emails(self):
369 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
369 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
370 return [self.email] + [x.email for x in other]
370 return [self.email] + [x.email for x in other]
371
371
372 @property
372 @property
373 def ip_addresses(self):
373 def ip_addresses(self):
374 ret = UserIpMap.query().filter(UserIpMap.user == self).all()
374 ret = UserIpMap.query().filter(UserIpMap.user == self).all()
375 return [x.ip_addr for x in ret]
375 return [x.ip_addr for x in ret]
376
376
377 @property
377 @property
378 def username_and_name(self):
378 def username_and_name(self):
379 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
379 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
380
380
381 @property
381 @property
382 def full_name(self):
382 def full_name(self):
383 return '%s %s' % (self.firstname, self.lastname)
383 return '%s %s' % (self.firstname, self.lastname)
384
384
385 @property
385 @property
386 def full_name_or_username(self):
386 def full_name_or_username(self):
387 return ('%s %s' % (self.firstname, self.lastname)
387 return ('%s %s' % (self.firstname, self.lastname)
388 if (self.firstname and self.lastname) else self.username)
388 if (self.firstname and self.lastname) else self.username)
389
389
390 @property
390 @property
391 def full_contact(self):
391 def full_contact(self):
392 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
392 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
393
393
394 @property
394 @property
395 def short_contact(self):
395 def short_contact(self):
396 return '%s %s' % (self.firstname, self.lastname)
396 return '%s %s' % (self.firstname, self.lastname)
397
397
398 @property
398 @property
399 def is_admin(self):
399 def is_admin(self):
400 return self.admin
400 return self.admin
401
401
402 def __unicode__(self):
402 def __unicode__(self):
403 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
403 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
404 self.user_id, self.username)
404 self.user_id, self.username)
405
405
406 @classmethod
406 @classmethod
407 def get_by_username(cls, username, case_insensitive=False, cache=False):
407 def get_by_username(cls, username, case_insensitive=False, cache=False):
408 if case_insensitive:
408 if case_insensitive:
409 q = cls.query().filter(cls.username.ilike(username))
409 q = cls.query().filter(cls.username.ilike(username))
410 else:
410 else:
411 q = cls.query().filter(cls.username == username)
411 q = cls.query().filter(cls.username == username)
412
412
413 if cache:
413 if cache:
414 q = q.options(FromCache(
414 q = q.options(FromCache(
415 "sql_cache_short",
415 "sql_cache_short",
416 "get_user_%s" % _hash_key(username)
416 "get_user_%s" % _hash_key(username)
417 )
417 )
418 )
418 )
419 return q.scalar()
419 return q.scalar()
420
420
421 @classmethod
421 @classmethod
422 def get_by_api_key(cls, api_key, cache=False):
422 def get_by_api_key(cls, api_key, cache=False):
423 q = cls.query().filter(cls.api_key == api_key)
423 q = cls.query().filter(cls.api_key == api_key)
424
424
425 if cache:
425 if cache:
426 q = q.options(FromCache("sql_cache_short",
426 q = q.options(FromCache("sql_cache_short",
427 "get_api_key_%s" % api_key))
427 "get_api_key_%s" % api_key))
428 return q.scalar()
428 return q.scalar()
429
429
430 @classmethod
430 @classmethod
431 def get_by_email(cls, email, case_insensitive=False, cache=False):
431 def get_by_email(cls, email, case_insensitive=False, cache=False):
432 if case_insensitive:
432 if case_insensitive:
433 q = cls.query().filter(cls.email.ilike(email))
433 q = cls.query().filter(cls.email.ilike(email))
434 else:
434 else:
435 q = cls.query().filter(cls.email == email)
435 q = cls.query().filter(cls.email == email)
436
436
437 if cache:
437 if cache:
438 q = q.options(FromCache("sql_cache_short",
438 q = q.options(FromCache("sql_cache_short",
439 "get_email_key_%s" % email))
439 "get_email_key_%s" % email))
440
440
441 ret = q.scalar()
441 ret = q.scalar()
442 if ret is None:
442 if ret is None:
443 q = UserEmailMap.query()
443 q = UserEmailMap.query()
444 # try fetching in alternate email map
444 # try fetching in alternate email map
445 if case_insensitive:
445 if case_insensitive:
446 q = q.filter(UserEmailMap.email.ilike(email))
446 q = q.filter(UserEmailMap.email.ilike(email))
447 else:
447 else:
448 q = q.filter(UserEmailMap.email == email)
448 q = q.filter(UserEmailMap.email == email)
449 q = q.options(joinedload(UserEmailMap.user))
449 q = q.options(joinedload(UserEmailMap.user))
450 if cache:
450 if cache:
451 q = q.options(FromCache("sql_cache_short",
451 q = q.options(FromCache("sql_cache_short",
452 "get_email_map_key_%s" % email))
452 "get_email_map_key_%s" % email))
453 ret = getattr(q.scalar(), 'user', None)
453 ret = getattr(q.scalar(), 'user', None)
454
454
455 return ret
455 return ret
456
456
457 @classmethod
458 def get_from_cs_author(cls, author):
459 """
460 Tries to get User objects out of commit author string
461
462 :param author:
463 """
464 from rhodecode.lib.helpers import email, author_name
465 # Valid email in the attribute passed, see if they're in the system
466 _email = email(author)
467 if _email:
468 user = cls.get_by_email(_email, case_insensitive=True)
469 if user:
470 return user
471 # Maybe we can match by username?
472 _author = author_name(author)
473 user = cls.get_by_username(_author, case_insensitive=True)
474 if user:
475 return user
476
457 def update_lastlogin(self):
477 def update_lastlogin(self):
458 """Update user lastlogin"""
478 """Update user lastlogin"""
459 self.last_login = datetime.datetime.now()
479 self.last_login = datetime.datetime.now()
460 Session().add(self)
480 Session().add(self)
461 log.debug('updated user %s lastlogin' % self.username)
481 log.debug('updated user %s lastlogin' % self.username)
462
482
463 def get_api_data(self):
483 def get_api_data(self):
464 """
484 """
465 Common function for generating user related data for API
485 Common function for generating user related data for API
466 """
486 """
467 user = self
487 user = self
468 data = dict(
488 data = dict(
469 user_id=user.user_id,
489 user_id=user.user_id,
470 username=user.username,
490 username=user.username,
471 firstname=user.name,
491 firstname=user.name,
472 lastname=user.lastname,
492 lastname=user.lastname,
473 email=user.email,
493 email=user.email,
474 emails=user.emails,
494 emails=user.emails,
475 api_key=user.api_key,
495 api_key=user.api_key,
476 active=user.active,
496 active=user.active,
477 admin=user.admin,
497 admin=user.admin,
478 ldap_dn=user.ldap_dn,
498 ldap_dn=user.ldap_dn,
479 last_login=user.last_login,
499 last_login=user.last_login,
480 ip_addresses=user.ip_addresses
500 ip_addresses=user.ip_addresses
481 )
501 )
482 return data
502 return data
483
503
484 def __json__(self):
504 def __json__(self):
485 data = dict(
505 data = dict(
486 full_name=self.full_name,
506 full_name=self.full_name,
487 full_name_or_username=self.full_name_or_username,
507 full_name_or_username=self.full_name_or_username,
488 short_contact=self.short_contact,
508 short_contact=self.short_contact,
489 full_contact=self.full_contact
509 full_contact=self.full_contact
490 )
510 )
491 data.update(self.get_api_data())
511 data.update(self.get_api_data())
492 return data
512 return data
493
513
494
514
495 class UserEmailMap(Base, BaseModel):
515 class UserEmailMap(Base, BaseModel):
496 __tablename__ = 'user_email_map'
516 __tablename__ = 'user_email_map'
497 __table_args__ = (
517 __table_args__ = (
498 Index('uem_email_idx', 'email'),
518 Index('uem_email_idx', 'email'),
499 UniqueConstraint('email'),
519 UniqueConstraint('email'),
500 {'extend_existing': True, 'mysql_engine': 'InnoDB',
520 {'extend_existing': True, 'mysql_engine': 'InnoDB',
501 'mysql_charset': 'utf8'}
521 'mysql_charset': 'utf8'}
502 )
522 )
503 __mapper_args__ = {}
523 __mapper_args__ = {}
504
524
505 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
525 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
506 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
526 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
507 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
527 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
508 user = relationship('User', lazy='joined')
528 user = relationship('User', lazy='joined')
509
529
510 @validates('_email')
530 @validates('_email')
511 def validate_email(self, key, email):
531 def validate_email(self, key, email):
512 # check if this email is not main one
532 # check if this email is not main one
513 main_email = Session().query(User).filter(User.email == email).scalar()
533 main_email = Session().query(User).filter(User.email == email).scalar()
514 if main_email is not None:
534 if main_email is not None:
515 raise AttributeError('email %s is present is user table' % email)
535 raise AttributeError('email %s is present is user table' % email)
516 return email
536 return email
517
537
518 @hybrid_property
538 @hybrid_property
519 def email(self):
539 def email(self):
520 return self._email
540 return self._email
521
541
522 @email.setter
542 @email.setter
523 def email(self, val):
543 def email(self, val):
524 self._email = val.lower() if val else None
544 self._email = val.lower() if val else None
525
545
526
546
527 class UserIpMap(Base, BaseModel):
547 class UserIpMap(Base, BaseModel):
528 __tablename__ = 'user_ip_map'
548 __tablename__ = 'user_ip_map'
529 __table_args__ = (
549 __table_args__ = (
530 UniqueConstraint('user_id', 'ip_addr'),
550 UniqueConstraint('user_id', 'ip_addr'),
531 {'extend_existing': True, 'mysql_engine': 'InnoDB',
551 {'extend_existing': True, 'mysql_engine': 'InnoDB',
532 'mysql_charset': 'utf8'}
552 'mysql_charset': 'utf8'}
533 )
553 )
534 __mapper_args__ = {}
554 __mapper_args__ = {}
535
555
536 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
556 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
537 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
557 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
538 ip_addr = Column("ip_addr", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
558 ip_addr = Column("ip_addr", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
539 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
559 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
540 user = relationship('User', lazy='joined')
560 user = relationship('User', lazy='joined')
541
561
542 @classmethod
562 @classmethod
543 def _get_ip_range(cls, ip_addr):
563 def _get_ip_range(cls, ip_addr):
544 from rhodecode.lib import ipaddr
564 from rhodecode.lib import ipaddr
545 net = ipaddr.IPv4Network(ip_addr)
565 net = ipaddr.IPv4Network(ip_addr)
546 return [str(net.network), str(net.broadcast)]
566 return [str(net.network), str(net.broadcast)]
547
567
548 def __json__(self):
568 def __json__(self):
549 return dict(
569 return dict(
550 ip_addr=self.ip_addr,
570 ip_addr=self.ip_addr,
551 ip_range=self._get_ip_range(self.ip_addr)
571 ip_range=self._get_ip_range(self.ip_addr)
552 )
572 )
553
573
554
574
555 class UserLog(Base, BaseModel):
575 class UserLog(Base, BaseModel):
556 __tablename__ = 'user_logs'
576 __tablename__ = 'user_logs'
557 __table_args__ = (
577 __table_args__ = (
558 {'extend_existing': True, 'mysql_engine': 'InnoDB',
578 {'extend_existing': True, 'mysql_engine': 'InnoDB',
559 'mysql_charset': 'utf8'},
579 'mysql_charset': 'utf8'},
560 )
580 )
561 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
581 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
562 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
582 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
563 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
583 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
564 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
584 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
565 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
585 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
566 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
586 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
567 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
587 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
568 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
588 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
569
589
570 @property
590 @property
571 def action_as_day(self):
591 def action_as_day(self):
572 return datetime.date(*self.action_date.timetuple()[:3])
592 return datetime.date(*self.action_date.timetuple()[:3])
573
593
574 user = relationship('User')
594 user = relationship('User')
575 repository = relationship('Repository', cascade='')
595 repository = relationship('Repository', cascade='')
576
596
577
597
578 class UsersGroup(Base, BaseModel):
598 class UsersGroup(Base, BaseModel):
579 __tablename__ = 'users_groups'
599 __tablename__ = 'users_groups'
580 __table_args__ = (
600 __table_args__ = (
581 {'extend_existing': True, 'mysql_engine': 'InnoDB',
601 {'extend_existing': True, 'mysql_engine': 'InnoDB',
582 'mysql_charset': 'utf8'},
602 'mysql_charset': 'utf8'},
583 )
603 )
584
604
585 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
605 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
586 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
606 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
587 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
607 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
588 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
608 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
589
609
590 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
610 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
591 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
611 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
592 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
612 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
593
613
594 def __unicode__(self):
614 def __unicode__(self):
595 return u'<userGroup(%s)>' % (self.users_group_name)
615 return u'<userGroup(%s)>' % (self.users_group_name)
596
616
597 @classmethod
617 @classmethod
598 def get_by_group_name(cls, group_name, cache=False,
618 def get_by_group_name(cls, group_name, cache=False,
599 case_insensitive=False):
619 case_insensitive=False):
600 if case_insensitive:
620 if case_insensitive:
601 q = cls.query().filter(cls.users_group_name.ilike(group_name))
621 q = cls.query().filter(cls.users_group_name.ilike(group_name))
602 else:
622 else:
603 q = cls.query().filter(cls.users_group_name == group_name)
623 q = cls.query().filter(cls.users_group_name == group_name)
604 if cache:
624 if cache:
605 q = q.options(FromCache(
625 q = q.options(FromCache(
606 "sql_cache_short",
626 "sql_cache_short",
607 "get_user_%s" % _hash_key(group_name)
627 "get_user_%s" % _hash_key(group_name)
608 )
628 )
609 )
629 )
610 return q.scalar()
630 return q.scalar()
611
631
612 @classmethod
632 @classmethod
613 def get(cls, users_group_id, cache=False):
633 def get(cls, users_group_id, cache=False):
614 users_group = cls.query()
634 users_group = cls.query()
615 if cache:
635 if cache:
616 users_group = users_group.options(FromCache("sql_cache_short",
636 users_group = users_group.options(FromCache("sql_cache_short",
617 "get_users_group_%s" % users_group_id))
637 "get_users_group_%s" % users_group_id))
618 return users_group.get(users_group_id)
638 return users_group.get(users_group_id)
619
639
620 def get_api_data(self):
640 def get_api_data(self):
621 users_group = self
641 users_group = self
622
642
623 data = dict(
643 data = dict(
624 users_group_id=users_group.users_group_id,
644 users_group_id=users_group.users_group_id,
625 group_name=users_group.users_group_name,
645 group_name=users_group.users_group_name,
626 active=users_group.users_group_active,
646 active=users_group.users_group_active,
627 )
647 )
628
648
629 return data
649 return data
630
650
631
651
632 class UsersGroupMember(Base, BaseModel):
652 class UsersGroupMember(Base, BaseModel):
633 __tablename__ = 'users_groups_members'
653 __tablename__ = 'users_groups_members'
634 __table_args__ = (
654 __table_args__ = (
635 {'extend_existing': True, 'mysql_engine': 'InnoDB',
655 {'extend_existing': True, 'mysql_engine': 'InnoDB',
636 'mysql_charset': 'utf8'},
656 'mysql_charset': 'utf8'},
637 )
657 )
638
658
639 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
659 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
640 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
660 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
641 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
661 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
642
662
643 user = relationship('User', lazy='joined')
663 user = relationship('User', lazy='joined')
644 users_group = relationship('UsersGroup')
664 users_group = relationship('UsersGroup')
645
665
646 def __init__(self, gr_id='', u_id=''):
666 def __init__(self, gr_id='', u_id=''):
647 self.users_group_id = gr_id
667 self.users_group_id = gr_id
648 self.user_id = u_id
668 self.user_id = u_id
649
669
650
670
651 class Repository(Base, BaseModel):
671 class Repository(Base, BaseModel):
652 __tablename__ = 'repositories'
672 __tablename__ = 'repositories'
653 __table_args__ = (
673 __table_args__ = (
654 UniqueConstraint('repo_name'),
674 UniqueConstraint('repo_name'),
655 Index('r_repo_name_idx', 'repo_name'),
675 Index('r_repo_name_idx', 'repo_name'),
656 {'extend_existing': True, 'mysql_engine': 'InnoDB',
676 {'extend_existing': True, 'mysql_engine': 'InnoDB',
657 'mysql_charset': 'utf8'},
677 'mysql_charset': 'utf8'},
658 )
678 )
659
679
660 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
680 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
661 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
681 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
662 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
682 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
663 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
683 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
664 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
684 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
665 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
685 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
666 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
686 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
667 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
687 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
668 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
688 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
669 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
689 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
670 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
690 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
671 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
691 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
672 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
692 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
673 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
693 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
674 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
694 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
675
695
676 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
696 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
677 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
697 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
678
698
679 user = relationship('User')
699 user = relationship('User')
680 fork = relationship('Repository', remote_side=repo_id)
700 fork = relationship('Repository', remote_side=repo_id)
681 group = relationship('RepoGroup')
701 group = relationship('RepoGroup')
682 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
702 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
683 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
703 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
684 stats = relationship('Statistics', cascade='all', uselist=False)
704 stats = relationship('Statistics', cascade='all', uselist=False)
685
705
686 followers = relationship('UserFollowing',
706 followers = relationship('UserFollowing',
687 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
707 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
688 cascade='all')
708 cascade='all')
689
709
690 logs = relationship('UserLog')
710 logs = relationship('UserLog')
691 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
711 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
692
712
693 pull_requests_org = relationship('PullRequest',
713 pull_requests_org = relationship('PullRequest',
694 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
714 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
695 cascade="all, delete, delete-orphan")
715 cascade="all, delete, delete-orphan")
696
716
697 pull_requests_other = relationship('PullRequest',
717 pull_requests_other = relationship('PullRequest',
698 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
718 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
699 cascade="all, delete, delete-orphan")
719 cascade="all, delete, delete-orphan")
700
720
701 def __unicode__(self):
721 def __unicode__(self):
702 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
722 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
703 self.repo_name)
723 self.repo_name)
704
724
705 @hybrid_property
725 @hybrid_property
706 def locked(self):
726 def locked(self):
707 # always should return [user_id, timelocked]
727 # always should return [user_id, timelocked]
708 if self._locked:
728 if self._locked:
709 _lock_info = self._locked.split(':')
729 _lock_info = self._locked.split(':')
710 return int(_lock_info[0]), _lock_info[1]
730 return int(_lock_info[0]), _lock_info[1]
711 return [None, None]
731 return [None, None]
712
732
713 @locked.setter
733 @locked.setter
714 def locked(self, val):
734 def locked(self, val):
715 if val and isinstance(val, (list, tuple)):
735 if val and isinstance(val, (list, tuple)):
716 self._locked = ':'.join(map(str, val))
736 self._locked = ':'.join(map(str, val))
717 else:
737 else:
718 self._locked = None
738 self._locked = None
719
739
720 @hybrid_property
740 @hybrid_property
721 def changeset_cache(self):
741 def changeset_cache(self):
722 from rhodecode.lib.vcs.backends.base import EmptyChangeset
742 from rhodecode.lib.vcs.backends.base import EmptyChangeset
723 dummy = EmptyChangeset().__json__()
743 dummy = EmptyChangeset().__json__()
724 if not self._changeset_cache:
744 if not self._changeset_cache:
725 return dummy
745 return dummy
726 try:
746 try:
727 return json.loads(self._changeset_cache)
747 return json.loads(self._changeset_cache)
728 except TypeError:
748 except TypeError:
729 return dummy
749 return dummy
730
750
731 @changeset_cache.setter
751 @changeset_cache.setter
732 def changeset_cache(self, val):
752 def changeset_cache(self, val):
733 try:
753 try:
734 self._changeset_cache = json.dumps(val)
754 self._changeset_cache = json.dumps(val)
735 except:
755 except:
736 log.error(traceback.format_exc())
756 log.error(traceback.format_exc())
737
757
738 @classmethod
758 @classmethod
739 def url_sep(cls):
759 def url_sep(cls):
740 return URL_SEP
760 return URL_SEP
741
761
742 @classmethod
762 @classmethod
743 def normalize_repo_name(cls, repo_name):
763 def normalize_repo_name(cls, repo_name):
744 """
764 """
745 Normalizes os specific repo_name to the format internally stored inside
765 Normalizes os specific repo_name to the format internally stored inside
746 dabatabase using URL_SEP
766 dabatabase using URL_SEP
747
767
748 :param cls:
768 :param cls:
749 :param repo_name:
769 :param repo_name:
750 """
770 """
751 return cls.url_sep().join(repo_name.split(os.sep))
771 return cls.url_sep().join(repo_name.split(os.sep))
752
772
753 @classmethod
773 @classmethod
754 def get_by_repo_name(cls, repo_name):
774 def get_by_repo_name(cls, repo_name):
755 q = Session().query(cls).filter(cls.repo_name == repo_name)
775 q = Session().query(cls).filter(cls.repo_name == repo_name)
756 q = q.options(joinedload(Repository.fork))\
776 q = q.options(joinedload(Repository.fork))\
757 .options(joinedload(Repository.user))\
777 .options(joinedload(Repository.user))\
758 .options(joinedload(Repository.group))
778 .options(joinedload(Repository.group))
759 return q.scalar()
779 return q.scalar()
760
780
761 @classmethod
781 @classmethod
762 def get_by_full_path(cls, repo_full_path):
782 def get_by_full_path(cls, repo_full_path):
763 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
783 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
764 repo_name = cls.normalize_repo_name(repo_name)
784 repo_name = cls.normalize_repo_name(repo_name)
765 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
785 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
766
786
767 @classmethod
787 @classmethod
768 def get_repo_forks(cls, repo_id):
788 def get_repo_forks(cls, repo_id):
769 return cls.query().filter(Repository.fork_id == repo_id)
789 return cls.query().filter(Repository.fork_id == repo_id)
770
790
771 @classmethod
791 @classmethod
772 def base_path(cls):
792 def base_path(cls):
773 """
793 """
774 Returns base path when all repos are stored
794 Returns base path when all repos are stored
775
795
776 :param cls:
796 :param cls:
777 """
797 """
778 q = Session().query(RhodeCodeUi)\
798 q = Session().query(RhodeCodeUi)\
779 .filter(RhodeCodeUi.ui_key == cls.url_sep())
799 .filter(RhodeCodeUi.ui_key == cls.url_sep())
780 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
800 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
781 return q.one().ui_value
801 return q.one().ui_value
782
802
783 @property
803 @property
784 def forks(self):
804 def forks(self):
785 """
805 """
786 Return forks of this repo
806 Return forks of this repo
787 """
807 """
788 return Repository.get_repo_forks(self.repo_id)
808 return Repository.get_repo_forks(self.repo_id)
789
809
790 @property
810 @property
791 def parent(self):
811 def parent(self):
792 """
812 """
793 Returns fork parent
813 Returns fork parent
794 """
814 """
795 return self.fork
815 return self.fork
796
816
797 @property
817 @property
798 def just_name(self):
818 def just_name(self):
799 return self.repo_name.split(Repository.url_sep())[-1]
819 return self.repo_name.split(Repository.url_sep())[-1]
800
820
801 @property
821 @property
802 def groups_with_parents(self):
822 def groups_with_parents(self):
803 groups = []
823 groups = []
804 if self.group is None:
824 if self.group is None:
805 return groups
825 return groups
806
826
807 cur_gr = self.group
827 cur_gr = self.group
808 groups.insert(0, cur_gr)
828 groups.insert(0, cur_gr)
809 while 1:
829 while 1:
810 gr = getattr(cur_gr, 'parent_group', None)
830 gr = getattr(cur_gr, 'parent_group', None)
811 cur_gr = cur_gr.parent_group
831 cur_gr = cur_gr.parent_group
812 if gr is None:
832 if gr is None:
813 break
833 break
814 groups.insert(0, gr)
834 groups.insert(0, gr)
815
835
816 return groups
836 return groups
817
837
818 @property
838 @property
819 def groups_and_repo(self):
839 def groups_and_repo(self):
820 return self.groups_with_parents, self.just_name
840 return self.groups_with_parents, self.just_name
821
841
822 @LazyProperty
842 @LazyProperty
823 def repo_path(self):
843 def repo_path(self):
824 """
844 """
825 Returns base full path for that repository means where it actually
845 Returns base full path for that repository means where it actually
826 exists on a filesystem
846 exists on a filesystem
827 """
847 """
828 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
848 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
829 Repository.url_sep())
849 Repository.url_sep())
830 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
850 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
831 return q.one().ui_value
851 return q.one().ui_value
832
852
833 @property
853 @property
834 def repo_full_path(self):
854 def repo_full_path(self):
835 p = [self.repo_path]
855 p = [self.repo_path]
836 # we need to split the name by / since this is how we store the
856 # we need to split the name by / since this is how we store the
837 # names in the database, but that eventually needs to be converted
857 # names in the database, but that eventually needs to be converted
838 # into a valid system path
858 # into a valid system path
839 p += self.repo_name.split(Repository.url_sep())
859 p += self.repo_name.split(Repository.url_sep())
840 return os.path.join(*p)
860 return os.path.join(*p)
841
861
842 @property
862 @property
843 def cache_keys(self):
863 def cache_keys(self):
844 """
864 """
845 Returns associated cache keys for that repo
865 Returns associated cache keys for that repo
846 """
866 """
847 return CacheInvalidation.query()\
867 return CacheInvalidation.query()\
848 .filter(CacheInvalidation.cache_args == self.repo_name)\
868 .filter(CacheInvalidation.cache_args == self.repo_name)\
849 .order_by(CacheInvalidation.cache_key)\
869 .order_by(CacheInvalidation.cache_key)\
850 .all()
870 .all()
851
871
852 def get_new_name(self, repo_name):
872 def get_new_name(self, repo_name):
853 """
873 """
854 returns new full repository name based on assigned group and new new
874 returns new full repository name based on assigned group and new new
855
875
856 :param group_name:
876 :param group_name:
857 """
877 """
858 path_prefix = self.group.full_path_splitted if self.group else []
878 path_prefix = self.group.full_path_splitted if self.group else []
859 return Repository.url_sep().join(path_prefix + [repo_name])
879 return Repository.url_sep().join(path_prefix + [repo_name])
860
880
861 @property
881 @property
862 def _ui(self):
882 def _ui(self):
863 """
883 """
864 Creates an db based ui object for this repository
884 Creates an db based ui object for this repository
865 """
885 """
866 from rhodecode.lib.utils import make_ui
886 from rhodecode.lib.utils import make_ui
867 return make_ui('db', clear_session=False)
887 return make_ui('db', clear_session=False)
868
888
869 @classmethod
889 @classmethod
870 def inject_ui(cls, repo, extras={}):
890 def inject_ui(cls, repo, extras={}):
871 from rhodecode.lib.vcs.backends.hg import MercurialRepository
891 from rhodecode.lib.vcs.backends.hg import MercurialRepository
872 from rhodecode.lib.vcs.backends.git import GitRepository
892 from rhodecode.lib.vcs.backends.git import GitRepository
873 required = (MercurialRepository, GitRepository)
893 required = (MercurialRepository, GitRepository)
874 if not isinstance(repo, required):
894 if not isinstance(repo, required):
875 raise Exception('repo must be instance of %s' % required)
895 raise Exception('repo must be instance of %s' % required)
876
896
877 # inject ui extra param to log this action via push logger
897 # inject ui extra param to log this action via push logger
878 for k, v in extras.items():
898 for k, v in extras.items():
879 repo._repo.ui.setconfig('rhodecode_extras', k, v)
899 repo._repo.ui.setconfig('rhodecode_extras', k, v)
880
900
881 @classmethod
901 @classmethod
882 def is_valid(cls, repo_name):
902 def is_valid(cls, repo_name):
883 """
903 """
884 returns True if given repo name is a valid filesystem repository
904 returns True if given repo name is a valid filesystem repository
885
905
886 :param cls:
906 :param cls:
887 :param repo_name:
907 :param repo_name:
888 """
908 """
889 from rhodecode.lib.utils import is_valid_repo
909 from rhodecode.lib.utils import is_valid_repo
890
910
891 return is_valid_repo(repo_name, cls.base_path())
911 return is_valid_repo(repo_name, cls.base_path())
892
912
893 def get_api_data(self):
913 def get_api_data(self):
894 """
914 """
895 Common function for generating repo api data
915 Common function for generating repo api data
896
916
897 """
917 """
898 repo = self
918 repo = self
899 data = dict(
919 data = dict(
900 repo_id=repo.repo_id,
920 repo_id=repo.repo_id,
901 repo_name=repo.repo_name,
921 repo_name=repo.repo_name,
902 repo_type=repo.repo_type,
922 repo_type=repo.repo_type,
903 clone_uri=repo.clone_uri,
923 clone_uri=repo.clone_uri,
904 private=repo.private,
924 private=repo.private,
905 created_on=repo.created_on,
925 created_on=repo.created_on,
906 description=repo.description,
926 description=repo.description,
907 landing_rev=repo.landing_rev,
927 landing_rev=repo.landing_rev,
908 owner=repo.user.username,
928 owner=repo.user.username,
909 fork_of=repo.fork.repo_name if repo.fork else None,
929 fork_of=repo.fork.repo_name if repo.fork else None,
910 enable_statistics=repo.enable_statistics,
930 enable_statistics=repo.enable_statistics,
911 enable_locking=repo.enable_locking,
931 enable_locking=repo.enable_locking,
912 enable_downloads=repo.enable_downloads,
932 enable_downloads=repo.enable_downloads,
913 last_changeset=repo.changeset_cache
933 last_changeset=repo.changeset_cache
914 )
934 )
915
935
916 return data
936 return data
917
937
918 @classmethod
938 @classmethod
919 def lock(cls, repo, user_id):
939 def lock(cls, repo, user_id):
920 repo.locked = [user_id, time.time()]
940 repo.locked = [user_id, time.time()]
921 Session().add(repo)
941 Session().add(repo)
922 Session().commit()
942 Session().commit()
923
943
924 @classmethod
944 @classmethod
925 def unlock(cls, repo):
945 def unlock(cls, repo):
926 repo.locked = None
946 repo.locked = None
927 Session().add(repo)
947 Session().add(repo)
928 Session().commit()
948 Session().commit()
929
949
930 @property
950 @property
931 def last_db_change(self):
951 def last_db_change(self):
932 return self.updated_on
952 return self.updated_on
933
953
934 def clone_url(self, **override):
954 def clone_url(self, **override):
935 from pylons import url
955 from pylons import url
936 from urlparse import urlparse
956 from urlparse import urlparse
937 import urllib
957 import urllib
938 parsed_url = urlparse(url('home', qualified=True))
958 parsed_url = urlparse(url('home', qualified=True))
939 default_clone_uri = '%(scheme)s://%(user)s%(pass)s%(netloc)s%(prefix)s%(path)s'
959 default_clone_uri = '%(scheme)s://%(user)s%(pass)s%(netloc)s%(prefix)s%(path)s'
940 decoded_path = safe_unicode(urllib.unquote(parsed_url.path))
960 decoded_path = safe_unicode(urllib.unquote(parsed_url.path))
941 args = {
961 args = {
942 'user': '',
962 'user': '',
943 'pass': '',
963 'pass': '',
944 'scheme': parsed_url.scheme,
964 'scheme': parsed_url.scheme,
945 'netloc': parsed_url.netloc,
965 'netloc': parsed_url.netloc,
946 'prefix': decoded_path,
966 'prefix': decoded_path,
947 'path': self.repo_name
967 'path': self.repo_name
948 }
968 }
949
969
950 args.update(override)
970 args.update(override)
951 return default_clone_uri % args
971 return default_clone_uri % args
952
972
953 #==========================================================================
973 #==========================================================================
954 # SCM PROPERTIES
974 # SCM PROPERTIES
955 #==========================================================================
975 #==========================================================================
956
976
957 def get_changeset(self, rev=None):
977 def get_changeset(self, rev=None):
958 return get_changeset_safe(self.scm_instance, rev)
978 return get_changeset_safe(self.scm_instance, rev)
959
979
960 def get_landing_changeset(self):
980 def get_landing_changeset(self):
961 """
981 """
962 Returns landing changeset, or if that doesn't exist returns the tip
982 Returns landing changeset, or if that doesn't exist returns the tip
963 """
983 """
964 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
984 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
965 return cs
985 return cs
966
986
967 def update_changeset_cache(self, cs_cache=None):
987 def update_changeset_cache(self, cs_cache=None):
968 """
988 """
969 Update cache of last changeset for repository, keys should be::
989 Update cache of last changeset for repository, keys should be::
970
990
971 short_id
991 short_id
972 raw_id
992 raw_id
973 revision
993 revision
974 message
994 message
975 date
995 date
976 author
996 author
977
997
978 :param cs_cache:
998 :param cs_cache:
979 """
999 """
980 from rhodecode.lib.vcs.backends.base import BaseChangeset
1000 from rhodecode.lib.vcs.backends.base import BaseChangeset
981 if cs_cache is None:
1001 if cs_cache is None:
982 cs_cache = self.get_changeset()
1002 cs_cache = self.get_changeset()
983 if isinstance(cs_cache, BaseChangeset):
1003 if isinstance(cs_cache, BaseChangeset):
984 cs_cache = cs_cache.__json__()
1004 cs_cache = cs_cache.__json__()
985
1005
986 if cs_cache != self.changeset_cache:
1006 if cs_cache != self.changeset_cache:
987 last_change = cs_cache.get('date') or self.last_change
1007 last_change = cs_cache.get('date') or self.last_change
988 log.debug('updated repo %s with new cs cache %s' % (self, cs_cache))
1008 log.debug('updated repo %s with new cs cache %s' % (self, cs_cache))
989 self.updated_on = last_change
1009 self.updated_on = last_change
990 self.changeset_cache = cs_cache
1010 self.changeset_cache = cs_cache
991 Session().add(self)
1011 Session().add(self)
992 Session().commit()
1012 Session().commit()
993
1013
994 @property
1014 @property
995 def tip(self):
1015 def tip(self):
996 return self.get_changeset('tip')
1016 return self.get_changeset('tip')
997
1017
998 @property
1018 @property
999 def author(self):
1019 def author(self):
1000 return self.tip.author
1020 return self.tip.author
1001
1021
1002 @property
1022 @property
1003 def last_change(self):
1023 def last_change(self):
1004 return self.scm_instance.last_change
1024 return self.scm_instance.last_change
1005
1025
1006 def get_comments(self, revisions=None):
1026 def get_comments(self, revisions=None):
1007 """
1027 """
1008 Returns comments for this repository grouped by revisions
1028 Returns comments for this repository grouped by revisions
1009
1029
1010 :param revisions: filter query by revisions only
1030 :param revisions: filter query by revisions only
1011 """
1031 """
1012 cmts = ChangesetComment.query()\
1032 cmts = ChangesetComment.query()\
1013 .filter(ChangesetComment.repo == self)
1033 .filter(ChangesetComment.repo == self)
1014 if revisions:
1034 if revisions:
1015 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
1035 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
1016 grouped = defaultdict(list)
1036 grouped = defaultdict(list)
1017 for cmt in cmts.all():
1037 for cmt in cmts.all():
1018 grouped[cmt.revision].append(cmt)
1038 grouped[cmt.revision].append(cmt)
1019 return grouped
1039 return grouped
1020
1040
1021 def statuses(self, revisions=None):
1041 def statuses(self, revisions=None):
1022 """
1042 """
1023 Returns statuses for this repository
1043 Returns statuses for this repository
1024
1044
1025 :param revisions: list of revisions to get statuses for
1045 :param revisions: list of revisions to get statuses for
1026 :type revisions: list
1046 :type revisions: list
1027 """
1047 """
1028
1048
1029 statuses = ChangesetStatus.query()\
1049 statuses = ChangesetStatus.query()\
1030 .filter(ChangesetStatus.repo == self)\
1050 .filter(ChangesetStatus.repo == self)\
1031 .filter(ChangesetStatus.version == 0)
1051 .filter(ChangesetStatus.version == 0)
1032 if revisions:
1052 if revisions:
1033 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
1053 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
1034 grouped = {}
1054 grouped = {}
1035
1055
1036 #maybe we have open new pullrequest without a status ?
1056 #maybe we have open new pullrequest without a status ?
1037 stat = ChangesetStatus.STATUS_UNDER_REVIEW
1057 stat = ChangesetStatus.STATUS_UNDER_REVIEW
1038 status_lbl = ChangesetStatus.get_status_lbl(stat)
1058 status_lbl = ChangesetStatus.get_status_lbl(stat)
1039 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
1059 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
1040 for rev in pr.revisions:
1060 for rev in pr.revisions:
1041 pr_id = pr.pull_request_id
1061 pr_id = pr.pull_request_id
1042 pr_repo = pr.other_repo.repo_name
1062 pr_repo = pr.other_repo.repo_name
1043 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
1063 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
1044
1064
1045 for stat in statuses.all():
1065 for stat in statuses.all():
1046 pr_id = pr_repo = None
1066 pr_id = pr_repo = None
1047 if stat.pull_request:
1067 if stat.pull_request:
1048 pr_id = stat.pull_request.pull_request_id
1068 pr_id = stat.pull_request.pull_request_id
1049 pr_repo = stat.pull_request.other_repo.repo_name
1069 pr_repo = stat.pull_request.other_repo.repo_name
1050 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
1070 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
1051 pr_id, pr_repo]
1071 pr_id, pr_repo]
1052 return grouped
1072 return grouped
1053
1073
1054 #==========================================================================
1074 #==========================================================================
1055 # SCM CACHE INSTANCE
1075 # SCM CACHE INSTANCE
1056 #==========================================================================
1076 #==========================================================================
1057
1077
1058 @property
1078 @property
1059 def invalidate(self):
1079 def invalidate(self):
1060 return CacheInvalidation.invalidate(self.repo_name)
1080 return CacheInvalidation.invalidate(self.repo_name)
1061
1081
1062 def set_invalidate(self):
1082 def set_invalidate(self):
1063 """
1083 """
1064 set a cache for invalidation for this instance
1084 set a cache for invalidation for this instance
1065 """
1085 """
1066 CacheInvalidation.set_invalidate(repo_name=self.repo_name)
1086 CacheInvalidation.set_invalidate(repo_name=self.repo_name)
1067
1087
1068 @LazyProperty
1088 @LazyProperty
1069 def scm_instance(self):
1089 def scm_instance(self):
1070 import rhodecode
1090 import rhodecode
1071 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
1091 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
1072 if full_cache:
1092 if full_cache:
1073 return self.scm_instance_cached()
1093 return self.scm_instance_cached()
1074 return self.__get_instance()
1094 return self.__get_instance()
1075
1095
1076 def scm_instance_cached(self, cache_map=None):
1096 def scm_instance_cached(self, cache_map=None):
1077 @cache_region('long_term')
1097 @cache_region('long_term')
1078 def _c(repo_name):
1098 def _c(repo_name):
1079 return self.__get_instance()
1099 return self.__get_instance()
1080 rn = self.repo_name
1100 rn = self.repo_name
1081 log.debug('Getting cached instance of repo')
1101 log.debug('Getting cached instance of repo')
1082
1102
1083 if cache_map:
1103 if cache_map:
1084 # get using prefilled cache_map
1104 # get using prefilled cache_map
1085 invalidate_repo = cache_map[self.repo_name]
1105 invalidate_repo = cache_map[self.repo_name]
1086 if invalidate_repo:
1106 if invalidate_repo:
1087 invalidate_repo = (None if invalidate_repo.cache_active
1107 invalidate_repo = (None if invalidate_repo.cache_active
1088 else invalidate_repo)
1108 else invalidate_repo)
1089 else:
1109 else:
1090 # get from invalidate
1110 # get from invalidate
1091 invalidate_repo = self.invalidate
1111 invalidate_repo = self.invalidate
1092
1112
1093 if invalidate_repo is not None:
1113 if invalidate_repo is not None:
1094 region_invalidate(_c, None, rn)
1114 region_invalidate(_c, None, rn)
1095 # update our cache
1115 # update our cache
1096 CacheInvalidation.set_valid(invalidate_repo.cache_key)
1116 CacheInvalidation.set_valid(invalidate_repo.cache_key)
1097 return _c(rn)
1117 return _c(rn)
1098
1118
1099 def __get_instance(self):
1119 def __get_instance(self):
1100 repo_full_path = self.repo_full_path
1120 repo_full_path = self.repo_full_path
1101 try:
1121 try:
1102 alias = get_scm(repo_full_path)[0]
1122 alias = get_scm(repo_full_path)[0]
1103 log.debug('Creating instance of %s repository' % alias)
1123 log.debug('Creating instance of %s repository' % alias)
1104 backend = get_backend(alias)
1124 backend = get_backend(alias)
1105 except VCSError:
1125 except VCSError:
1106 log.error(traceback.format_exc())
1126 log.error(traceback.format_exc())
1107 log.error('Perhaps this repository is in db and not in '
1127 log.error('Perhaps this repository is in db and not in '
1108 'filesystem run rescan repositories with '
1128 'filesystem run rescan repositories with '
1109 '"destroy old data " option from admin panel')
1129 '"destroy old data " option from admin panel')
1110 return
1130 return
1111
1131
1112 if alias == 'hg':
1132 if alias == 'hg':
1113
1133
1114 repo = backend(safe_str(repo_full_path), create=False,
1134 repo = backend(safe_str(repo_full_path), create=False,
1115 baseui=self._ui)
1135 baseui=self._ui)
1116 # skip hidden web repository
1136 # skip hidden web repository
1117 if repo._get_hidden():
1137 if repo._get_hidden():
1118 return
1138 return
1119 else:
1139 else:
1120 repo = backend(repo_full_path, create=False)
1140 repo = backend(repo_full_path, create=False)
1121
1141
1122 return repo
1142 return repo
1123
1143
1124
1144
1125 class RepoGroup(Base, BaseModel):
1145 class RepoGroup(Base, BaseModel):
1126 __tablename__ = 'groups'
1146 __tablename__ = 'groups'
1127 __table_args__ = (
1147 __table_args__ = (
1128 UniqueConstraint('group_name', 'group_parent_id'),
1148 UniqueConstraint('group_name', 'group_parent_id'),
1129 CheckConstraint('group_id != group_parent_id'),
1149 CheckConstraint('group_id != group_parent_id'),
1130 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1150 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1131 'mysql_charset': 'utf8'},
1151 'mysql_charset': 'utf8'},
1132 )
1152 )
1133 __mapper_args__ = {'order_by': 'group_name'}
1153 __mapper_args__ = {'order_by': 'group_name'}
1134
1154
1135 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1155 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1136 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
1156 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
1137 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1157 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1138 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1158 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1139 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1159 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1140
1160
1141 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1161 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1142 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
1162 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
1143
1163
1144 parent_group = relationship('RepoGroup', remote_side=group_id)
1164 parent_group = relationship('RepoGroup', remote_side=group_id)
1145
1165
1146 def __init__(self, group_name='', parent_group=None):
1166 def __init__(self, group_name='', parent_group=None):
1147 self.group_name = group_name
1167 self.group_name = group_name
1148 self.parent_group = parent_group
1168 self.parent_group = parent_group
1149
1169
1150 def __unicode__(self):
1170 def __unicode__(self):
1151 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
1171 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
1152 self.group_name)
1172 self.group_name)
1153
1173
1154 @classmethod
1174 @classmethod
1155 def groups_choices(cls, check_perms=False):
1175 def groups_choices(cls, check_perms=False):
1156 from webhelpers.html import literal as _literal
1176 from webhelpers.html import literal as _literal
1157 from rhodecode.model.scm import ScmModel
1177 from rhodecode.model.scm import ScmModel
1158 groups = cls.query().all()
1178 groups = cls.query().all()
1159 if check_perms:
1179 if check_perms:
1160 #filter group user have access to, it's done
1180 #filter group user have access to, it's done
1161 #magically inside ScmModel based on current user
1181 #magically inside ScmModel based on current user
1162 groups = ScmModel().get_repos_groups(groups)
1182 groups = ScmModel().get_repos_groups(groups)
1163 repo_groups = [('', '')]
1183 repo_groups = [('', '')]
1164 sep = ' &raquo; '
1184 sep = ' &raquo; '
1165 _name = lambda k: _literal(sep.join(k))
1185 _name = lambda k: _literal(sep.join(k))
1166
1186
1167 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1187 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1168 for x in groups])
1188 for x in groups])
1169
1189
1170 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1190 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1171 return repo_groups
1191 return repo_groups
1172
1192
1173 @classmethod
1193 @classmethod
1174 def url_sep(cls):
1194 def url_sep(cls):
1175 return URL_SEP
1195 return URL_SEP
1176
1196
1177 @classmethod
1197 @classmethod
1178 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1198 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1179 if case_insensitive:
1199 if case_insensitive:
1180 gr = cls.query()\
1200 gr = cls.query()\
1181 .filter(cls.group_name.ilike(group_name))
1201 .filter(cls.group_name.ilike(group_name))
1182 else:
1202 else:
1183 gr = cls.query()\
1203 gr = cls.query()\
1184 .filter(cls.group_name == group_name)
1204 .filter(cls.group_name == group_name)
1185 if cache:
1205 if cache:
1186 gr = gr.options(FromCache(
1206 gr = gr.options(FromCache(
1187 "sql_cache_short",
1207 "sql_cache_short",
1188 "get_group_%s" % _hash_key(group_name)
1208 "get_group_%s" % _hash_key(group_name)
1189 )
1209 )
1190 )
1210 )
1191 return gr.scalar()
1211 return gr.scalar()
1192
1212
1193 @property
1213 @property
1194 def parents(self):
1214 def parents(self):
1195 parents_recursion_limit = 5
1215 parents_recursion_limit = 5
1196 groups = []
1216 groups = []
1197 if self.parent_group is None:
1217 if self.parent_group is None:
1198 return groups
1218 return groups
1199 cur_gr = self.parent_group
1219 cur_gr = self.parent_group
1200 groups.insert(0, cur_gr)
1220 groups.insert(0, cur_gr)
1201 cnt = 0
1221 cnt = 0
1202 while 1:
1222 while 1:
1203 cnt += 1
1223 cnt += 1
1204 gr = getattr(cur_gr, 'parent_group', None)
1224 gr = getattr(cur_gr, 'parent_group', None)
1205 cur_gr = cur_gr.parent_group
1225 cur_gr = cur_gr.parent_group
1206 if gr is None:
1226 if gr is None:
1207 break
1227 break
1208 if cnt == parents_recursion_limit:
1228 if cnt == parents_recursion_limit:
1209 # this will prevent accidental infinit loops
1229 # this will prevent accidental infinit loops
1210 log.error('group nested more than %s' %
1230 log.error('group nested more than %s' %
1211 parents_recursion_limit)
1231 parents_recursion_limit)
1212 break
1232 break
1213
1233
1214 groups.insert(0, gr)
1234 groups.insert(0, gr)
1215 return groups
1235 return groups
1216
1236
1217 @property
1237 @property
1218 def children(self):
1238 def children(self):
1219 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1239 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1220
1240
1221 @property
1241 @property
1222 def name(self):
1242 def name(self):
1223 return self.group_name.split(RepoGroup.url_sep())[-1]
1243 return self.group_name.split(RepoGroup.url_sep())[-1]
1224
1244
1225 @property
1245 @property
1226 def full_path(self):
1246 def full_path(self):
1227 return self.group_name
1247 return self.group_name
1228
1248
1229 @property
1249 @property
1230 def full_path_splitted(self):
1250 def full_path_splitted(self):
1231 return self.group_name.split(RepoGroup.url_sep())
1251 return self.group_name.split(RepoGroup.url_sep())
1232
1252
1233 @property
1253 @property
1234 def repositories(self):
1254 def repositories(self):
1235 return Repository.query()\
1255 return Repository.query()\
1236 .filter(Repository.group == self)\
1256 .filter(Repository.group == self)\
1237 .order_by(Repository.repo_name)
1257 .order_by(Repository.repo_name)
1238
1258
1239 @property
1259 @property
1240 def repositories_recursive_count(self):
1260 def repositories_recursive_count(self):
1241 cnt = self.repositories.count()
1261 cnt = self.repositories.count()
1242
1262
1243 def children_count(group):
1263 def children_count(group):
1244 cnt = 0
1264 cnt = 0
1245 for child in group.children:
1265 for child in group.children:
1246 cnt += child.repositories.count()
1266 cnt += child.repositories.count()
1247 cnt += children_count(child)
1267 cnt += children_count(child)
1248 return cnt
1268 return cnt
1249
1269
1250 return cnt + children_count(self)
1270 return cnt + children_count(self)
1251
1271
1252 def recursive_groups_and_repos(self):
1272 def recursive_groups_and_repos(self):
1253 """
1273 """
1254 Recursive return all groups, with repositories in those groups
1274 Recursive return all groups, with repositories in those groups
1255 """
1275 """
1256 all_ = []
1276 all_ = []
1257
1277
1258 def _get_members(root_gr):
1278 def _get_members(root_gr):
1259 for r in root_gr.repositories:
1279 for r in root_gr.repositories:
1260 all_.append(r)
1280 all_.append(r)
1261 childs = root_gr.children.all()
1281 childs = root_gr.children.all()
1262 if childs:
1282 if childs:
1263 for gr in childs:
1283 for gr in childs:
1264 all_.append(gr)
1284 all_.append(gr)
1265 _get_members(gr)
1285 _get_members(gr)
1266
1286
1267 _get_members(self)
1287 _get_members(self)
1268 return [self] + all_
1288 return [self] + all_
1269
1289
1270 def get_new_name(self, group_name):
1290 def get_new_name(self, group_name):
1271 """
1291 """
1272 returns new full group name based on parent and new name
1292 returns new full group name based on parent and new name
1273
1293
1274 :param group_name:
1294 :param group_name:
1275 """
1295 """
1276 path_prefix = (self.parent_group.full_path_splitted if
1296 path_prefix = (self.parent_group.full_path_splitted if
1277 self.parent_group else [])
1297 self.parent_group else [])
1278 return RepoGroup.url_sep().join(path_prefix + [group_name])
1298 return RepoGroup.url_sep().join(path_prefix + [group_name])
1279
1299
1280
1300
1281 class Permission(Base, BaseModel):
1301 class Permission(Base, BaseModel):
1282 __tablename__ = 'permissions'
1302 __tablename__ = 'permissions'
1283 __table_args__ = (
1303 __table_args__ = (
1284 Index('p_perm_name_idx', 'permission_name'),
1304 Index('p_perm_name_idx', 'permission_name'),
1285 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1305 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1286 'mysql_charset': 'utf8'},
1306 'mysql_charset': 'utf8'},
1287 )
1307 )
1288 PERMS = [
1308 PERMS = [
1289 ('repository.none', _('Repository no access')),
1309 ('repository.none', _('Repository no access')),
1290 ('repository.read', _('Repository read access')),
1310 ('repository.read', _('Repository read access')),
1291 ('repository.write', _('Repository write access')),
1311 ('repository.write', _('Repository write access')),
1292 ('repository.admin', _('Repository admin access')),
1312 ('repository.admin', _('Repository admin access')),
1293
1313
1294 ('group.none', _('Repositories Group no access')),
1314 ('group.none', _('Repositories Group no access')),
1295 ('group.read', _('Repositories Group read access')),
1315 ('group.read', _('Repositories Group read access')),
1296 ('group.write', _('Repositories Group write access')),
1316 ('group.write', _('Repositories Group write access')),
1297 ('group.admin', _('Repositories Group admin access')),
1317 ('group.admin', _('Repositories Group admin access')),
1298
1318
1299 ('hg.admin', _('RhodeCode Administrator')),
1319 ('hg.admin', _('RhodeCode Administrator')),
1300 ('hg.create.none', _('Repository creation disabled')),
1320 ('hg.create.none', _('Repository creation disabled')),
1301 ('hg.create.repository', _('Repository creation enabled')),
1321 ('hg.create.repository', _('Repository creation enabled')),
1302 ('hg.fork.none', _('Repository forking disabled')),
1322 ('hg.fork.none', _('Repository forking disabled')),
1303 ('hg.fork.repository', _('Repository forking enabled')),
1323 ('hg.fork.repository', _('Repository forking enabled')),
1304 ('hg.register.none', _('Register disabled')),
1324 ('hg.register.none', _('Register disabled')),
1305 ('hg.register.manual_activate', _('Register new user with RhodeCode '
1325 ('hg.register.manual_activate', _('Register new user with RhodeCode '
1306 'with manual activation')),
1326 'with manual activation')),
1307
1327
1308 ('hg.register.auto_activate', _('Register new user with RhodeCode '
1328 ('hg.register.auto_activate', _('Register new user with RhodeCode '
1309 'with auto activation')),
1329 'with auto activation')),
1310 ]
1330 ]
1311
1331
1312 # defines which permissions are more important higher the more important
1332 # defines which permissions are more important higher the more important
1313 PERM_WEIGHTS = {
1333 PERM_WEIGHTS = {
1314 'repository.none': 0,
1334 'repository.none': 0,
1315 'repository.read': 1,
1335 'repository.read': 1,
1316 'repository.write': 3,
1336 'repository.write': 3,
1317 'repository.admin': 4,
1337 'repository.admin': 4,
1318
1338
1319 'group.none': 0,
1339 'group.none': 0,
1320 'group.read': 1,
1340 'group.read': 1,
1321 'group.write': 3,
1341 'group.write': 3,
1322 'group.admin': 4,
1342 'group.admin': 4,
1323
1343
1324 'hg.fork.none': 0,
1344 'hg.fork.none': 0,
1325 'hg.fork.repository': 1,
1345 'hg.fork.repository': 1,
1326 'hg.create.none': 0,
1346 'hg.create.none': 0,
1327 'hg.create.repository':1
1347 'hg.create.repository':1
1328 }
1348 }
1329
1349
1330 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1350 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1331 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1351 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1332 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1352 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1333
1353
1334 def __unicode__(self):
1354 def __unicode__(self):
1335 return u"<%s('%s:%s')>" % (
1355 return u"<%s('%s:%s')>" % (
1336 self.__class__.__name__, self.permission_id, self.permission_name
1356 self.__class__.__name__, self.permission_id, self.permission_name
1337 )
1357 )
1338
1358
1339 @classmethod
1359 @classmethod
1340 def get_by_key(cls, key):
1360 def get_by_key(cls, key):
1341 return cls.query().filter(cls.permission_name == key).scalar()
1361 return cls.query().filter(cls.permission_name == key).scalar()
1342
1362
1343 @classmethod
1363 @classmethod
1344 def get_default_perms(cls, default_user_id):
1364 def get_default_perms(cls, default_user_id):
1345 q = Session().query(UserRepoToPerm, Repository, cls)\
1365 q = Session().query(UserRepoToPerm, Repository, cls)\
1346 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1366 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1347 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1367 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1348 .filter(UserRepoToPerm.user_id == default_user_id)
1368 .filter(UserRepoToPerm.user_id == default_user_id)
1349
1369
1350 return q.all()
1370 return q.all()
1351
1371
1352 @classmethod
1372 @classmethod
1353 def get_default_group_perms(cls, default_user_id):
1373 def get_default_group_perms(cls, default_user_id):
1354 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1374 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1355 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1375 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1356 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1376 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1357 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1377 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1358
1378
1359 return q.all()
1379 return q.all()
1360
1380
1361
1381
1362 class UserRepoToPerm(Base, BaseModel):
1382 class UserRepoToPerm(Base, BaseModel):
1363 __tablename__ = 'repo_to_perm'
1383 __tablename__ = 'repo_to_perm'
1364 __table_args__ = (
1384 __table_args__ = (
1365 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1385 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1366 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1386 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1367 'mysql_charset': 'utf8'}
1387 'mysql_charset': 'utf8'}
1368 )
1388 )
1369 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1389 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1370 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1390 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1371 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1391 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1372 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1392 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1373
1393
1374 user = relationship('User')
1394 user = relationship('User')
1375 repository = relationship('Repository')
1395 repository = relationship('Repository')
1376 permission = relationship('Permission')
1396 permission = relationship('Permission')
1377
1397
1378 @classmethod
1398 @classmethod
1379 def create(cls, user, repository, permission):
1399 def create(cls, user, repository, permission):
1380 n = cls()
1400 n = cls()
1381 n.user = user
1401 n.user = user
1382 n.repository = repository
1402 n.repository = repository
1383 n.permission = permission
1403 n.permission = permission
1384 Session().add(n)
1404 Session().add(n)
1385 return n
1405 return n
1386
1406
1387 def __unicode__(self):
1407 def __unicode__(self):
1388 return u'<user:%s => %s >' % (self.user, self.repository)
1408 return u'<user:%s => %s >' % (self.user, self.repository)
1389
1409
1390
1410
1391 class UserToPerm(Base, BaseModel):
1411 class UserToPerm(Base, BaseModel):
1392 __tablename__ = 'user_to_perm'
1412 __tablename__ = 'user_to_perm'
1393 __table_args__ = (
1413 __table_args__ = (
1394 UniqueConstraint('user_id', 'permission_id'),
1414 UniqueConstraint('user_id', 'permission_id'),
1395 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1415 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1396 'mysql_charset': 'utf8'}
1416 'mysql_charset': 'utf8'}
1397 )
1417 )
1398 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1418 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1399 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1419 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1400 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1420 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1401
1421
1402 user = relationship('User')
1422 user = relationship('User')
1403 permission = relationship('Permission', lazy='joined')
1423 permission = relationship('Permission', lazy='joined')
1404
1424
1405
1425
1406 class UsersGroupRepoToPerm(Base, BaseModel):
1426 class UsersGroupRepoToPerm(Base, BaseModel):
1407 __tablename__ = 'users_group_repo_to_perm'
1427 __tablename__ = 'users_group_repo_to_perm'
1408 __table_args__ = (
1428 __table_args__ = (
1409 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1429 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1410 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1430 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1411 'mysql_charset': 'utf8'}
1431 'mysql_charset': 'utf8'}
1412 )
1432 )
1413 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1433 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1414 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1434 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1415 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1435 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1416 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1436 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1417
1437
1418 users_group = relationship('UsersGroup')
1438 users_group = relationship('UsersGroup')
1419 permission = relationship('Permission')
1439 permission = relationship('Permission')
1420 repository = relationship('Repository')
1440 repository = relationship('Repository')
1421
1441
1422 @classmethod
1442 @classmethod
1423 def create(cls, users_group, repository, permission):
1443 def create(cls, users_group, repository, permission):
1424 n = cls()
1444 n = cls()
1425 n.users_group = users_group
1445 n.users_group = users_group
1426 n.repository = repository
1446 n.repository = repository
1427 n.permission = permission
1447 n.permission = permission
1428 Session().add(n)
1448 Session().add(n)
1429 return n
1449 return n
1430
1450
1431 def __unicode__(self):
1451 def __unicode__(self):
1432 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1452 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1433
1453
1434
1454
1435 class UsersGroupToPerm(Base, BaseModel):
1455 class UsersGroupToPerm(Base, BaseModel):
1436 __tablename__ = 'users_group_to_perm'
1456 __tablename__ = 'users_group_to_perm'
1437 __table_args__ = (
1457 __table_args__ = (
1438 UniqueConstraint('users_group_id', 'permission_id',),
1458 UniqueConstraint('users_group_id', 'permission_id',),
1439 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1459 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1440 'mysql_charset': 'utf8'}
1460 'mysql_charset': 'utf8'}
1441 )
1461 )
1442 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1462 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1443 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1463 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1444 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1464 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1445
1465
1446 users_group = relationship('UsersGroup')
1466 users_group = relationship('UsersGroup')
1447 permission = relationship('Permission')
1467 permission = relationship('Permission')
1448
1468
1449
1469
1450 class UserRepoGroupToPerm(Base, BaseModel):
1470 class UserRepoGroupToPerm(Base, BaseModel):
1451 __tablename__ = 'user_repo_group_to_perm'
1471 __tablename__ = 'user_repo_group_to_perm'
1452 __table_args__ = (
1472 __table_args__ = (
1453 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1473 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1454 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1474 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1455 'mysql_charset': 'utf8'}
1475 'mysql_charset': 'utf8'}
1456 )
1476 )
1457
1477
1458 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1478 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1459 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1479 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1460 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1480 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1461 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1481 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1462
1482
1463 user = relationship('User')
1483 user = relationship('User')
1464 group = relationship('RepoGroup')
1484 group = relationship('RepoGroup')
1465 permission = relationship('Permission')
1485 permission = relationship('Permission')
1466
1486
1467
1487
1468 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1488 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1469 __tablename__ = 'users_group_repo_group_to_perm'
1489 __tablename__ = 'users_group_repo_group_to_perm'
1470 __table_args__ = (
1490 __table_args__ = (
1471 UniqueConstraint('users_group_id', 'group_id'),
1491 UniqueConstraint('users_group_id', 'group_id'),
1472 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1492 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1473 'mysql_charset': 'utf8'}
1493 'mysql_charset': 'utf8'}
1474 )
1494 )
1475
1495
1476 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)
1496 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)
1477 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1497 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1478 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1498 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1479 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1499 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1480
1500
1481 users_group = relationship('UsersGroup')
1501 users_group = relationship('UsersGroup')
1482 permission = relationship('Permission')
1502 permission = relationship('Permission')
1483 group = relationship('RepoGroup')
1503 group = relationship('RepoGroup')
1484
1504
1485
1505
1486 class Statistics(Base, BaseModel):
1506 class Statistics(Base, BaseModel):
1487 __tablename__ = 'statistics'
1507 __tablename__ = 'statistics'
1488 __table_args__ = (
1508 __table_args__ = (
1489 UniqueConstraint('repository_id'),
1509 UniqueConstraint('repository_id'),
1490 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1510 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1491 'mysql_charset': 'utf8'}
1511 'mysql_charset': 'utf8'}
1492 )
1512 )
1493 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1513 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1494 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1514 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1495 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1515 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1496 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1516 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1497 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1517 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1498 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1518 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1499
1519
1500 repository = relationship('Repository', single_parent=True)
1520 repository = relationship('Repository', single_parent=True)
1501
1521
1502
1522
1503 class UserFollowing(Base, BaseModel):
1523 class UserFollowing(Base, BaseModel):
1504 __tablename__ = 'user_followings'
1524 __tablename__ = 'user_followings'
1505 __table_args__ = (
1525 __table_args__ = (
1506 UniqueConstraint('user_id', 'follows_repository_id'),
1526 UniqueConstraint('user_id', 'follows_repository_id'),
1507 UniqueConstraint('user_id', 'follows_user_id'),
1527 UniqueConstraint('user_id', 'follows_user_id'),
1508 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1528 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1509 'mysql_charset': 'utf8'}
1529 'mysql_charset': 'utf8'}
1510 )
1530 )
1511
1531
1512 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1532 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1513 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1533 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1514 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1534 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1515 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1535 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1516 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1536 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1517
1537
1518 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1538 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1519
1539
1520 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1540 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1521 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1541 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1522
1542
1523 @classmethod
1543 @classmethod
1524 def get_repo_followers(cls, repo_id):
1544 def get_repo_followers(cls, repo_id):
1525 return cls.query().filter(cls.follows_repo_id == repo_id)
1545 return cls.query().filter(cls.follows_repo_id == repo_id)
1526
1546
1527
1547
1528 class CacheInvalidation(Base, BaseModel):
1548 class CacheInvalidation(Base, BaseModel):
1529 __tablename__ = 'cache_invalidation'
1549 __tablename__ = 'cache_invalidation'
1530 __table_args__ = (
1550 __table_args__ = (
1531 UniqueConstraint('cache_key'),
1551 UniqueConstraint('cache_key'),
1532 Index('key_idx', 'cache_key'),
1552 Index('key_idx', 'cache_key'),
1533 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1553 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1534 'mysql_charset': 'utf8'},
1554 'mysql_charset': 'utf8'},
1535 )
1555 )
1536 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1556 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1537 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1557 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1538 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1558 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1539 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1559 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1540
1560
1541 def __init__(self, cache_key, cache_args=''):
1561 def __init__(self, cache_key, cache_args=''):
1542 self.cache_key = cache_key
1562 self.cache_key = cache_key
1543 self.cache_args = cache_args
1563 self.cache_args = cache_args
1544 self.cache_active = False
1564 self.cache_active = False
1545
1565
1546 def __unicode__(self):
1566 def __unicode__(self):
1547 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1567 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1548 self.cache_id, self.cache_key)
1568 self.cache_id, self.cache_key)
1549
1569
1550 @property
1570 @property
1551 def prefix(self):
1571 def prefix(self):
1552 _split = self.cache_key.split(self.cache_args, 1)
1572 _split = self.cache_key.split(self.cache_args, 1)
1553 if _split and len(_split) == 2:
1573 if _split and len(_split) == 2:
1554 return _split[0]
1574 return _split[0]
1555 return ''
1575 return ''
1556
1576
1557 @classmethod
1577 @classmethod
1558 def clear_cache(cls):
1578 def clear_cache(cls):
1559 cls.query().delete()
1579 cls.query().delete()
1560
1580
1561 @classmethod
1581 @classmethod
1562 def _get_key(cls, key):
1582 def _get_key(cls, key):
1563 """
1583 """
1564 Wrapper for generating a key, together with a prefix
1584 Wrapper for generating a key, together with a prefix
1565
1585
1566 :param key:
1586 :param key:
1567 """
1587 """
1568 import rhodecode
1588 import rhodecode
1569 prefix = ''
1589 prefix = ''
1570 org_key = key
1590 org_key = key
1571 iid = rhodecode.CONFIG.get('instance_id')
1591 iid = rhodecode.CONFIG.get('instance_id')
1572 if iid:
1592 if iid:
1573 prefix = iid
1593 prefix = iid
1574
1594
1575 return "%s%s" % (prefix, key), prefix, org_key
1595 return "%s%s" % (prefix, key), prefix, org_key
1576
1596
1577 @classmethod
1597 @classmethod
1578 def get_by_key(cls, key):
1598 def get_by_key(cls, key):
1579 return cls.query().filter(cls.cache_key == key).scalar()
1599 return cls.query().filter(cls.cache_key == key).scalar()
1580
1600
1581 @classmethod
1601 @classmethod
1582 def get_by_repo_name(cls, repo_name):
1602 def get_by_repo_name(cls, repo_name):
1583 return cls.query().filter(cls.cache_args == repo_name).all()
1603 return cls.query().filter(cls.cache_args == repo_name).all()
1584
1604
1585 @classmethod
1605 @classmethod
1586 def _get_or_create_key(cls, key, repo_name, commit=True):
1606 def _get_or_create_key(cls, key, repo_name, commit=True):
1587 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1607 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1588 if not inv_obj:
1608 if not inv_obj:
1589 try:
1609 try:
1590 inv_obj = CacheInvalidation(key, repo_name)
1610 inv_obj = CacheInvalidation(key, repo_name)
1591 Session().add(inv_obj)
1611 Session().add(inv_obj)
1592 if commit:
1612 if commit:
1593 Session().commit()
1613 Session().commit()
1594 except Exception:
1614 except Exception:
1595 log.error(traceback.format_exc())
1615 log.error(traceback.format_exc())
1596 Session().rollback()
1616 Session().rollback()
1597 return inv_obj
1617 return inv_obj
1598
1618
1599 @classmethod
1619 @classmethod
1600 def invalidate(cls, key):
1620 def invalidate(cls, key):
1601 """
1621 """
1602 Returns Invalidation object if this given key should be invalidated
1622 Returns Invalidation object if this given key should be invalidated
1603 None otherwise. `cache_active = False` means that this cache
1623 None otherwise. `cache_active = False` means that this cache
1604 state is not valid and needs to be invalidated
1624 state is not valid and needs to be invalidated
1605
1625
1606 :param key:
1626 :param key:
1607 """
1627 """
1608 repo_name = key
1628 repo_name = key
1609 repo_name = remove_suffix(repo_name, '_README')
1629 repo_name = remove_suffix(repo_name, '_README')
1610 repo_name = remove_suffix(repo_name, '_RSS')
1630 repo_name = remove_suffix(repo_name, '_RSS')
1611 repo_name = remove_suffix(repo_name, '_ATOM')
1631 repo_name = remove_suffix(repo_name, '_ATOM')
1612
1632
1613 # adds instance prefix
1633 # adds instance prefix
1614 key, _prefix, _org_key = cls._get_key(key)
1634 key, _prefix, _org_key = cls._get_key(key)
1615 inv = cls._get_or_create_key(key, repo_name)
1635 inv = cls._get_or_create_key(key, repo_name)
1616
1636
1617 if inv and inv.cache_active is False:
1637 if inv and inv.cache_active is False:
1618 return inv
1638 return inv
1619
1639
1620 @classmethod
1640 @classmethod
1621 def set_invalidate(cls, key=None, repo_name=None):
1641 def set_invalidate(cls, key=None, repo_name=None):
1622 """
1642 """
1623 Mark this Cache key for invalidation, either by key or whole
1643 Mark this Cache key for invalidation, either by key or whole
1624 cache sets based on repo_name
1644 cache sets based on repo_name
1625
1645
1626 :param key:
1646 :param key:
1627 """
1647 """
1628 if key:
1648 if key:
1629 key, _prefix, _org_key = cls._get_key(key)
1649 key, _prefix, _org_key = cls._get_key(key)
1630 inv_objs = Session().query(cls).filter(cls.cache_key == key).all()
1650 inv_objs = Session().query(cls).filter(cls.cache_key == key).all()
1631 elif repo_name:
1651 elif repo_name:
1632 inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
1652 inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
1633
1653
1634 log.debug('marking %s key[s] for invalidation based on key=%s,repo_name=%s'
1654 log.debug('marking %s key[s] for invalidation based on key=%s,repo_name=%s'
1635 % (len(inv_objs), key, repo_name))
1655 % (len(inv_objs), key, repo_name))
1636 try:
1656 try:
1637 for inv_obj in inv_objs:
1657 for inv_obj in inv_objs:
1638 inv_obj.cache_active = False
1658 inv_obj.cache_active = False
1639 Session().add(inv_obj)
1659 Session().add(inv_obj)
1640 Session().commit()
1660 Session().commit()
1641 except Exception:
1661 except Exception:
1642 log.error(traceback.format_exc())
1662 log.error(traceback.format_exc())
1643 Session().rollback()
1663 Session().rollback()
1644
1664
1645 @classmethod
1665 @classmethod
1646 def set_valid(cls, key):
1666 def set_valid(cls, key):
1647 """
1667 """
1648 Mark this cache key as active and currently cached
1668 Mark this cache key as active and currently cached
1649
1669
1650 :param key:
1670 :param key:
1651 """
1671 """
1652 inv_obj = cls.get_by_key(key)
1672 inv_obj = cls.get_by_key(key)
1653 inv_obj.cache_active = True
1673 inv_obj.cache_active = True
1654 Session().add(inv_obj)
1674 Session().add(inv_obj)
1655 Session().commit()
1675 Session().commit()
1656
1676
1657 @classmethod
1677 @classmethod
1658 def get_cache_map(cls):
1678 def get_cache_map(cls):
1659
1679
1660 class cachemapdict(dict):
1680 class cachemapdict(dict):
1661
1681
1662 def __init__(self, *args, **kwargs):
1682 def __init__(self, *args, **kwargs):
1663 fixkey = kwargs.get('fixkey')
1683 fixkey = kwargs.get('fixkey')
1664 if fixkey:
1684 if fixkey:
1665 del kwargs['fixkey']
1685 del kwargs['fixkey']
1666 self.fixkey = fixkey
1686 self.fixkey = fixkey
1667 super(cachemapdict, self).__init__(*args, **kwargs)
1687 super(cachemapdict, self).__init__(*args, **kwargs)
1668
1688
1669 def __getattr__(self, name):
1689 def __getattr__(self, name):
1670 key = name
1690 key = name
1671 if self.fixkey:
1691 if self.fixkey:
1672 key, _prefix, _org_key = cls._get_key(key)
1692 key, _prefix, _org_key = cls._get_key(key)
1673 if key in self.__dict__:
1693 if key in self.__dict__:
1674 return self.__dict__[key]
1694 return self.__dict__[key]
1675 else:
1695 else:
1676 return self[key]
1696 return self[key]
1677
1697
1678 def __getitem__(self, key):
1698 def __getitem__(self, key):
1679 if self.fixkey:
1699 if self.fixkey:
1680 key, _prefix, _org_key = cls._get_key(key)
1700 key, _prefix, _org_key = cls._get_key(key)
1681 try:
1701 try:
1682 return super(cachemapdict, self).__getitem__(key)
1702 return super(cachemapdict, self).__getitem__(key)
1683 except KeyError:
1703 except KeyError:
1684 return
1704 return
1685
1705
1686 cache_map = cachemapdict(fixkey=True)
1706 cache_map = cachemapdict(fixkey=True)
1687 for obj in cls.query().all():
1707 for obj in cls.query().all():
1688 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1708 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1689 return cache_map
1709 return cache_map
1690
1710
1691
1711
1692 class ChangesetComment(Base, BaseModel):
1712 class ChangesetComment(Base, BaseModel):
1693 __tablename__ = 'changeset_comments'
1713 __tablename__ = 'changeset_comments'
1694 __table_args__ = (
1714 __table_args__ = (
1695 Index('cc_revision_idx', 'revision'),
1715 Index('cc_revision_idx', 'revision'),
1696 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1716 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1697 'mysql_charset': 'utf8'},
1717 'mysql_charset': 'utf8'},
1698 )
1718 )
1699 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1719 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1700 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1720 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1701 revision = Column('revision', String(40), nullable=True)
1721 revision = Column('revision', String(40), nullable=True)
1702 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1722 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1703 line_no = Column('line_no', Unicode(10), nullable=True)
1723 line_no = Column('line_no', Unicode(10), nullable=True)
1704 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1724 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1705 f_path = Column('f_path', Unicode(1000), nullable=True)
1725 f_path = Column('f_path', Unicode(1000), nullable=True)
1706 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1726 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1707 text = Column('text', UnicodeText(25000), nullable=False)
1727 text = Column('text', UnicodeText(25000), nullable=False)
1708 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1728 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1709 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1729 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1710
1730
1711 author = relationship('User', lazy='joined')
1731 author = relationship('User', lazy='joined')
1712 repo = relationship('Repository')
1732 repo = relationship('Repository')
1713 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1733 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1714 pull_request = relationship('PullRequest', lazy='joined')
1734 pull_request = relationship('PullRequest', lazy='joined')
1715
1735
1716 @classmethod
1736 @classmethod
1717 def get_users(cls, revision=None, pull_request_id=None):
1737 def get_users(cls, revision=None, pull_request_id=None):
1718 """
1738 """
1719 Returns user associated with this ChangesetComment. ie those
1739 Returns user associated with this ChangesetComment. ie those
1720 who actually commented
1740 who actually commented
1721
1741
1722 :param cls:
1742 :param cls:
1723 :param revision:
1743 :param revision:
1724 """
1744 """
1725 q = Session().query(User)\
1745 q = Session().query(User)\
1726 .join(ChangesetComment.author)
1746 .join(ChangesetComment.author)
1727 if revision:
1747 if revision:
1728 q = q.filter(cls.revision == revision)
1748 q = q.filter(cls.revision == revision)
1729 elif pull_request_id:
1749 elif pull_request_id:
1730 q = q.filter(cls.pull_request_id == pull_request_id)
1750 q = q.filter(cls.pull_request_id == pull_request_id)
1731 return q.all()
1751 return q.all()
1732
1752
1733
1753
1734 class ChangesetStatus(Base, BaseModel):
1754 class ChangesetStatus(Base, BaseModel):
1735 __tablename__ = 'changeset_statuses'
1755 __tablename__ = 'changeset_statuses'
1736 __table_args__ = (
1756 __table_args__ = (
1737 Index('cs_revision_idx', 'revision'),
1757 Index('cs_revision_idx', 'revision'),
1738 Index('cs_version_idx', 'version'),
1758 Index('cs_version_idx', 'version'),
1739 UniqueConstraint('repo_id', 'revision', 'version'),
1759 UniqueConstraint('repo_id', 'revision', 'version'),
1740 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1760 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1741 'mysql_charset': 'utf8'}
1761 'mysql_charset': 'utf8'}
1742 )
1762 )
1743 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1763 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1744 STATUS_APPROVED = 'approved'
1764 STATUS_APPROVED = 'approved'
1745 STATUS_REJECTED = 'rejected'
1765 STATUS_REJECTED = 'rejected'
1746 STATUS_UNDER_REVIEW = 'under_review'
1766 STATUS_UNDER_REVIEW = 'under_review'
1747
1767
1748 STATUSES = [
1768 STATUSES = [
1749 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1769 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1750 (STATUS_APPROVED, _("Approved")),
1770 (STATUS_APPROVED, _("Approved")),
1751 (STATUS_REJECTED, _("Rejected")),
1771 (STATUS_REJECTED, _("Rejected")),
1752 (STATUS_UNDER_REVIEW, _("Under Review")),
1772 (STATUS_UNDER_REVIEW, _("Under Review")),
1753 ]
1773 ]
1754
1774
1755 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1775 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1756 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1776 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1757 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1777 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1758 revision = Column('revision', String(40), nullable=False)
1778 revision = Column('revision', String(40), nullable=False)
1759 status = Column('status', String(128), nullable=False, default=DEFAULT)
1779 status = Column('status', String(128), nullable=False, default=DEFAULT)
1760 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1780 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1761 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1781 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1762 version = Column('version', Integer(), nullable=False, default=0)
1782 version = Column('version', Integer(), nullable=False, default=0)
1763 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1783 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1764
1784
1765 author = relationship('User', lazy='joined')
1785 author = relationship('User', lazy='joined')
1766 repo = relationship('Repository')
1786 repo = relationship('Repository')
1767 comment = relationship('ChangesetComment', lazy='joined')
1787 comment = relationship('ChangesetComment', lazy='joined')
1768 pull_request = relationship('PullRequest', lazy='joined')
1788 pull_request = relationship('PullRequest', lazy='joined')
1769
1789
1770 def __unicode__(self):
1790 def __unicode__(self):
1771 return u"<%s('%s:%s')>" % (
1791 return u"<%s('%s:%s')>" % (
1772 self.__class__.__name__,
1792 self.__class__.__name__,
1773 self.status, self.author
1793 self.status, self.author
1774 )
1794 )
1775
1795
1776 @classmethod
1796 @classmethod
1777 def get_status_lbl(cls, value):
1797 def get_status_lbl(cls, value):
1778 return dict(cls.STATUSES).get(value)
1798 return dict(cls.STATUSES).get(value)
1779
1799
1780 @property
1800 @property
1781 def status_lbl(self):
1801 def status_lbl(self):
1782 return ChangesetStatus.get_status_lbl(self.status)
1802 return ChangesetStatus.get_status_lbl(self.status)
1783
1803
1784
1804
1785 class PullRequest(Base, BaseModel):
1805 class PullRequest(Base, BaseModel):
1786 __tablename__ = 'pull_requests'
1806 __tablename__ = 'pull_requests'
1787 __table_args__ = (
1807 __table_args__ = (
1788 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1808 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1789 'mysql_charset': 'utf8'},
1809 'mysql_charset': 'utf8'},
1790 )
1810 )
1791
1811
1792 STATUS_NEW = u'new'
1812 STATUS_NEW = u'new'
1793 STATUS_OPEN = u'open'
1813 STATUS_OPEN = u'open'
1794 STATUS_CLOSED = u'closed'
1814 STATUS_CLOSED = u'closed'
1795
1815
1796 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1816 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1797 title = Column('title', Unicode(256), nullable=True)
1817 title = Column('title', Unicode(256), nullable=True)
1798 description = Column('description', UnicodeText(10240), nullable=True)
1818 description = Column('description', UnicodeText(10240), nullable=True)
1799 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1819 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1800 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1820 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1801 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1821 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1802 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1822 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1803 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1823 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1804 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1824 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1805 org_ref = Column('org_ref', Unicode(256), nullable=False)
1825 org_ref = Column('org_ref', Unicode(256), nullable=False)
1806 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1826 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1807 other_ref = Column('other_ref', Unicode(256), nullable=False)
1827 other_ref = Column('other_ref', Unicode(256), nullable=False)
1808
1828
1809 @hybrid_property
1829 @hybrid_property
1810 def revisions(self):
1830 def revisions(self):
1811 return self._revisions.split(':')
1831 return self._revisions.split(':')
1812
1832
1813 @revisions.setter
1833 @revisions.setter
1814 def revisions(self, val):
1834 def revisions(self, val):
1815 self._revisions = ':'.join(val)
1835 self._revisions = ':'.join(val)
1816
1836
1817 @property
1837 @property
1818 def org_ref_parts(self):
1838 def org_ref_parts(self):
1819 return self.org_ref.split(':')
1839 return self.org_ref.split(':')
1820
1840
1821 @property
1841 @property
1822 def other_ref_parts(self):
1842 def other_ref_parts(self):
1823 return self.other_ref.split(':')
1843 return self.other_ref.split(':')
1824
1844
1825 author = relationship('User', lazy='joined')
1845 author = relationship('User', lazy='joined')
1826 reviewers = relationship('PullRequestReviewers',
1846 reviewers = relationship('PullRequestReviewers',
1827 cascade="all, delete, delete-orphan")
1847 cascade="all, delete, delete-orphan")
1828 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1848 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1829 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1849 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1830 statuses = relationship('ChangesetStatus')
1850 statuses = relationship('ChangesetStatus')
1831 comments = relationship('ChangesetComment',
1851 comments = relationship('ChangesetComment',
1832 cascade="all, delete, delete-orphan")
1852 cascade="all, delete, delete-orphan")
1833
1853
1834 def is_closed(self):
1854 def is_closed(self):
1835 return self.status == self.STATUS_CLOSED
1855 return self.status == self.STATUS_CLOSED
1836
1856
1837 def __json__(self):
1857 def __json__(self):
1838 return dict(
1858 return dict(
1839 revisions=self.revisions
1859 revisions=self.revisions
1840 )
1860 )
1841
1861
1842
1862
1843 class PullRequestReviewers(Base, BaseModel):
1863 class PullRequestReviewers(Base, BaseModel):
1844 __tablename__ = 'pull_request_reviewers'
1864 __tablename__ = 'pull_request_reviewers'
1845 __table_args__ = (
1865 __table_args__ = (
1846 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1866 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1847 'mysql_charset': 'utf8'},
1867 'mysql_charset': 'utf8'},
1848 )
1868 )
1849
1869
1850 def __init__(self, user=None, pull_request=None):
1870 def __init__(self, user=None, pull_request=None):
1851 self.user = user
1871 self.user = user
1852 self.pull_request = pull_request
1872 self.pull_request = pull_request
1853
1873
1854 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1874 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1855 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1875 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1856 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1876 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1857
1877
1858 user = relationship('User')
1878 user = relationship('User')
1859 pull_request = relationship('PullRequest')
1879 pull_request = relationship('PullRequest')
1860
1880
1861
1881
1862 class Notification(Base, BaseModel):
1882 class Notification(Base, BaseModel):
1863 __tablename__ = 'notifications'
1883 __tablename__ = 'notifications'
1864 __table_args__ = (
1884 __table_args__ = (
1865 Index('notification_type_idx', 'type'),
1885 Index('notification_type_idx', 'type'),
1866 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1886 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1867 'mysql_charset': 'utf8'},
1887 'mysql_charset': 'utf8'},
1868 )
1888 )
1869
1889
1870 TYPE_CHANGESET_COMMENT = u'cs_comment'
1890 TYPE_CHANGESET_COMMENT = u'cs_comment'
1871 TYPE_MESSAGE = u'message'
1891 TYPE_MESSAGE = u'message'
1872 TYPE_MENTION = u'mention'
1892 TYPE_MENTION = u'mention'
1873 TYPE_REGISTRATION = u'registration'
1893 TYPE_REGISTRATION = u'registration'
1874 TYPE_PULL_REQUEST = u'pull_request'
1894 TYPE_PULL_REQUEST = u'pull_request'
1875 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1895 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1876
1896
1877 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1897 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1878 subject = Column('subject', Unicode(512), nullable=True)
1898 subject = Column('subject', Unicode(512), nullable=True)
1879 body = Column('body', UnicodeText(50000), nullable=True)
1899 body = Column('body', UnicodeText(50000), nullable=True)
1880 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1900 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1881 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1901 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1882 type_ = Column('type', Unicode(256))
1902 type_ = Column('type', Unicode(256))
1883
1903
1884 created_by_user = relationship('User')
1904 created_by_user = relationship('User')
1885 notifications_to_users = relationship('UserNotification', lazy='joined',
1905 notifications_to_users = relationship('UserNotification', lazy='joined',
1886 cascade="all, delete, delete-orphan")
1906 cascade="all, delete, delete-orphan")
1887
1907
1888 @property
1908 @property
1889 def recipients(self):
1909 def recipients(self):
1890 return [x.user for x in UserNotification.query()\
1910 return [x.user for x in UserNotification.query()\
1891 .filter(UserNotification.notification == self)\
1911 .filter(UserNotification.notification == self)\
1892 .order_by(UserNotification.user_id.asc()).all()]
1912 .order_by(UserNotification.user_id.asc()).all()]
1893
1913
1894 @classmethod
1914 @classmethod
1895 def create(cls, created_by, subject, body, recipients, type_=None):
1915 def create(cls, created_by, subject, body, recipients, type_=None):
1896 if type_ is None:
1916 if type_ is None:
1897 type_ = Notification.TYPE_MESSAGE
1917 type_ = Notification.TYPE_MESSAGE
1898
1918
1899 notification = cls()
1919 notification = cls()
1900 notification.created_by_user = created_by
1920 notification.created_by_user = created_by
1901 notification.subject = subject
1921 notification.subject = subject
1902 notification.body = body
1922 notification.body = body
1903 notification.type_ = type_
1923 notification.type_ = type_
1904 notification.created_on = datetime.datetime.now()
1924 notification.created_on = datetime.datetime.now()
1905
1925
1906 for u in recipients:
1926 for u in recipients:
1907 assoc = UserNotification()
1927 assoc = UserNotification()
1908 assoc.notification = notification
1928 assoc.notification = notification
1909 u.notifications.append(assoc)
1929 u.notifications.append(assoc)
1910 Session().add(notification)
1930 Session().add(notification)
1911 return notification
1931 return notification
1912
1932
1913 @property
1933 @property
1914 def description(self):
1934 def description(self):
1915 from rhodecode.model.notification import NotificationModel
1935 from rhodecode.model.notification import NotificationModel
1916 return NotificationModel().make_description(self)
1936 return NotificationModel().make_description(self)
1917
1937
1918
1938
1919 class UserNotification(Base, BaseModel):
1939 class UserNotification(Base, BaseModel):
1920 __tablename__ = 'user_to_notification'
1940 __tablename__ = 'user_to_notification'
1921 __table_args__ = (
1941 __table_args__ = (
1922 UniqueConstraint('user_id', 'notification_id'),
1942 UniqueConstraint('user_id', 'notification_id'),
1923 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1943 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1924 'mysql_charset': 'utf8'}
1944 'mysql_charset': 'utf8'}
1925 )
1945 )
1926 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1946 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1927 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1947 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1928 read = Column('read', Boolean, default=False)
1948 read = Column('read', Boolean, default=False)
1929 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1949 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1930
1950
1931 user = relationship('User', lazy="joined")
1951 user = relationship('User', lazy="joined")
1932 notification = relationship('Notification', lazy="joined",
1952 notification = relationship('Notification', lazy="joined",
1933 order_by=lambda: Notification.created_on.desc(),)
1953 order_by=lambda: Notification.created_on.desc(),)
1934
1954
1935 def mark_as_read(self):
1955 def mark_as_read(self):
1936 self.read = True
1956 self.read = True
1937 Session().add(self)
1957 Session().add(self)
1938
1958
1939
1959
1940 class DbMigrateVersion(Base, BaseModel):
1960 class DbMigrateVersion(Base, BaseModel):
1941 __tablename__ = 'db_migrate_version'
1961 __tablename__ = 'db_migrate_version'
1942 __table_args__ = (
1962 __table_args__ = (
1943 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1963 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1944 'mysql_charset': 'utf8'},
1964 'mysql_charset': 'utf8'},
1945 )
1965 )
1946 repository_id = Column('repository_id', String(250), primary_key=True)
1966 repository_id = Column('repository_id', String(250), primary_key=True)
1947 repository_path = Column('repository_path', Text)
1967 repository_path = Column('repository_path', Text)
1948 version = Column('version', Integer)
1968 version = Column('version', Integer)
General Comments 0
You need to be logged in to leave comments. Login now