##// END OF EJS Templates
Move changes for git implementation
marcink -
r633:fcf599cd beta
parent child Browse files
Show More
@@ -1,533 +1,537 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # Utilities for RhodeCode
3 # Utilities for RhodeCode
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 # This program is free software; you can redistribute it and/or
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; version 2
7 # as published by the Free Software Foundation; version 2
8 # of the License or (at your opinion) any later version of the license.
8 # of the License or (at your opinion) any later version of the license.
9 #
9 #
10 # This program is distributed in the hope that it will be useful,
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
13 # GNU General Public License for more details.
14 #
14 #
15 # You should have received a copy of the GNU General Public License
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # MA 02110-1301, USA.
18 # MA 02110-1301, USA.
19 """
20 Created on April 18, 2010
21 Utilities for RhodeCode
22 @author: marcink
23 """
24
19 from UserDict import DictMixin
25 from UserDict import DictMixin
20 from mercurial import ui, config, hg
26 from mercurial import ui, config, hg
21 from mercurial.error import RepoError
27 from mercurial.error import RepoError
22 from rhodecode.model import meta
28 from rhodecode.model import meta
23 from rhodecode.model.caching_query import FromCache
29 from rhodecode.model.caching_query import FromCache
24 from rhodecode.model.db import Repository, User, RhodeCodeUi, RhodeCodeSettings, \
30 from rhodecode.model.db import Repository, User, RhodeCodeUi, RhodeCodeSettings, \
25 UserLog
31 UserLog
26 from rhodecode.model.repo import RepoModel
32 from rhodecode.model.repo import RepoModel
27 from rhodecode.model.user import UserModel
33 from rhodecode.model.user import UserModel
28 from vcs.backends.base import BaseChangeset
34 from vcs.backends.base import BaseChangeset
29 from vcs.backends.git import GitRepository
35 from vcs.backends.git import GitRepository
30 from vcs.backends.hg import MercurialRepository
36 from vcs.backends.hg import MercurialRepository
31 from vcs.utils.lazy import LazyProperty
37 from vcs.utils.lazy import LazyProperty
32 import datetime
38 import datetime
33 import logging
39 import logging
34 import os
40 import os
35
41
36 """
37 Created on April 18, 2010
38 Utilities for RhodeCode
39 @author: marcink
40 """
41
42 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
43
43
44
44
45 def get_repo_slug(request):
45 def get_repo_slug(request):
46 return request.environ['pylons.routes_dict'].get('repo_name')
46 return request.environ['pylons.routes_dict'].get('repo_name')
47
47
48 def is_mercurial(environ):
48 def is_mercurial(environ):
49 """
49 """
50 Returns True if request's target is mercurial server - header
50 Returns True if request's target is mercurial server - header
51 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
51 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
52 """
52 """
53 http_accept = environ.get('HTTP_ACCEPT')
53 http_accept = environ.get('HTTP_ACCEPT')
54 if http_accept and http_accept.startswith('application/mercurial'):
54 if http_accept and http_accept.startswith('application/mercurial'):
55 return True
55 return True
56 return False
56 return False
57
57
58 def is_git(environ):
58 def is_git(environ):
59 """
59 """
60 Returns True if request's target is git server. ``HTTP_USER_AGENT`` would
60 Returns True if request's target is git server. ``HTTP_USER_AGENT`` would
61 then have git client version given.
61 then have git client version given.
62
62
63 :param environ:
63 :param environ:
64 """
64 """
65 http_user_agent = environ.get('HTTP_USER_AGENT')
65 http_user_agent = environ.get('HTTP_USER_AGENT')
66 if http_user_agent.startswith('git'):
66 if http_user_agent.startswith('git'):
67 return True
67 return True
68 return False
68 return False
69
69
70 def action_logger(user, action, repo, ipaddr, sa=None):
70 def action_logger(user, action, repo, ipaddr, sa=None):
71 """
71 """
72 Action logger for various action made by users
72 Action logger for various action made by users
73 """
73 """
74
74
75 if not sa:
75 if not sa:
76 sa = meta.Session()
76 sa = meta.Session()
77
77
78 try:
78 try:
79 if hasattr(user, 'user_id'):
79 if hasattr(user, 'user_id'):
80 user_id = user.user_id
80 user_id = user.user_id
81 elif isinstance(user, basestring):
81 elif isinstance(user, basestring):
82 user_id = UserModel(sa).get_by_username(user, cache=False).user_id
82 user_id = UserModel(sa).get_by_username(user, cache=False).user_id
83 else:
83 else:
84 raise Exception('You have to provide user object or username')
84 raise Exception('You have to provide user object or username')
85
85
86 repo_name = repo.lstrip('/')
86 repo_name = repo.lstrip('/')
87 user_log = UserLog()
87 user_log = UserLog()
88 user_log.user_id = user_id
88 user_log.user_id = user_id
89 user_log.action = action
89 user_log.action = action
90 user_log.repository_name = repo_name
90 user_log.repository_name = repo_name
91 user_log.repository = RepoModel(sa).get(repo_name, cache=False)
91 user_log.repository = RepoModel(sa).get(repo_name, cache=False)
92 user_log.action_date = datetime.datetime.now()
92 user_log.action_date = datetime.datetime.now()
93 user_log.user_ip = ipaddr
93 user_log.user_ip = ipaddr
94 sa.add(user_log)
94 sa.add(user_log)
95 sa.commit()
95 sa.commit()
96
96
97 log.info('Adding user %s, action %s on %s',
97 log.info('Adding user %s, action %s on %s',
98 user.username, action, repo)
98 user.username, action, repo)
99 except Exception, e:
99 except Exception, e:
100 sa.rollback()
100 sa.rollback()
101 log.error('could not log user action:%s', str(e))
101 log.error('could not log user action:%s', str(e))
102
102
103 def get_repos(path, recursive=False, initial=False):
103 def get_repos(path, recursive=False, initial=False):
104 """
104 """
105 Scans given path for repos and return (name,(type,path)) tuple
105 Scans given path for repos and return (name,(type,path)) tuple
106 :param prefix:
106 :param prefix:
107 :param path:
107 :param path:
108 :param recursive:
108 :param recursive:
109 :param initial:
109 :param initial:
110 """
110 """
111 from vcs.utils.helpers import get_scm
111 from vcs.utils.helpers import get_scm
112 from vcs.exceptions import VCSError
112 from vcs.exceptions import VCSError
113 scm = get_scm(path)
113
114 if scm:
114 try:
115 scm = get_scm(path)
116 except:
117 pass
118 else:
115 raise Exception('The given path %s should not be a repository got %s',
119 raise Exception('The given path %s should not be a repository got %s',
116 path, scm)
120 path, scm)
117
121
118 for dirpath in os.listdir(path):
122 for dirpath in os.listdir(path):
119 try:
123 try:
120 yield dirpath, get_scm(os.path.join(path, dirpath))
124 yield dirpath, get_scm(os.path.join(path, dirpath))
121 except VCSError:
125 except VCSError:
122 pass
126 pass
123
127
124 if __name__ == '__main__':
128 if __name__ == '__main__':
125 get_repos('', '/home/marcink/workspace-python')
129 get_repos('', '/home/marcink/workspace-python')
126
130
127
131
128 def check_repo_fast(repo_name, base_path):
132 def check_repo_fast(repo_name, base_path):
129 if os.path.isdir(os.path.join(base_path, repo_name)):return False
133 if os.path.isdir(os.path.join(base_path, repo_name)):return False
130 return True
134 return True
131
135
132 def check_repo(repo_name, base_path, verify=True):
136 def check_repo(repo_name, base_path, verify=True):
133
137
134 repo_path = os.path.join(base_path, repo_name)
138 repo_path = os.path.join(base_path, repo_name)
135
139
136 try:
140 try:
137 if not check_repo_fast(repo_name, base_path):
141 if not check_repo_fast(repo_name, base_path):
138 return False
142 return False
139 r = hg.repository(ui.ui(), repo_path)
143 r = hg.repository(ui.ui(), repo_path)
140 if verify:
144 if verify:
141 hg.verify(r)
145 hg.verify(r)
142 #here we hnow that repo exists it was verified
146 #here we hnow that repo exists it was verified
143 log.info('%s repo is already created', repo_name)
147 log.info('%s repo is already created', repo_name)
144 return False
148 return False
145 except RepoError:
149 except RepoError:
146 #it means that there is no valid repo there...
150 #it means that there is no valid repo there...
147 log.info('%s repo is free for creation', repo_name)
151 log.info('%s repo is free for creation', repo_name)
148 return True
152 return True
149
153
150 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
154 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
151 while True:
155 while True:
152 ok = raw_input(prompt)
156 ok = raw_input(prompt)
153 if ok in ('y', 'ye', 'yes'): return True
157 if ok in ('y', 'ye', 'yes'): return True
154 if ok in ('n', 'no', 'nop', 'nope'): return False
158 if ok in ('n', 'no', 'nop', 'nope'): return False
155 retries = retries - 1
159 retries = retries - 1
156 if retries < 0: raise IOError
160 if retries < 0: raise IOError
157 print complaint
161 print complaint
158
162
159 def get_hg_ui_cached():
163 def get_hg_ui_cached():
160 try:
164 try:
161 sa = meta.Session
165 sa = meta.Session
162 ret = sa.query(RhodeCodeUi)\
166 ret = sa.query(RhodeCodeUi)\
163 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
167 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
164 .all()
168 .all()
165 except:
169 except:
166 pass
170 pass
167 finally:
171 finally:
168 meta.Session.remove()
172 meta.Session.remove()
169 return ret
173 return ret
170
174
171
175
172 def get_hg_settings():
176 def get_hg_settings():
173 try:
177 try:
174 sa = meta.Session()
178 sa = meta.Session()
175 ret = sa.query(RhodeCodeSettings)\
179 ret = sa.query(RhodeCodeSettings)\
176 .options(FromCache("sql_cache_short", "get_hg_settings"))\
180 .options(FromCache("sql_cache_short", "get_hg_settings"))\
177 .all()
181 .all()
178 except:
182 except:
179 pass
183 pass
180 finally:
184 finally:
181 meta.Session.remove()
185 meta.Session.remove()
182
186
183 if not ret:
187 if not ret:
184 raise Exception('Could not get application settings !')
188 raise Exception('Could not get application settings !')
185 settings = {}
189 settings = {}
186 for each in ret:
190 for each in ret:
187 settings['rhodecode_' + each.app_settings_name] = each.app_settings_value
191 settings['rhodecode_' + each.app_settings_name] = each.app_settings_value
188
192
189 return settings
193 return settings
190
194
191 def get_hg_ui_settings():
195 def get_hg_ui_settings():
192 try:
196 try:
193 sa = meta.Session()
197 sa = meta.Session()
194 ret = sa.query(RhodeCodeUi).all()
198 ret = sa.query(RhodeCodeUi).all()
195 except:
199 except:
196 pass
200 pass
197 finally:
201 finally:
198 meta.Session.remove()
202 meta.Session.remove()
199
203
200 if not ret:
204 if not ret:
201 raise Exception('Could not get application ui settings !')
205 raise Exception('Could not get application ui settings !')
202 settings = {}
206 settings = {}
203 for each in ret:
207 for each in ret:
204 k = each.ui_key
208 k = each.ui_key
205 v = each.ui_value
209 v = each.ui_value
206 if k == '/':
210 if k == '/':
207 k = 'root_path'
211 k = 'root_path'
208
212
209 if k.find('.') != -1:
213 if k.find('.') != -1:
210 k = k.replace('.', '_')
214 k = k.replace('.', '_')
211
215
212 if each.ui_section == 'hooks':
216 if each.ui_section == 'hooks':
213 v = each.ui_active
217 v = each.ui_active
214
218
215 settings[each.ui_section + '_' + k] = v
219 settings[each.ui_section + '_' + k] = v
216
220
217 return settings
221 return settings
218
222
219 #propagated from mercurial documentation
223 #propagated from mercurial documentation
220 ui_sections = ['alias', 'auth',
224 ui_sections = ['alias', 'auth',
221 'decode/encode', 'defaults',
225 'decode/encode', 'defaults',
222 'diff', 'email',
226 'diff', 'email',
223 'extensions', 'format',
227 'extensions', 'format',
224 'merge-patterns', 'merge-tools',
228 'merge-patterns', 'merge-tools',
225 'hooks', 'http_proxy',
229 'hooks', 'http_proxy',
226 'smtp', 'patch',
230 'smtp', 'patch',
227 'paths', 'profiling',
231 'paths', 'profiling',
228 'server', 'trusted',
232 'server', 'trusted',
229 'ui', 'web', ]
233 'ui', 'web', ]
230
234
231 def make_ui(read_from='file', path=None, checkpaths=True):
235 def make_ui(read_from='file', path=None, checkpaths=True):
232 """
236 """
233 A function that will read python rc files or database
237 A function that will read python rc files or database
234 and make an mercurial ui object from read options
238 and make an mercurial ui object from read options
235
239
236 :param path: path to mercurial config file
240 :param path: path to mercurial config file
237 :param checkpaths: check the path
241 :param checkpaths: check the path
238 :param read_from: read from 'file' or 'db'
242 :param read_from: read from 'file' or 'db'
239 """
243 """
240
244
241 baseui = ui.ui()
245 baseui = ui.ui()
242
246
243 if read_from == 'file':
247 if read_from == 'file':
244 if not os.path.isfile(path):
248 if not os.path.isfile(path):
245 log.warning('Unable to read config file %s' % path)
249 log.warning('Unable to read config file %s' % path)
246 return False
250 return False
247 log.debug('reading hgrc from %s', path)
251 log.debug('reading hgrc from %s', path)
248 cfg = config.config()
252 cfg = config.config()
249 cfg.read(path)
253 cfg.read(path)
250 for section in ui_sections:
254 for section in ui_sections:
251 for k, v in cfg.items(section):
255 for k, v in cfg.items(section):
252 baseui.setconfig(section, k, v)
256 baseui.setconfig(section, k, v)
253 log.debug('settings ui from file[%s]%s:%s', section, k, v)
257 log.debug('settings ui from file[%s]%s:%s', section, k, v)
254
258
255 elif read_from == 'db':
259 elif read_from == 'db':
256 hg_ui = get_hg_ui_cached()
260 hg_ui = get_hg_ui_cached()
257 for ui_ in hg_ui:
261 for ui_ in hg_ui:
258 if ui_.ui_active:
262 if ui_.ui_active:
259 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section, ui_.ui_key, ui_.ui_value)
263 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section, ui_.ui_key, ui_.ui_value)
260 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
264 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
261
265
262
266
263 return baseui
267 return baseui
264
268
265
269
266 def set_rhodecode_config(config):
270 def set_rhodecode_config(config):
267 hgsettings = get_hg_settings()
271 hgsettings = get_hg_settings()
268
272
269 for k, v in hgsettings.items():
273 for k, v in hgsettings.items():
270 config[k] = v
274 config[k] = v
271
275
272 def invalidate_cache(name, *args):
276 def invalidate_cache(name, *args):
273 """Invalidates given name cache"""
277 """Invalidates given name cache"""
274
278
275 from beaker.cache import region_invalidate
279 from beaker.cache import region_invalidate
276 log.info('INVALIDATING CACHE FOR %s', name)
280 log.info('INVALIDATING CACHE FOR %s', name)
277
281
278 """propagate our arguments to make sure invalidation works. First
282 """propagate our arguments to make sure invalidation works. First
279 argument has to be the name of cached func name give to cache decorator
283 argument has to be the name of cached func name give to cache decorator
280 without that the invalidation would not work"""
284 without that the invalidation would not work"""
281 tmp = [name]
285 tmp = [name]
282 tmp.extend(args)
286 tmp.extend(args)
283 args = tuple(tmp)
287 args = tuple(tmp)
284
288
285 if name == 'cached_repo_list':
289 if name == 'cached_repo_list':
286 from rhodecode.model.hg import _get_repos_cached
290 from rhodecode.model.hg import _get_repos_cached
287 region_invalidate(_get_repos_cached, None, *args)
291 region_invalidate(_get_repos_cached, None, *args)
288
292
289 if name == 'full_changelog':
293 if name == 'full_changelog':
290 from rhodecode.model.hg import _full_changelog_cached
294 from rhodecode.model.hg import _full_changelog_cached
291 region_invalidate(_full_changelog_cached, None, *args)
295 region_invalidate(_full_changelog_cached, None, *args)
292
296
293 class EmptyChangeset(BaseChangeset):
297 class EmptyChangeset(BaseChangeset):
294 """
298 """
295 An dummy empty changeset.
299 An dummy empty changeset.
296 """
300 """
297
301
298 revision = -1
302 revision = -1
299 message = ''
303 message = ''
300 author = ''
304 author = ''
301 date = ''
305 date = ''
302 @LazyProperty
306 @LazyProperty
303 def raw_id(self):
307 def raw_id(self):
304 """
308 """
305 Returns raw string identifying this changeset, useful for web
309 Returns raw string identifying this changeset, useful for web
306 representation.
310 representation.
307 """
311 """
308 return '0' * 40
312 return '0' * 40
309
313
310 @LazyProperty
314 @LazyProperty
311 def short_id(self):
315 def short_id(self):
312 return self.raw_id[:12]
316 return self.raw_id[:12]
313
317
314 def get_file_changeset(self, path):
318 def get_file_changeset(self, path):
315 return self
319 return self
316
320
317 def get_file_content(self, path):
321 def get_file_content(self, path):
318 return u''
322 return u''
319
323
320 def get_file_size(self, path):
324 def get_file_size(self, path):
321 return 0
325 return 0
322
326
323 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
327 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
324 """
328 """
325 maps all found repositories into db
329 maps all found repositories into db
326 """
330 """
327
331
328 sa = meta.Session()
332 sa = meta.Session()
329 rm = RepoModel(sa)
333 rm = RepoModel(sa)
330 user = sa.query(User).filter(User.admin == True).first()
334 user = sa.query(User).filter(User.admin == True).first()
331
335
332 for name, repo in initial_repo_list.items():
336 for name, repo in initial_repo_list.items():
333 if not rm.get(name, cache=False):
337 if not rm.get(name, cache=False):
334 log.info('repository %s not found creating default', name)
338 log.info('repository %s not found creating default', name)
335
339
336 if isinstance(repo, MercurialRepository):
340 if isinstance(repo, MercurialRepository):
337 repo_type = 'hg'
341 repo_type = 'hg'
338 if isinstance(repo, GitRepository):
342 if isinstance(repo, GitRepository):
339 repo_type = 'git'
343 repo_type = 'git'
340
344
341 form_data = {
345 form_data = {
342 'repo_name':name,
346 'repo_name':name,
343 'repo_type':repo_type,
347 'repo_type':repo_type,
344 'description':repo.description if repo.description != 'unknown' else \
348 'description':repo.description if repo.description != 'unknown' else \
345 'auto description for %s' % name,
349 'auto description for %s' % name,
346 'private':False
350 'private':False
347 }
351 }
348 rm.create(form_data, user, just_db=True)
352 rm.create(form_data, user, just_db=True)
349
353
350
354
351 if remove_obsolete:
355 if remove_obsolete:
352 #remove from database those repositories that are not in the filesystem
356 #remove from database those repositories that are not in the filesystem
353 for repo in sa.query(Repository).all():
357 for repo in sa.query(Repository).all():
354 if repo.repo_name not in initial_repo_list.keys():
358 if repo.repo_name not in initial_repo_list.keys():
355 sa.delete(repo)
359 sa.delete(repo)
356 sa.commit()
360 sa.commit()
357
361
358
362
359 meta.Session.remove()
363 meta.Session.remove()
360
364
361
365
362 class OrderedDict(dict, DictMixin):
366 class OrderedDict(dict, DictMixin):
363
367
364 def __init__(self, *args, **kwds):
368 def __init__(self, *args, **kwds):
365 if len(args) > 1:
369 if len(args) > 1:
366 raise TypeError('expected at most 1 arguments, got %d' % len(args))
370 raise TypeError('expected at most 1 arguments, got %d' % len(args))
367 try:
371 try:
368 self.__end
372 self.__end
369 except AttributeError:
373 except AttributeError:
370 self.clear()
374 self.clear()
371 self.update(*args, **kwds)
375 self.update(*args, **kwds)
372
376
373 def clear(self):
377 def clear(self):
374 self.__end = end = []
378 self.__end = end = []
375 end += [None, end, end] # sentinel node for doubly linked list
379 end += [None, end, end] # sentinel node for doubly linked list
376 self.__map = {} # key --> [key, prev, next]
380 self.__map = {} # key --> [key, prev, next]
377 dict.clear(self)
381 dict.clear(self)
378
382
379 def __setitem__(self, key, value):
383 def __setitem__(self, key, value):
380 if key not in self:
384 if key not in self:
381 end = self.__end
385 end = self.__end
382 curr = end[1]
386 curr = end[1]
383 curr[2] = end[1] = self.__map[key] = [key, curr, end]
387 curr[2] = end[1] = self.__map[key] = [key, curr, end]
384 dict.__setitem__(self, key, value)
388 dict.__setitem__(self, key, value)
385
389
386 def __delitem__(self, key):
390 def __delitem__(self, key):
387 dict.__delitem__(self, key)
391 dict.__delitem__(self, key)
388 key, prev, next = self.__map.pop(key)
392 key, prev, next = self.__map.pop(key)
389 prev[2] = next
393 prev[2] = next
390 next[1] = prev
394 next[1] = prev
391
395
392 def __iter__(self):
396 def __iter__(self):
393 end = self.__end
397 end = self.__end
394 curr = end[2]
398 curr = end[2]
395 while curr is not end:
399 while curr is not end:
396 yield curr[0]
400 yield curr[0]
397 curr = curr[2]
401 curr = curr[2]
398
402
399 def __reversed__(self):
403 def __reversed__(self):
400 end = self.__end
404 end = self.__end
401 curr = end[1]
405 curr = end[1]
402 while curr is not end:
406 while curr is not end:
403 yield curr[0]
407 yield curr[0]
404 curr = curr[1]
408 curr = curr[1]
405
409
406 def popitem(self, last=True):
410 def popitem(self, last=True):
407 if not self:
411 if not self:
408 raise KeyError('dictionary is empty')
412 raise KeyError('dictionary is empty')
409 if last:
413 if last:
410 key = reversed(self).next()
414 key = reversed(self).next()
411 else:
415 else:
412 key = iter(self).next()
416 key = iter(self).next()
413 value = self.pop(key)
417 value = self.pop(key)
414 return key, value
418 return key, value
415
419
416 def __reduce__(self):
420 def __reduce__(self):
417 items = [[k, self[k]] for k in self]
421 items = [[k, self[k]] for k in self]
418 tmp = self.__map, self.__end
422 tmp = self.__map, self.__end
419 del self.__map, self.__end
423 del self.__map, self.__end
420 inst_dict = vars(self).copy()
424 inst_dict = vars(self).copy()
421 self.__map, self.__end = tmp
425 self.__map, self.__end = tmp
422 if inst_dict:
426 if inst_dict:
423 return (self.__class__, (items,), inst_dict)
427 return (self.__class__, (items,), inst_dict)
424 return self.__class__, (items,)
428 return self.__class__, (items,)
425
429
426 def keys(self):
430 def keys(self):
427 return list(self)
431 return list(self)
428
432
429 setdefault = DictMixin.setdefault
433 setdefault = DictMixin.setdefault
430 update = DictMixin.update
434 update = DictMixin.update
431 pop = DictMixin.pop
435 pop = DictMixin.pop
432 values = DictMixin.values
436 values = DictMixin.values
433 items = DictMixin.items
437 items = DictMixin.items
434 iterkeys = DictMixin.iterkeys
438 iterkeys = DictMixin.iterkeys
435 itervalues = DictMixin.itervalues
439 itervalues = DictMixin.itervalues
436 iteritems = DictMixin.iteritems
440 iteritems = DictMixin.iteritems
437
441
438 def __repr__(self):
442 def __repr__(self):
439 if not self:
443 if not self:
440 return '%s()' % (self.__class__.__name__,)
444 return '%s()' % (self.__class__.__name__,)
441 return '%s(%r)' % (self.__class__.__name__, self.items())
445 return '%s(%r)' % (self.__class__.__name__, self.items())
442
446
443 def copy(self):
447 def copy(self):
444 return self.__class__(self)
448 return self.__class__(self)
445
449
446 @classmethod
450 @classmethod
447 def fromkeys(cls, iterable, value=None):
451 def fromkeys(cls, iterable, value=None):
448 d = cls()
452 d = cls()
449 for key in iterable:
453 for key in iterable:
450 d[key] = value
454 d[key] = value
451 return d
455 return d
452
456
453 def __eq__(self, other):
457 def __eq__(self, other):
454 if isinstance(other, OrderedDict):
458 if isinstance(other, OrderedDict):
455 return len(self) == len(other) and self.items() == other.items()
459 return len(self) == len(other) and self.items() == other.items()
456 return dict.__eq__(self, other)
460 return dict.__eq__(self, other)
457
461
458 def __ne__(self, other):
462 def __ne__(self, other):
459 return not self == other
463 return not self == other
460
464
461
465
462 #===============================================================================
466 #===============================================================================
463 # TEST FUNCTIONS AND CREATORS
467 # TEST FUNCTIONS AND CREATORS
464 #===============================================================================
468 #===============================================================================
465 def create_test_index(repo_location, full_index):
469 def create_test_index(repo_location, full_index):
466 """Makes default test index
470 """Makes default test index
467 :param repo_location:
471 :param repo_location:
468 :param full_index:
472 :param full_index:
469 """
473 """
470 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
474 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
471 from rhodecode.lib.pidlock import DaemonLock, LockHeld
475 from rhodecode.lib.pidlock import DaemonLock, LockHeld
472 from rhodecode.lib.indexers import IDX_LOCATION
476 from rhodecode.lib.indexers import IDX_LOCATION
473 import shutil
477 import shutil
474
478
475 if os.path.exists(IDX_LOCATION):
479 if os.path.exists(IDX_LOCATION):
476 shutil.rmtree(IDX_LOCATION)
480 shutil.rmtree(IDX_LOCATION)
477
481
478 try:
482 try:
479 l = DaemonLock()
483 l = DaemonLock()
480 WhooshIndexingDaemon(repo_location=repo_location)\
484 WhooshIndexingDaemon(repo_location=repo_location)\
481 .run(full_index=full_index)
485 .run(full_index=full_index)
482 l.release()
486 l.release()
483 except LockHeld:
487 except LockHeld:
484 pass
488 pass
485
489
486 def create_test_env(repos_test_path, config):
490 def create_test_env(repos_test_path, config):
487 """Makes a fresh database and
491 """Makes a fresh database and
488 install test repository into tmp dir
492 install test repository into tmp dir
489 """
493 """
490 from rhodecode.lib.db_manage import DbManage
494 from rhodecode.lib.db_manage import DbManage
491 import tarfile
495 import tarfile
492 import shutil
496 import shutil
493 from os.path import dirname as dn, join as jn, abspath
497 from os.path import dirname as dn, join as jn, abspath
494
498
495 log = logging.getLogger('TestEnvCreator')
499 log = logging.getLogger('TestEnvCreator')
496 # create logger
500 # create logger
497 log.setLevel(logging.DEBUG)
501 log.setLevel(logging.DEBUG)
498 log.propagate = True
502 log.propagate = True
499 # create console handler and set level to debug
503 # create console handler and set level to debug
500 ch = logging.StreamHandler()
504 ch = logging.StreamHandler()
501 ch.setLevel(logging.DEBUG)
505 ch.setLevel(logging.DEBUG)
502
506
503 # create formatter
507 # create formatter
504 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
508 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
505
509
506 # add formatter to ch
510 # add formatter to ch
507 ch.setFormatter(formatter)
511 ch.setFormatter(formatter)
508
512
509 # add ch to logger
513 # add ch to logger
510 log.addHandler(ch)
514 log.addHandler(ch)
511
515
512 #PART ONE create db
516 #PART ONE create db
513 dbname = config['sqlalchemy.db1.url'].split('/')[-1]
517 dbname = config['sqlalchemy.db1.url'].split('/')[-1]
514 log.debug('making test db %s', dbname)
518 log.debug('making test db %s', dbname)
515
519
516 dbmanage = DbManage(log_sql=True, dbname=dbname, root=config['here'],
520 dbmanage = DbManage(log_sql=True, dbname=dbname, root=config['here'],
517 tests=True)
521 tests=True)
518 dbmanage.create_tables(override=True)
522 dbmanage.create_tables(override=True)
519 dbmanage.config_prompt(repos_test_path)
523 dbmanage.config_prompt(repos_test_path)
520 dbmanage.create_default_user()
524 dbmanage.create_default_user()
521 dbmanage.admin_prompt()
525 dbmanage.admin_prompt()
522 dbmanage.create_permissions()
526 dbmanage.create_permissions()
523 dbmanage.populate_default_permissions()
527 dbmanage.populate_default_permissions()
524
528
525 #PART TWO make test repo
529 #PART TWO make test repo
526 log.debug('making test vcs repo')
530 log.debug('making test vcs repo')
527 if os.path.isdir('/tmp/vcs_test'):
531 if os.path.isdir('/tmp/vcs_test'):
528 shutil.rmtree('/tmp/vcs_test')
532 shutil.rmtree('/tmp/vcs_test')
529
533
530 cur_dir = dn(dn(abspath(__file__)))
534 cur_dir = dn(dn(abspath(__file__)))
531 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test.tar.gz"))
535 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test.tar.gz"))
532 tar.extractall('/tmp')
536 tar.extractall('/tmp')
533 tar.close()
537 tar.close()
@@ -1,181 +1,183 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # Model for RhodeCode
3 # Model for RhodeCode
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 9, 2010
21 Created on April 9, 2010
22 Model for RhodeCode
22 Model for RhodeCode
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from beaker.cache import cache_region
25 from beaker.cache import cache_region
26 from mercurial import ui
26 from mercurial import ui
27 from rhodecode.lib import helpers as h
27 from rhodecode.lib import helpers as h
28 from rhodecode.lib.utils import invalidate_cache
28 from rhodecode.lib.utils import invalidate_cache
29 from rhodecode.lib.auth import HasRepoPermissionAny
29 from rhodecode.lib.auth import HasRepoPermissionAny
30 from rhodecode.model import meta
30 from rhodecode.model import meta
31 from rhodecode.model.db import Repository, User
31 from rhodecode.model.db import Repository, User
32 from sqlalchemy.orm import joinedload
32 from sqlalchemy.orm import joinedload
33 from vcs.exceptions import RepositoryError, VCSError
33 from vcs.exceptions import RepositoryError, VCSError
34 import logging
34 import logging
35 import sys
35 import sys
36 import time
37
36 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
37
39
38 try:
40 try:
39 from vcs.backends.hg import MercurialRepository
41 from vcs.backends.hg import MercurialRepository
40 from vcs.backends.git import GitRepository
42 from vcs.backends.git import GitRepository
41 except ImportError:
43 except ImportError:
42 sys.stderr.write('You have to import vcs module')
44 sys.stderr.write('You have to import vcs module')
43 raise Exception('Unable to import vcs')
45 raise Exception('Unable to import vcs')
44
46
45 def _get_repos_cached_initial(app_globals, initial):
47 def _get_repos_cached_initial(app_globals, initial):
46 """return cached dict with repos
48 """return cached dict with repos
47 """
49 """
48 g = app_globals
50 g = app_globals
49 return HgModel().repo_scan(g.paths[0][1], g.baseui, initial)
51 return HgModel().repo_scan(g.paths[0][1], g.baseui, initial)
50
52
51 @cache_region('long_term', 'cached_repo_list')
53 @cache_region('long_term', 'cached_repo_list')
52 def _get_repos_cached():
54 def _get_repos_cached():
53 """return cached dict with repos
55 """return cached dict with repos
54 """
56 """
55 log.info('getting all repositories list')
57 log.info('getting all repositories list')
56 from pylons import app_globals as g
58 from pylons import app_globals as g
57 return HgModel().repo_scan(g.paths[0][1], g.baseui)
59 return HgModel().repo_scan(g.paths[0][1], g.baseui)
58
60
59 @cache_region('super_short_term', 'cached_repos_switcher_list')
61 @cache_region('super_short_term', 'cached_repos_switcher_list')
60 def _get_repos_switcher_cached(cached_repo_list):
62 def _get_repos_switcher_cached(cached_repo_list):
61 repos_lst = []
63 repos_lst = []
62 for repo in [x for x in cached_repo_list.values()]:
64 for repo in [x for x in cached_repo_list.values()]:
63 if HasRepoPermissionAny('repository.write', 'repository.read',
65 if HasRepoPermissionAny('repository.write', 'repository.read',
64 'repository.admin')(repo.name, 'main page check'):
66 'repository.admin')(repo.name, 'main page check'):
65 repos_lst.append((repo.name, repo.dbrepo.private,))
67 repos_lst.append((repo.name, repo.dbrepo.private,))
66
68
67 return sorted(repos_lst, key=lambda k:k[0].lower())
69 return sorted(repos_lst, key=lambda k:k[0].lower())
68
70
69 @cache_region('long_term', 'full_changelog')
71 @cache_region('long_term', 'full_changelog')
70 def _full_changelog_cached(repo_name):
72 def _full_changelog_cached(repo_name):
71 log.info('getting full changelog for %s', repo_name)
73 log.info('getting full changelog for %s', repo_name)
72 return list(reversed(list(HgModel().get_repo(repo_name))))
74 return list(reversed(list(HgModel().get_repo(repo_name))))
73
75
74 class HgModel(object):
76 class HgModel(object):
75 """
77 """
76 Mercurial Model
78 Mercurial Model
77 """
79 """
78
80
79 def __init__(self, sa=None):
81 def __init__(self, sa=None):
80 if not sa:
82 if not sa:
81 self.sa = meta.Session()
83 self.sa = meta.Session()
82 else:
84 else:
83 self.sa = sa
85 self.sa = sa
84
86
85 def repo_scan(self, repos_path, baseui, initial=False):
87 def repo_scan(self, repos_path, baseui, initial=False):
86 """
88 """
87 Listing of repositories in given path. This path should not be a
89 Listing of repositories in given path. This path should not be a
88 repository itself. Return a dictionary of repository objects
90 repository itself. Return a dictionary of repository objects
89
91
90 :param repos_path: path to directory containing repositories
92 :param repos_path: path to directory containing repositories
91 :param baseui
93 :param baseui
92 :param initial: initial scann
94 :param initial: initial scann
93 """
95 """
94 log.info('scanning for repositories in %s', repos_path)
96 log.info('scanning for repositories in %s', repos_path)
95
97
96 if not isinstance(baseui, ui.ui):
98 if not isinstance(baseui, ui.ui):
97 baseui = ui.ui()
99 baseui = ui.ui()
98
100
99 from rhodecode.lib.utils import get_repos
101 from rhodecode.lib.utils import get_repos
100 repos = get_repos(repos_path)
102 repos = get_repos(repos_path)
101
103
102
104
103 repos_list = {}
105 repos_list = {}
104 for name, path in repos:
106 for name, path in repos:
105 try:
107 try:
106 #name = name.split('/')[-1]
108 #name = name.split('/')[-1]
107 if repos_list.has_key(name):
109 if repos_list.has_key(name):
108 raise RepositoryError('Duplicate repository name %s found in'
110 raise RepositoryError('Duplicate repository name %s found in'
109 ' %s' % (name, path))
111 ' %s' % (name, path))
110 else:
112 else:
111 if path[0] == 'hg':
113 if path[0] == 'hg':
112 repos_list[name] = MercurialRepository(path[1], baseui=baseui)
114 repos_list[name] = MercurialRepository(path[1], baseui=baseui)
113 repos_list[name].name = name
115 repos_list[name].name = name
114
116
115 if path[0] == 'git':
117 if path[0] == 'git':
116 repos_list[name] = GitRepository(path[1])
118 repos_list[name] = GitRepository(path[1])
117 repos_list[name].name = name
119 repos_list[name].name = name
118
120
119 dbrepo = None
121 dbrepo = None
120 if not initial:
122 if not initial:
121 #for initial scann on application first run we don't
123 #for initial scann on application first run we don't
122 #have db repos yet.
124 #have db repos yet.
123 dbrepo = self.sa.query(Repository)\
125 dbrepo = self.sa.query(Repository)\
124 .options(joinedload(Repository.fork))\
126 .options(joinedload(Repository.fork))\
125 .filter(Repository.repo_name == name)\
127 .filter(Repository.repo_name == name)\
126 .scalar()
128 .scalar()
127
129
128 if dbrepo:
130 if dbrepo:
129 log.info('Adding db instance to cached list')
131 log.info('Adding db instance to cached list')
130 repos_list[name].dbrepo = dbrepo
132 repos_list[name].dbrepo = dbrepo
131 repos_list[name].description = dbrepo.description
133 repos_list[name].description = dbrepo.description
132 if dbrepo.user:
134 if dbrepo.user:
133 repos_list[name].contact = dbrepo.user.full_contact
135 repos_list[name].contact = dbrepo.user.full_contact
134 else:
136 else:
135 repos_list[name].contact = self.sa.query(User)\
137 repos_list[name].contact = self.sa.query(User)\
136 .filter(User.admin == True).first().full_contact
138 .filter(User.admin == True).first().full_contact
137 except OSError:
139 except OSError:
138 continue
140 continue
139
141
140 return repos_list
142 return repos_list
141
143
142 def get_repos(self):
144 def get_repos(self):
143 for name, repo in _get_repos_cached().items():
145 for name, repo in _get_repos_cached().items():
144
146
145 if isinstance(repo, MercurialRepository) and repo._get_hidden():
147 if isinstance(repo, MercurialRepository) and repo._get_hidden():
146 #skip hidden web repository
148 #skip hidden web repository
147 continue
149 continue
148
150
149 last_change = repo.last_change
151 last_change = repo.last_change
150 tip = h.get_changeset_safe(repo, 'tip')
152 tip = h.get_changeset_safe(repo, 'tip')
151
153
152 tmp_d = {}
154 tmp_d = {}
153 tmp_d['name'] = repo.name
155 tmp_d['name'] = repo.name
154 tmp_d['name_sort'] = tmp_d['name'].lower()
156 tmp_d['name_sort'] = tmp_d['name'].lower()
155 tmp_d['description'] = repo.description
157 tmp_d['description'] = repo.description
156 tmp_d['description_sort'] = tmp_d['description']
158 tmp_d['description_sort'] = tmp_d['description']
157 tmp_d['last_change'] = last_change
159 tmp_d['last_change'] = last_change
158 tmp_d['last_change_sort'] = last_change[1] - last_change[0]
160 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
159 tmp_d['tip'] = tip.short_id
161 tmp_d['tip'] = tip.short_id
160 tmp_d['tip_sort'] = tip.revision
162 tmp_d['tip_sort'] = tip.revision
161 tmp_d['rev'] = tip.revision
163 tmp_d['rev'] = tip.revision
162 tmp_d['contact'] = repo.contact
164 tmp_d['contact'] = repo.contact
163 tmp_d['contact_sort'] = tmp_d['contact']
165 tmp_d['contact_sort'] = tmp_d['contact']
164 tmp_d['repo_archives'] = list(repo._get_archives())
166 tmp_d['repo_archives'] = list(repo._get_archives())
165 tmp_d['last_msg'] = tip.message
167 tmp_d['last_msg'] = tip.message
166 tmp_d['repo'] = repo
168 tmp_d['repo'] = repo
167 yield tmp_d
169 yield tmp_d
168
170
169 def get_repo(self, repo_name):
171 def get_repo(self, repo_name):
170 try:
172 try:
171 repo = _get_repos_cached()[repo_name]
173 repo = _get_repos_cached()[repo_name]
172 return repo
174 return repo
173 except KeyError:
175 except KeyError:
174 #i we're here and we got key errors let's try to invalidate the
176 #i we're here and we got key errors let's try to invalidate the
175 #cahce and try again
177 #cahce and try again
176 invalidate_cache('cached_repo_list')
178 invalidate_cache('cached_repo_list')
177 repo = _get_repos_cached()[repo_name]
179 repo = _get_repos_cached()[repo_name]
178 return repo
180 return repo
179
181
180
182
181
183
@@ -1,30 +1,30 b''
1 % if c.repo_branches:
1 % if c.repo_branches:
2 <table class="table_disp">
2 <table class="table_disp">
3 <tr>
3 <tr>
4 <th class="left">${_('date')}</th>
4 <th class="left">${_('date')}</th>
5 <th class="left">${_('revision')}</th>
5 <th class="left">${_('revision')}</th>
6 <th class="left">${_('name')}</th>
6 <th class="left">${_('name')}</th>
7 <th class="left">${_('links')}</th>
7 <th class="left">${_('links')}</th>
8 </tr>
8 </tr>
9 %for cnt,branch in enumerate(c.repo_branches.items()):
9 %for cnt,branch in enumerate(c.repo_branches.items()):
10 <tr class="parity${cnt%2}">
10 <tr class="parity${cnt%2}">
11 <td>${h.age(branch[1]._ctx.date())}</td>
11 <td>${h.age(branch[1].date)}</td>
12 <td>r${branch[1].revision}:${branch[1].short_id}</td>
12 <td>r${branch[1].revision}:${branch[1].short_id}</td>
13 <td>
13 <td>
14 <span class="logtags">
14 <span class="logtags">
15 <span class="branchtag">${h.link_to(branch[0],
15 <span class="branchtag">${h.link_to(branch[0],
16 h.url('changeset_home',repo_name=c.repo_name,revision=branch[1].short_id))}</span>
16 h.url('changeset_home',repo_name=c.repo_name,revision=branch[1].short_id))}</span>
17 </span>
17 </span>
18 </td>
18 </td>
19 <td class="nowrap">
19 <td class="nowrap">
20 ${h.link_to(_('changeset'),h.url('changeset_home',repo_name=c.repo_name,revision=branch[1].short_id))}
20 ${h.link_to(_('changeset'),h.url('changeset_home',repo_name=c.repo_name,revision=branch[1].short_id))}
21 |
21 |
22 ${h.link_to(_('files'),h.url('files_home',repo_name=c.repo_name,revision=branch[1].short_id))}
22 ${h.link_to(_('files'),h.url('files_home',repo_name=c.repo_name,revision=branch[1].short_id))}
23 </td>
23 </td>
24 </tr>
24 </tr>
25 %endfor
25 %endfor
26 </table>
26 </table>
27 %else:
27 %else:
28 ${_('There are no branches yet')}
28 ${_('There are no branches yet')}
29 %endif
29 %endif
30
30
@@ -1,8 +1,8 b''
1 # HG changeset patch
1 # c.scm_type changeset patch
2 # User ${c.changeset.author|n}
2 # User ${c.changeset.author|n}
3 # Date ${"%d %d" % c.changeset._ctx.date()}
3 # Date ${"%d %d" % c.changeset.date}
4 # Node ID ${c.changeset.raw_id}
4 # Node ID ${c.changeset.raw_id}
5 # ${c.parent_tmpl}
5 # ${c.parent_tmpl}
6 ${c.changeset.message}
6 ${c.changeset.message}
7
7
8 ${c.diffs|n} No newline at end of file
8 ${c.diffs|n}
@@ -1,80 +1,80 b''
1 <%def name="file_class(node)">
1 <%def name="file_class(node)">
2 %if node.is_file():
2 %if node.is_file():
3 <%return "browser-file" %>
3 <%return "browser-file" %>
4 %else:
4 %else:
5 <%return "browser-dir"%>
5 <%return "browser-dir"%>
6 %endif
6 %endif
7 </%def>
7 </%def>
8 <div id="body" class="browserblock">
8 <div id="body" class="browserblock">
9 <div class="browser-header">
9 <div class="browser-header">
10 ${h.form(h.url.current())}
10 ${h.form(h.url.current())}
11 <div class="info_box">
11 <div class="info_box">
12 <span >${_('view')}@rev</span>
12 <span >${_('view')}@rev</span>
13 <a href="${c.url_prev}">&laquo;</a>
13 <a href="${c.url_prev}">&laquo;</a>
14 ${h.text('at_rev',value=c.rev_nr,size=3)}
14 ${h.text('at_rev',value=c.rev_nr,size=3)}
15 <a href="${c.url_next}">&raquo;</a>
15 <a href="${c.url_next}">&raquo;</a>
16 ${h.submit('view','view')}
16 ${h.submit('view','view')}
17 </div>
17 </div>
18 ${h.end_form()}
18 ${h.end_form()}
19 </div>
19 </div>
20 <div class="browser-body">
20 <div class="browser-body">
21 <table class="code-browser">
21 <table class="code-browser">
22 <thead>
22 <thead>
23 <tr>
23 <tr>
24 <th>${_('Name')}</th>
24 <th>${_('Name')}</th>
25 <th>${_('Size')}</th>
25 <th>${_('Size')}</th>
26 <th>${_('Mimetype')}</th>
26 <th>${_('Mimetype')}</th>
27 <th>${_('Revision')}</th>
27 <th>${_('Revision')}</th>
28 <th>${_('Last modified')}</th>
28 <th>${_('Last modified')}</th>
29 <th>${_('Last commiter')}</th>
29 <th>${_('Last commiter')}</th>
30 </tr>
30 </tr>
31 </thead>
31 </thead>
32
32
33 % if c.files_list.parent:
33 %if c.files_list.parent:
34 <tr class="parity0">
34 <tr class="parity0">
35 <td>
35 <td>
36 ${h.link_to('..',h.url('files_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.files_list.parent.path),class_="browser-dir")}
36 ${h.link_to('..',h.url('files_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.files_list.parent.path),class_="browser-dir")}
37 </td>
37 </td>
38 <td></td>
38 <td></td>
39 <td></td>
39 <td></td>
40 <td></td>
40 <td></td>
41 <td></td>
41 <td></td>
42 <td></td>
42 <td></td>
43 </tr>
43 </tr>
44 %endif
44 %endif
45
45
46 %for cnt,node in enumerate(c.files_list,1):
46 %for cnt,node in enumerate(c.files_list,1):
47 <tr class="parity${cnt%2}">
47 <tr class="parity${cnt%2}">
48 <td>
48 <td>
49 ${h.link_to(node.name,h.url('files_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=node.path),class_=file_class(node))}
49 ${h.link_to(node.name,h.url('files_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=node.path),class_=file_class(node))}
50 </td>
50 </td>
51 <td>
51 <td>
52 %if node.is_file():
52 %if node.is_file():
53 ${h.format_byte_size(node.size,binary=True)}
53 ${h.format_byte_size(node.size,binary=True)}
54 %endif
54 %endif
55 </td>
55 </td>
56 <td>
56 <td>
57 %if node.is_file():
57 %if node.is_file():
58 ${node.mimetype}
58 ${node.mimetype}
59 %endif
59 %endif
60 </td>
60 </td>
61 <td>
61 <td>
62 %if node.is_file():
62 %if node.is_file():
63 ${node.last_changeset.revision}
63 ${node.last_changeset.revision}
64 %endif
64 %endif
65 </td>
65 </td>
66 <td>
66 <td>
67 %if node.is_file():
67 %if node.is_file():
68 ${h.age(node.last_changeset._ctx.date())} - ${node.last_changeset.date}
68 ${h.age(node.last_changeset.date)} - ${node.last_changeset.date}
69 %endif
69 %endif
70 </td>
70 </td>
71 <td>
71 <td>
72 %if node.is_file():
72 %if node.is_file():
73 ${node.last_changeset.author}
73 ${node.last_changeset.author}
74 %endif
74 %endif
75 </td>
75 </td>
76 </tr>
76 </tr>
77 %endfor
77 %endfor
78 </table>
78 </table>
79 </div>
79 </div>
80 </div> No newline at end of file
80 </div>
@@ -1,98 +1,98 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="base/base.html"/>
2 <%inherit file="base/base.html"/>
3 <%def name="title()">
3 <%def name="title()">
4 ${_('Dashboard')} - ${c.rhodecode_name}
4 ${_('Dashboard')} - ${c.rhodecode_name}
5 </%def>
5 </%def>
6 <%def name="breadcrumbs()">
6 <%def name="breadcrumbs()">
7 ${c.rhodecode_name}
7 ${c.rhodecode_name}
8 </%def>
8 </%def>
9 <%def name="page_nav()">
9 <%def name="page_nav()">
10 ${self.menu('home')}
10 ${self.menu('home')}
11 </%def>
11 </%def>
12 <%def name="main()">
12 <%def name="main()">
13 <%def name="get_sort(name)">
13 <%def name="get_sort(name)">
14 <%name_slug = name.lower().replace(' ','_') %>
14 <%name_slug = name.lower().replace(' ','_') %>
15
15
16 %if name_slug == c.sort_slug:
16 %if name_slug == c.sort_slug:
17 %if c.sort_by.startswith('-'):
17 %if c.sort_by.startswith('-'):
18 <a href="?sort=${name_slug}">${name}&uarr;</a>
18 <a href="?sort=${name_slug}">${name}&uarr;</a>
19 %else:
19 %else:
20 <a href="?sort=-${name_slug}">${name}&darr;</a>
20 <a href="?sort=-${name_slug}">${name}&darr;</a>
21 %endif:
21 %endif:
22 %else:
22 %else:
23 <a href="?sort=${name_slug}">${name}</a>
23 <a href="?sort=${name_slug}">${name}</a>
24 %endif
24 %endif
25 </%def>
25 </%def>
26
26
27 <div class="box">
27 <div class="box">
28 <!-- box / title -->
28 <!-- box / title -->
29 <div class="title">
29 <div class="title">
30 <h5>${_('Dashboard')}</h5>
30 <h5>${_('Dashboard')}</h5>
31 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
31 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
32 <ul class="links">
32 <ul class="links">
33 <li>
33 <li>
34 <span>${h.link_to(_('ADD NEW REPOSITORY'),h.url('admin_settings_create_repository'))}</span>
34 <span>${h.link_to(_('ADD NEW REPOSITORY'),h.url('admin_settings_create_repository'))}</span>
35 </li>
35 </li>
36 </ul>
36 </ul>
37 %endif
37 %endif
38 </div>
38 </div>
39 <!-- end box / title -->
39 <!-- end box / title -->
40 <div class="table">
40 <div class="table">
41 <table>
41 <table>
42 <thead>
42 <thead>
43 <tr>
43 <tr>
44 <th class="left">${get_sort(_('Name'))}</th>
44 <th class="left">${get_sort(_('Name'))}</th>
45 <th class="left">${get_sort(_('Description'))}</th>
45 <th class="left">${get_sort(_('Description'))}</th>
46 <th class="left">${get_sort(_('Last change'))}</th>
46 <th class="left">${get_sort(_('Last change'))}</th>
47 <th class="left">${get_sort(_('Tip'))}</th>
47 <th class="left">${get_sort(_('Tip'))}</th>
48 <th class="left">${get_sort(_('Contact'))}</th>
48 <th class="left">${get_sort(_('Owner'))}</th>
49 <th class="left">${_('RSS')}</th>
49 <th class="left">${_('RSS')}</th>
50 <th class="left">${_('Atom')}</th>
50 <th class="left">${_('Atom')}</th>
51 </tr>
51 </tr>
52 </thead>
52 </thead>
53 <tbody>
53 <tbody>
54 %for cnt,repo in enumerate(c.repos_list):
54 %for cnt,repo in enumerate(c.repos_list):
55 %if h.HasRepoPermissionAny('repository.write','repository.read','repository.admin')(repo['name'],'main page check'):
55 %if h.HasRepoPermissionAny('repository.write','repository.read','repository.admin')(repo['name'],'main page check'):
56 <tr class="parity${cnt%2}">
56 <tr class="parity${cnt%2}">
57 <td>
57 <td>
58 %if repo['repo'].dbrepo.private:
58 %if repo['repo'].dbrepo.private:
59 <img class="icon" alt="${_('private')}" src="/images/icons/lock.png"/>
59 <img class="icon" alt="${_('private')}" src="/images/icons/lock.png"/>
60 %else:
60 %else:
61 <img class="icon" alt="${_('public')}" src="/images/icons/lock_open.png"/>
61 <img class="icon" alt="${_('public')}" src="/images/icons/lock_open.png"/>
62 %endif
62 %endif
63 ${h.link_to(repo['name'],
63 ${h.link_to(repo['name'],
64 h.url('summary_home',repo_name=repo['name']))}
64 h.url('summary_home',repo_name=repo['name']))}
65 %if repo['repo'].dbrepo.fork:
65 %if repo['repo'].dbrepo.fork:
66 <a href="${h.url('summary_home',repo_name=repo['repo'].dbrepo.fork.repo_name)}">
66 <a href="${h.url('summary_home',repo_name=repo['repo'].dbrepo.fork.repo_name)}">
67 <img class="icon" alt="${_('public')}"
67 <img class="icon" alt="${_('public')}"
68 title="${_('Fork of')} ${repo['repo'].dbrepo.fork.repo_name}"
68 title="${_('Fork of')} ${repo['repo'].dbrepo.fork.repo_name}"
69 src="/images/icons/arrow_divide.png"/></a>
69 src="/images/icons/arrow_divide.png"/></a>
70 %endif
70 %endif
71 </td>
71 </td>
72 <td title="${repo['description']}">${h.truncate(repo['description'],60)}</td>
72 <td title="${repo['description']}">${h.truncate(repo['description'],60)}</td>
73 <td>${h.age(repo['last_change'])}</td>
73 <td>${h.age(repo['last_change'])}</td>
74 <td>
74 <td>
75 %if repo['rev']>=0:
75 %if repo['rev']>=0:
76 ${h.link_to('r%s:%s' % (repo['rev'],repo['tip']),
76 ${h.link_to('r%s:%s' % (repo['rev'],repo['tip']),
77 h.url('changeset_home',repo_name=repo['name'],revision=repo['tip']),
77 h.url('changeset_home',repo_name=repo['name'],revision=repo['tip']),
78 class_="tooltip",
78 class_="tooltip",
79 tooltip_title=h.tooltip(repo['last_msg']))}
79 tooltip_title=h.tooltip(repo['last_msg']))}
80 %else:
80 %else:
81 ${_('No changesets yet')}
81 ${_('No changesets yet')}
82 %endif
82 %endif
83 </td>
83 </td>
84 <td title="${repo['contact']}">${h.person(repo['contact'])}</td>
84 <td title="${repo['contact']}">${h.person(repo['contact'])}</td>
85 <td>
85 <td>
86 <a title="${_('Subscribe to %s rss feed')%repo['name']}" class="rss_icon" href="${h.url('rss_feed_home',repo_name=repo['name'])}"></a>
86 <a title="${_('Subscribe to %s rss feed')%repo['name']}" class="rss_icon" href="${h.url('rss_feed_home',repo_name=repo['name'])}"></a>
87 </td>
87 </td>
88 <td>
88 <td>
89 <a title="${_('Subscribe to %s atom feed')%repo['name']}" class="atom_icon" href="${h.url('atom_feed_home',repo_name=repo['name'])}"></a>
89 <a title="${_('Subscribe to %s atom feed')%repo['name']}" class="atom_icon" href="${h.url('atom_feed_home',repo_name=repo['name'])}"></a>
90 </td>
90 </td>
91 </tr>
91 </tr>
92 %endif
92 %endif
93 %endfor
93 %endfor
94 </tbody>
94 </tbody>
95 </table>
95 </table>
96 </div>
96 </div>
97 </div>
97 </div>
98 </%def>
98 </%def>
@@ -1,29 +1,29 b''
1 %if c.repo_tags:
1 %if c.repo_tags:
2 <table>
2 <table>
3 <tr>
3 <tr>
4 <th class="left">${_('date')}</th>
4 <th class="left">${_('date')}</th>
5 <th class="left">${_('revision')}</th>
5 <th class="left">${_('revision')}</th>
6 <th class="left">${_('name')}</th>
6 <th class="left">${_('name')}</th>
7 <th class="left">${_('links')}</th>
7 <th class="left">${_('links')}</th>
8 </tr>
8 </tr>
9 %for cnt,tag in enumerate(c.repo_tags.items()):
9 %for cnt,tag in enumerate(c.repo_tags.items()):
10 <tr class="parity${cnt%2}">
10 <tr class="parity${cnt%2}">
11 <td>${h.age(tag[1]._ctx.date())}</td>
11 <td>${h.age(tag[1].date)}</td>
12 <td>r${tag[1].revision}:${tag[1].short_id}</td>
12 <td>r${tag[1].revision}:${tag[1].short_id}</td>
13 <td>
13 <td>
14 <span class="logtags">
14 <span class="logtags">
15 <span class="tagtag">${h.link_to(tag[0],
15 <span class="tagtag">${h.link_to(tag[0],
16 h.url('changeset_home',repo_name=c.repo_name,revision=tag[1].short_id))}</span>
16 h.url('changeset_home',repo_name=c.repo_name,revision=tag[1].short_id))}</span>
17 </span>
17 </span>
18 </td>
18 </td>
19 <td class="nowrap">
19 <td class="nowrap">
20 ${h.link_to(_('changeset'),h.url('changeset_home',repo_name=c.repo_name,revision=tag[1].short_id))}
20 ${h.link_to(_('changeset'),h.url('changeset_home',repo_name=c.repo_name,revision=tag[1].short_id))}
21 |
21 |
22 ${h.link_to(_('files'),h.url('files_home',repo_name=c.repo_name,revision=tag[1].short_id))}
22 ${h.link_to(_('files'),h.url('files_home',repo_name=c.repo_name,revision=tag[1].short_id))}
23 </td>
23 </td>
24 </tr>
24 </tr>
25 %endfor
25 %endfor
26 </table>
26 </table>
27 %else:
27 %else:
28 ${_('There are no tags yet')}
28 ${_('There are no tags yet')}
29 %endif No newline at end of file
29 %endif
General Comments 0
You need to be logged in to leave comments. Login now