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