##// END OF EJS Templates
added author to main page tooltip
marcink -
r1459:6691d409 beta
parent child Browse files
Show More
@@ -1,370 +1,370 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.scm
4 4 ~~~~~~~~~~~~~~~~~~~
5 5
6 6 Scm model for RhodeCode
7 7
8 8 :created_on: Apr 9, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25 import os
26 26 import time
27 27 import traceback
28 28 import logging
29 29
30 30 from sqlalchemy.exc import DatabaseError
31 31
32 32 from vcs import get_backend
33 33 from vcs.exceptions import RepositoryError
34 34 from vcs.utils.lazy import LazyProperty
35 35 from vcs.nodes import FileNode
36 36
37 37 from rhodecode import BACKENDS
38 38 from rhodecode.lib import helpers as h
39 39 from rhodecode.lib import safe_str
40 40 from rhodecode.lib.auth import HasRepoPermissionAny
41 41 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
42 42 action_logger
43 43 from rhodecode.model import BaseModel
44 44 from rhodecode.model.user import UserModel
45 45 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
46 46 UserFollowing, UserLog
47 47
48 48 log = logging.getLogger(__name__)
49 49
50 50
51 51 class UserTemp(object):
52 52 def __init__(self, user_id):
53 53 self.user_id = user_id
54 54
55 55 def __repr__(self):
56 56 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
57 57
58 58
59 59 class RepoTemp(object):
60 60 def __init__(self, repo_id):
61 61 self.repo_id = repo_id
62 62
63 63 def __repr__(self):
64 64 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
65 65
66 66 class CachedRepoList(object):
67 67
68 68 def __init__(self, db_repo_list, repos_path, order_by=None):
69 69 self.db_repo_list = db_repo_list
70 70 self.repos_path = repos_path
71 71 self.order_by = order_by
72 72 self.reversed = (order_by or '').startswith('-')
73 73
74 74 def __len__(self):
75 75 return len(self.db_repo_list)
76 76
77 77 def __repr__(self):
78 78 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
79 79
80 80 def __iter__(self):
81 81 for dbr in self.db_repo_list:
82 82
83 83 scmr = dbr.scm_instance_cached
84 84
85 85 # check permission at this level
86 86 if not HasRepoPermissionAny('repository.read', 'repository.write',
87 87 'repository.admin')(dbr.repo_name,
88 88 'get repo check'):
89 89 continue
90 90
91 91 if scmr is None:
92 92 log.error('%s this repository is present in database but it '
93 93 'cannot be created as an scm instance',
94 94 dbr.repo_name)
95 95 continue
96 96
97 97 last_change = scmr.last_change
98 98 tip = h.get_changeset_safe(scmr, 'tip')
99 99
100 100 tmp_d = {}
101 101 tmp_d['name'] = dbr.repo_name
102 102 tmp_d['name_sort'] = tmp_d['name'].lower()
103 103 tmp_d['description'] = dbr.description
104 104 tmp_d['description_sort'] = tmp_d['description']
105 105 tmp_d['last_change'] = last_change
106 106 tmp_d['last_change_sort'] = time.mktime(last_change \
107 107 .timetuple())
108 108 tmp_d['tip'] = tip.raw_id
109 109 tmp_d['tip_sort'] = tip.revision
110 110 tmp_d['rev'] = tip.revision
111 111 tmp_d['contact'] = dbr.user.full_contact
112 112 tmp_d['contact_sort'] = tmp_d['contact']
113 113 tmp_d['owner_sort'] = tmp_d['contact']
114 114 tmp_d['repo_archives'] = list(scmr._get_archives())
115 115 tmp_d['last_msg'] = tip.message
116 tmp_d['repo'] = scmr
116 tmp_d['author'] = tip.author
117 117 tmp_d['dbrepo'] = dbr.get_dict()
118 118 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork \
119 119 else {}
120 120 yield tmp_d
121 121
122 122 class ScmModel(BaseModel):
123 123 """Generic Scm Model
124 124 """
125 125
126 126 @LazyProperty
127 127 def repos_path(self):
128 128 """Get's the repositories root path from database
129 129 """
130 130
131 131 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
132 132
133 133 return q.ui_value
134 134
135 135 def repo_scan(self, repos_path=None):
136 136 """Listing of repositories in given path. This path should not be a
137 137 repository itself. Return a dictionary of repository objects
138 138
139 139 :param repos_path: path to directory containing repositories
140 140 """
141 141
142 142 log.info('scanning for repositories in %s', repos_path)
143 143
144 144 if repos_path is None:
145 145 repos_path = self.repos_path
146 146
147 147 baseui = make_ui('db')
148 148 repos_list = {}
149 149
150 150 for name, path in get_filesystem_repos(repos_path, recursive=True):
151 151 try:
152 152 if name in repos_list:
153 153 raise RepositoryError('Duplicate repository name %s '
154 154 'found in %s' % (name, path))
155 155 else:
156 156
157 157 klass = get_backend(path[0])
158 158
159 159 if path[0] == 'hg' and path[0] in BACKENDS.keys():
160 160
161 161 # for mercurial we need to have an str path
162 162 repos_list[name] = klass(safe_str(path[1]),
163 163 baseui=baseui)
164 164
165 165 if path[0] == 'git' and path[0] in BACKENDS.keys():
166 166 repos_list[name] = klass(path[1])
167 167 except OSError:
168 168 continue
169 169
170 170 return repos_list
171 171
172 172 def get_repos(self, all_repos=None, sort_key=None):
173 173 """
174 174 Get all repos from db and for each repo create it's
175 175 backend instance and fill that backed with information from database
176 176
177 177 :param all_repos: list of repository names as strings
178 178 give specific repositories list, good for filtering
179 179 """
180 180 if all_repos is None:
181 181 all_repos = self.sa.query(Repository)\
182 182 .filter(Repository.group_id == None)\
183 183 .order_by(Repository.repo_name).all()
184 184
185 185 repo_iter = CachedRepoList(all_repos, repos_path=self.repos_path,
186 186 order_by=sort_key)
187 187
188 188 return repo_iter
189 189
190 190 def mark_for_invalidation(self, repo_name):
191 191 """Puts cache invalidation task into db for
192 192 further global cache invalidation
193 193
194 194 :param repo_name: this repo that should invalidation take place
195 195 """
196 196
197 197 log.debug('marking %s for invalidation', repo_name)
198 198 cache = self.sa.query(CacheInvalidation)\
199 199 .filter(CacheInvalidation.cache_key == repo_name).scalar()
200 200
201 201 if cache:
202 202 # mark this cache as inactive
203 203 cache.cache_active = False
204 204 else:
205 205 log.debug('cache key not found in invalidation db -> creating one')
206 206 cache = CacheInvalidation(repo_name)
207 207
208 208 try:
209 209 self.sa.add(cache)
210 210 self.sa.commit()
211 211 except (DatabaseError,):
212 212 log.error(traceback.format_exc())
213 213 self.sa.rollback()
214 214
215 215 def toggle_following_repo(self, follow_repo_id, user_id):
216 216
217 217 f = self.sa.query(UserFollowing)\
218 218 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
219 219 .filter(UserFollowing.user_id == user_id).scalar()
220 220
221 221 if f is not None:
222 222
223 223 try:
224 224 self.sa.delete(f)
225 225 self.sa.commit()
226 226 action_logger(UserTemp(user_id),
227 227 'stopped_following_repo',
228 228 RepoTemp(follow_repo_id))
229 229 return
230 230 except:
231 231 log.error(traceback.format_exc())
232 232 self.sa.rollback()
233 233 raise
234 234
235 235 try:
236 236 f = UserFollowing()
237 237 f.user_id = user_id
238 238 f.follows_repo_id = follow_repo_id
239 239 self.sa.add(f)
240 240 self.sa.commit()
241 241 action_logger(UserTemp(user_id),
242 242 'started_following_repo',
243 243 RepoTemp(follow_repo_id))
244 244 except:
245 245 log.error(traceback.format_exc())
246 246 self.sa.rollback()
247 247 raise
248 248
249 249 def toggle_following_user(self, follow_user_id, user_id):
250 250 f = self.sa.query(UserFollowing)\
251 251 .filter(UserFollowing.follows_user_id == follow_user_id)\
252 252 .filter(UserFollowing.user_id == user_id).scalar()
253 253
254 254 if f is not None:
255 255 try:
256 256 self.sa.delete(f)
257 257 self.sa.commit()
258 258 return
259 259 except:
260 260 log.error(traceback.format_exc())
261 261 self.sa.rollback()
262 262 raise
263 263
264 264 try:
265 265 f = UserFollowing()
266 266 f.user_id = user_id
267 267 f.follows_user_id = follow_user_id
268 268 self.sa.add(f)
269 269 self.sa.commit()
270 270 except:
271 271 log.error(traceback.format_exc())
272 272 self.sa.rollback()
273 273 raise
274 274
275 275 def is_following_repo(self, repo_name, user_id, cache=False):
276 276 r = self.sa.query(Repository)\
277 277 .filter(Repository.repo_name == repo_name).scalar()
278 278
279 279 f = self.sa.query(UserFollowing)\
280 280 .filter(UserFollowing.follows_repository == r)\
281 281 .filter(UserFollowing.user_id == user_id).scalar()
282 282
283 283 return f is not None
284 284
285 285 def is_following_user(self, username, user_id, cache=False):
286 286 u = UserModel(self.sa).get_by_username(username)
287 287
288 288 f = self.sa.query(UserFollowing)\
289 289 .filter(UserFollowing.follows_user == u)\
290 290 .filter(UserFollowing.user_id == user_id).scalar()
291 291
292 292 return f is not None
293 293
294 294 def get_followers(self, repo_id):
295 295 if not isinstance(repo_id, int):
296 296 repo_id = getattr(Repository.by_repo_name(repo_id), 'repo_id')
297 297
298 298 return self.sa.query(UserFollowing)\
299 299 .filter(UserFollowing.follows_repo_id == repo_id).count()
300 300
301 301 def get_forks(self, repo_id):
302 302 if not isinstance(repo_id, int):
303 303 repo_id = getattr(Repository.by_repo_name(repo_id), 'repo_id')
304 304
305 305 return self.sa.query(Repository)\
306 306 .filter(Repository.fork_id == repo_id).count()
307 307
308 308 def pull_changes(self, repo_name, username):
309 309 dbrepo = Repository.by_repo_name(repo_name)
310 310 repo = dbrepo.scm_instance
311 311 try:
312 312 extras = {'ip': '',
313 313 'username': username,
314 314 'action': 'push_remote',
315 315 'repository': repo_name}
316 316
317 317 #inject ui extra param to log this action via push logger
318 318 for k, v in extras.items():
319 319 repo._repo.ui.setconfig('rhodecode_extras', k, v)
320 320
321 321 repo.pull(dbrepo.clone_uri)
322 322 self.mark_for_invalidation(repo_name)
323 323 except:
324 324 log.error(traceback.format_exc())
325 325 raise
326 326
327 327
328 328 def commit_change(self, repo, repo_name, cs, user, author, message, content,
329 329 f_path):
330 330
331 331 if repo.alias == 'hg':
332 332 from vcs.backends.hg import MercurialInMemoryChangeset as IMC
333 333 elif repo.alias == 'git':
334 334 from vcs.backends.git import GitInMemoryChangeset as IMC
335 335
336 336 # decoding here will force that we have proper encoded values
337 337 # in any other case this will throw exceptions and deny commit
338 338 content = safe_str(content)
339 339 message = safe_str(message)
340 340 path = safe_str(f_path)
341 341 author = safe_str(author)
342 342 m = IMC(repo)
343 343 m.change(FileNode(path, content))
344 344 tip = m.commit(message=message,
345 345 author=author,
346 346 parents=[cs], branch=cs.branch)
347 347
348 348 new_cs = tip.short_id
349 349 action = 'push_local:%s' % new_cs
350 350
351 351 action_logger(user, action, repo_name)
352 352
353 353 self.mark_for_invalidation(repo_name)
354 354
355 355
356 356 def get_unread_journal(self):
357 357 return self.sa.query(UserLog).count()
358 358
359 359 def _should_invalidate(self, repo_name):
360 360 """Looks up database for invalidation signals for this repo_name
361 361
362 362 :param repo_name:
363 363 """
364 364
365 365 ret = self.sa.query(CacheInvalidation)\
366 366 .filter(CacheInvalidation.cache_key == repo_name)\
367 367 .filter(CacheInvalidation.cache_active == False)\
368 368 .scalar()
369 369
370 370 return ret
General Comments 0
You need to be logged in to leave comments. Login now