##// END OF EJS Templates
White space cleanup
marcink -
r2150:a8c9c009 beta
parent child Browse files
Show More
@@ -1,148 +1,148 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
32 from rhodecode.lib.utils2 import extract_mentioned_users
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, Notification
35 from rhodecode.model.db import ChangesetComment, User, Repository, Notification
36 from rhodecode.model.notification import NotificationModel
36 from rhodecode.model.notification import NotificationModel
37
37
38 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
39
39
40
40
41 class ChangesetCommentsModel(BaseModel):
41 class ChangesetCommentsModel(BaseModel):
42
42
43 def __get_changeset_comment(self, changeset_comment):
43 def __get_changeset_comment(self, changeset_comment):
44 return self._get_instance(ChangesetComment, changeset_comment)
44 return self._get_instance(ChangesetComment, changeset_comment)
45
45
46 def _extract_mentions(self, s):
46 def _extract_mentions(self, s):
47 user_objects = []
47 user_objects = []
48 for username in extract_mentioned_users(s):
48 for username in extract_mentioned_users(s):
49 user_obj = User.get_by_username(username, case_insensitive=True)
49 user_obj = User.get_by_username(username, case_insensitive=True)
50 if user_obj:
50 if user_obj:
51 user_objects.append(user_obj)
51 user_objects.append(user_obj)
52 return user_objects
52 return user_objects
53
53
54 def create(self, text, repo_id, user_id, revision, f_path=None,
54 def create(self, text, repo_id, user_id, revision, f_path=None,
55 line_no=None):
55 line_no=None):
56 """
56 """
57 Creates new comment for changeset
57 Creates new comment for changeset
58
58
59 :param text:
59 :param text:
60 :param repo_id:
60 :param repo_id:
61 :param user_id:
61 :param user_id:
62 :param revision:
62 :param revision:
63 :param f_path:
63 :param f_path:
64 :param line_no:
64 :param line_no:
65 """
65 """
66
66 if text:
67 if text:
67 repo = Repository.get(repo_id)
68 repo = Repository.get(repo_id)
68 cs = repo.scm_instance.get_changeset(revision)
69 cs = repo.scm_instance.get_changeset(revision)
69 desc = cs.message
70 desc = cs.message
70 author_email = cs.author_email
71 author_email = cs.author_email
71 comment = ChangesetComment()
72 comment = ChangesetComment()
72 comment.repo = repo
73 comment.repo = repo
73 comment.user_id = user_id
74 comment.user_id = user_id
74 comment.revision = revision
75 comment.revision = revision
75 comment.text = text
76 comment.text = text
76 comment.f_path = f_path
77 comment.f_path = f_path
77 comment.line_no = line_no
78 comment.line_no = line_no
78
79
79 self.sa.add(comment)
80 self.sa.add(comment)
80 self.sa.flush()
81 self.sa.flush()
81
82 # make notification
82 # make notification
83 line = ''
83 line = ''
84 if line_no:
84 if line_no:
85 line = _('on line %s') % line_no
85 line = _('on line %s') % line_no
86 subj = h.link_to('Re commit: %(commit_desc)s %(line)s' % \
86 subj = h.link_to('Re commit: %(commit_desc)s %(line)s' % \
87 {'commit_desc': desc, 'line': line},
87 {'commit_desc': desc, 'line': line},
88 h.url('changeset_home', repo_name=repo.repo_name,
88 h.url('changeset_home', repo_name=repo.repo_name,
89 revision=revision,
89 revision=revision,
90 anchor='comment-%s' % comment.comment_id,
90 anchor='comment-%s' % comment.comment_id,
91 qualified=True,
91 qualified=True,
92 )
92 )
93 )
93 )
94 body = text
94 body = text
95
95
96 # get the current participants of this changeset
96 # get the current participants of this changeset
97 recipients = ChangesetComment.get_users(revision=revision)
97 recipients = ChangesetComment.get_users(revision=revision)
98
98
99 # add changeset author if it's in rhodecode system
99 # add changeset author if it's in rhodecode system
100 recipients += [User.get_by_email(author_email)]
100 recipients += [User.get_by_email(author_email)]
101
101
102 NotificationModel().create(
102 NotificationModel().create(
103 created_by=user_id, subject=subj, body=body,
103 created_by=user_id, subject=subj, body=body,
104 recipients=recipients, type_=Notification.TYPE_CHANGESET_COMMENT
104 recipients=recipients, type_=Notification.TYPE_CHANGESET_COMMENT
105 )
105 )
106
106
107 mention_recipients = set(self._extract_mentions(body))\
107 mention_recipients = set(self._extract_mentions(body))\
108 .difference(recipients)
108 .difference(recipients)
109 if mention_recipients:
109 if mention_recipients:
110 subj = _('[Mention]') + ' ' + subj
110 subj = _('[Mention]') + ' ' + subj
111 NotificationModel().create(
111 NotificationModel().create(
112 created_by=user_id, subject=subj, body=body,
112 created_by=user_id, subject=subj, body=body,
113 recipients=mention_recipients,
113 recipients=mention_recipients,
114 type_=Notification.TYPE_CHANGESET_COMMENT
114 type_=Notification.TYPE_CHANGESET_COMMENT
115 )
115 )
116
116
117 return comment
117 return comment
118
118
119 def delete(self, comment):
119 def delete(self, comment):
120 """
120 """
121 Deletes given comment
121 Deletes given comment
122
122
123 :param comment_id:
123 :param comment_id:
124 """
124 """
125 comment = self.__get_changeset_comment(comment)
125 comment = self.__get_changeset_comment(comment)
126 self.sa.delete(comment)
126 self.sa.delete(comment)
127
127
128 return comment
128 return comment
129
129
130 def get_comments(self, repo_id, revision):
130 def get_comments(self, repo_id, revision):
131 return ChangesetComment.query()\
131 return ChangesetComment.query()\
132 .filter(ChangesetComment.repo_id == repo_id)\
132 .filter(ChangesetComment.repo_id == repo_id)\
133 .filter(ChangesetComment.revision == revision)\
133 .filter(ChangesetComment.revision == revision)\
134 .filter(ChangesetComment.line_no == None)\
134 .filter(ChangesetComment.line_no == None)\
135 .filter(ChangesetComment.f_path == None).all()
135 .filter(ChangesetComment.f_path == None).all()
136
136
137 def get_inline_comments(self, repo_id, revision):
137 def get_inline_comments(self, repo_id, revision):
138 comments = self.sa.query(ChangesetComment)\
138 comments = self.sa.query(ChangesetComment)\
139 .filter(ChangesetComment.repo_id == repo_id)\
139 .filter(ChangesetComment.repo_id == repo_id)\
140 .filter(ChangesetComment.revision == revision)\
140 .filter(ChangesetComment.revision == revision)\
141 .filter(ChangesetComment.line_no != None)\
141 .filter(ChangesetComment.line_no != None)\
142 .filter(ChangesetComment.f_path != None).all()
142 .filter(ChangesetComment.f_path != None).all()
143
143
144 paths = defaultdict(lambda: defaultdict(list))
144 paths = defaultdict(lambda: defaultdict(list))
145
145
146 for co in comments:
146 for co in comments:
147 paths[co.f_path][co.line_no].append(co)
147 paths[co.f_path][co.line_no].append(co)
148 return paths.items()
148 return paths.items()
@@ -1,1280 +1,1280 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 from collections import defaultdict
30 from collections import defaultdict
31
31
32 from sqlalchemy import *
32 from sqlalchemy import *
33 from sqlalchemy.ext.hybrid import hybrid_property
33 from sqlalchemy.ext.hybrid import hybrid_property
34 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
34 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
35 from beaker.cache import cache_region, region_invalidate
35 from beaker.cache import cache_region, region_invalidate
36
36
37 from rhodecode.lib.vcs import get_backend
37 from rhodecode.lib.vcs import get_backend
38 from rhodecode.lib.vcs.utils.helpers import get_scm
38 from rhodecode.lib.vcs.utils.helpers import get_scm
39 from rhodecode.lib.vcs.exceptions import VCSError
39 from rhodecode.lib.vcs.exceptions import VCSError
40 from rhodecode.lib.vcs.utils.lazy import LazyProperty
40 from rhodecode.lib.vcs.utils.lazy import LazyProperty
41
41
42 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
42 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
43 safe_unicode
43 safe_unicode
44 from rhodecode.lib.compat import json
44 from rhodecode.lib.compat import json
45 from rhodecode.lib.caching_query import FromCache
45 from rhodecode.lib.caching_query import FromCache
46
46
47 from rhodecode.model.meta import Base, Session
47 from rhodecode.model.meta import Base, Session
48 import hashlib
48 import hashlib
49
49
50
50
51 log = logging.getLogger(__name__)
51 log = logging.getLogger(__name__)
52
52
53 #==============================================================================
53 #==============================================================================
54 # BASE CLASSES
54 # BASE CLASSES
55 #==============================================================================
55 #==============================================================================
56
56
57 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
57 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
58
58
59
59
60 class ModelSerializer(json.JSONEncoder):
60 class ModelSerializer(json.JSONEncoder):
61 """
61 """
62 Simple Serializer for JSON,
62 Simple Serializer for JSON,
63
63
64 usage::
64 usage::
65
65
66 to make object customized for serialization implement a __json__
66 to make object customized for serialization implement a __json__
67 method that will return a dict for serialization into json
67 method that will return a dict for serialization into json
68
68
69 example::
69 example::
70
70
71 class Task(object):
71 class Task(object):
72
72
73 def __init__(self, name, value):
73 def __init__(self, name, value):
74 self.name = name
74 self.name = name
75 self.value = value
75 self.value = value
76
76
77 def __json__(self):
77 def __json__(self):
78 return dict(name=self.name,
78 return dict(name=self.name,
79 value=self.value)
79 value=self.value)
80
80
81 """
81 """
82
82
83 def default(self, obj):
83 def default(self, obj):
84
84
85 if hasattr(obj, '__json__'):
85 if hasattr(obj, '__json__'):
86 return obj.__json__()
86 return obj.__json__()
87 else:
87 else:
88 return json.JSONEncoder.default(self, obj)
88 return json.JSONEncoder.default(self, obj)
89
89
90
90
91 class BaseModel(object):
91 class BaseModel(object):
92 """
92 """
93 Base Model for all classess
93 Base Model for all classess
94 """
94 """
95
95
96 @classmethod
96 @classmethod
97 def _get_keys(cls):
97 def _get_keys(cls):
98 """return column names for this model """
98 """return column names for this model """
99 return class_mapper(cls).c.keys()
99 return class_mapper(cls).c.keys()
100
100
101 def get_dict(self):
101 def get_dict(self):
102 """
102 """
103 return dict with keys and values corresponding
103 return dict with keys and values corresponding
104 to this model data """
104 to this model data """
105
105
106 d = {}
106 d = {}
107 for k in self._get_keys():
107 for k in self._get_keys():
108 d[k] = getattr(self, k)
108 d[k] = getattr(self, k)
109
109
110 # also use __json__() if present to get additional fields
110 # also use __json__() if present to get additional fields
111 for k, val in getattr(self, '__json__', lambda: {})().iteritems():
111 for k, val in getattr(self, '__json__', lambda: {})().iteritems():
112 d[k] = val
112 d[k] = val
113 return d
113 return d
114
114
115 def get_appstruct(self):
115 def get_appstruct(self):
116 """return list with keys and values tupples corresponding
116 """return list with keys and values tupples corresponding
117 to this model data """
117 to this model data """
118
118
119 l = []
119 l = []
120 for k in self._get_keys():
120 for k in self._get_keys():
121 l.append((k, getattr(self, k),))
121 l.append((k, getattr(self, k),))
122 return l
122 return l
123
123
124 def populate_obj(self, populate_dict):
124 def populate_obj(self, populate_dict):
125 """populate model with data from given populate_dict"""
125 """populate model with data from given populate_dict"""
126
126
127 for k in self._get_keys():
127 for k in self._get_keys():
128 if k in populate_dict:
128 if k in populate_dict:
129 setattr(self, k, populate_dict[k])
129 setattr(self, k, populate_dict[k])
130
130
131 @classmethod
131 @classmethod
132 def query(cls):
132 def query(cls):
133 return Session.query(cls)
133 return Session.query(cls)
134
134
135 @classmethod
135 @classmethod
136 def get(cls, id_):
136 def get(cls, id_):
137 if id_:
137 if id_:
138 return cls.query().get(id_)
138 return cls.query().get(id_)
139
139
140 @classmethod
140 @classmethod
141 def getAll(cls):
141 def getAll(cls):
142 return cls.query().all()
142 return cls.query().all()
143
143
144 @classmethod
144 @classmethod
145 def delete(cls, id_):
145 def delete(cls, id_):
146 obj = cls.query().get(id_)
146 obj = cls.query().get(id_)
147 Session.delete(obj)
147 Session.delete(obj)
148
148
149
149
150 class RhodeCodeSetting(Base, BaseModel):
150 class RhodeCodeSetting(Base, BaseModel):
151 __tablename__ = 'rhodecode_settings'
151 __tablename__ = 'rhodecode_settings'
152 __table_args__ = (
152 __table_args__ = (
153 UniqueConstraint('app_settings_name'),
153 UniqueConstraint('app_settings_name'),
154 {'extend_existing': True, 'mysql_engine':'InnoDB',
154 {'extend_existing': True, 'mysql_engine':'InnoDB',
155 'mysql_charset': 'utf8'}
155 'mysql_charset': 'utf8'}
156 )
156 )
157 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
157 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
158 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
158 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
159 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
159 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
160
160
161 def __init__(self, k='', v=''):
161 def __init__(self, k='', v=''):
162 self.app_settings_name = k
162 self.app_settings_name = k
163 self.app_settings_value = v
163 self.app_settings_value = v
164
164
165 @validates('_app_settings_value')
165 @validates('_app_settings_value')
166 def validate_settings_value(self, key, val):
166 def validate_settings_value(self, key, val):
167 assert type(val) == unicode
167 assert type(val) == unicode
168 return val
168 return val
169
169
170 @hybrid_property
170 @hybrid_property
171 def app_settings_value(self):
171 def app_settings_value(self):
172 v = self._app_settings_value
172 v = self._app_settings_value
173 if self.app_settings_name == 'ldap_active':
173 if self.app_settings_name == 'ldap_active':
174 v = str2bool(v)
174 v = str2bool(v)
175 return v
175 return v
176
176
177 @app_settings_value.setter
177 @app_settings_value.setter
178 def app_settings_value(self, val):
178 def app_settings_value(self, val):
179 """
179 """
180 Setter that will always make sure we use unicode in app_settings_value
180 Setter that will always make sure we use unicode in app_settings_value
181
181
182 :param val:
182 :param val:
183 """
183 """
184 self._app_settings_value = safe_unicode(val)
184 self._app_settings_value = safe_unicode(val)
185
185
186 def __repr__(self):
186 def __repr__(self):
187 return "<%s('%s:%s')>" % (
187 return "<%s('%s:%s')>" % (
188 self.__class__.__name__,
188 self.__class__.__name__,
189 self.app_settings_name, self.app_settings_value
189 self.app_settings_name, self.app_settings_value
190 )
190 )
191
191
192 @classmethod
192 @classmethod
193 def get_by_name(cls, ldap_key):
193 def get_by_name(cls, ldap_key):
194 return cls.query()\
194 return cls.query()\
195 .filter(cls.app_settings_name == ldap_key).scalar()
195 .filter(cls.app_settings_name == ldap_key).scalar()
196
196
197 @classmethod
197 @classmethod
198 def get_app_settings(cls, cache=False):
198 def get_app_settings(cls, cache=False):
199
199
200 ret = cls.query()
200 ret = cls.query()
201
201
202 if cache:
202 if cache:
203 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
203 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
204
204
205 if not ret:
205 if not ret:
206 raise Exception('Could not get application settings !')
206 raise Exception('Could not get application settings !')
207 settings = {}
207 settings = {}
208 for each in ret:
208 for each in ret:
209 settings['rhodecode_' + each.app_settings_name] = \
209 settings['rhodecode_' + each.app_settings_name] = \
210 each.app_settings_value
210 each.app_settings_value
211
211
212 return settings
212 return settings
213
213
214 @classmethod
214 @classmethod
215 def get_ldap_settings(cls, cache=False):
215 def get_ldap_settings(cls, cache=False):
216 ret = cls.query()\
216 ret = cls.query()\
217 .filter(cls.app_settings_name.startswith('ldap_')).all()
217 .filter(cls.app_settings_name.startswith('ldap_')).all()
218 fd = {}
218 fd = {}
219 for row in ret:
219 for row in ret:
220 fd.update({row.app_settings_name:row.app_settings_value})
220 fd.update({row.app_settings_name:row.app_settings_value})
221
221
222 return fd
222 return fd
223
223
224
224
225 class RhodeCodeUi(Base, BaseModel):
225 class RhodeCodeUi(Base, BaseModel):
226 __tablename__ = 'rhodecode_ui'
226 __tablename__ = 'rhodecode_ui'
227 __table_args__ = (
227 __table_args__ = (
228 UniqueConstraint('ui_key'),
228 UniqueConstraint('ui_key'),
229 {'extend_existing': True, 'mysql_engine':'InnoDB',
229 {'extend_existing': True, 'mysql_engine':'InnoDB',
230 'mysql_charset': 'utf8'}
230 'mysql_charset': 'utf8'}
231 )
231 )
232
232
233 HOOK_UPDATE = 'changegroup.update'
233 HOOK_UPDATE = 'changegroup.update'
234 HOOK_REPO_SIZE = 'changegroup.repo_size'
234 HOOK_REPO_SIZE = 'changegroup.repo_size'
235 HOOK_PUSH = 'pretxnchangegroup.push_logger'
235 HOOK_PUSH = 'pretxnchangegroup.push_logger'
236 HOOK_PULL = 'preoutgoing.pull_logger'
236 HOOK_PULL = 'preoutgoing.pull_logger'
237
237
238 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
238 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
239 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
239 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
240 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
240 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
241 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
241 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
242 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
242 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
243
243
244 @classmethod
244 @classmethod
245 def get_by_key(cls, key):
245 def get_by_key(cls, key):
246 return cls.query().filter(cls.ui_key == key)
246 return cls.query().filter(cls.ui_key == key)
247
247
248 @classmethod
248 @classmethod
249 def get_builtin_hooks(cls):
249 def get_builtin_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 return q.all()
254 return q.all()
255
255
256 @classmethod
256 @classmethod
257 def get_custom_hooks(cls):
257 def get_custom_hooks(cls):
258 q = cls.query()
258 q = cls.query()
259 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
259 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
260 cls.HOOK_REPO_SIZE,
260 cls.HOOK_REPO_SIZE,
261 cls.HOOK_PUSH, cls.HOOK_PULL]))
261 cls.HOOK_PUSH, cls.HOOK_PULL]))
262 q = q.filter(cls.ui_section == 'hooks')
262 q = q.filter(cls.ui_section == 'hooks')
263 return q.all()
263 return q.all()
264
264
265 @classmethod
265 @classmethod
266 def create_or_update_hook(cls, key, val):
266 def create_or_update_hook(cls, key, val):
267 new_ui = cls.get_by_key(key).scalar() or cls()
267 new_ui = cls.get_by_key(key).scalar() or cls()
268 new_ui.ui_section = 'hooks'
268 new_ui.ui_section = 'hooks'
269 new_ui.ui_active = True
269 new_ui.ui_active = True
270 new_ui.ui_key = key
270 new_ui.ui_key = key
271 new_ui.ui_value = val
271 new_ui.ui_value = val
272
272
273 Session.add(new_ui)
273 Session.add(new_ui)
274
274
275
275
276 class User(Base, BaseModel):
276 class User(Base, BaseModel):
277 __tablename__ = 'users'
277 __tablename__ = 'users'
278 __table_args__ = (
278 __table_args__ = (
279 UniqueConstraint('username'), UniqueConstraint('email'),
279 UniqueConstraint('username'), UniqueConstraint('email'),
280 {'extend_existing': True, 'mysql_engine':'InnoDB',
280 {'extend_existing': True, 'mysql_engine':'InnoDB',
281 'mysql_charset': 'utf8'}
281 'mysql_charset': 'utf8'}
282 )
282 )
283 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
283 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
284 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
284 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
285 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
285 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
286 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
286 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
287 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
287 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
288 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
288 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
289 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
289 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
290 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
290 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
291 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
291 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
292 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
292 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
293 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
293 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
294
294
295 user_log = relationship('UserLog', cascade='all')
295 user_log = relationship('UserLog', cascade='all')
296 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
296 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
297
297
298 repositories = relationship('Repository')
298 repositories = relationship('Repository')
299 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
299 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
300 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
300 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
301 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
301 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
302
302
303 group_member = relationship('UsersGroupMember', cascade='all')
303 group_member = relationship('UsersGroupMember', cascade='all')
304
304
305 notifications = relationship('UserNotification',)
305 notifications = relationship('UserNotification',)
306
306
307 @hybrid_property
307 @hybrid_property
308 def email(self):
308 def email(self):
309 return self._email
309 return self._email
310
310
311 @email.setter
311 @email.setter
312 def email(self, val):
312 def email(self, val):
313 self._email = val.lower() if val else None
313 self._email = val.lower() if val else None
314
314
315 @property
315 @property
316 def full_name(self):
316 def full_name(self):
317 return '%s %s' % (self.name, self.lastname)
317 return '%s %s' % (self.name, self.lastname)
318
318
319 @property
319 @property
320 def full_name_or_username(self):
320 def full_name_or_username(self):
321 return ('%s %s' % (self.name, self.lastname)
321 return ('%s %s' % (self.name, self.lastname)
322 if (self.name and self.lastname) else self.username)
322 if (self.name and self.lastname) else self.username)
323
323
324 @property
324 @property
325 def full_contact(self):
325 def full_contact(self):
326 return '%s %s <%s>' % (self.name, self.lastname, self.email)
326 return '%s %s <%s>' % (self.name, self.lastname, self.email)
327
327
328 @property
328 @property
329 def short_contact(self):
329 def short_contact(self):
330 return '%s %s' % (self.name, self.lastname)
330 return '%s %s' % (self.name, self.lastname)
331
331
332 @property
332 @property
333 def is_admin(self):
333 def is_admin(self):
334 return self.admin
334 return self.admin
335
335
336 def __repr__(self):
336 def __repr__(self):
337 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
337 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
338 self.user_id, self.username)
338 self.user_id, self.username)
339
339
340 @classmethod
340 @classmethod
341 def get_by_username(cls, username, case_insensitive=False, cache=False):
341 def get_by_username(cls, username, case_insensitive=False, cache=False):
342 if case_insensitive:
342 if case_insensitive:
343 q = cls.query().filter(cls.username.ilike(username))
343 q = cls.query().filter(cls.username.ilike(username))
344 else:
344 else:
345 q = cls.query().filter(cls.username == username)
345 q = cls.query().filter(cls.username == username)
346
346
347 if cache:
347 if cache:
348 q = q.options(FromCache(
348 q = q.options(FromCache(
349 "sql_cache_short",
349 "sql_cache_short",
350 "get_user_%s" % _hash_key(username)
350 "get_user_%s" % _hash_key(username)
351 )
351 )
352 )
352 )
353 return q.scalar()
353 return q.scalar()
354
354
355 @classmethod
355 @classmethod
356 def get_by_api_key(cls, api_key, cache=False):
356 def get_by_api_key(cls, api_key, cache=False):
357 q = cls.query().filter(cls.api_key == api_key)
357 q = cls.query().filter(cls.api_key == api_key)
358
358
359 if cache:
359 if cache:
360 q = q.options(FromCache("sql_cache_short",
360 q = q.options(FromCache("sql_cache_short",
361 "get_api_key_%s" % api_key))
361 "get_api_key_%s" % api_key))
362 return q.scalar()
362 return q.scalar()
363
363
364 @classmethod
364 @classmethod
365 def get_by_email(cls, email, case_insensitive=False, cache=False):
365 def get_by_email(cls, email, case_insensitive=False, cache=False):
366 if case_insensitive:
366 if case_insensitive:
367 q = cls.query().filter(cls.email.ilike(email))
367 q = cls.query().filter(cls.email.ilike(email))
368 else:
368 else:
369 q = cls.query().filter(cls.email == email)
369 q = cls.query().filter(cls.email == email)
370
370
371 if cache:
371 if cache:
372 q = q.options(FromCache("sql_cache_short",
372 q = q.options(FromCache("sql_cache_short",
373 "get_api_key_%s" % email))
373 "get_api_key_%s" % email))
374 return q.scalar()
374 return q.scalar()
375
375
376 def update_lastlogin(self):
376 def update_lastlogin(self):
377 """Update user lastlogin"""
377 """Update user lastlogin"""
378 self.last_login = datetime.datetime.now()
378 self.last_login = datetime.datetime.now()
379 Session.add(self)
379 Session.add(self)
380 log.debug('updated user %s lastlogin' % self.username)
380 log.debug('updated user %s lastlogin' % self.username)
381
381
382 def __json__(self):
382 def __json__(self):
383 return dict(
383 return dict(
384 email=self.email,
384 email=self.email,
385 full_name=self.full_name,
385 full_name=self.full_name,
386 full_name_or_username=self.full_name_or_username,
386 full_name_or_username=self.full_name_or_username,
387 short_contact=self.short_contact,
387 short_contact=self.short_contact,
388 full_contact=self.full_contact
388 full_contact=self.full_contact
389 )
389 )
390
390
391
391
392 class UserLog(Base, BaseModel):
392 class UserLog(Base, BaseModel):
393 __tablename__ = 'user_logs'
393 __tablename__ = 'user_logs'
394 __table_args__ = (
394 __table_args__ = (
395 {'extend_existing': True, 'mysql_engine':'InnoDB',
395 {'extend_existing': True, 'mysql_engine':'InnoDB',
396 'mysql_charset': 'utf8'},
396 'mysql_charset': 'utf8'},
397 )
397 )
398 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
398 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
399 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
399 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
400 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
400 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
401 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
401 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
402 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
402 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
403 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
403 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
404 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
404 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
405
405
406 @property
406 @property
407 def action_as_day(self):
407 def action_as_day(self):
408 return datetime.date(*self.action_date.timetuple()[:3])
408 return datetime.date(*self.action_date.timetuple()[:3])
409
409
410 user = relationship('User')
410 user = relationship('User')
411 repository = relationship('Repository', cascade='')
411 repository = relationship('Repository', cascade='')
412
412
413
413
414 class UsersGroup(Base, BaseModel):
414 class UsersGroup(Base, BaseModel):
415 __tablename__ = 'users_groups'
415 __tablename__ = 'users_groups'
416 __table_args__ = (
416 __table_args__ = (
417 {'extend_existing': True, 'mysql_engine':'InnoDB',
417 {'extend_existing': True, 'mysql_engine':'InnoDB',
418 'mysql_charset': 'utf8'},
418 'mysql_charset': 'utf8'},
419 )
419 )
420
420
421 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
421 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
422 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
422 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
423 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
423 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
424
424
425 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
425 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
426 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
426 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
427 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
427 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
428
428
429 def __repr__(self):
429 def __repr__(self):
430 return '<userGroup(%s)>' % (self.users_group_name)
430 return '<userGroup(%s)>' % (self.users_group_name)
431
431
432 @classmethod
432 @classmethod
433 def get_by_group_name(cls, group_name, cache=False,
433 def get_by_group_name(cls, group_name, cache=False,
434 case_insensitive=False):
434 case_insensitive=False):
435 if case_insensitive:
435 if case_insensitive:
436 q = cls.query().filter(cls.users_group_name.ilike(group_name))
436 q = cls.query().filter(cls.users_group_name.ilike(group_name))
437 else:
437 else:
438 q = cls.query().filter(cls.users_group_name == group_name)
438 q = cls.query().filter(cls.users_group_name == group_name)
439 if cache:
439 if cache:
440 q = q.options(FromCache(
440 q = q.options(FromCache(
441 "sql_cache_short",
441 "sql_cache_short",
442 "get_user_%s" % _hash_key(group_name)
442 "get_user_%s" % _hash_key(group_name)
443 )
443 )
444 )
444 )
445 return q.scalar()
445 return q.scalar()
446
446
447 @classmethod
447 @classmethod
448 def get(cls, users_group_id, cache=False):
448 def get(cls, users_group_id, cache=False):
449 users_group = cls.query()
449 users_group = cls.query()
450 if cache:
450 if cache:
451 users_group = users_group.options(FromCache("sql_cache_short",
451 users_group = users_group.options(FromCache("sql_cache_short",
452 "get_users_group_%s" % users_group_id))
452 "get_users_group_%s" % users_group_id))
453 return users_group.get(users_group_id)
453 return users_group.get(users_group_id)
454
454
455
455
456 class UsersGroupMember(Base, BaseModel):
456 class UsersGroupMember(Base, BaseModel):
457 __tablename__ = 'users_groups_members'
457 __tablename__ = 'users_groups_members'
458 __table_args__ = (
458 __table_args__ = (
459 {'extend_existing': True, 'mysql_engine':'InnoDB',
459 {'extend_existing': True, 'mysql_engine':'InnoDB',
460 'mysql_charset': 'utf8'},
460 'mysql_charset': 'utf8'},
461 )
461 )
462
462
463 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
463 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
464 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
464 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
465 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
465 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
466
466
467 user = relationship('User', lazy='joined')
467 user = relationship('User', lazy='joined')
468 users_group = relationship('UsersGroup')
468 users_group = relationship('UsersGroup')
469
469
470 def __init__(self, gr_id='', u_id=''):
470 def __init__(self, gr_id='', u_id=''):
471 self.users_group_id = gr_id
471 self.users_group_id = gr_id
472 self.user_id = u_id
472 self.user_id = u_id
473
473
474
474
475 class Repository(Base, BaseModel):
475 class Repository(Base, BaseModel):
476 __tablename__ = 'repositories'
476 __tablename__ = 'repositories'
477 __table_args__ = (
477 __table_args__ = (
478 UniqueConstraint('repo_name'),
478 UniqueConstraint('repo_name'),
479 {'extend_existing': True, 'mysql_engine':'InnoDB',
479 {'extend_existing': True, 'mysql_engine':'InnoDB',
480 'mysql_charset': 'utf8'},
480 'mysql_charset': 'utf8'},
481 )
481 )
482
482
483 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
483 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
484 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
484 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
485 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
485 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
486 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
486 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
487 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
487 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
488 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
488 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
489 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
489 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
490 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
490 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
491 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
491 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
492 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
492 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
493
493
494 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
494 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
495 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
495 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
496
496
497 user = relationship('User')
497 user = relationship('User')
498 fork = relationship('Repository', remote_side=repo_id)
498 fork = relationship('Repository', remote_side=repo_id)
499 group = relationship('RepoGroup')
499 group = relationship('RepoGroup')
500 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
500 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
501 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
501 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
502 stats = relationship('Statistics', cascade='all', uselist=False)
502 stats = relationship('Statistics', cascade='all', uselist=False)
503
503
504 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
504 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
505
505
506 logs = relationship('UserLog')
506 logs = relationship('UserLog')
507
507
508 def __repr__(self):
508 def __repr__(self):
509 return "<%s('%s:%s')>" % (self.__class__.__name__,
509 return "<%s('%s:%s')>" % (self.__class__.__name__,
510 self.repo_id, self.repo_name)
510 self.repo_id, self.repo_name)
511
511
512 @classmethod
512 @classmethod
513 def url_sep(cls):
513 def url_sep(cls):
514 return '/'
514 return '/'
515
515
516 @classmethod
516 @classmethod
517 def get_by_repo_name(cls, repo_name):
517 def get_by_repo_name(cls, repo_name):
518 q = Session.query(cls).filter(cls.repo_name == repo_name)
518 q = Session.query(cls).filter(cls.repo_name == repo_name)
519 q = q.options(joinedload(Repository.fork))\
519 q = q.options(joinedload(Repository.fork))\
520 .options(joinedload(Repository.user))\
520 .options(joinedload(Repository.user))\
521 .options(joinedload(Repository.group))
521 .options(joinedload(Repository.group))
522 return q.scalar()
522 return q.scalar()
523
523
524 @classmethod
524 @classmethod
525 def get_repo_forks(cls, repo_id):
525 def get_repo_forks(cls, repo_id):
526 return cls.query().filter(Repository.fork_id == repo_id)
526 return cls.query().filter(Repository.fork_id == repo_id)
527
527
528 @classmethod
528 @classmethod
529 def base_path(cls):
529 def base_path(cls):
530 """
530 """
531 Returns base path when all repos are stored
531 Returns base path when all repos are stored
532
532
533 :param cls:
533 :param cls:
534 """
534 """
535 q = Session.query(RhodeCodeUi)\
535 q = Session.query(RhodeCodeUi)\
536 .filter(RhodeCodeUi.ui_key == cls.url_sep())
536 .filter(RhodeCodeUi.ui_key == cls.url_sep())
537 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
537 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
538 return q.one().ui_value
538 return q.one().ui_value
539
539
540 @property
540 @property
541 def just_name(self):
541 def just_name(self):
542 return self.repo_name.split(Repository.url_sep())[-1]
542 return self.repo_name.split(Repository.url_sep())[-1]
543
543
544 @property
544 @property
545 def groups_with_parents(self):
545 def groups_with_parents(self):
546 groups = []
546 groups = []
547 if self.group is None:
547 if self.group is None:
548 return groups
548 return groups
549
549
550 cur_gr = self.group
550 cur_gr = self.group
551 groups.insert(0, cur_gr)
551 groups.insert(0, cur_gr)
552 while 1:
552 while 1:
553 gr = getattr(cur_gr, 'parent_group', None)
553 gr = getattr(cur_gr, 'parent_group', None)
554 cur_gr = cur_gr.parent_group
554 cur_gr = cur_gr.parent_group
555 if gr is None:
555 if gr is None:
556 break
556 break
557 groups.insert(0, gr)
557 groups.insert(0, gr)
558
558
559 return groups
559 return groups
560
560
561 @property
561 @property
562 def groups_and_repo(self):
562 def groups_and_repo(self):
563 return self.groups_with_parents, self.just_name
563 return self.groups_with_parents, self.just_name
564
564
565 @LazyProperty
565 @LazyProperty
566 def repo_path(self):
566 def repo_path(self):
567 """
567 """
568 Returns base full path for that repository means where it actually
568 Returns base full path for that repository means where it actually
569 exists on a filesystem
569 exists on a filesystem
570 """
570 """
571 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
571 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
572 Repository.url_sep())
572 Repository.url_sep())
573 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
573 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
574 return q.one().ui_value
574 return q.one().ui_value
575
575
576 @property
576 @property
577 def repo_full_path(self):
577 def repo_full_path(self):
578 p = [self.repo_path]
578 p = [self.repo_path]
579 # we need to split the name by / since this is how we store the
579 # we need to split the name by / since this is how we store the
580 # names in the database, but that eventually needs to be converted
580 # names in the database, but that eventually needs to be converted
581 # into a valid system path
581 # into a valid system path
582 p += self.repo_name.split(Repository.url_sep())
582 p += self.repo_name.split(Repository.url_sep())
583 return os.path.join(*p)
583 return os.path.join(*p)
584
584
585 def get_new_name(self, repo_name):
585 def get_new_name(self, repo_name):
586 """
586 """
587 returns new full repository name based on assigned group and new new
587 returns new full repository name based on assigned group and new new
588
588
589 :param group_name:
589 :param group_name:
590 """
590 """
591 path_prefix = self.group.full_path_splitted if self.group else []
591 path_prefix = self.group.full_path_splitted if self.group else []
592 return Repository.url_sep().join(path_prefix + [repo_name])
592 return Repository.url_sep().join(path_prefix + [repo_name])
593
593
594 @property
594 @property
595 def _ui(self):
595 def _ui(self):
596 """
596 """
597 Creates an db based ui object for this repository
597 Creates an db based ui object for this repository
598 """
598 """
599 from mercurial import ui
599 from mercurial import ui
600 from mercurial import config
600 from mercurial import config
601 baseui = ui.ui()
601 baseui = ui.ui()
602
602
603 #clean the baseui object
603 #clean the baseui object
604 baseui._ocfg = config.config()
604 baseui._ocfg = config.config()
605 baseui._ucfg = config.config()
605 baseui._ucfg = config.config()
606 baseui._tcfg = config.config()
606 baseui._tcfg = config.config()
607
607
608 ret = RhodeCodeUi.query()\
608 ret = RhodeCodeUi.query()\
609 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
609 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
610
610
611 hg_ui = ret
611 hg_ui = ret
612 for ui_ in hg_ui:
612 for ui_ in hg_ui:
613 if ui_.ui_active:
613 if ui_.ui_active:
614 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
614 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
615 ui_.ui_key, ui_.ui_value)
615 ui_.ui_key, ui_.ui_value)
616 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
616 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
617
617
618 return baseui
618 return baseui
619
619
620 @classmethod
620 @classmethod
621 def is_valid(cls, repo_name):
621 def is_valid(cls, repo_name):
622 """
622 """
623 returns True if given repo name is a valid filesystem repository
623 returns True if given repo name is a valid filesystem repository
624
624
625 :param cls:
625 :param cls:
626 :param repo_name:
626 :param repo_name:
627 """
627 """
628 from rhodecode.lib.utils import is_valid_repo
628 from rhodecode.lib.utils import is_valid_repo
629
629
630 return is_valid_repo(repo_name, cls.base_path())
630 return is_valid_repo(repo_name, cls.base_path())
631
631
632 #==========================================================================
632 #==========================================================================
633 # SCM PROPERTIES
633 # SCM PROPERTIES
634 #==========================================================================
634 #==========================================================================
635
635
636 def get_changeset(self, rev):
636 def get_changeset(self, rev):
637 return get_changeset_safe(self.scm_instance, rev)
637 return get_changeset_safe(self.scm_instance, rev)
638
638
639 @property
639 @property
640 def tip(self):
640 def tip(self):
641 return self.get_changeset('tip')
641 return self.get_changeset('tip')
642
642
643 @property
643 @property
644 def author(self):
644 def author(self):
645 return self.tip.author
645 return self.tip.author
646
646
647 @property
647 @property
648 def last_change(self):
648 def last_change(self):
649 return self.scm_instance.last_change
649 return self.scm_instance.last_change
650
650
651 def comments(self, revisions=None):
651 def comments(self, revisions=None):
652 """
652 """
653 Returns comments for this repository grouped by revisions
653 Returns comments for this repository grouped by revisions
654
654
655 :param revisions: filter query by revisions only
655 :param revisions: filter query by revisions only
656 """
656 """
657 cmts = ChangesetComment.query()\
657 cmts = ChangesetComment.query()\
658 .filter(ChangesetComment.repo == self)
658 .filter(ChangesetComment.repo == self)
659 if revisions:
659 if revisions:
660 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
660 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
661 grouped = defaultdict(list)
661 grouped = defaultdict(list)
662 for cmt in cmts.all():
662 for cmt in cmts.all():
663 grouped[cmt.revision].append(cmt)
663 grouped[cmt.revision].append(cmt)
664 return grouped
664 return grouped
665
665
666 #==========================================================================
666 #==========================================================================
667 # SCM CACHE INSTANCE
667 # SCM CACHE INSTANCE
668 #==========================================================================
668 #==========================================================================
669
669
670 @property
670 @property
671 def invalidate(self):
671 def invalidate(self):
672 return CacheInvalidation.invalidate(self.repo_name)
672 return CacheInvalidation.invalidate(self.repo_name)
673
673
674 def set_invalidate(self):
674 def set_invalidate(self):
675 """
675 """
676 set a cache for invalidation for this instance
676 set a cache for invalidation for this instance
677 """
677 """
678 CacheInvalidation.set_invalidate(self.repo_name)
678 CacheInvalidation.set_invalidate(self.repo_name)
679
679
680 @LazyProperty
680 @LazyProperty
681 def scm_instance(self):
681 def scm_instance(self):
682 return self.__get_instance()
682 return self.__get_instance()
683
683
684 @property
684 @property
685 def scm_instance_cached(self):
685 def scm_instance_cached(self):
686 @cache_region('long_term')
686 @cache_region('long_term')
687 def _c(repo_name):
687 def _c(repo_name):
688 return self.__get_instance()
688 return self.__get_instance()
689 rn = self.repo_name
689 rn = self.repo_name
690 log.debug('Getting cached instance of repo')
690 log.debug('Getting cached instance of repo')
691 inv = self.invalidate
691 inv = self.invalidate
692 if inv is not None:
692 if inv is not None:
693 region_invalidate(_c, None, rn)
693 region_invalidate(_c, None, rn)
694 # update our cache
694 # update our cache
695 CacheInvalidation.set_valid(inv.cache_key)
695 CacheInvalidation.set_valid(inv.cache_key)
696 return _c(rn)
696 return _c(rn)
697
697
698 def __get_instance(self):
698 def __get_instance(self):
699 repo_full_path = self.repo_full_path
699 repo_full_path = self.repo_full_path
700 try:
700 try:
701 alias = get_scm(repo_full_path)[0]
701 alias = get_scm(repo_full_path)[0]
702 log.debug('Creating instance of %s repository' % alias)
702 log.debug('Creating instance of %s repository' % alias)
703 backend = get_backend(alias)
703 backend = get_backend(alias)
704 except VCSError:
704 except VCSError:
705 log.error(traceback.format_exc())
705 log.error(traceback.format_exc())
706 log.error('Perhaps this repository is in db and not in '
706 log.error('Perhaps this repository is in db and not in '
707 'filesystem run rescan repositories with '
707 'filesystem run rescan repositories with '
708 '"destroy old data " option from admin panel')
708 '"destroy old data " option from admin panel')
709 return
709 return
710
710
711 if alias == 'hg':
711 if alias == 'hg':
712
712
713 repo = backend(safe_str(repo_full_path), create=False,
713 repo = backend(safe_str(repo_full_path), create=False,
714 baseui=self._ui)
714 baseui=self._ui)
715 # skip hidden web repository
715 # skip hidden web repository
716 if repo._get_hidden():
716 if repo._get_hidden():
717 return
717 return
718 else:
718 else:
719 repo = backend(repo_full_path, create=False)
719 repo = backend(repo_full_path, create=False)
720
720
721 return repo
721 return repo
722
722
723
723
724 class RepoGroup(Base, BaseModel):
724 class RepoGroup(Base, BaseModel):
725 __tablename__ = 'groups'
725 __tablename__ = 'groups'
726 __table_args__ = (
726 __table_args__ = (
727 UniqueConstraint('group_name', 'group_parent_id'),
727 UniqueConstraint('group_name', 'group_parent_id'),
728 CheckConstraint('group_id != group_parent_id'),
728 CheckConstraint('group_id != group_parent_id'),
729 {'extend_existing': True, 'mysql_engine':'InnoDB',
729 {'extend_existing': True, 'mysql_engine':'InnoDB',
730 'mysql_charset': 'utf8'},
730 'mysql_charset': 'utf8'},
731 )
731 )
732 __mapper_args__ = {'order_by': 'group_name'}
732 __mapper_args__ = {'order_by': 'group_name'}
733
733
734 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
734 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
735 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
735 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
736 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
736 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
737 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
737 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
738
738
739 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
739 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
740 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
740 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
741
741
742 parent_group = relationship('RepoGroup', remote_side=group_id)
742 parent_group = relationship('RepoGroup', remote_side=group_id)
743
743
744 def __init__(self, group_name='', parent_group=None):
744 def __init__(self, group_name='', parent_group=None):
745 self.group_name = group_name
745 self.group_name = group_name
746 self.parent_group = parent_group
746 self.parent_group = parent_group
747
747
748 def __repr__(self):
748 def __repr__(self):
749 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
749 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
750 self.group_name)
750 self.group_name)
751
751
752 @classmethod
752 @classmethod
753 def groups_choices(cls):
753 def groups_choices(cls):
754 from webhelpers.html import literal as _literal
754 from webhelpers.html import literal as _literal
755 repo_groups = [('', '')]
755 repo_groups = [('', '')]
756 sep = ' &raquo; '
756 sep = ' &raquo; '
757 _name = lambda k: _literal(sep.join(k))
757 _name = lambda k: _literal(sep.join(k))
758
758
759 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
759 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
760 for x in cls.query().all()])
760 for x in cls.query().all()])
761
761
762 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
762 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
763 return repo_groups
763 return repo_groups
764
764
765 @classmethod
765 @classmethod
766 def url_sep(cls):
766 def url_sep(cls):
767 return '/'
767 return '/'
768
768
769 @classmethod
769 @classmethod
770 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
770 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
771 if case_insensitive:
771 if case_insensitive:
772 gr = cls.query()\
772 gr = cls.query()\
773 .filter(cls.group_name.ilike(group_name))
773 .filter(cls.group_name.ilike(group_name))
774 else:
774 else:
775 gr = cls.query()\
775 gr = cls.query()\
776 .filter(cls.group_name == group_name)
776 .filter(cls.group_name == group_name)
777 if cache:
777 if cache:
778 gr = gr.options(FromCache(
778 gr = gr.options(FromCache(
779 "sql_cache_short",
779 "sql_cache_short",
780 "get_group_%s" % _hash_key(group_name)
780 "get_group_%s" % _hash_key(group_name)
781 )
781 )
782 )
782 )
783 return gr.scalar()
783 return gr.scalar()
784
784
785 @property
785 @property
786 def parents(self):
786 def parents(self):
787 parents_recursion_limit = 5
787 parents_recursion_limit = 5
788 groups = []
788 groups = []
789 if self.parent_group is None:
789 if self.parent_group is None:
790 return groups
790 return groups
791 cur_gr = self.parent_group
791 cur_gr = self.parent_group
792 groups.insert(0, cur_gr)
792 groups.insert(0, cur_gr)
793 cnt = 0
793 cnt = 0
794 while 1:
794 while 1:
795 cnt += 1
795 cnt += 1
796 gr = getattr(cur_gr, 'parent_group', None)
796 gr = getattr(cur_gr, 'parent_group', None)
797 cur_gr = cur_gr.parent_group
797 cur_gr = cur_gr.parent_group
798 if gr is None:
798 if gr is None:
799 break
799 break
800 if cnt == parents_recursion_limit:
800 if cnt == parents_recursion_limit:
801 # this will prevent accidental infinit loops
801 # this will prevent accidental infinit loops
802 log.error('group nested more than %s' %
802 log.error('group nested more than %s' %
803 parents_recursion_limit)
803 parents_recursion_limit)
804 break
804 break
805
805
806 groups.insert(0, gr)
806 groups.insert(0, gr)
807 return groups
807 return groups
808
808
809 @property
809 @property
810 def children(self):
810 def children(self):
811 return RepoGroup.query().filter(RepoGroup.parent_group == self)
811 return RepoGroup.query().filter(RepoGroup.parent_group == self)
812
812
813 @property
813 @property
814 def name(self):
814 def name(self):
815 return self.group_name.split(RepoGroup.url_sep())[-1]
815 return self.group_name.split(RepoGroup.url_sep())[-1]
816
816
817 @property
817 @property
818 def full_path(self):
818 def full_path(self):
819 return self.group_name
819 return self.group_name
820
820
821 @property
821 @property
822 def full_path_splitted(self):
822 def full_path_splitted(self):
823 return self.group_name.split(RepoGroup.url_sep())
823 return self.group_name.split(RepoGroup.url_sep())
824
824
825 @property
825 @property
826 def repositories(self):
826 def repositories(self):
827 return Repository.query()\
827 return Repository.query()\
828 .filter(Repository.group == self)\
828 .filter(Repository.group == self)\
829 .order_by(Repository.repo_name)
829 .order_by(Repository.repo_name)
830
830
831 @property
831 @property
832 def repositories_recursive_count(self):
832 def repositories_recursive_count(self):
833 cnt = self.repositories.count()
833 cnt = self.repositories.count()
834
834
835 def children_count(group):
835 def children_count(group):
836 cnt = 0
836 cnt = 0
837 for child in group.children:
837 for child in group.children:
838 cnt += child.repositories.count()
838 cnt += child.repositories.count()
839 cnt += children_count(child)
839 cnt += children_count(child)
840 return cnt
840 return cnt
841
841
842 return cnt + children_count(self)
842 return cnt + children_count(self)
843
843
844 def get_new_name(self, group_name):
844 def get_new_name(self, group_name):
845 """
845 """
846 returns new full group name based on parent and new name
846 returns new full group name based on parent and new name
847
847
848 :param group_name:
848 :param group_name:
849 """
849 """
850 path_prefix = (self.parent_group.full_path_splitted if
850 path_prefix = (self.parent_group.full_path_splitted if
851 self.parent_group else [])
851 self.parent_group else [])
852 return RepoGroup.url_sep().join(path_prefix + [group_name])
852 return RepoGroup.url_sep().join(path_prefix + [group_name])
853
853
854
854
855 class Permission(Base, BaseModel):
855 class Permission(Base, BaseModel):
856 __tablename__ = 'permissions'
856 __tablename__ = 'permissions'
857 __table_args__ = (
857 __table_args__ = (
858 {'extend_existing': True, 'mysql_engine':'InnoDB',
858 {'extend_existing': True, 'mysql_engine':'InnoDB',
859 'mysql_charset': 'utf8'},
859 'mysql_charset': 'utf8'},
860 )
860 )
861 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
861 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
862 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
862 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
863 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
863 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
864
864
865 def __repr__(self):
865 def __repr__(self):
866 return "<%s('%s:%s')>" % (
866 return "<%s('%s:%s')>" % (
867 self.__class__.__name__, self.permission_id, self.permission_name
867 self.__class__.__name__, self.permission_id, self.permission_name
868 )
868 )
869
869
870 @classmethod
870 @classmethod
871 def get_by_key(cls, key):
871 def get_by_key(cls, key):
872 return cls.query().filter(cls.permission_name == key).scalar()
872 return cls.query().filter(cls.permission_name == key).scalar()
873
873
874 @classmethod
874 @classmethod
875 def get_default_perms(cls, default_user_id):
875 def get_default_perms(cls, default_user_id):
876 q = Session.query(UserRepoToPerm, Repository, cls)\
876 q = Session.query(UserRepoToPerm, Repository, cls)\
877 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
877 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
878 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
878 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
879 .filter(UserRepoToPerm.user_id == default_user_id)
879 .filter(UserRepoToPerm.user_id == default_user_id)
880
880
881 return q.all()
881 return q.all()
882
882
883 @classmethod
883 @classmethod
884 def get_default_group_perms(cls, default_user_id):
884 def get_default_group_perms(cls, default_user_id):
885 q = Session.query(UserRepoGroupToPerm, RepoGroup, cls)\
885 q = Session.query(UserRepoGroupToPerm, RepoGroup, cls)\
886 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
886 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
887 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
887 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
888 .filter(UserRepoGroupToPerm.user_id == default_user_id)
888 .filter(UserRepoGroupToPerm.user_id == default_user_id)
889
889
890 return q.all()
890 return q.all()
891
891
892
892
893 class UserRepoToPerm(Base, BaseModel):
893 class UserRepoToPerm(Base, BaseModel):
894 __tablename__ = 'repo_to_perm'
894 __tablename__ = 'repo_to_perm'
895 __table_args__ = (
895 __table_args__ = (
896 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
896 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
897 {'extend_existing': True, 'mysql_engine':'InnoDB',
897 {'extend_existing': True, 'mysql_engine':'InnoDB',
898 'mysql_charset': 'utf8'}
898 'mysql_charset': 'utf8'}
899 )
899 )
900 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
900 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
901 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
901 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
902 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
902 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
903 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
903 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
904
904
905 user = relationship('User')
905 user = relationship('User')
906 repository = relationship('Repository')
906 repository = relationship('Repository')
907 permission = relationship('Permission')
907 permission = relationship('Permission')
908
908
909 @classmethod
909 @classmethod
910 def create(cls, user, repository, permission):
910 def create(cls, user, repository, permission):
911 n = cls()
911 n = cls()
912 n.user = user
912 n.user = user
913 n.repository = repository
913 n.repository = repository
914 n.permission = permission
914 n.permission = permission
915 Session.add(n)
915 Session.add(n)
916 return n
916 return n
917
917
918 def __repr__(self):
918 def __repr__(self):
919 return '<user:%s => %s >' % (self.user, self.repository)
919 return '<user:%s => %s >' % (self.user, self.repository)
920
920
921
921
922 class UserToPerm(Base, BaseModel):
922 class UserToPerm(Base, BaseModel):
923 __tablename__ = 'user_to_perm'
923 __tablename__ = 'user_to_perm'
924 __table_args__ = (
924 __table_args__ = (
925 UniqueConstraint('user_id', 'permission_id'),
925 UniqueConstraint('user_id', 'permission_id'),
926 {'extend_existing': True, 'mysql_engine':'InnoDB',
926 {'extend_existing': True, 'mysql_engine':'InnoDB',
927 'mysql_charset': 'utf8'}
927 'mysql_charset': 'utf8'}
928 )
928 )
929 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
929 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
930 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
930 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
931 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
931 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
932
932
933 user = relationship('User')
933 user = relationship('User')
934 permission = relationship('Permission', lazy='joined')
934 permission = relationship('Permission', lazy='joined')
935
935
936
936
937 class UsersGroupRepoToPerm(Base, BaseModel):
937 class UsersGroupRepoToPerm(Base, BaseModel):
938 __tablename__ = 'users_group_repo_to_perm'
938 __tablename__ = 'users_group_repo_to_perm'
939 __table_args__ = (
939 __table_args__ = (
940 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
940 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
941 {'extend_existing': True, 'mysql_engine':'InnoDB',
941 {'extend_existing': True, 'mysql_engine':'InnoDB',
942 'mysql_charset': 'utf8'}
942 'mysql_charset': 'utf8'}
943 )
943 )
944 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
944 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
945 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
945 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
946 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
946 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
947 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
947 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
948
948
949 users_group = relationship('UsersGroup')
949 users_group = relationship('UsersGroup')
950 permission = relationship('Permission')
950 permission = relationship('Permission')
951 repository = relationship('Repository')
951 repository = relationship('Repository')
952
952
953 @classmethod
953 @classmethod
954 def create(cls, users_group, repository, permission):
954 def create(cls, users_group, repository, permission):
955 n = cls()
955 n = cls()
956 n.users_group = users_group
956 n.users_group = users_group
957 n.repository = repository
957 n.repository = repository
958 n.permission = permission
958 n.permission = permission
959 Session.add(n)
959 Session.add(n)
960 return n
960 return n
961
961
962 def __repr__(self):
962 def __repr__(self):
963 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
963 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
964
964
965
965
966 class UsersGroupToPerm(Base, BaseModel):
966 class UsersGroupToPerm(Base, BaseModel):
967 __tablename__ = 'users_group_to_perm'
967 __tablename__ = 'users_group_to_perm'
968 __table_args__ = (
968 __table_args__ = (
969 UniqueConstraint('users_group_id', 'permission_id',),
969 UniqueConstraint('users_group_id', 'permission_id',),
970 {'extend_existing': True, 'mysql_engine':'InnoDB',
970 {'extend_existing': True, 'mysql_engine':'InnoDB',
971 'mysql_charset': 'utf8'}
971 'mysql_charset': 'utf8'}
972 )
972 )
973 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
973 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
974 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
974 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
975 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
975 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
976
976
977 users_group = relationship('UsersGroup')
977 users_group = relationship('UsersGroup')
978 permission = relationship('Permission')
978 permission = relationship('Permission')
979
979
980
980
981 class UserRepoGroupToPerm(Base, BaseModel):
981 class UserRepoGroupToPerm(Base, BaseModel):
982 __tablename__ = 'user_repo_group_to_perm'
982 __tablename__ = 'user_repo_group_to_perm'
983 __table_args__ = (
983 __table_args__ = (
984 UniqueConstraint('user_id', 'group_id', 'permission_id'),
984 UniqueConstraint('user_id', 'group_id', 'permission_id'),
985 {'extend_existing': True, 'mysql_engine':'InnoDB',
985 {'extend_existing': True, 'mysql_engine':'InnoDB',
986 'mysql_charset': 'utf8'}
986 'mysql_charset': 'utf8'}
987 )
987 )
988
988
989 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
989 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
990 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
990 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
991 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
991 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
992 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
992 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
993
993
994 user = relationship('User')
994 user = relationship('User')
995 group = relationship('RepoGroup')
995 group = relationship('RepoGroup')
996 permission = relationship('Permission')
996 permission = relationship('Permission')
997
997
998
998
999 class UsersGroupRepoGroupToPerm(Base, BaseModel):
999 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1000 __tablename__ = 'users_group_repo_group_to_perm'
1000 __tablename__ = 'users_group_repo_group_to_perm'
1001 __table_args__ = (
1001 __table_args__ = (
1002 UniqueConstraint('users_group_id', 'group_id'),
1002 UniqueConstraint('users_group_id', 'group_id'),
1003 {'extend_existing': True, 'mysql_engine':'InnoDB',
1003 {'extend_existing': True, 'mysql_engine':'InnoDB',
1004 'mysql_charset': 'utf8'}
1004 'mysql_charset': 'utf8'}
1005 )
1005 )
1006
1006
1007 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)
1007 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)
1008 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1008 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1009 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1009 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1010 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1010 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1011
1011
1012 users_group = relationship('UsersGroup')
1012 users_group = relationship('UsersGroup')
1013 permission = relationship('Permission')
1013 permission = relationship('Permission')
1014 group = relationship('RepoGroup')
1014 group = relationship('RepoGroup')
1015
1015
1016
1016
1017 class Statistics(Base, BaseModel):
1017 class Statistics(Base, BaseModel):
1018 __tablename__ = 'statistics'
1018 __tablename__ = 'statistics'
1019 __table_args__ = (
1019 __table_args__ = (
1020 UniqueConstraint('repository_id'),
1020 UniqueConstraint('repository_id'),
1021 {'extend_existing': True, 'mysql_engine':'InnoDB',
1021 {'extend_existing': True, 'mysql_engine':'InnoDB',
1022 'mysql_charset': 'utf8'}
1022 'mysql_charset': 'utf8'}
1023 )
1023 )
1024 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1024 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1025 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1025 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1026 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1026 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1027 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1027 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1028 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1028 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1029 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1029 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1030
1030
1031 repository = relationship('Repository', single_parent=True)
1031 repository = relationship('Repository', single_parent=True)
1032
1032
1033
1033
1034 class UserFollowing(Base, BaseModel):
1034 class UserFollowing(Base, BaseModel):
1035 __tablename__ = 'user_followings'
1035 __tablename__ = 'user_followings'
1036 __table_args__ = (
1036 __table_args__ = (
1037 UniqueConstraint('user_id', 'follows_repository_id'),
1037 UniqueConstraint('user_id', 'follows_repository_id'),
1038 UniqueConstraint('user_id', 'follows_user_id'),
1038 UniqueConstraint('user_id', 'follows_user_id'),
1039 {'extend_existing': True, 'mysql_engine':'InnoDB',
1039 {'extend_existing': True, 'mysql_engine':'InnoDB',
1040 'mysql_charset': 'utf8'}
1040 'mysql_charset': 'utf8'}
1041 )
1041 )
1042
1042
1043 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1043 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1044 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1044 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1045 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1045 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1046 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1046 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1047 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1047 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1048
1048
1049 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1049 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1050
1050
1051 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1051 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1052 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1052 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1053
1053
1054 @classmethod
1054 @classmethod
1055 def get_repo_followers(cls, repo_id):
1055 def get_repo_followers(cls, repo_id):
1056 return cls.query().filter(cls.follows_repo_id == repo_id)
1056 return cls.query().filter(cls.follows_repo_id == repo_id)
1057
1057
1058
1058
1059 class CacheInvalidation(Base, BaseModel):
1059 class CacheInvalidation(Base, BaseModel):
1060 __tablename__ = 'cache_invalidation'
1060 __tablename__ = 'cache_invalidation'
1061 __table_args__ = (
1061 __table_args__ = (
1062 UniqueConstraint('cache_key'),
1062 UniqueConstraint('cache_key'),
1063 {'extend_existing': True, 'mysql_engine':'InnoDB',
1063 {'extend_existing': True, 'mysql_engine':'InnoDB',
1064 'mysql_charset': 'utf8'},
1064 'mysql_charset': 'utf8'},
1065 )
1065 )
1066 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1066 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1067 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1067 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1068 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1068 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1069 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1069 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1070
1070
1071 def __init__(self, cache_key, cache_args=''):
1071 def __init__(self, cache_key, cache_args=''):
1072 self.cache_key = cache_key
1072 self.cache_key = cache_key
1073 self.cache_args = cache_args
1073 self.cache_args = cache_args
1074 self.cache_active = False
1074 self.cache_active = False
1075
1075
1076 def __repr__(self):
1076 def __repr__(self):
1077 return "<%s('%s:%s')>" % (self.__class__.__name__,
1077 return "<%s('%s:%s')>" % (self.__class__.__name__,
1078 self.cache_id, self.cache_key)
1078 self.cache_id, self.cache_key)
1079 @classmethod
1079 @classmethod
1080 def clear_cache(cls):
1080 def clear_cache(cls):
1081 cls.query().delete()
1081 cls.query().delete()
1082
1082
1083 @classmethod
1083 @classmethod
1084 def _get_key(cls, key):
1084 def _get_key(cls, key):
1085 """
1085 """
1086 Wrapper for generating a key, together with a prefix
1086 Wrapper for generating a key, together with a prefix
1087
1087
1088 :param key:
1088 :param key:
1089 """
1089 """
1090 import rhodecode
1090 import rhodecode
1091 prefix = ''
1091 prefix = ''
1092 iid = rhodecode.CONFIG.get('instance_id')
1092 iid = rhodecode.CONFIG.get('instance_id')
1093 if iid:
1093 if iid:
1094 prefix = iid
1094 prefix = iid
1095 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1095 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1096
1096
1097 @classmethod
1097 @classmethod
1098 def get_by_key(cls, key):
1098 def get_by_key(cls, key):
1099 return cls.query().filter(cls.cache_key == key).scalar()
1099 return cls.query().filter(cls.cache_key == key).scalar()
1100
1100
1101 @classmethod
1101 @classmethod
1102 def _get_or_create_key(cls, key, prefix, org_key):
1102 def _get_or_create_key(cls, key, prefix, org_key):
1103 inv_obj = Session.query(cls).filter(cls.cache_key == key).scalar()
1103 inv_obj = Session.query(cls).filter(cls.cache_key == key).scalar()
1104 if not inv_obj:
1104 if not inv_obj:
1105 try:
1105 try:
1106 inv_obj = CacheInvalidation(key, org_key)
1106 inv_obj = CacheInvalidation(key, org_key)
1107 Session.add(inv_obj)
1107 Session.add(inv_obj)
1108 Session.commit()
1108 Session.commit()
1109 except Exception:
1109 except Exception:
1110 log.error(traceback.format_exc())
1110 log.error(traceback.format_exc())
1111 Session.rollback()
1111 Session.rollback()
1112 return inv_obj
1112 return inv_obj
1113
1113
1114 @classmethod
1114 @classmethod
1115 def invalidate(cls, key):
1115 def invalidate(cls, key):
1116 """
1116 """
1117 Returns Invalidation object if this given key should be invalidated
1117 Returns Invalidation object if this given key should be invalidated
1118 None otherwise. `cache_active = False` means that this cache
1118 None otherwise. `cache_active = False` means that this cache
1119 state is not valid and needs to be invalidated
1119 state is not valid and needs to be invalidated
1120
1120
1121 :param key:
1121 :param key:
1122 """
1122 """
1123
1123
1124 key, _prefix, _org_key = cls._get_key(key)
1124 key, _prefix, _org_key = cls._get_key(key)
1125 inv = cls._get_or_create_key(key, _prefix, _org_key)
1125 inv = cls._get_or_create_key(key, _prefix, _org_key)
1126
1126
1127 if inv and inv.cache_active is False:
1127 if inv and inv.cache_active is False:
1128 return inv
1128 return inv
1129
1129
1130 @classmethod
1130 @classmethod
1131 def set_invalidate(cls, key):
1131 def set_invalidate(cls, key):
1132 """
1132 """
1133 Mark this Cache key for invalidation
1133 Mark this Cache key for invalidation
1134
1134
1135 :param key:
1135 :param key:
1136 """
1136 """
1137
1137
1138 key, _prefix, _org_key = cls._get_key(key)
1138 key, _prefix, _org_key = cls._get_key(key)
1139 inv_objs = Session.query(cls).filter(cls.cache_args == _org_key).all()
1139 inv_objs = Session.query(cls).filter(cls.cache_args == _org_key).all()
1140 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1140 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1141 _org_key))
1141 _org_key))
1142 try:
1142 try:
1143 for inv_obj in inv_objs:
1143 for inv_obj in inv_objs:
1144 if inv_obj:
1144 if inv_obj:
1145 inv_obj.cache_active = False
1145 inv_obj.cache_active = False
1146
1146
1147 Session.add(inv_obj)
1147 Session.add(inv_obj)
1148 Session.commit()
1148 Session.commit()
1149 except Exception:
1149 except Exception:
1150 log.error(traceback.format_exc())
1150 log.error(traceback.format_exc())
1151 Session.rollback()
1151 Session.rollback()
1152
1152
1153 @classmethod
1153 @classmethod
1154 def set_valid(cls, key):
1154 def set_valid(cls, key):
1155 """
1155 """
1156 Mark this cache key as active and currently cached
1156 Mark this cache key as active and currently cached
1157
1157
1158 :param key:
1158 :param key:
1159 """
1159 """
1160 inv_obj = cls.get_by_key(key)
1160 inv_obj = cls.get_by_key(key)
1161 inv_obj.cache_active = True
1161 inv_obj.cache_active = True
1162 Session.add(inv_obj)
1162 Session.add(inv_obj)
1163 Session.commit()
1163 Session.commit()
1164
1164
1165
1165
1166 class ChangesetComment(Base, BaseModel):
1166 class ChangesetComment(Base, BaseModel):
1167 __tablename__ = 'changeset_comments'
1167 __tablename__ = 'changeset_comments'
1168 __table_args__ = (
1168 __table_args__ = (
1169 {'extend_existing': True, 'mysql_engine':'InnoDB',
1169 {'extend_existing': True, 'mysql_engine':'InnoDB',
1170 'mysql_charset': 'utf8'},
1170 'mysql_charset': 'utf8'},
1171 )
1171 )
1172 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1172 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1173 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1173 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1174 revision = Column('revision', String(40), nullable=False)
1174 revision = Column('revision', String(40), nullable=False)
1175 line_no = Column('line_no', Unicode(10), nullable=True)
1175 line_no = Column('line_no', Unicode(10), nullable=True)
1176 f_path = Column('f_path', Unicode(1000), nullable=True)
1176 f_path = Column('f_path', Unicode(1000), nullable=True)
1177 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1177 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1178 text = Column('text', Unicode(25000), nullable=False)
1178 text = Column('text', Unicode(25000), nullable=False)
1179 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1179 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1180
1180
1181 author = relationship('User', lazy='joined')
1181 author = relationship('User', lazy='joined')
1182 repo = relationship('Repository')
1182 repo = relationship('Repository')
1183
1183
1184 @classmethod
1184 @classmethod
1185 def get_users(cls, revision):
1185 def get_users(cls, revision):
1186 """
1186 """
1187 Returns user associated with this changesetComment. ie those
1187 Returns user associated with this changesetComment. ie those
1188 who actually commented
1188 who actually commented
1189
1189
1190 :param cls:
1190 :param cls:
1191 :param revision:
1191 :param revision:
1192 """
1192 """
1193 return Session.query(User)\
1193 return Session.query(User)\
1194 .filter(cls.revision == revision)\
1194 .filter(cls.revision == revision)\
1195 .join(ChangesetComment.author).all()
1195 .join(ChangesetComment.author).all()
1196
1196
1197
1197
1198 class Notification(Base, BaseModel):
1198 class Notification(Base, BaseModel):
1199 __tablename__ = 'notifications'
1199 __tablename__ = 'notifications'
1200 __table_args__ = (
1200 __table_args__ = (
1201 {'extend_existing': True, 'mysql_engine':'InnoDB',
1201 {'extend_existing': True, 'mysql_engine':'InnoDB',
1202 'mysql_charset': 'utf8'},
1202 'mysql_charset': 'utf8'},
1203 )
1203 )
1204
1204
1205 TYPE_CHANGESET_COMMENT = u'cs_comment'
1205 TYPE_CHANGESET_COMMENT = u'cs_comment'
1206 TYPE_MESSAGE = u'message'
1206 TYPE_MESSAGE = u'message'
1207 TYPE_MENTION = u'mention'
1207 TYPE_MENTION = u'mention'
1208 TYPE_REGISTRATION = u'registration'
1208 TYPE_REGISTRATION = u'registration'
1209
1209
1210 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1210 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1211 subject = Column('subject', Unicode(512), nullable=True)
1211 subject = Column('subject', Unicode(512), nullable=True)
1212 body = Column('body', Unicode(50000), nullable=True)
1212 body = Column('body', Unicode(50000), nullable=True)
1213 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1213 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1214 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1214 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1215 type_ = Column('type', Unicode(256))
1215 type_ = Column('type', Unicode(256))
1216
1216
1217 created_by_user = relationship('User')
1217 created_by_user = relationship('User')
1218 notifications_to_users = relationship('UserNotification', lazy='joined',
1218 notifications_to_users = relationship('UserNotification', lazy='joined',
1219 cascade="all, delete, delete-orphan")
1219 cascade="all, delete, delete-orphan")
1220
1220
1221 @property
1221 @property
1222 def recipients(self):
1222 def recipients(self):
1223 return [x.user for x in UserNotification.query()\
1223 return [x.user for x in UserNotification.query()\
1224 .filter(UserNotification.notification == self).all()]
1224 .filter(UserNotification.notification == self).all()]
1225
1225
1226 @classmethod
1226 @classmethod
1227 def create(cls, created_by, subject, body, recipients, type_=None):
1227 def create(cls, created_by, subject, body, recipients, type_=None):
1228 if type_ is None:
1228 if type_ is None:
1229 type_ = Notification.TYPE_MESSAGE
1229 type_ = Notification.TYPE_MESSAGE
1230
1230
1231 notification = cls()
1231 notification = cls()
1232 notification.created_by_user = created_by
1232 notification.created_by_user = created_by
1233 notification.subject = subject
1233 notification.subject = subject
1234 notification.body = body
1234 notification.body = body
1235 notification.type_ = type_
1235 notification.type_ = type_
1236 notification.created_on = datetime.datetime.now()
1236 notification.created_on = datetime.datetime.now()
1237
1237
1238 for u in recipients:
1238 for u in recipients:
1239 assoc = UserNotification()
1239 assoc = UserNotification()
1240 assoc.notification = notification
1240 assoc.notification = notification
1241 u.notifications.append(assoc)
1241 u.notifications.append(assoc)
1242 Session.add(notification)
1242 Session.add(notification)
1243 return notification
1243 return notification
1244
1244
1245 @property
1245 @property
1246 def description(self):
1246 def description(self):
1247 from rhodecode.model.notification import NotificationModel
1247 from rhodecode.model.notification import NotificationModel
1248 return NotificationModel().make_description(self)
1248 return NotificationModel().make_description(self)
1249
1249
1250
1250
1251 class UserNotification(Base, BaseModel):
1251 class UserNotification(Base, BaseModel):
1252 __tablename__ = 'user_to_notification'
1252 __tablename__ = 'user_to_notification'
1253 __table_args__ = (
1253 __table_args__ = (
1254 UniqueConstraint('user_id', 'notification_id'),
1254 UniqueConstraint('user_id', 'notification_id'),
1255 {'extend_existing': True, 'mysql_engine':'InnoDB',
1255 {'extend_existing': True, 'mysql_engine':'InnoDB',
1256 'mysql_charset': 'utf8'}
1256 'mysql_charset': 'utf8'}
1257 )
1257 )
1258 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1258 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1259 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1259 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1260 read = Column('read', Boolean, default=False)
1260 read = Column('read', Boolean, default=False)
1261 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1261 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1262
1262
1263 user = relationship('User', lazy="joined")
1263 user = relationship('User', lazy="joined")
1264 notification = relationship('Notification', lazy="joined",
1264 notification = relationship('Notification', lazy="joined",
1265 order_by=lambda: Notification.created_on.desc(),)
1265 order_by=lambda: Notification.created_on.desc(),)
1266
1266
1267 def mark_as_read(self):
1267 def mark_as_read(self):
1268 self.read = True
1268 self.read = True
1269 Session.add(self)
1269 Session.add(self)
1270
1270
1271
1271
1272 class DbMigrateVersion(Base, BaseModel):
1272 class DbMigrateVersion(Base, BaseModel):
1273 __tablename__ = 'db_migrate_version'
1273 __tablename__ = 'db_migrate_version'
1274 __table_args__ = (
1274 __table_args__ = (
1275 {'extend_existing': True, 'mysql_engine':'InnoDB',
1275 {'extend_existing': True, 'mysql_engine':'InnoDB',
1276 'mysql_charset': 'utf8'},
1276 'mysql_charset': 'utf8'},
1277 )
1277 )
1278 repository_id = Column('repository_id', String(250), primary_key=True)
1278 repository_id = Column('repository_id', String(250), primary_key=True)
1279 repository_path = Column('repository_path', Text)
1279 repository_path = Column('repository_path', Text)
1280 version = Column('version', Integer)
1280 version = Column('version', Integer)
@@ -1,588 +1,588 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.user
3 rhodecode.model.user
4 ~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~
5
5
6 users model for RhodeCode
6 users model for RhodeCode
7
7
8 :created_on: Apr 9, 2010
8 :created_on: Apr 9, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 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 logging
26 import logging
27 import traceback
27 import traceback
28
28
29 from pylons import url
29 from pylons import url
30 from pylons.i18n.translation import _
30 from pylons.i18n.translation import _
31
31
32 from rhodecode.lib.utils2 import safe_unicode, generate_api_key
32 from rhodecode.lib.utils2 import safe_unicode, generate_api_key
33 from rhodecode.lib.caching_query import FromCache
33 from rhodecode.lib.caching_query import FromCache
34
34
35 from rhodecode.model import BaseModel
35 from rhodecode.model import BaseModel
36 from rhodecode.model.db import User, UserRepoToPerm, Repository, Permission, \
36 from rhodecode.model.db import User, UserRepoToPerm, Repository, Permission, \
37 UserToPerm, UsersGroupRepoToPerm, UsersGroupToPerm, UsersGroupMember, \
37 UserToPerm, UsersGroupRepoToPerm, UsersGroupToPerm, UsersGroupMember, \
38 Notification, RepoGroup, UserRepoGroupToPerm, UsersGroup,\
38 Notification, RepoGroup, UserRepoGroupToPerm, UsersGroup,\
39 UsersGroupRepoGroupToPerm
39 UsersGroupRepoGroupToPerm
40 from rhodecode.lib.exceptions import DefaultUserException, \
40 from rhodecode.lib.exceptions import DefaultUserException, \
41 UserOwnsReposException
41 UserOwnsReposException
42
42
43 from sqlalchemy.exc import DatabaseError
43 from sqlalchemy.exc import DatabaseError
44
44
45 from sqlalchemy.orm import joinedload
45 from sqlalchemy.orm import joinedload
46
46
47 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
48
48
49
49
50 PERM_WEIGHTS = {
50 PERM_WEIGHTS = {
51 'repository.none': 0,
51 'repository.none': 0,
52 'repository.read': 1,
52 'repository.read': 1,
53 'repository.write': 3,
53 'repository.write': 3,
54 'repository.admin': 4,
54 'repository.admin': 4,
55 'group.none': 0,
55 'group.none': 0,
56 'group.read': 1,
56 'group.read': 1,
57 'group.write': 3,
57 'group.write': 3,
58 'group.admin': 4,
58 'group.admin': 4,
59 }
59 }
60
60
61
61
62 class UserModel(BaseModel):
62 class UserModel(BaseModel):
63
63
64 def __get_user(self, user):
64 def __get_user(self, user):
65 return self._get_instance(User, user, callback=User.get_by_username)
65 return self._get_instance(User, user, callback=User.get_by_username)
66
66
67 def __get_perm(self, permission):
67 def __get_perm(self, permission):
68 return self._get_instance(Permission, permission,
68 return self._get_instance(Permission, permission,
69 callback=Permission.get_by_key)
69 callback=Permission.get_by_key)
70
70
71 def get(self, user_id, cache=False):
71 def get(self, user_id, cache=False):
72 user = self.sa.query(User)
72 user = self.sa.query(User)
73 if cache:
73 if cache:
74 user = user.options(FromCache("sql_cache_short",
74 user = user.options(FromCache("sql_cache_short",
75 "get_user_%s" % user_id))
75 "get_user_%s" % user_id))
76 return user.get(user_id)
76 return user.get(user_id)
77
77
78 def get_user(self, user):
78 def get_user(self, user):
79 return self.__get_user(user)
79 return self.__get_user(user)
80
80
81 def get_by_username(self, username, cache=False, case_insensitive=False):
81 def get_by_username(self, username, cache=False, case_insensitive=False):
82
82
83 if case_insensitive:
83 if case_insensitive:
84 user = self.sa.query(User).filter(User.username.ilike(username))
84 user = self.sa.query(User).filter(User.username.ilike(username))
85 else:
85 else:
86 user = self.sa.query(User)\
86 user = self.sa.query(User)\
87 .filter(User.username == username)
87 .filter(User.username == username)
88 if cache:
88 if cache:
89 user = user.options(FromCache("sql_cache_short",
89 user = user.options(FromCache("sql_cache_short",
90 "get_user_%s" % username))
90 "get_user_%s" % username))
91 return user.scalar()
91 return user.scalar()
92
92
93 def get_by_api_key(self, api_key, cache=False):
93 def get_by_api_key(self, api_key, cache=False):
94 return User.get_by_api_key(api_key, cache)
94 return User.get_by_api_key(api_key, cache)
95
95
96 def create(self, form_data):
96 def create(self, form_data):
97 try:
97 try:
98 new_user = User()
98 new_user = User()
99 for k, v in form_data.items():
99 for k, v in form_data.items():
100 setattr(new_user, k, v)
100 setattr(new_user, k, v)
101
101
102 new_user.api_key = generate_api_key(form_data['username'])
102 new_user.api_key = generate_api_key(form_data['username'])
103 self.sa.add(new_user)
103 self.sa.add(new_user)
104 return new_user
104 return new_user
105 except:
105 except:
106 log.error(traceback.format_exc())
106 log.error(traceback.format_exc())
107 raise
107 raise
108
108
109 def create_or_update(self, username, password, email, name, lastname,
109 def create_or_update(self, username, password, email, name, lastname,
110 active=True, admin=False, ldap_dn=None):
110 active=True, admin=False, ldap_dn=None):
111 """
111 """
112 Creates a new instance if not found, or updates current one
112 Creates a new instance if not found, or updates current one
113
113
114 :param username:
114 :param username:
115 :param password:
115 :param password:
116 :param email:
116 :param email:
117 :param active:
117 :param active:
118 :param name:
118 :param name:
119 :param lastname:
119 :param lastname:
120 :param active:
120 :param active:
121 :param admin:
121 :param admin:
122 :param ldap_dn:
122 :param ldap_dn:
123 """
123 """
124
124
125 from rhodecode.lib.auth import get_crypt_password
125 from rhodecode.lib.auth import get_crypt_password
126
126
127 log.debug('Checking for %s account in RhodeCode database' % username)
127 log.debug('Checking for %s account in RhodeCode database' % username)
128 user = User.get_by_username(username, case_insensitive=True)
128 user = User.get_by_username(username, case_insensitive=True)
129 if user is None:
129 if user is None:
130 log.debug('creating new user %s' % username)
130 log.debug('creating new user %s' % username)
131 new_user = User()
131 new_user = User()
132 else:
132 else:
133 log.debug('updating user %s' % username)
133 log.debug('updating user %s' % username)
134 new_user = user
134 new_user = user
135
135
136 try:
136 try:
137 new_user.username = username
137 new_user.username = username
138 new_user.admin = admin
138 new_user.admin = admin
139 new_user.password = get_crypt_password(password)
139 new_user.password = get_crypt_password(password)
140 new_user.api_key = generate_api_key(username)
140 new_user.api_key = generate_api_key(username)
141 new_user.email = email
141 new_user.email = email
142 new_user.active = active
142 new_user.active = active
143 new_user.ldap_dn = safe_unicode(ldap_dn) if ldap_dn else None
143 new_user.ldap_dn = safe_unicode(ldap_dn) if ldap_dn else None
144 new_user.name = name
144 new_user.name = name
145 new_user.lastname = lastname
145 new_user.lastname = lastname
146 self.sa.add(new_user)
146 self.sa.add(new_user)
147 return new_user
147 return new_user
148 except (DatabaseError,):
148 except (DatabaseError,):
149 log.error(traceback.format_exc())
149 log.error(traceback.format_exc())
150 raise
150 raise
151
151
152 def create_for_container_auth(self, username, attrs):
152 def create_for_container_auth(self, username, attrs):
153 """
153 """
154 Creates the given user if it's not already in the database
154 Creates the given user if it's not already in the database
155
155
156 :param username:
156 :param username:
157 :param attrs:
157 :param attrs:
158 """
158 """
159 if self.get_by_username(username, case_insensitive=True) is None:
159 if self.get_by_username(username, case_insensitive=True) is None:
160
160
161 # autogenerate email for container account without one
161 # autogenerate email for container account without one
162 generate_email = lambda usr: '%s@container_auth.account' % usr
162 generate_email = lambda usr: '%s@container_auth.account' % usr
163
163
164 try:
164 try:
165 new_user = User()
165 new_user = User()
166 new_user.username = username
166 new_user.username = username
167 new_user.password = None
167 new_user.password = None
168 new_user.api_key = generate_api_key(username)
168 new_user.api_key = generate_api_key(username)
169 new_user.email = attrs['email']
169 new_user.email = attrs['email']
170 new_user.active = attrs.get('active', True)
170 new_user.active = attrs.get('active', True)
171 new_user.name = attrs['name'] or generate_email(username)
171 new_user.name = attrs['name'] or generate_email(username)
172 new_user.lastname = attrs['lastname']
172 new_user.lastname = attrs['lastname']
173
173
174 self.sa.add(new_user)
174 self.sa.add(new_user)
175 return new_user
175 return new_user
176 except (DatabaseError,):
176 except (DatabaseError,):
177 log.error(traceback.format_exc())
177 log.error(traceback.format_exc())
178 self.sa.rollback()
178 self.sa.rollback()
179 raise
179 raise
180 log.debug('User %s already exists. Skipping creation of account'
180 log.debug('User %s already exists. Skipping creation of account'
181 ' for container auth.', username)
181 ' for container auth.', username)
182 return None
182 return None
183
183
184 def create_ldap(self, username, password, user_dn, attrs):
184 def create_ldap(self, username, password, user_dn, attrs):
185 """
185 """
186 Checks if user is in database, if not creates this user marked
186 Checks if user is in database, if not creates this user marked
187 as ldap user
187 as ldap user
188
188
189 :param username:
189 :param username:
190 :param password:
190 :param password:
191 :param user_dn:
191 :param user_dn:
192 :param attrs:
192 :param attrs:
193 """
193 """
194 from rhodecode.lib.auth import get_crypt_password
194 from rhodecode.lib.auth import get_crypt_password
195 log.debug('Checking for such ldap account in RhodeCode database')
195 log.debug('Checking for such ldap account in RhodeCode database')
196 if self.get_by_username(username, case_insensitive=True) is None:
196 if self.get_by_username(username, case_insensitive=True) is None:
197
197
198 # autogenerate email for ldap account without one
198 # autogenerate email for ldap account without one
199 generate_email = lambda usr: '%s@ldap.account' % usr
199 generate_email = lambda usr: '%s@ldap.account' % usr
200
200
201 try:
201 try:
202 new_user = User()
202 new_user = User()
203 username = username.lower()
203 username = username.lower()
204 # add ldap account always lowercase
204 # add ldap account always lowercase
205 new_user.username = username
205 new_user.username = username
206 new_user.password = get_crypt_password(password)
206 new_user.password = get_crypt_password(password)
207 new_user.api_key = generate_api_key(username)
207 new_user.api_key = generate_api_key(username)
208 new_user.email = attrs['email'] or generate_email(username)
208 new_user.email = attrs['email'] or generate_email(username)
209 new_user.active = attrs.get('active', True)
209 new_user.active = attrs.get('active', True)
210 new_user.ldap_dn = safe_unicode(user_dn)
210 new_user.ldap_dn = safe_unicode(user_dn)
211 new_user.name = attrs['name']
211 new_user.name = attrs['name']
212 new_user.lastname = attrs['lastname']
212 new_user.lastname = attrs['lastname']
213
213
214 self.sa.add(new_user)
214 self.sa.add(new_user)
215 return new_user
215 return new_user
216 except (DatabaseError,):
216 except (DatabaseError,):
217 log.error(traceback.format_exc())
217 log.error(traceback.format_exc())
218 self.sa.rollback()
218 self.sa.rollback()
219 raise
219 raise
220 log.debug('this %s user exists skipping creation of ldap account',
220 log.debug('this %s user exists skipping creation of ldap account',
221 username)
221 username)
222 return None
222 return None
223
223
224 def create_registration(self, form_data):
224 def create_registration(self, form_data):
225 from rhodecode.model.notification import NotificationModel
225 from rhodecode.model.notification import NotificationModel
226
226
227 try:
227 try:
228 new_user = User()
228 new_user = User()
229 for k, v in form_data.items():
229 for k, v in form_data.items():
230 if k != 'admin':
230 if k != 'admin':
231 setattr(new_user, k, v)
231 setattr(new_user, k, v)
232
232
233 self.sa.add(new_user)
233 self.sa.add(new_user)
234 self.sa.flush()
234 self.sa.flush()
235
235
236 # notification to admins
236 # notification to admins
237 subject = _('new user registration')
237 subject = _('new user registration')
238 body = ('New user registration\n'
238 body = ('New user registration\n'
239 '---------------------\n'
239 '---------------------\n'
240 '- Username: %s\n'
240 '- Username: %s\n'
241 '- Full Name: %s\n'
241 '- Full Name: %s\n'
242 '- Email: %s\n')
242 '- Email: %s\n')
243 body = body % (new_user.username, new_user.full_name,
243 body = body % (new_user.username, new_user.full_name,
244 new_user.email)
244 new_user.email)
245 edit_url = url('edit_user', id=new_user.user_id, qualified=True)
245 edit_url = url('edit_user', id=new_user.user_id, qualified=True)
246 kw = {'registered_user_url': edit_url}
246 kw = {'registered_user_url': edit_url}
247 NotificationModel().create(created_by=new_user, subject=subject,
247 NotificationModel().create(created_by=new_user, subject=subject,
248 body=body, recipients=None,
248 body=body, recipients=None,
249 type_=Notification.TYPE_REGISTRATION,
249 type_=Notification.TYPE_REGISTRATION,
250 email_kwargs=kw)
250 email_kwargs=kw)
251
251
252 except:
252 except:
253 log.error(traceback.format_exc())
253 log.error(traceback.format_exc())
254 raise
254 raise
255
255
256 def update(self, user_id, form_data):
256 def update(self, user_id, form_data):
257 try:
257 try:
258 user = self.get(user_id, cache=False)
258 user = self.get(user_id, cache=False)
259 if user.username == 'default':
259 if user.username == 'default':
260 raise DefaultUserException(
260 raise DefaultUserException(
261 _("You can't Edit this user since it's"
261 _("You can't Edit this user since it's"
262 " crucial for entire application"))
262 " crucial for entire application"))
263
263
264 for k, v in form_data.items():
264 for k, v in form_data.items():
265 if k == 'new_password' and v != '':
265 if k == 'new_password' and v != '':
266 user.password = v
266 user.password = v
267 user.api_key = generate_api_key(user.username)
267 user.api_key = generate_api_key(user.username)
268 else:
268 else:
269 setattr(user, k, v)
269 setattr(user, k, v)
270
270
271 self.sa.add(user)
271 self.sa.add(user)
272 except:
272 except:
273 log.error(traceback.format_exc())
273 log.error(traceback.format_exc())
274 raise
274 raise
275
275
276 def update_my_account(self, user_id, form_data):
276 def update_my_account(self, user_id, form_data):
277 try:
277 try:
278 user = self.get(user_id, cache=False)
278 user = self.get(user_id, cache=False)
279 if user.username == 'default':
279 if user.username == 'default':
280 raise DefaultUserException(
280 raise DefaultUserException(
281 _("You can't Edit this user since it's"
281 _("You can't Edit this user since it's"
282 " crucial for entire application"))
282 " crucial for entire application"))
283 for k, v in form_data.items():
283 for k, v in form_data.items():
284 if k == 'new_password' and v != '':
284 if k == 'new_password' and v != '':
285 user.password = v
285 user.password = v
286 user.api_key = generate_api_key(user.username)
286 user.api_key = generate_api_key(user.username)
287 else:
287 else:
288 if k not in ['admin', 'active']:
288 if k not in ['admin', 'active']:
289 setattr(user, k, v)
289 setattr(user, k, v)
290
290
291 self.sa.add(user)
291 self.sa.add(user)
292 except:
292 except:
293 log.error(traceback.format_exc())
293 log.error(traceback.format_exc())
294 raise
294 raise
295
295
296 def delete(self, user):
296 def delete(self, user):
297 user = self.__get_user(user)
297 user = self.__get_user(user)
298
298
299 try:
299 try:
300 if user.username == 'default':
300 if user.username == 'default':
301 raise DefaultUserException(
301 raise DefaultUserException(
302 _("You can't remove this user since it's"
302 _("You can't remove this user since it's"
303 " crucial for entire application")
303 " crucial for entire application")
304 )
304 )
305 if user.repositories:
305 if user.repositories:
306 raise UserOwnsReposException(
306 raise UserOwnsReposException(
307 _('user "%s" still owns %s repositories and cannot be '
307 _('user "%s" still owns %s repositories and cannot be '
308 'removed. Switch owners or remove those repositories')
308 'removed. Switch owners or remove those repositories')
309 % (user.username, user.repositories)
309 % (user.username, user.repositories)
310 )
310 )
311 self.sa.delete(user)
311 self.sa.delete(user)
312 except:
312 except:
313 log.error(traceback.format_exc())
313 log.error(traceback.format_exc())
314 raise
314 raise
315
315
316 def reset_password_link(self, data):
316 def reset_password_link(self, data):
317 from rhodecode.lib.celerylib import tasks, run_task
317 from rhodecode.lib.celerylib import tasks, run_task
318 run_task(tasks.send_password_link, data['email'])
318 run_task(tasks.send_password_link, data['email'])
319
319
320 def reset_password(self, data):
320 def reset_password(self, data):
321 from rhodecode.lib.celerylib import tasks, run_task
321 from rhodecode.lib.celerylib import tasks, run_task
322 run_task(tasks.reset_user_password, data['email'])
322 run_task(tasks.reset_user_password, data['email'])
323
323
324 def fill_data(self, auth_user, user_id=None, api_key=None):
324 def fill_data(self, auth_user, user_id=None, api_key=None):
325 """
325 """
326 Fetches auth_user by user_id,or api_key if present.
326 Fetches auth_user by user_id,or api_key if present.
327 Fills auth_user attributes with those taken from database.
327 Fills auth_user attributes with those taken from database.
328 Additionally set's is_authenitated if lookup fails
328 Additionally set's is_authenitated if lookup fails
329 present in database
329 present in database
330
330
331 :param auth_user: instance of user to set attributes
331 :param auth_user: instance of user to set attributes
332 :param user_id: user id to fetch by
332 :param user_id: user id to fetch by
333 :param api_key: api key to fetch by
333 :param api_key: api key to fetch by
334 """
334 """
335 if user_id is None and api_key is None:
335 if user_id is None and api_key is None:
336 raise Exception('You need to pass user_id or api_key')
336 raise Exception('You need to pass user_id or api_key')
337
337
338 try:
338 try:
339 if api_key:
339 if api_key:
340 dbuser = self.get_by_api_key(api_key)
340 dbuser = self.get_by_api_key(api_key)
341 else:
341 else:
342 dbuser = self.get(user_id)
342 dbuser = self.get(user_id)
343
343
344 if dbuser is not None and dbuser.active:
344 if dbuser is not None and dbuser.active:
345 log.debug('filling %s data' % dbuser)
345 log.debug('filling %s data' % dbuser)
346 for k, v in dbuser.get_dict().items():
346 for k, v in dbuser.get_dict().items():
347 setattr(auth_user, k, v)
347 setattr(auth_user, k, v)
348 else:
348 else:
349 return False
349 return False
350
350
351 except:
351 except:
352 log.error(traceback.format_exc())
352 log.error(traceback.format_exc())
353 auth_user.is_authenticated = False
353 auth_user.is_authenticated = False
354 return False
354 return False
355
355
356 return True
356 return True
357
357
358 def fill_perms(self, user):
358 def fill_perms(self, user):
359 """
359 """
360 Fills user permission attribute with permissions taken from database
360 Fills user permission attribute with permissions taken from database
361 works for permissions given for repositories, and for permissions that
361 works for permissions given for repositories, and for permissions that
362 are granted to groups
362 are granted to groups
363
363
364 :param user: user instance to fill his perms
364 :param user: user instance to fill his perms
365 """
365 """
366 RK = 'repositories'
366 RK = 'repositories'
367 GK = 'repositories_groups'
367 GK = 'repositories_groups'
368 GLOBAL = 'global'
368 GLOBAL = 'global'
369 user.permissions[RK] = {}
369 user.permissions[RK] = {}
370 user.permissions[GK] = {}
370 user.permissions[GK] = {}
371 user.permissions[GLOBAL] = set()
371 user.permissions[GLOBAL] = set()
372
372
373 #======================================================================
373 #======================================================================
374 # fetch default permissions
374 # fetch default permissions
375 #======================================================================
375 #======================================================================
376 default_user = User.get_by_username('default', cache=True)
376 default_user = User.get_by_username('default', cache=True)
377 default_user_id = default_user.user_id
377 default_user_id = default_user.user_id
378
378
379 default_repo_perms = Permission.get_default_perms(default_user_id)
379 default_repo_perms = Permission.get_default_perms(default_user_id)
380 default_repo_groups_perms = Permission.get_default_group_perms(default_user_id)
380 default_repo_groups_perms = Permission.get_default_group_perms(default_user_id)
381
381
382 if user.is_admin:
382 if user.is_admin:
383 #==================================================================
383 #==================================================================
384 # admin user have all default rights for repositories
384 # admin user have all default rights for repositories
385 # and groups set to admin
385 # and groups set to admin
386 #==================================================================
386 #==================================================================
387 user.permissions[GLOBAL].add('hg.admin')
387 user.permissions[GLOBAL].add('hg.admin')
388
388
389 # repositories
389 # repositories
390 for perm in default_repo_perms:
390 for perm in default_repo_perms:
391 r_k = perm.UserRepoToPerm.repository.repo_name
391 r_k = perm.UserRepoToPerm.repository.repo_name
392 p = 'repository.admin'
392 p = 'repository.admin'
393 user.permissions[RK][r_k] = p
393 user.permissions[RK][r_k] = p
394
394
395 # repositories groups
395 # repositories groups
396 for perm in default_repo_groups_perms:
396 for perm in default_repo_groups_perms:
397 rg_k = perm.UserRepoGroupToPerm.group.group_name
397 rg_k = perm.UserRepoGroupToPerm.group.group_name
398 p = 'group.admin'
398 p = 'group.admin'
399 user.permissions[GK][rg_k] = p
399 user.permissions[GK][rg_k] = p
400
400
401 else:
401 else:
402 #==================================================================
402 #==================================================================
403 # set default permissions first for repositories and groups
403 # set default permissions first for repositories and groups
404 #==================================================================
404 #==================================================================
405 uid = user.user_id
405 uid = user.user_id
406
406
407 # default global permissions
407 # default global permissions
408 default_global_perms = self.sa.query(UserToPerm)\
408 default_global_perms = self.sa.query(UserToPerm)\
409 .filter(UserToPerm.user_id == default_user_id)
409 .filter(UserToPerm.user_id == default_user_id)
410
410
411 for perm in default_global_perms:
411 for perm in default_global_perms:
412 user.permissions[GLOBAL].add(perm.permission.permission_name)
412 user.permissions[GLOBAL].add(perm.permission.permission_name)
413
413
414 # defaults for repositories, taken from default user
414 # defaults for repositories, taken from default user
415 for perm in default_repo_perms:
415 for perm in default_repo_perms:
416 r_k = perm.UserRepoToPerm.repository.repo_name
416 r_k = perm.UserRepoToPerm.repository.repo_name
417 if perm.Repository.private and not (perm.Repository.user_id == uid):
417 if perm.Repository.private and not (perm.Repository.user_id == uid):
418 # disable defaults for private repos,
418 # disable defaults for private repos,
419 p = 'repository.none'
419 p = 'repository.none'
420 elif perm.Repository.user_id == uid:
420 elif perm.Repository.user_id == uid:
421 # set admin if owner
421 # set admin if owner
422 p = 'repository.admin'
422 p = 'repository.admin'
423 else:
423 else:
424 p = perm.Permission.permission_name
424 p = perm.Permission.permission_name
425
425
426 user.permissions[RK][r_k] = p
426 user.permissions[RK][r_k] = p
427
427
428 # defaults for repositories groups taken from default user permission
428 # defaults for repositories groups taken from default user permission
429 # on given group
429 # on given group
430 for perm in default_repo_groups_perms:
430 for perm in default_repo_groups_perms:
431 rg_k = perm.UserRepoGroupToPerm.group.group_name
431 rg_k = perm.UserRepoGroupToPerm.group.group_name
432 p = perm.Permission.permission_name
432 p = perm.Permission.permission_name
433 user.permissions[GK][rg_k] = p
433 user.permissions[GK][rg_k] = p
434
434
435 #==================================================================
435 #==================================================================
436 # overwrite defaults with user permissions if any found
436 # overwrite defaults with user permissions if any found
437 #==================================================================
437 #==================================================================
438
438
439 # user global permissions
439 # user global permissions
440 user_perms = self.sa.query(UserToPerm)\
440 user_perms = self.sa.query(UserToPerm)\
441 .options(joinedload(UserToPerm.permission))\
441 .options(joinedload(UserToPerm.permission))\
442 .filter(UserToPerm.user_id == uid).all()
442 .filter(UserToPerm.user_id == uid).all()
443
443
444 for perm in user_perms:
444 for perm in user_perms:
445 user.permissions[GLOBAL].add(perm.permission.permission_name)
445 user.permissions[GLOBAL].add(perm.permission.permission_name)
446
446
447 # user explicit permissions for repositories
447 # user explicit permissions for repositories
448 user_repo_perms = \
448 user_repo_perms = \
449 self.sa.query(UserRepoToPerm, Permission, Repository)\
449 self.sa.query(UserRepoToPerm, Permission, Repository)\
450 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
450 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
451 .join((Permission, UserRepoToPerm.permission_id == Permission.permission_id))\
451 .join((Permission, UserRepoToPerm.permission_id == Permission.permission_id))\
452 .filter(UserRepoToPerm.user_id == uid)\
452 .filter(UserRepoToPerm.user_id == uid)\
453 .all()
453 .all()
454
454
455 for perm in user_repo_perms:
455 for perm in user_repo_perms:
456 # set admin if owner
456 # set admin if owner
457 r_k = perm.UserRepoToPerm.repository.repo_name
457 r_k = perm.UserRepoToPerm.repository.repo_name
458 if perm.Repository.user_id == uid:
458 if perm.Repository.user_id == uid:
459 p = 'repository.admin'
459 p = 'repository.admin'
460 else:
460 else:
461 p = perm.Permission.permission_name
461 p = perm.Permission.permission_name
462 user.permissions[RK][r_k] = p
462 user.permissions[RK][r_k] = p
463
463
464 #==================================================================
464 #==================================================================
465 # check if user is part of user groups for this repository and
465 # check if user is part of user groups for this repository and
466 # fill in (or replace with higher) permissions
466 # fill in (or replace with higher) permissions
467 #==================================================================
467 #==================================================================
468
468
469 # users group global
469 # users group global
470 user_perms_from_users_groups = self.sa.query(UsersGroupToPerm)\
470 user_perms_from_users_groups = self.sa.query(UsersGroupToPerm)\
471 .options(joinedload(UsersGroupToPerm.permission))\
471 .options(joinedload(UsersGroupToPerm.permission))\
472 .join((UsersGroupMember, UsersGroupToPerm.users_group_id ==
472 .join((UsersGroupMember, UsersGroupToPerm.users_group_id ==
473 UsersGroupMember.users_group_id))\
473 UsersGroupMember.users_group_id))\
474 .filter(UsersGroupMember.user_id == uid).all()
474 .filter(UsersGroupMember.user_id == uid).all()
475
475
476 for perm in user_perms_from_users_groups:
476 for perm in user_perms_from_users_groups:
477 user.permissions[GLOBAL].add(perm.permission.permission_name)
477 user.permissions[GLOBAL].add(perm.permission.permission_name)
478
478
479 # users group for repositories permissions
479 # users group for repositories permissions
480 user_repo_perms_from_users_groups = \
480 user_repo_perms_from_users_groups = \
481 self.sa.query(UsersGroupRepoToPerm, Permission, Repository,)\
481 self.sa.query(UsersGroupRepoToPerm, Permission, Repository,)\
482 .join((Repository, UsersGroupRepoToPerm.repository_id == Repository.repo_id))\
482 .join((Repository, UsersGroupRepoToPerm.repository_id == Repository.repo_id))\
483 .join((Permission, UsersGroupRepoToPerm.permission_id == Permission.permission_id))\
483 .join((Permission, UsersGroupRepoToPerm.permission_id == Permission.permission_id))\
484 .join((UsersGroupMember, UsersGroupRepoToPerm.users_group_id == UsersGroupMember.users_group_id))\
484 .join((UsersGroupMember, UsersGroupRepoToPerm.users_group_id == UsersGroupMember.users_group_id))\
485 .filter(UsersGroupMember.user_id == uid)\
485 .filter(UsersGroupMember.user_id == uid)\
486 .all()
486 .all()
487
487
488 for perm in user_repo_perms_from_users_groups:
488 for perm in user_repo_perms_from_users_groups:
489 r_k = perm.UsersGroupRepoToPerm.repository.repo_name
489 r_k = perm.UsersGroupRepoToPerm.repository.repo_name
490 p = perm.Permission.permission_name
490 p = perm.Permission.permission_name
491 cur_perm = user.permissions[RK][r_k]
491 cur_perm = user.permissions[RK][r_k]
492 # overwrite permission only if it's greater than permission
492 # overwrite permission only if it's greater than permission
493 # given from other sources
493 # given from other sources
494 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
494 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
495 user.permissions[RK][r_k] = p
495 user.permissions[RK][r_k] = p
496
496
497 #==================================================================
497 #==================================================================
498 # get access for this user for repos group and override defaults
498 # get access for this user for repos group and override defaults
499 #==================================================================
499 #==================================================================
500
500
501 # user explicit permissions for repository
501 # user explicit permissions for repository
502 user_repo_groups_perms = \
502 user_repo_groups_perms = \
503 self.sa.query(UserRepoGroupToPerm, Permission, RepoGroup)\
503 self.sa.query(UserRepoGroupToPerm, Permission, RepoGroup)\
504 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
504 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
505 .join((Permission, UserRepoGroupToPerm.permission_id == Permission.permission_id))\
505 .join((Permission, UserRepoGroupToPerm.permission_id == Permission.permission_id))\
506 .filter(UserRepoGroupToPerm.user_id == uid)\
506 .filter(UserRepoGroupToPerm.user_id == uid)\
507 .all()
507 .all()
508
508
509 for perm in user_repo_groups_perms:
509 for perm in user_repo_groups_perms:
510 rg_k = perm.UserRepoGroupToPerm.group.group_name
510 rg_k = perm.UserRepoGroupToPerm.group.group_name
511 p = perm.Permission.permission_name
511 p = perm.Permission.permission_name
512 cur_perm = user.permissions[GK][rg_k]
512 cur_perm = user.permissions[GK][rg_k]
513 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
513 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
514 user.permissions[GK][rg_k] = p
514 user.permissions[GK][rg_k] = p
515
515
516 #==================================================================
516 #==================================================================
517 # check if user is part of user groups for this repo group and
517 # check if user is part of user groups for this repo group and
518 # fill in (or replace with higher) permissions
518 # fill in (or replace with higher) permissions
519 #==================================================================
519 #==================================================================
520
520
521 # users group for repositories permissions
521 # users group for repositories permissions
522 user_repo_group_perms_from_users_groups = \
522 user_repo_group_perms_from_users_groups = \
523 self.sa.query(UsersGroupRepoGroupToPerm, Permission, RepoGroup)\
523 self.sa.query(UsersGroupRepoGroupToPerm, Permission, RepoGroup)\
524 .join((RepoGroup, UsersGroupRepoGroupToPerm.group_id == RepoGroup.group_id))\
524 .join((RepoGroup, UsersGroupRepoGroupToPerm.group_id == RepoGroup.group_id))\
525 .join((Permission, UsersGroupRepoGroupToPerm.permission_id == Permission.permission_id))\
525 .join((Permission, UsersGroupRepoGroupToPerm.permission_id == Permission.permission_id))\
526 .join((UsersGroupMember, UsersGroupRepoGroupToPerm.users_group_id == UsersGroupMember.users_group_id))\
526 .join((UsersGroupMember, UsersGroupRepoGroupToPerm.users_group_id == UsersGroupMember.users_group_id))\
527 .filter(UsersGroupMember.user_id == uid)\
527 .filter(UsersGroupMember.user_id == uid)\
528 .all()
528 .all()
529
529
530 for perm in user_repo_group_perms_from_users_groups:
530 for perm in user_repo_group_perms_from_users_groups:
531 g_k = perm.UsersGroupRepoGroupToPerm.group.group_name
531 g_k = perm.UsersGroupRepoGroupToPerm.group.group_name
532 print perm, g_k
532 print perm, g_k
533 p = perm.Permission.permission_name
533 p = perm.Permission.permission_name
534 cur_perm = user.permissions[GK][g_k]
534 cur_perm = user.permissions[GK][g_k]
535 # overwrite permission only if it's greater than permission
535 # overwrite permission only if it's greater than permission
536 # given from other sources
536 # given from other sources
537 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
537 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
538 user.permissions[GK][g_k] = p
538 user.permissions[GK][g_k] = p
539
539
540 return user
540 return user
541
541
542 def has_perm(self, user, perm):
542 def has_perm(self, user, perm):
543 if not isinstance(perm, Permission):
543 if not isinstance(perm, Permission):
544 raise Exception('perm needs to be an instance of Permission class '
544 raise Exception('perm needs to be an instance of Permission class '
545 'got %s instead' % type(perm))
545 'got %s instead' % type(perm))
546
546
547 user = self.__get_user(user)
547 user = self.__get_user(user)
548
548
549 return UserToPerm.query().filter(UserToPerm.user == user)\
549 return UserToPerm.query().filter(UserToPerm.user == user)\
550 .filter(UserToPerm.permission == perm).scalar() is not None
550 .filter(UserToPerm.permission == perm).scalar() is not None
551
551
552 def grant_perm(self, user, perm):
552 def grant_perm(self, user, perm):
553 """
553 """
554 Grant user global permissions
554 Grant user global permissions
555
555
556 :param user:
556 :param user:
557 :param perm:
557 :param perm:
558 """
558 """
559 user = self.__get_user(user)
559 user = self.__get_user(user)
560 perm = self.__get_perm(perm)
560 perm = self.__get_perm(perm)
561 # if this permission is already granted skip it
561 # if this permission is already granted skip it
562 _perm = UserToPerm.query()\
562 _perm = UserToPerm.query()\
563 .filter(UserToPerm.user == user)\
563 .filter(UserToPerm.user == user)\
564 .filter(UserToPerm.permission == perm)\
564 .filter(UserToPerm.permission == perm)\
565 .scalar()
565 .scalar()
566 if _perm:
566 if _perm:
567 return
567 return
568 new = UserToPerm()
568 new = UserToPerm()
569 new.user = user
569 new.user = user
570 new.permission = perm
570 new.permission = perm
571 self.sa.add(new)
571 self.sa.add(new)
572
572
573 def revoke_perm(self, user, perm):
573 def revoke_perm(self, user, perm):
574 """
574 """
575 Revoke users global permissions
575 Revoke users global permissions
576
576
577 :param user:
577 :param user:
578 :param perm:
578 :param perm:
579 """
579 """
580 user = self.__get_user(user)
580 user = self.__get_user(user)
581 perm = self.__get_perm(perm)
581 perm = self.__get_perm(perm)
582
582
583 obj = UserToPerm.query()\
583 obj = UserToPerm.query()\
584 .filter(UserToPerm.user == user)\
584 .filter(UserToPerm.user == user)\
585 .filter(UserToPerm.permission == perm)\
585 .filter(UserToPerm.permission == perm)\
586 .scalar()
586 .scalar()
587 if obj:
587 if obj:
588 self.sa.delete(obj)
588 self.sa.delete(obj)
@@ -1,128 +1,128 b''
1 <table id="permissions_manage" class="noborder">
1 <table id="permissions_manage" class="noborder">
2 <tr>
2 <tr>
3 <td>${_('none')}</td>
3 <td>${_('none')}</td>
4 <td>${_('read')}</td>
4 <td>${_('read')}</td>
5 <td>${_('write')}</td>
5 <td>${_('write')}</td>
6 <td>${_('admin')}</td>
6 <td>${_('admin')}</td>
7 <td>${_('member')}</td>
7 <td>${_('member')}</td>
8 <td></td>
8 <td></td>
9 </tr>
9 </tr>
10 ## USERS
10 ## USERS
11 %for r2p in c.repo_info.repo_to_perm:
11 %for r2p in c.repo_info.repo_to_perm:
12 %if r2p.user.username =='default' and c.repo_info.private:
12 %if r2p.user.username =='default' and c.repo_info.private:
13 <tr>
13 <tr>
14 <td colspan="4">
14 <td colspan="4">
15 <span class="private_repo_msg">
15 <span class="private_repo_msg">
16 ${_('private repository')}
16 ${_('private repository')}
17 </span>
17 </span>
18 </td>
18 </td>
19 <td class="private_repo_msg"><img style="vertical-align:bottom" src="${h.url('/images/icons/user.png')}"/>${r2p.user.username}</td>
19 <td class="private_repo_msg"><img style="vertical-align:bottom" src="${h.url('/images/icons/user.png')}"/>${r2p.user.username}</td>
20 </tr>
20 </tr>
21 %else:
21 %else:
22 <tr id="id${id(r2p.user.username)}">
22 <tr id="id${id(r2p.user.username)}">
23 <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.none')}</td>
23 <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.none')}</td>
24 <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.read')}</td>
24 <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.read')}</td>
25 <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.write')}</td>
25 <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.write')}</td>
26 <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.admin')}</td>
26 <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.admin')}</td>
27 <td style="white-space: nowrap;">
27 <td style="white-space: nowrap;">
28 <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username}
28 <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username}
29 </td>
29 </td>
30 <td>
30 <td>
31 %if r2p.user.username !='default':
31 %if r2p.user.username !='default':
32 <span class="delete_icon action_button" onclick="ajaxActionUser(${r2p.user.user_id},'${'id%s'%id(r2p.user.username)}')">
32 <span class="delete_icon action_button" onclick="ajaxActionUser(${r2p.user.user_id},'${'id%s'%id(r2p.user.username)}')">
33 ${_('revoke')}
33 ${_('revoke')}
34 </span>
34 </span>
35 %endif
35 %endif
36 </td>
36 </td>
37 </tr>
37 </tr>
38 %endif
38 %endif
39 %endfor
39 %endfor
40
40
41 ## USERS GROUPS
41 ## USERS GROUPS
42 %for g2p in c.repo_info.users_group_to_perm:
42 %for g2p in c.repo_info.users_group_to_perm:
43 <tr id="id${id(g2p.users_group.users_group_name)}">
43 <tr id="id${id(g2p.users_group.users_group_name)}">
44 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.none')}</td>
44 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.none')}</td>
45 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.read')}</td>
45 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.read')}</td>
46 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.write')}</td>
46 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.write')}</td>
47 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.admin')}</td>
47 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.admin')}</td>
48 <td style="white-space: nowrap;">
48 <td style="white-space: nowrap;">
49 <img class="perm-gravatar" src="${h.url('/images/icons/group.png')}"/>${g2p.users_group.users_group_name}
49 <img class="perm-gravatar" src="${h.url('/images/icons/group.png')}"/>${g2p.users_group.users_group_name}
50 </td>
50 </td>
51 <td>
51 <td>
52 <span class="delete_icon action_button" onclick="ajaxActionUsersGroup(${g2p.users_group.users_group_id},'${'id%s'%id(g2p.users_group.users_group_name)}')">
52 <span class="delete_icon action_button" onclick="ajaxActionUsersGroup(${g2p.users_group.users_group_id},'${'id%s'%id(g2p.users_group.users_group_name)}')">
53 ${_('revoke')}
53 ${_('revoke')}
54 </span>
54 </span>
55 </td>
55 </td>
56 </tr>
56 </tr>
57 %endfor
57 %endfor
58 <tr id="add_perm_input">
58 <tr id="add_perm_input">
59 <td>${h.radio('perm_new_member','repository.none')}</td>
59 <td>${h.radio('perm_new_member','repository.none')}</td>
60 <td>${h.radio('perm_new_member','repository.read')}</td>
60 <td>${h.radio('perm_new_member','repository.read')}</td>
61 <td>${h.radio('perm_new_member','repository.write')}</td>
61 <td>${h.radio('perm_new_member','repository.write')}</td>
62 <td>${h.radio('perm_new_member','repository.admin')}</td>
62 <td>${h.radio('perm_new_member','repository.admin')}</td>
63 <td class='ac'>
63 <td class='ac'>
64 <div class="perm_ac" id="perm_ac">
64 <div class="perm_ac" id="perm_ac">
65 ${h.text('perm_new_member_name',class_='yui-ac-input')}
65 ${h.text('perm_new_member_name',class_='yui-ac-input')}
66 ${h.hidden('perm_new_member_type')}
66 ${h.hidden('perm_new_member_type')}
67 <div id="perm_container"></div>
67 <div id="perm_container"></div>
68 </div>
68 </div>
69 </td>
69 </td>
70 <td></td>
70 <td></td>
71 </tr>
71 </tr>
72 <tr>
72 <tr>
73 <td colspan="6">
73 <td colspan="6">
74 <span id="add_perm" class="add_icon" style="cursor: pointer;">
74 <span id="add_perm" class="add_icon" style="cursor: pointer;">
75 ${_('Add another member')}
75 ${_('Add another member')}
76 </span>
76 </span>
77 </td>
77 </td>
78 </tr>
78 </tr>
79 </table>
79 </table>
80 <script type="text/javascript">
80 <script type="text/javascript">
81 function ajaxActionUser(user_id, field_id) {
81 function ajaxActionUser(user_id, field_id) {
82 var sUrl = "${h.url('delete_repo_user',repo_name=c.repo_name)}";
82 var sUrl = "${h.url('delete_repo_user',repo_name=c.repo_name)}";
83 var callback = {
83 var callback = {
84 success: function (o) {
84 success: function (o) {
85 var tr = YUD.get(String(field_id));
85 var tr = YUD.get(String(field_id));
86 tr.parentNode.removeChild(tr);
86 tr.parentNode.removeChild(tr);
87 },
87 },
88 failure: function (o) {
88 failure: function (o) {
89 alert("${_('Failed to remove user')}");
89 alert("${_('Failed to remove user')}");
90 },
90 },
91 };
91 };
92 var postData = '_method=delete&user_id=' + user_id;
92 var postData = '_method=delete&user_id=' + user_id;
93 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
93 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
94 };
94 };
95
95
96 function ajaxActionUsersGroup(users_group_id,field_id){
96 function ajaxActionUsersGroup(users_group_id,field_id){
97 var sUrl = "${h.url('delete_repo_users_group',repo_name=c.repo_name)}";
97 var sUrl = "${h.url('delete_repo_users_group',repo_name=c.repo_name)}";
98 var callback = {
98 var callback = {
99 success:function(o){
99 success:function(o){
100 var tr = YUD.get(String(field_id));
100 var tr = YUD.get(String(field_id));
101 tr.parentNode.removeChild(tr);
101 tr.parentNode.removeChild(tr);
102 },
102 },
103 failure:function(o){
103 failure:function(o){
104 alert("${_('Failed to remove users group')}");
104 alert("${_('Failed to remove users group')}");
105 },
105 },
106 };
106 };
107 var postData = '_method=delete&users_group_id='+users_group_id;
107 var postData = '_method=delete&users_group_id='+users_group_id;
108 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
108 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
109 };
109 };
110
110
111 YUE.onDOMReady(function () {
111 YUE.onDOMReady(function () {
112 if (!YUD.hasClass('perm_new_member_name', 'error')) {
112 if (!YUD.hasClass('perm_new_member_name', 'error')) {
113 YUD.setStyle('add_perm_input', 'display', 'none');
113 YUD.setStyle('add_perm_input', 'display', 'none');
114 }
114 }
115 YAHOO.util.Event.addListener('add_perm', 'click', function () {
115 YAHOO.util.Event.addListener('add_perm', 'click', function () {
116 YUD.setStyle('add_perm_input', 'display', '');
116 YUD.setStyle('add_perm_input', 'display', '');
117 YUD.setStyle('add_perm', 'opacity', '0.6');
117 YUD.setStyle('add_perm', 'opacity', '0.6');
118 YUD.setStyle('add_perm', 'cursor', 'default');
118 YUD.setStyle('add_perm', 'cursor', 'default');
119 });
119 });
120 MembersAutoComplete(
120 MembersAutoComplete(
121 ${c.users_array|n},
121 ${c.users_array|n},
122 ${c.users_groups_array|n},
122 ${c.users_groups_array|n},
123 "${_('Group')}",
123 "${_('Group')}",
124 "${_('members')}"
124 "${_('members')}"
125 );
125 );
126 });
126 });
127
127
128 </script>
128 </script>
@@ -1,117 +1,117 b''
1 <table id="permissions_manage" class="noborder">
1 <table id="permissions_manage" class="noborder">
2 <tr>
2 <tr>
3 <td>${_('none')}</td>
3 <td>${_('none')}</td>
4 <td>${_('read')}</td>
4 <td>${_('read')}</td>
5 <td>${_('write')}</td>
5 <td>${_('write')}</td>
6 <td>${_('admin')}</td>
6 <td>${_('admin')}</td>
7 <td>${_('member')}</td>
7 <td>${_('member')}</td>
8 <td></td>
8 <td></td>
9 </tr>
9 </tr>
10 ## USERS
10 ## USERS
11 %for r2p in c.repos_group.repo_group_to_perm:
11 %for r2p in c.repos_group.repo_group_to_perm:
12 <tr id="id${id(r2p.user.username)}">
12 <tr id="id${id(r2p.user.username)}">
13 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.none')}</td>
13 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.none')}</td>
14 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.read')}</td>
14 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.read')}</td>
15 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.write')}</td>
15 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.write')}</td>
16 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.admin')}</td>
16 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.admin')}</td>
17 <td style="white-space: nowrap;">
17 <td style="white-space: nowrap;">
18 <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username}
18 <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username}
19 </td>
19 </td>
20 <td>
20 <td>
21 %if r2p.user.username !='default':
21 %if r2p.user.username !='default':
22 <span class="delete_icon action_button" onclick="ajaxActionUser(${r2p.user.user_id},'${'id%s'%id(r2p.user.username)}')">
22 <span class="delete_icon action_button" onclick="ajaxActionUser(${r2p.user.user_id},'${'id%s'%id(r2p.user.username)}')">
23 ${_('revoke')}
23 ${_('revoke')}
24 </span>
24 </span>
25 %endif
25 %endif
26 </td>
26 </td>
27 </tr>
27 </tr>
28 %endfor
28 %endfor
29
29
30 ## USERS GROUPS
30 ## USERS GROUPS
31 %for g2p in c.repos_group.users_group_to_perm:
31 %for g2p in c.repos_group.users_group_to_perm:
32 <tr id="id${id(g2p.users_group.users_group_name)}">
32 <tr id="id${id(g2p.users_group.users_group_name)}">
33 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.none')}</td>
33 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.none')}</td>
34 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.read')}</td>
34 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.read')}</td>
35 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.write')}</td>
35 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.write')}</td>
36 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.admin')}</td>
36 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.admin')}</td>
37 <td style="white-space: nowrap;">
37 <td style="white-space: nowrap;">
38 <img class="perm-gravatar" src="${h.url('/images/icons/group.png')}"/>${g2p.users_group.users_group_name}
38 <img class="perm-gravatar" src="${h.url('/images/icons/group.png')}"/>${g2p.users_group.users_group_name}
39 </td>
39 </td>
40 <td>
40 <td>
41 <span class="delete_icon action_button" onclick="ajaxActionUsersGroup(${g2p.users_group.users_group_id},'${'id%s'%id(g2p.users_group.users_group_name)}')">
41 <span class="delete_icon action_button" onclick="ajaxActionUsersGroup(${g2p.users_group.users_group_id},'${'id%s'%id(g2p.users_group.users_group_name)}')">
42 ${_('revoke')}
42 ${_('revoke')}
43 </span>
43 </span>
44 </td>
44 </td>
45 </tr>
45 </tr>
46 %endfor
46 %endfor
47 <tr id="add_perm_input">
47 <tr id="add_perm_input">
48 <td>${h.radio('perm_new_member','group.none')}</td>
48 <td>${h.radio('perm_new_member','group.none')}</td>
49 <td>${h.radio('perm_new_member','group.read')}</td>
49 <td>${h.radio('perm_new_member','group.read')}</td>
50 <td>${h.radio('perm_new_member','group.write')}</td>
50 <td>${h.radio('perm_new_member','group.write')}</td>
51 <td>${h.radio('perm_new_member','group.admin')}</td>
51 <td>${h.radio('perm_new_member','group.admin')}</td>
52 <td class='ac'>
52 <td class='ac'>
53 <div class="perm_ac" id="perm_ac">
53 <div class="perm_ac" id="perm_ac">
54 ${h.text('perm_new_member_name',class_='yui-ac-input')}
54 ${h.text('perm_new_member_name',class_='yui-ac-input')}
55 ${h.hidden('perm_new_member_type')}
55 ${h.hidden('perm_new_member_type')}
56 <div id="perm_container"></div>
56 <div id="perm_container"></div>
57 </div>
57 </div>
58 </td>
58 </td>
59 <td></td>
59 <td></td>
60 </tr>
60 </tr>
61 <tr>
61 <tr>
62 <td colspan="6">
62 <td colspan="6">
63 <span id="add_perm" class="add_icon" style="cursor: pointer;">
63 <span id="add_perm" class="add_icon" style="cursor: pointer;">
64 ${_('Add another member')}
64 ${_('Add another member')}
65 </span>
65 </span>
66 </td>
66 </td>
67 </tr>
67 </tr>
68 </table>
68 </table>
69 <script type="text/javascript">
69 <script type="text/javascript">
70 function ajaxActionUser(user_id, field_id) {
70 function ajaxActionUser(user_id, field_id) {
71 var sUrl = "${h.url('delete_repos_group_user_perm',group_name=c.repos_group.group_name)}";
71 var sUrl = "${h.url('delete_repos_group_user_perm',group_name=c.repos_group.group_name)}";
72 var callback = {
72 var callback = {
73 success: function (o) {
73 success: function (o) {
74 var tr = YUD.get(String(field_id));
74 var tr = YUD.get(String(field_id));
75 tr.parentNode.removeChild(tr);
75 tr.parentNode.removeChild(tr);
76 },
76 },
77 failure: function (o) {
77 failure: function (o) {
78 alert("${_('Failed to remove user')}");
78 alert("${_('Failed to remove user')}");
79 },
79 },
80 };
80 };
81 var postData = '_method=delete&user_id=' + user_id;
81 var postData = '_method=delete&user_id=' + user_id;
82 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
82 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
83 };
83 };
84
84
85 function ajaxActionUsersGroup(users_group_id,field_id){
85 function ajaxActionUsersGroup(users_group_id,field_id){
86 var sUrl = "${h.url('delete_repos_group_users_group_perm',group_name=c.repos_group.group_name)}";
86 var sUrl = "${h.url('delete_repos_group_users_group_perm',group_name=c.repos_group.group_name)}";
87 var callback = {
87 var callback = {
88 success:function(o){
88 success:function(o){
89 var tr = YUD.get(String(field_id));
89 var tr = YUD.get(String(field_id));
90 tr.parentNode.removeChild(tr);
90 tr.parentNode.removeChild(tr);
91 },
91 },
92 failure:function(o){
92 failure:function(o){
93 alert("${_('Failed to remove users group')}");
93 alert("${_('Failed to remove users group')}");
94 },
94 },
95 };
95 };
96 var postData = '_method=delete&users_group_id='+users_group_id;
96 var postData = '_method=delete&users_group_id='+users_group_id;
97 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
97 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
98 };
98 };
99
99
100 YUE.onDOMReady(function () {
100 YUE.onDOMReady(function () {
101 if (!YUD.hasClass('perm_new_member_name', 'error')) {
101 if (!YUD.hasClass('perm_new_member_name', 'error')) {
102 YUD.setStyle('add_perm_input', 'display', 'none');
102 YUD.setStyle('add_perm_input', 'display', 'none');
103 }
103 }
104 YAHOO.util.Event.addListener('add_perm', 'click', function () {
104 YAHOO.util.Event.addListener('add_perm', 'click', function () {
105 YUD.setStyle('add_perm_input', 'display', '');
105 YUD.setStyle('add_perm_input', 'display', '');
106 YUD.setStyle('add_perm', 'opacity', '0.6');
106 YUD.setStyle('add_perm', 'opacity', '0.6');
107 YUD.setStyle('add_perm', 'cursor', 'default');
107 YUD.setStyle('add_perm', 'cursor', 'default');
108 });
108 });
109 MembersAutoComplete(
109 MembersAutoComplete(
110 ${c.users_array|n},
110 ${c.users_array|n},
111 ${c.users_groups_array|n},
111 ${c.users_groups_array|n},
112 "${_('Group')}",
112 "${_('Group')}",
113 "${_('members')}"
113 "${_('members')}"
114 );
114 );
115 });
115 });
116
116
117 </script>
117 </script>
@@ -1,201 +1,201 b''
1 <%page args="parent" />
1 <%page args="parent" />
2 <div class="box">
2 <div class="box">
3 <!-- box / title -->
3 <!-- box / title -->
4 <div class="title">
4 <div class="title">
5 <h5>
5 <h5>
6 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}"/> ${parent.breadcrumbs()} <span id="repo_count">0</span> ${_('repositories')}
6 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}"/> ${parent.breadcrumbs()} <span id="repo_count">0</span> ${_('repositories')}
7 </h5>
7 </h5>
8 %if c.rhodecode_user.username != 'default':
8 %if c.rhodecode_user.username != 'default':
9 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
9 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
10 <ul class="links">
10 <ul class="links">
11 <li>
11 <li>
12 %if c.group:
12 %if c.group:
13 <span>${h.link_to(_('ADD REPOSITORY'),h.url('admin_settings_create_repository',parent_group=c.group.group_id))}</span>
13 <span>${h.link_to(_('ADD REPOSITORY'),h.url('admin_settings_create_repository',parent_group=c.group.group_id))}</span>
14 %else:
14 %else:
15 <span>${h.link_to(_('ADD REPOSITORY'),h.url('admin_settings_create_repository'))}</span>
15 <span>${h.link_to(_('ADD REPOSITORY'),h.url('admin_settings_create_repository'))}</span>
16 %endif
16 %endif
17 </li>
17 </li>
18 </ul>
18 </ul>
19 %endif
19 %endif
20 %endif
20 %endif
21 </div>
21 </div>
22 <!-- end box / title -->
22 <!-- end box / title -->
23 <div class="table">
23 <div class="table">
24 % if c.groups:
24 % if c.groups:
25 <div id='groups_list_wrap' class="yui-skin-sam">
25 <div id='groups_list_wrap' class="yui-skin-sam">
26 <table id="groups_list">
26 <table id="groups_list">
27 <thead>
27 <thead>
28 <tr>
28 <tr>
29 <th class="left"><a href="#">${_('Group name')}</a></th>
29 <th class="left"><a href="#">${_('Group name')}</a></th>
30 <th class="left"><a href="#">${_('Description')}</a></th>
30 <th class="left"><a href="#">${_('Description')}</a></th>
31 ##<th class="left"><a href="#">${_('Number of repositories')}</a></th>
31 ##<th class="left"><a href="#">${_('Number of repositories')}</a></th>
32 </tr>
32 </tr>
33 </thead>
33 </thead>
34
34
35 ## REPO GROUPS
35 ## REPO GROUPS
36 % for gr in c.groups:
36 % for gr in c.groups:
37 <tr>
37 <tr>
38 <td>
38 <td>
39 <div style="white-space: nowrap">
39 <div style="white-space: nowrap">
40 <img class="icon" alt="${_('Repositories group')}" src="${h.url('/images/icons/database_link.png')}"/>
40 <img class="icon" alt="${_('Repositories group')}" src="${h.url('/images/icons/database_link.png')}"/>
41 ${h.link_to(gr.name,url('repos_group_home',group_name=gr.group_name))}
41 ${h.link_to(gr.name,url('repos_group_home',group_name=gr.group_name))}
42 </div>
42 </div>
43 </td>
43 </td>
44 <td>${gr.group_description}</td>
44 <td>${gr.group_description}</td>
45 ## this is commented out since for multi nested repos can be HEAVY!
45 ## this is commented out since for multi nested repos can be HEAVY!
46 ## in number of executed queries during traversing uncomment at will
46 ## in number of executed queries during traversing uncomment at will
47 ##<td><b>${gr.repositories_recursive_count}</b></td>
47 ##<td><b>${gr.repositories_recursive_count}</b></td>
48 </tr>
48 </tr>
49 % endfor
49 % endfor
50
50
51 </table>
51 </table>
52 </div>
52 </div>
53 <div style="height: 20px"></div>
53 <div style="height: 20px"></div>
54 % endif
54 % endif
55 <div id="welcome" style="display:none;text-align:center">
55 <div id="welcome" style="display:none;text-align:center">
56 <h1><a href="${h.url('home')}">${c.rhodecode_name} ${c.rhodecode_version}</a></h1>
56 <h1><a href="${h.url('home')}">${c.rhodecode_name} ${c.rhodecode_version}</a></h1>
57 </div>
57 </div>
58 <div id='repos_list_wrap' class="yui-skin-sam">
58 <div id='repos_list_wrap' class="yui-skin-sam">
59 <%cnt=0%>
59 <%cnt=0%>
60 <%namespace name="dt" file="/_data_table/_dt_elements.html"/>
60 <%namespace name="dt" file="/_data_table/_dt_elements.html"/>
61
61
62 <table id="repos_list">
62 <table id="repos_list">
63 <thead>
63 <thead>
64 <tr>
64 <tr>
65 <th class="left"></th>
65 <th class="left"></th>
66 <th class="left">${_('Name')}</th>
66 <th class="left">${_('Name')}</th>
67 <th class="left">${_('Description')}</th>
67 <th class="left">${_('Description')}</th>
68 <th class="left">${_('Last change')}</th>
68 <th class="left">${_('Last change')}</th>
69 <th class="left">${_('Tip')}</th>
69 <th class="left">${_('Tip')}</th>
70 <th class="left">${_('Owner')}</th>
70 <th class="left">${_('Owner')}</th>
71 <th class="left">${_('RSS')}</th>
71 <th class="left">${_('RSS')}</th>
72 <th class="left">${_('Atom')}</th>
72 <th class="left">${_('Atom')}</th>
73 </tr>
73 </tr>
74 </thead>
74 </thead>
75 <tbody>
75 <tbody>
76 %for cnt,repo in enumerate(c.repos_list):
76 %for cnt,repo in enumerate(c.repos_list):
77 <tr class="parity${(cnt+1)%2}">
77 <tr class="parity${(cnt+1)%2}">
78 ##QUICK MENU
78 ##QUICK MENU
79 <td class="quick_repo_menu">
79 <td class="quick_repo_menu">
80 ${dt.quick_menu(repo['name'])}
80 ${dt.quick_menu(repo['name'])}
81 </td>
81 </td>
82 ##REPO NAME AND ICONS
82 ##REPO NAME AND ICONS
83 <td class="reponame">
83 <td class="reponame">
84 ${dt.repo_name(repo['name'],repo['dbrepo']['repo_type'],repo['dbrepo']['private'],repo['dbrepo_fork'].get('repo_name'),pageargs.get('short_repo_names'))}
84 ${dt.repo_name(repo['name'],repo['dbrepo']['repo_type'],repo['dbrepo']['private'],repo['dbrepo_fork'].get('repo_name'),pageargs.get('short_repo_names'))}
85 </td>
85 </td>
86 ##DESCRIPTION
86 ##DESCRIPTION
87 <td><span class="tooltip" title="${h.tooltip(repo['description'])}">
87 <td><span class="tooltip" title="${h.tooltip(repo['description'])}">
88 ${h.truncate(repo['description'],60)}</span>
88 ${h.truncate(repo['description'],60)}</span>
89 </td>
89 </td>
90 ##LAST CHANGE DATE
90 ##LAST CHANGE DATE
91 <td>
91 <td>
92 <span class="tooltip" title="${repo['last_change']}">${h.age(repo['last_change'])}</span>
92 <span class="tooltip" title="${repo['last_change']}">${h.age(repo['last_change'])}</span>
93 </td>
93 </td>
94 ##LAST REVISION
94 ##LAST REVISION
95 <td>
95 <td>
96 ${dt.revision(repo['name'],repo['rev'],repo['tip'],repo['author'],repo['last_msg'])}
96 ${dt.revision(repo['name'],repo['rev'],repo['tip'],repo['author'],repo['last_msg'])}
97 </td>
97 </td>
98 ##
98 ##
99 <td title="${repo['contact']}">${h.person(repo['contact'])}</td>
99 <td title="${repo['contact']}">${h.person(repo['contact'])}</td>
100 <td>
100 <td>
101 %if c.rhodecode_user.username != 'default':
101 %if c.rhodecode_user.username != 'default':
102 <a title="${_('Subscribe to %s rss feed')%repo['name']}" class="rss_icon" href="${h.url('rss_feed_home',repo_name=repo['name'],api_key=c.rhodecode_user.api_key)}"></a>
102 <a title="${_('Subscribe to %s rss feed')%repo['name']}" class="rss_icon" href="${h.url('rss_feed_home',repo_name=repo['name'],api_key=c.rhodecode_user.api_key)}"></a>
103 %else:
103 %else:
104 <a title="${_('Subscribe to %s rss feed')%repo['name']}" class="rss_icon" href="${h.url('rss_feed_home',repo_name=repo['name'])}"></a>
104 <a title="${_('Subscribe to %s rss feed')%repo['name']}" class="rss_icon" href="${h.url('rss_feed_home',repo_name=repo['name'])}"></a>
105 %endif:
105 %endif:
106 </td>
106 </td>
107 <td>
107 <td>
108 %if c.rhodecode_user.username != 'default':
108 %if c.rhodecode_user.username != 'default':
109 <a title="${_('Subscribe to %s atom feed')%repo['name']}" class="atom_icon" href="${h.url('atom_feed_home',repo_name=repo['name'],api_key=c.rhodecode_user.api_key)}"></a>
109 <a title="${_('Subscribe to %s atom feed')%repo['name']}" class="atom_icon" href="${h.url('atom_feed_home',repo_name=repo['name'],api_key=c.rhodecode_user.api_key)}"></a>
110 %else:
110 %else:
111 <a title="${_('Subscribe to %s atom feed')%repo['name']}" class="atom_icon" href="${h.url('atom_feed_home',repo_name=repo['name'])}"></a>
111 <a title="${_('Subscribe to %s atom feed')%repo['name']}" class="atom_icon" href="${h.url('atom_feed_home',repo_name=repo['name'])}"></a>
112 %endif:
112 %endif:
113 </td>
113 </td>
114 </tr>
114 </tr>
115 %endfor
115 %endfor
116 </tbody>
116 </tbody>
117 </table>
117 </table>
118 </div>
118 </div>
119 </div>
119 </div>
120 </div>
120 </div>
121 <script>
121 <script>
122 YUD.get('repo_count').innerHTML = ${cnt};
122 YUD.get('repo_count').innerHTML = ${cnt};
123 var func = function(node){
123 var func = function(node){
124 return node.parentNode.parentNode.parentNode.parentNode;
124 return node.parentNode.parentNode.parentNode.parentNode;
125 }
125 }
126
126
127
127
128 // groups table sorting
128 // groups table sorting
129 var myColumnDefs = [
129 var myColumnDefs = [
130 {key:"name",label:"${_('Group Name')}",sortable:true,
130 {key:"name",label:"${_('Group Name')}",sortable:true,
131 sortOptions: { sortFunction: groupNameSort }},
131 sortOptions: { sortFunction: groupNameSort }},
132 {key:"desc",label:"${_('Description')}",sortable:true},
132 {key:"desc",label:"${_('Description')}",sortable:true},
133 ];
133 ];
134
134
135 var myDataSource = new YAHOO.util.DataSource(YUD.get("groups_list"));
135 var myDataSource = new YAHOO.util.DataSource(YUD.get("groups_list"));
136
136
137 myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
137 myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
138 myDataSource.responseSchema = {
138 myDataSource.responseSchema = {
139 fields: [
139 fields: [
140 {key:"name"},
140 {key:"name"},
141 {key:"desc"},
141 {key:"desc"},
142 ]
142 ]
143 };
143 };
144
144
145 var myDataTable = new YAHOO.widget.DataTable("groups_list_wrap", myColumnDefs, myDataSource,
145 var myDataTable = new YAHOO.widget.DataTable("groups_list_wrap", myColumnDefs, myDataSource,
146 {
146 {
147 sortedBy:{key:"name",dir:"asc"},
147 sortedBy:{key:"name",dir:"asc"},
148 MSG_SORTASC:"${_('Click to sort ascending')}",
148 MSG_SORTASC:"${_('Click to sort ascending')}",
149 MSG_SORTDESC:"${_('Click to sort descending')}"
149 MSG_SORTDESC:"${_('Click to sort descending')}"
150 }
150 }
151 );
151 );
152
152
153 // main table sorting
153 // main table sorting
154 var myColumnDefs = [
154 var myColumnDefs = [
155 {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"},
155 {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"},
156 {key:"name",label:"${_('Name')}",sortable:true,
156 {key:"name",label:"${_('Name')}",sortable:true,
157 sortOptions: { sortFunction: nameSort }},
157 sortOptions: { sortFunction: nameSort }},
158 {key:"desc",label:"${_('Description')}",sortable:true},
158 {key:"desc",label:"${_('Description')}",sortable:true},
159 {key:"last_change",label:"${_('Last Change')}",sortable:true,
159 {key:"last_change",label:"${_('Last Change')}",sortable:true,
160 sortOptions: { sortFunction: ageSort }},
160 sortOptions: { sortFunction: ageSort }},
161 {key:"tip",label:"${_('Tip')}",sortable:true,
161 {key:"tip",label:"${_('Tip')}",sortable:true,
162 sortOptions: { sortFunction: revisionSort }},
162 sortOptions: { sortFunction: revisionSort }},
163 {key:"owner",label:"${_('Owner')}",sortable:true},
163 {key:"owner",label:"${_('Owner')}",sortable:true},
164 {key:"rss",label:"",sortable:false},
164 {key:"rss",label:"",sortable:false},
165 {key:"atom",label:"",sortable:false},
165 {key:"atom",label:"",sortable:false},
166 ];
166 ];
167
167
168 var myDataSource = new YAHOO.util.DataSource(YUD.get("repos_list"));
168 var myDataSource = new YAHOO.util.DataSource(YUD.get("repos_list"));
169
169
170 myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
170 myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
171
171
172 myDataSource.responseSchema = {
172 myDataSource.responseSchema = {
173 fields: [
173 fields: [
174 {key:"menu"},
174 {key:"menu"},
175 {key:"name"},
175 {key:"name"},
176 {key:"desc"},
176 {key:"desc"},
177 {key:"last_change"},
177 {key:"last_change"},
178 {key:"tip"},
178 {key:"tip"},
179 {key:"owner"},
179 {key:"owner"},
180 {key:"rss"},
180 {key:"rss"},
181 {key:"atom"},
181 {key:"atom"},
182 ]
182 ]
183 };
183 };
184
184
185 var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,
185 var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,
186 {
186 {
187 sortedBy:{key:"name",dir:"asc"},
187 sortedBy:{key:"name",dir:"asc"},
188 MSG_SORTASC:"${_('Click to sort ascending')}",
188 MSG_SORTASC:"${_('Click to sort ascending')}",
189 MSG_SORTDESC:"${_('Click to sort descending')}",
189 MSG_SORTDESC:"${_('Click to sort descending')}",
190 MSG_EMPTY:"${_('No records found.')}",
190 MSG_EMPTY:"${_('No records found.')}",
191 MSG_ERROR:"${_('Data error.')}",
191 MSG_ERROR:"${_('Data error.')}",
192 MSG_LOADING:"${_('Loading...')}",
192 MSG_LOADING:"${_('Loading...')}",
193 }
193 }
194 );
194 );
195 myDataTable.subscribe('postRenderEvent',function(oArgs) {
195 myDataTable.subscribe('postRenderEvent',function(oArgs) {
196 tooltip_activate();
196 tooltip_activate();
197 quick_repo_menu();
197 quick_repo_menu();
198 q_filter('q_filter',YUQ('div.table tr td a.repo_name'),func);
198 q_filter('q_filter',YUQ('div.table tr td a.repo_name'),func);
199 });
199 });
200
200
201 </script>
201 </script>
@@ -1,148 +1,148 b''
1 from rhodecode.tests import *
1 from rhodecode.tests import *
2 from rhodecode.model.db import ChangesetComment, Notification, User, \
2 from rhodecode.model.db import ChangesetComment, Notification, User, \
3 UserNotification
3 UserNotification
4
4
5
5
6 class TestChangeSetCommentsController(TestController):
6 class TestChangeSetCommentsController(TestController):
7
7
8 def setUp(self):
8 def setUp(self):
9 for x in ChangesetComment.query().all():
9 for x in ChangesetComment.query().all():
10 self.Session.delete(x)
10 self.Session.delete(x)
11 self.Session.commit()
11 self.Session.commit()
12
12
13 for x in Notification.query().all():
13 for x in Notification.query().all():
14 self.Session.delete(x)
14 self.Session.delete(x)
15 self.Session.commit()
15 self.Session.commit()
16
16
17 def tearDown(self):
17 def tearDown(self):
18 for x in ChangesetComment.query().all():
18 for x in ChangesetComment.query().all():
19 self.Session.delete(x)
19 self.Session.delete(x)
20 self.Session.commit()
20 self.Session.commit()
21
21
22 for x in Notification.query().all():
22 for x in Notification.query().all():
23 self.Session.delete(x)
23 self.Session.delete(x)
24 self.Session.commit()
24 self.Session.commit()
25
25
26 def test_create(self):
26 def test_create(self):
27 self.log_user()
27 self.log_user()
28 rev = '27cd5cce30c96924232dffcd24178a07ffeb5dfc'
28 rev = '27cd5cce30c96924232dffcd24178a07ffeb5dfc'
29 text = u'CommentOnRevision'
29 text = u'CommentOnRevision'
30
30
31 params = {'text': text}
31 params = {'text': text}
32 response = self.app.post(url(controller='changeset', action='comment',
32 response = self.app.post(url(controller='changeset', action='comment',
33 repo_name=HG_REPO, revision=rev),
33 repo_name=HG_REPO, revision=rev),
34 params=params)
34 params=params)
35 # Test response...
35 # Test response...
36 self.assertEqual(response.status, '302 Found')
36 self.assertEqual(response.status, '302 Found')
37 response.follow()
37 response.follow()
38
38
39 response = self.app.get(url(controller='changeset', action='index',
39 response = self.app.get(url(controller='changeset', action='index',
40 repo_name=HG_REPO, revision=rev))
40 repo_name=HG_REPO, revision=rev))
41 # test DB
41 # test DB
42 self.assertEqual(ChangesetComment.query().count(), 1)
42 self.assertEqual(ChangesetComment.query().count(), 1)
43 self.assertTrue('''<div class="comments-number">%s '''
43 self.assertTrue('''<div class="comments-number">%s '''
44 '''comment(s) (0 inline)</div>''' % 1 in response.body)
44 '''comment(s) (0 inline)</div>''' % 1 in response.body)
45
45
46 self.assertEqual(Notification.query().count(), 1)
46 self.assertEqual(Notification.query().count(), 1)
47 self.assertEqual(ChangesetComment.query().count(), 1)
47 self.assertEqual(ChangesetComment.query().count(), 1)
48
48
49 notification = Notification.query().all()[0]
49 notification = Notification.query().all()[0]
50
50
51 ID = ChangesetComment.query().first().comment_id
51 ID = ChangesetComment.query().first().comment_id
52 self.assertEqual(notification.type_,
52 self.assertEqual(notification.type_,
53 Notification.TYPE_CHANGESET_COMMENT)
53 Notification.TYPE_CHANGESET_COMMENT)
54 sbj = (u'/vcs_test_hg/changeset/'
54 sbj = (u'/vcs_test_hg/changeset/'
55 '27cd5cce30c96924232dffcd24178a07ffeb5dfc#comment-%s' % ID)
55 '27cd5cce30c96924232dffcd24178a07ffeb5dfc#comment-%s' % ID)
56 print "%s vs %s" % (sbj, notification.subject)
56 print "%s vs %s" % (sbj, notification.subject)
57 self.assertTrue(sbj in notification.subject)
57 self.assertTrue(sbj in notification.subject)
58
58
59 def test_create_inline(self):
59 def test_create_inline(self):
60 self.log_user()
60 self.log_user()
61 rev = '27cd5cce30c96924232dffcd24178a07ffeb5dfc'
61 rev = '27cd5cce30c96924232dffcd24178a07ffeb5dfc'
62 text = u'CommentOnRevision'
62 text = u'CommentOnRevision'
63 f_path = 'vcs/web/simplevcs/views/repository.py'
63 f_path = 'vcs/web/simplevcs/views/repository.py'
64 line = 'n1'
64 line = 'n1'
65
65
66 params = {'text': text, 'f_path': f_path, 'line': line}
66 params = {'text': text, 'f_path': f_path, 'line': line}
67 response = self.app.post(url(controller='changeset', action='comment',
67 response = self.app.post(url(controller='changeset', action='comment',
68 repo_name=HG_REPO, revision=rev),
68 repo_name=HG_REPO, revision=rev),
69 params=params)
69 params=params)
70 # Test response...
70 # Test response...
71 self.assertEqual(response.status, '302 Found')
71 self.assertEqual(response.status, '302 Found')
72 response.follow()
72 response.follow()
73
73
74 response = self.app.get(url(controller='changeset', action='index',
74 response = self.app.get(url(controller='changeset', action='index',
75 repo_name=HG_REPO, revision=rev))
75 repo_name=HG_REPO, revision=rev))
76 #test DB
76 #test DB
77 self.assertEqual(ChangesetComment.query().count(), 1)
77 self.assertEqual(ChangesetComment.query().count(), 1)
78 self.assertTrue('''<div class="comments-number">0 comment(s)'''
78 self.assertTrue('''<div class="comments-number">0 comment(s)'''
79 ''' (%s inline)</div>''' % 1 in response.body)
79 ''' (%s inline)</div>''' % 1 in response.body)
80 self.assertTrue('''<div class="inline-comment-placeholder-line"'''
80 self.assertTrue('''<div class="inline-comment-placeholder-line"'''
81 ''' line="n1" target_id="vcswebsimplevcsviews'''
81 ''' line="n1" target_id="vcswebsimplevcsviews'''
82 '''repositorypy">''' in response.body)
82 '''repositorypy">''' in response.body)
83
83
84 self.assertEqual(Notification.query().count(), 1)
84 self.assertEqual(Notification.query().count(), 1)
85 self.assertEqual(ChangesetComment.query().count(), 1)
85 self.assertEqual(ChangesetComment.query().count(), 1)
86
86
87 notification = Notification.query().all()[0]
87 notification = Notification.query().all()[0]
88 ID = ChangesetComment.query().first().comment_id
88 ID = ChangesetComment.query().first().comment_id
89 self.assertEqual(notification.type_,
89 self.assertEqual(notification.type_,
90 Notification.TYPE_CHANGESET_COMMENT)
90 Notification.TYPE_CHANGESET_COMMENT)
91 sbj = (u'/vcs_test_hg/changeset/'
91 sbj = (u'/vcs_test_hg/changeset/'
92 '27cd5cce30c96924232dffcd24178a07ffeb5dfc#comment-%s' % ID)
92 '27cd5cce30c96924232dffcd24178a07ffeb5dfc#comment-%s' % ID)
93 print "%s vs %s" % (sbj, notification.subject)
93 print "%s vs %s" % (sbj, notification.subject)
94 self.assertTrue(sbj in notification.subject)
94 self.assertTrue(sbj in notification.subject)
95
95
96 def test_create_with_mention(self):
96 def test_create_with_mention(self):
97 self.log_user()
97 self.log_user()
98
98
99 rev = '27cd5cce30c96924232dffcd24178a07ffeb5dfc'
99 rev = '27cd5cce30c96924232dffcd24178a07ffeb5dfc'
100 text = u'@test_regular check CommentOnRevision'
100 text = u'@test_regular check CommentOnRevision'
101
101
102 params = {'text':text}
102 params = {'text':text}
103 response = self.app.post(url(controller='changeset', action='comment',
103 response = self.app.post(url(controller='changeset', action='comment',
104 repo_name=HG_REPO, revision=rev),
104 repo_name=HG_REPO, revision=rev),
105 params=params)
105 params=params)
106 # Test response...
106 # Test response...
107 self.assertEqual(response.status, '302 Found')
107 self.assertEqual(response.status, '302 Found')
108 response.follow()
108 response.follow()
109
109
110 response = self.app.get(url(controller='changeset', action='index',
110 response = self.app.get(url(controller='changeset', action='index',
111 repo_name=HG_REPO, revision=rev))
111 repo_name=HG_REPO, revision=rev))
112 # test DB
112 # test DB
113 self.assertEqual(ChangesetComment.query().count(), 1)
113 self.assertEqual(ChangesetComment.query().count(), 1)
114 self.assertTrue('''<div class="comments-number">%s '''
114 self.assertTrue('''<div class="comments-number">%s '''
115 '''comment(s) (0 inline)</div>''' % 1 in response.body)
115 '''comment(s) (0 inline)</div>''' % 1 in response.body)
116
116
117 self.assertEqual(Notification.query().count(), 2)
117 self.assertEqual(Notification.query().count(), 2)
118 users = [x.user.username for x in UserNotification.query().all()]
118 users = [x.user.username for x in UserNotification.query().all()]
119
119
120 # test_regular get's notification by @mention
120 # test_regular get's notification by @mention
121 self.assertEqual(sorted(users), [u'test_admin', u'test_regular'])
121 self.assertEqual(sorted(users), [u'test_admin', u'test_regular'])
122
122
123 def test_delete(self):
123 def test_delete(self):
124 self.log_user()
124 self.log_user()
125 rev = '27cd5cce30c96924232dffcd24178a07ffeb5dfc'
125 rev = '27cd5cce30c96924232dffcd24178a07ffeb5dfc'
126 text = u'CommentOnRevision'
126 text = u'CommentOnRevision'
127
127
128 params = {'text': text}
128 params = {'text': text}
129 response = self.app.post(url(controller='changeset', action='comment',
129 response = self.app.post(url(controller='changeset', action='comment',
130 repo_name=HG_REPO, revision=rev),
130 repo_name=HG_REPO, revision=rev),
131 params=params)
131 params=params)
132
132
133 comments = ChangesetComment.query().all()
133 comments = ChangesetComment.query().all()
134 self.assertEqual(len(comments), 1)
134 self.assertEqual(len(comments), 1)
135 comment_id = comments[0].comment_id
135 comment_id = comments[0].comment_id
136
136
137 self.app.delete(url(controller='changeset',
137 self.app.delete(url(controller='changeset',
138 action='delete_comment',
138 action='delete_comment',
139 repo_name=HG_REPO,
139 repo_name=HG_REPO,
140 comment_id=comment_id))
140 comment_id=comment_id))
141
141
142 comments = ChangesetComment.query().all()
142 comments = ChangesetComment.query().all()
143 self.assertEqual(len(comments), 0)
143 self.assertEqual(len(comments), 0)
144
144
145 response = self.app.get(url(controller='changeset', action='index',
145 response = self.app.get(url(controller='changeset', action='index',
146 repo_name=HG_REPO, revision=rev))
146 repo_name=HG_REPO, revision=rev))
147 self.assertTrue('''<div class="comments-number">0 comment(s)'''
147 self.assertTrue('''<div class="comments-number">0 comment(s)'''
148 ''' (0 inline)</div>''' in response.body)
148 ''' (0 inline)</div>''' in response.body)
General Comments 0
You need to be logged in to leave comments. Login now