##// END OF EJS Templates
Reimplemented way of caching repos list, hg model now get's repos objects right from cached dict, this way we skipp creating instances of MercurialRepository and gain performance. Some import cleanup
marcink -
r245:a83a1799 default
parent child Browse files
Show More
@@ -1,24 +1,20 b''
1 from pylons import tmpl_context as c, app_globals as g
2 from pylons_app.lib.auth import LoginRequired
3 from pylons_app.lib.base import BaseController, render
4 from pylons_app.model.hg_model import HgModel
1 5 import logging
2 6
3 from pylons import tmpl_context as c, app_globals as g, session, request, config, url
4 from pylons.controllers.util import abort, redirect
5
6 from pylons_app.lib.base import BaseController, render
7 from pylons_app.lib.utils import get_repo_slug
8 from pylons_app.model.hg_model import HgModel
9 from pylons_app.lib.auth import LoginRequired
10 7 log = logging.getLogger(__name__)
11 8
12
13 9 class BranchesController(BaseController):
14 10
15 11 @LoginRequired()
16 12 def __before__(self):
17 13 super(BranchesController, self).__before__()
18 14
19 15 def index(self):
20 16 hg_model = HgModel()
21 17 c.repo_info = hg_model.get_repo(c.repo_name)
22 18 c.repo_branches = c.repo_info.branches
23 19
24 20 return render('branches/branches.html')
@@ -1,75 +1,74 b''
1 1 from mercurial.graphmod import revisions as graph_rev, colored, CHANGESET
2 2 from mercurial.node import short
3 from pylons import request, response, session, tmpl_context as c, url, config, \
4 app_globals as g
5 from pylons.controllers.util import abort, redirect
3 from pylons import request, session, tmpl_context as c
6 4 from pylons_app.lib.auth import LoginRequired
7 from pylons_app.lib.base import BaseController, render, _full_changelog_cached
5 from pylons_app.lib.base import BaseController, render
8 6 from pylons_app.lib.filters import age as _age, person
7 from pylons_app.model.hg_model import _full_changelog_cached
9 8 from simplejson import dumps
10 9 from webhelpers.paginate import Page
11 10 import logging
12 11 log = logging.getLogger(__name__)
13 12
14 13 class ChangelogController(BaseController):
15 14
16 15 @LoginRequired()
17 16 def __before__(self):
18 17 super(ChangelogController, self).__before__()
19 18
20 19 def index(self):
21 20 limit = 100
22 21 default = 20
23 22 if request.params.get('size'):
24 23 try:
25 24 int_size = int(request.params.get('size'))
26 25 except ValueError:
27 26 int_size = default
28 27 int_size = int_size if int_size <= limit else limit
29 28 c.size = int_size
30 29 session['changelog_size'] = c.size
31 30 session.save()
32 31 else:
33 32 c.size = session.get('changelog_size', default)
34 33
35 34 changesets = _full_changelog_cached(c.repo_name)
36 35
37 36 p = int(request.params.get('page', 1))
38 37 c.pagination = Page(changesets, page=p, item_count=len(changesets),
39 38 items_per_page=c.size)
40 39
41 40 #self._graph(c.repo, c.size,p)
42 41
43 42 return render('changelog/changelog.html')
44 43
45 44
46 45 def _graph(self, repo, size, p):
47 46 revcount = size
48 47 if not repo.revisions:return dumps([]), 0
49 48
50 49 max_rev = repo.revisions[-1]
51 50 offset = 1 if p == 1 else ((p - 1) * revcount)
52 51 rev_start = repo.revisions[(-1 * offset)]
53 52 c.bg_height = 120
54 53
55 54 revcount = min(max_rev, revcount)
56 55 rev_end = max(0, rev_start - revcount)
57 56 dag = graph_rev(repo.repo, rev_start, rev_end)
58 57
59 58 c.dag = tree = list(colored(dag))
60 59 canvasheight = (len(tree) + 1) * c.bg_height - 27
61 60 data = []
62 61 for (id, type, ctx, vtx, edges) in tree:
63 62 if type != CHANGESET:
64 63 continue
65 64 node = short(ctx.node())
66 65 age = _age(ctx.date())
67 66 desc = ctx.description()
68 67 user = person(ctx.user())
69 68 branch = ctx.branch()
70 69 branch = branch, repo.repo.branchtags().get(branch) == ctx.node()
71 70 data.append((node, vtx, edges, desc, user, age, branch, ctx.tags()))
72 71
73 72 c.jsdata = dumps(data)
74 73 c.canvasheight = canvasheight
75 74
@@ -1,42 +1,40 b''
1 from pylons import request, response, session, tmpl_context as c, url, config, \
2 app_globals as g
3 from pylons.controllers.util import abort, redirect
1 from pylons import tmpl_context as c
4 2 from pylons_app.lib.auth import LoginRequired
5 3 from pylons_app.lib.base import BaseController, render
6 4 from pylons_app.model.hg_model import HgModel
7 5 from vcs.utils import diffs as differ
8 6 import logging
9 7 from vcs.nodes import FileNode
10 8
11 9
12 10 log = logging.getLogger(__name__)
13 11
14 12 class ChangesetController(BaseController):
15 13
16 14 @LoginRequired()
17 15 def __before__(self):
18 16 super(ChangesetController, self).__before__()
19 17
20 18 def index(self, revision):
21 19 hg_model = HgModel()
22 20 c.changeset = hg_model.get_repo(c.repo_name).get_changeset(revision)
23 21 c.changeset_old = c.changeset.parents[0]
24 22 c.changes = []
25 23
26 24
27 25 for node in c.changeset.added:
28 26 filenode_old = FileNode(node.path, '')
29 27 f_udiff = differ.get_udiff(filenode_old, node)
30 28 diff = differ.DiffProcessor(f_udiff).as_html()
31 29 c.changes.append(('added', node, diff))
32 30
33 31 for node in c.changeset.changed:
34 32 filenode_old = c.changeset_old.get_node(node.path)
35 33 f_udiff = differ.get_udiff(filenode_old, node)
36 34 diff = differ.DiffProcessor(f_udiff).as_html()
37 35 c.changes.append(('changed', node, diff))
38 36
39 37 for node in c.changeset.removed:
40 38 c.changes.append(('removed', node, None))
41 39
42 40 return render('changeset/changeset.html')
@@ -1,61 +1,59 b''
1 1 #!/usr/bin/python
2 2 # -*- coding: utf-8 -*-
3 from pylons import tmpl_context as c, url, response
4 from pylons_app.lib.base import BaseController, render
5 from pylons_app.model.hg_model import _full_changelog_cached
6 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
3 7 import logging
4 from operator import itemgetter
5 from pylons import tmpl_context as c, request, config, url, response
6 from pylons_app.lib.base import BaseController, render, _full_changelog_cached
7 from pylons_app.lib.utils import get_repo_slug
8 from pylons_app.model.hg_model import HgModel
9 from pylons_app.lib.auth import LoginRequired
10 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
11 8 log = logging.getLogger(__name__)
12 9
13 10 class FeedController(BaseController):
14 11
15 12 #secure it or not ?
16 13 def __before__(self):
17 14 super(FeedController, self).__before__()
18 15 #common values for feeds
19 16 self.description = 'Changes on %s repository'
20 17 self.title = "%s feed"
21 18 self.language = 'en-us'
22 19 self.ttl = "5"
23 20 self.feed_nr = 10
24 21
25 22 def atom(self, repo_name):
26 23 """Produce an atom-1.0 feed via feedgenerator module"""
27 24 feed = Atom1Feed(title=self.title % repo_name,
28 25 link=url('summary_home', repo_name=repo_name, qualified=True),
29 26 description=self.description % repo_name,
30 27 language=self.language,
31 28 ttl=self.ttl)
32 29
33 30
34 31 for cnt, cs in enumerate(_full_changelog_cached(repo_name)):
35 32 if cnt > self.feed_nr:
36 33 break
37 34 feed.add_item(title=cs.message,
38 link=url('changeset_home', repo_name=repo_name, revision=cs.raw_id, qualified=True),
39 description=str(cs.date))
35 link=url('changeset_home', repo_name=repo_name,
36 revision=cs.raw_id, qualified=True),
37 description=str(cs.date))
40 38
41 39 response.content_type = feed.mime_type
42 40 return feed.writeString('utf-8')
43 41
44 42
45 43 def rss(self, repo_name):
46 44 """Produce an rss2 feed via feedgenerator module"""
47 45 feed = Rss201rev2Feed(title=self.title % repo_name,
48 46 link=url('summary_home', repo_name=repo_name, qualified=True),
49 47 description=self.description % repo_name,
50 48 language=self.language,
51 49 ttl=self.ttl)
52 50
53 51 for cnt, cs in enumerate(_full_changelog_cached(repo_name)):
54 52 if cnt > self.feed_nr:
55 53 break
56 54 feed.add_item(title=cs.message,
57 55 link=url('changeset_home', repo_name=repo_name, revision=cs.raw_id, qualified=True),
58 56 description=str(cs.date))
59 57
60 58 response.content_type = feed.mime_type
61 59 return feed.writeString('utf-8')
@@ -1,157 +1,154 b''
1 import tempfile
2 from pylons import request, response, session, tmpl_context as c, url, config, \
3 app_globals as g
4 from pylons.controllers.util import abort, redirect
1 from mercurial import archival
2 from pylons import request, response, session, tmpl_context as c, url
5 3 from pylons_app.lib.auth import LoginRequired
6 4 from pylons_app.lib.base import BaseController, render
7 from pylons_app.lib.utils import get_repo_slug
8 5 from pylons_app.model.hg_model import HgModel
9 6 from vcs.exceptions import RepositoryError, ChangesetError
10 7 from vcs.utils import diffs as differ
11 8 import logging
12 from mercurial import archival
9 import tempfile
13 10
14 11
15 12 log = logging.getLogger(__name__)
16 13
17 14 class FilesController(BaseController):
18 15
19 16 @LoginRequired()
20 17 def __before__(self):
21 18 super(FilesController, self).__before__()
22 19
23 20 def index(self, repo_name, revision, f_path):
24 21 hg_model = HgModel()
25 22 c.repo = repo = hg_model.get_repo(c.repo_name)
26 23 revision = request.POST.get('at_rev', None) or revision
27 24
28 25 def get_next_rev(cur):
29 26 max_rev = len(c.repo.revisions) - 1
30 27 r = cur + 1
31 28 if r > max_rev:
32 29 r = max_rev
33 30 return r
34 31
35 32 def get_prev_rev(cur):
36 33 r = cur - 1
37 34 return r
38 35
39 36 c.f_path = f_path
40 37
41 38
42 39 try:
43 40 cur_rev = repo.get_changeset(revision).revision
44 41 prev_rev = repo.get_changeset(get_prev_rev(cur_rev)).raw_id
45 42 next_rev = repo.get_changeset(get_next_rev(cur_rev)).raw_id
46 43
47 44 c.url_prev = url('files_home', repo_name=c.repo_name,
48 45 revision=prev_rev, f_path=f_path)
49 46 c.url_next = url('files_home', repo_name=c.repo_name,
50 47 revision=next_rev, f_path=f_path)
51 48
52 49 c.changeset = repo.get_changeset(revision)
53 50 try:
54 51 c.file_msg = c.changeset.get_file_message(f_path)
55 52 except:
56 53 c.file_msg = None
57 54
58 55 c.cur_rev = c.changeset.raw_id
59 56 c.rev_nr = c.changeset.revision
60 57 c.files_list = c.changeset.get_node(f_path)
61 58 c.file_history = self._get_history(repo, c.files_list, f_path)
62 59
63 60 except (RepositoryError, ChangesetError):
64 61 c.files_list = None
65 62
66 63 return render('files/files.html')
67 64
68 65 def rawfile(self, repo_name, revision, f_path):
69 66 hg_model = HgModel()
70 67 c.repo = hg_model.get_repo(c.repo_name)
71 68 file_node = c.repo.get_changeset(revision).get_node(f_path)
72 69 response.content_type = file_node.mimetype
73 70 response.content_disposition = 'attachment; filename=%s' \
74 71 % f_path.split('/')[-1]
75 72 return file_node.content
76 73
77 74 def annotate(self, repo_name, revision, f_path):
78 75 hg_model = HgModel()
79 76 c.repo = hg_model.get_repo(c.repo_name)
80 77 cs = c.repo.get_changeset(revision)
81 78 c.file = cs.get_node(f_path)
82 79 c.file_msg = cs.get_file_message(f_path)
83 80 c.cur_rev = cs.raw_id
84 81 c.f_path = f_path
85 82 c.annotate = cs.get_file_annotate(f_path)
86 83 return render('files/files_annotate.html')
87 84
88 85 def archivefile(self, repo_name, revision, fileformat):
89 86 archive_specs = {
90 87 '.tar.bz2': ('application/x-tar', 'tbz2'),
91 88 '.tar.gz': ('application/x-tar', 'tgz'),
92 89 '.zip': ('application/zip', 'zip'),
93 90 }
94 91 if not archive_specs.has_key(fileformat):
95 92 return 'Unknown archive type %s' % fileformat
96 93
97 94 def read_in_chunks(file_object, chunk_size=1024 * 40):
98 95 """Lazy function (generator) to read a file piece by piece.
99 96 Default chunk size: 40k."""
100 97 while True:
101 98 data = file_object.read(chunk_size)
102 99 if not data:
103 100 break
104 101 yield data
105 102
106 103 archive = tempfile.TemporaryFile()
107 104 repo = HgModel().get_repo(repo_name).repo
108 105 fname = '%s-%s%s' % (repo_name, revision, fileformat)
109 106 archival.archive(repo, archive, revision, archive_specs[fileformat][1],
110 107 prefix='%s-%s' % (repo_name, revision))
111 108 response.content_type = archive_specs[fileformat][0]
112 109 response.content_disposition = 'attachment; filename=%s' % fname
113 110 archive.seek(0)
114 111 return read_in_chunks(archive)
115 112
116 113 def diff(self, repo_name, f_path):
117 114 hg_model = HgModel()
118 115 diff1 = request.GET.get('diff1')
119 116 diff2 = request.GET.get('diff2')
120 117 c.action = action = request.GET.get('diff')
121 118 c.no_changes = diff1 == diff2
122 119 c.f_path = f_path
123 120 c.repo = hg_model.get_repo(c.repo_name)
124 121 c.changeset_1 = c.repo.get_changeset(diff1)
125 122 c.changeset_2 = c.repo.get_changeset(diff2)
126 123
127 124 c.diff1 = 'r%s:%s' % (c.changeset_1.revision, c.changeset_1._short)
128 125 c.diff2 = 'r%s:%s' % (c.changeset_2.revision, c.changeset_2._short)
129 126 f_udiff = differ.get_udiff(c.changeset_1.get_node(f_path),
130 127 c.changeset_2.get_node(f_path))
131 128
132 129 diff = differ.DiffProcessor(f_udiff)
133 130
134 131 if action == 'download':
135 132 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
136 133 response.content_type = 'text/plain'
137 134 response.content_disposition = 'attachment; filename=%s' \
138 135 % diff_name
139 136 return diff.raw_diff()
140 137
141 138 elif action == 'raw':
142 139 c.cur_diff = '<pre class="raw">%s</pre>' % diff.raw_diff()
143 140 elif action == 'diff':
144 141 c.cur_diff = diff.as_html()
145 142
146 143 return render('files/file_diff.html')
147 144
148 145 def _get_history(self, repo, node, f_path):
149 146 from vcs.nodes import NodeKind
150 147 if not node.kind is NodeKind.FILE:
151 148 return []
152 149 changesets = node.history
153 150 hist_l = []
154 151 for chs in changesets:
155 152 n_desc = 'r%s:%s' % (chs.revision, chs._short)
156 153 hist_l.append((chs._short, n_desc,))
157 154 return hist_l
@@ -1,29 +1,30 b''
1 1 #!/usr/bin/python
2 2 # -*- coding: utf-8 -*-
3 import logging
4 3 from operator import itemgetter
5 4 from pylons import tmpl_context as c, request, config
5 from pylons_app.lib.auth import LoginRequired
6 6 from pylons_app.lib.base import BaseController, render
7 from pylons_app.lib.auth import LoginRequired
7 from pylons_app.model.hg_model import HgModel
8 import logging
8 9 log = logging.getLogger(__name__)
9 10
10 11 class HgController(BaseController):
11 12
12 13 @LoginRequired()
13 14 def __before__(self):
14 15 super(HgController, self).__before__()
15 16
16 17 def index(self):
17 18 c.current_sort = request.GET.get('sort', 'name')
18 19 cs = c.current_sort
19 20 c.cs_slug = cs.replace('-', '')
20 21 sortables = ['name', 'description', 'last_change', 'tip', 'contact']
21
22 cached_repo_list = HgModel().get_repos()
22 23 if cs and c.cs_slug in sortables:
23 24 sort_key = c.cs_slug + '_sort'
24 25 if cs.startswith('-'):
25 c.repos_list = sorted(c.cached_repo_list, key=itemgetter(sort_key), reverse=True)
26 c.repos_list = sorted(cached_repo_list, key=itemgetter(sort_key), reverse=True)
26 27 else:
27 c.repos_list = sorted(c.cached_repo_list, key=itemgetter(sort_key), reverse=False)
28 c.repos_list = sorted(cached_repo_list, key=itemgetter(sort_key), reverse=False)
28 29
29 30 return render('/index.html')
@@ -1,29 +1,25 b''
1 import logging
2
3 from pylons import tmpl_context as c, app_globals as g, session, request, config, url
4 from pylons.controllers.util import abort, redirect
5
1 from pylons import tmpl_context as c, request
2 from pylons_app.lib.auth import LoginRequired
6 3 from pylons_app.lib.base import BaseController, render
7 from pylons_app.lib.utils import get_repo_slug
8 4 from pylons_app.model.hg_model import HgModel
9 5 from webhelpers.paginate import Page
10 from pylons_app.lib.auth import LoginRequired
6 import logging
11 7
12 8 log = logging.getLogger(__name__)
13 9
14 10 class ShortlogController(BaseController):
15 11
16 12 @LoginRequired()
17 13 def __before__(self):
18 14 super(ShortlogController, self).__before__()
19 15
20 16 def index(self):
21 17 hg_model = HgModel()
22 18 p = int(request.params.get('page', 1))
23 19 repo = hg_model.get_repo(c.repo_name)
24 20 c.repo_changesets = Page(repo, page=p, items_per_page=20)
25 21 c.shortlog_data = render('shortlog/shortlog_data.html')
26 22 if request.params.get('partial'):
27 23 return c.shortlog_data
28 24 r = render('shortlog/shortlog.html')
29 25 return r
@@ -1,29 +1,28 b''
1 1 from pylons import tmpl_context as c, request
2 2 from pylons_app.lib.auth import LoginRequired
3 from pylons_app.lib.base import BaseController, render, _full_changelog_cached
4 from pylons_app.model.hg_model import HgModel
3 from pylons_app.lib.base import BaseController, render
4 from pylons_app.model.hg_model import HgModel, _full_changelog_cached
5 5 import logging
6 6
7 7 log = logging.getLogger(__name__)
8 8
9 9 class SummaryController(BaseController):
10 10
11 11 @LoginRequired()
12 12 def __before__(self):
13 13 super(SummaryController, self).__before__()
14 14
15 15 def index(self):
16 16 hg_model = HgModel()
17 17 c.repo_info = hg_model.get_repo(c.repo_name)
18 18 c.repo_changesets = _full_changelog_cached(c.repo_name)[:10]
19
20 19 e = request.environ
21 20 uri = u'%(protocol)s://%(user)s@%(host)s/%(repo_name)s' % {
22 21 'protocol': e.get('wsgi.url_scheme'),
23 22 'user':str(c.hg_app_user.username),
24 23 'host':e.get('HTTP_HOST'),
25 24 'repo_name':c.repo_name, }
26 25 c.clone_repo_url = uri
27 26 c.repo_tags = c.repo_info.tags[:10]
28 27 c.repo_branches = c.repo_info.branches[:10]
29 28 return render('summary/summary.html')
@@ -1,24 +1,20 b''
1 from pylons import tmpl_context as c
2 from pylons_app.lib.auth import LoginRequired
3 from pylons_app.lib.base import BaseController, render
4 from pylons_app.model.hg_model import HgModel
1 5 import logging
2 6
3 from pylons import tmpl_context as c, app_globals as g, session, request, config, url
4 from pylons.controllers.util import abort, redirect
5
6 from pylons_app.lib.base import BaseController, render
7 from pylons_app.lib.utils import get_repo_slug
8 from pylons_app.model.hg_model import HgModel
9 from pylons_app.lib.auth import LoginRequired
10 7 log = logging.getLogger(__name__)
11 8
12
13 9 class TagsController(BaseController):
14 10
15 11 @LoginRequired()
16 12 def __before__(self):
17 13 super(TagsController, self).__before__()
18 14
19 15 def index(self):
20 16 hg_model = HgModel()
21 17 c.repo_info = hg_model.get_repo(c.repo_name)
22 18 c.repo_tags = c.repo_info.tags
23 19
24 20 return render('tags/tags.html')
@@ -1,118 +1,115 b''
1 1 from formencode import htmlfill
2 from pylons import request, response, session, tmpl_context as c, url, \
3 app_globals as g
2 from pylons import request, session, tmpl_context as c, url
3 from pylons.controllers.util import abort, redirect
4 4 from pylons.i18n.translation import _
5 from pylons_app.lib import helpers as h
6 from pylons.controllers.util import abort, redirect
5 from pylons_app.lib import helpers as h
7 6 from pylons_app.lib.auth import LoginRequired, CheckPermissionAll
8 7 from pylons_app.lib.base import BaseController, render
9 8 from pylons_app.model.db import User, UserLog
10 9 from pylons_app.model.forms import UserForm
11 10 from pylons_app.model.user_model import UserModel
12 11 import formencode
13 12 import logging
14 13
15
16
17 14 log = logging.getLogger(__name__)
18 15
19 16 class UsersController(BaseController):
20 17 """REST Controller styled on the Atom Publishing Protocol"""
21 18 # To properly map this controller, ensure your config/routing.py
22 19 # file has a resource setup:
23 20 # map.resource('user', 'users')
24 21 @LoginRequired()
25 22 def __before__(self):
26 23 c.admin_user = session.get('admin_user')
27 24 c.admin_username = session.get('admin_username')
28 25 super(UsersController, self).__before__()
29 26
30 27
31 28 def index(self, format='html'):
32 29 """GET /users: All items in the collection"""
33 30 # url('users')
34 31
35 32 c.users_list = self.sa.query(User).all()
36 33 return render('admin/users/users.html')
37 34
38 35 def create(self):
39 36 """POST /users: Create a new item"""
40 37 # url('users')
41 38
42 39 user_model = UserModel()
43 40 login_form = UserForm()()
44 41 try:
45 42 form_result = login_form.to_python(dict(request.POST))
46 43 user_model.create(form_result)
47 44 h.flash(_('created user %s') % form_result['username'], category='success')
48 45 return redirect(url('users'))
49 46
50 47 except formencode.Invalid as errors:
51 48 c.form_errors = errors.error_dict
52 49 return htmlfill.render(
53 50 render('admin/users/user_add.html'),
54 51 defaults=errors.value,
55 52 encoding="UTF-8")
56 53
57 54 def new(self, format='html'):
58 55 """GET /users/new: Form to create a new item"""
59 56 # url('new_user')
60 57 return render('admin/users/user_add.html')
61 58
62 59 def update(self, id):
63 60 """PUT /users/id: Update an existing item"""
64 61 # Forms posted to this method should contain a hidden field:
65 62 # <input type="hidden" name="_method" value="PUT" />
66 63 # Or using helpers:
67 64 # h.form(url('user', id=ID),
68 65 # method='put')
69 66 # url('user', id=ID)
70 67 user_model = UserModel()
71 68 login_form = UserForm(edit=True)()
72 69 try:
73 70 form_result = login_form.to_python(dict(request.POST))
74 71 user_model.update(id, form_result)
75 72 h.flash(_('User updated succesfully'), category='success')
76 73 return redirect(url('users'))
77 74
78 75 except formencode.Invalid as errors:
79 76 c.user = user_model.get_user(id)
80 77 c.form_errors = errors.error_dict
81 78 return htmlfill.render(
82 79 render('admin/users/user_edit.html'),
83 80 defaults=errors.value,
84 81 encoding="UTF-8")
85 82
86 83 def delete(self, id):
87 84 """DELETE /users/id: Delete an existing item"""
88 85 # Forms posted to this method should contain a hidden field:
89 86 # <input type="hidden" name="_method" value="DELETE" />
90 87 # Or using helpers:
91 88 # h.form(url('user', id=ID),
92 89 # method='delete')
93 90 # url('user', id=ID)
94 91 try:
95 92 self.sa.delete(self.sa.query(User).get(id))
96 93 self.sa.commit()
97 94 h.flash(_('sucessfully deleted user'), category='success')
98 95 except:
99 96 self.sa.rollback()
100 97 raise
101 98 return redirect(url('users'))
102 99
103 100 def show(self, id, format='html'):
104 101 """GET /users/id: Show a specific item"""
105 102 # url('user', id=ID)
106 103
107 104
108 105 def edit(self, id, format='html'):
109 106 """GET /users/id/edit: Form to edit an existing item"""
110 107 # url('edit_user', id=ID)
111 108 c.user = self.sa.query(User).get(id)
112 109 defaults = c.user.__dict__
113 110 return htmlfill.render(
114 111 render('admin/users/user_edit.html'),
115 112 defaults=defaults,
116 113 encoding="UTF-8",
117 114 force_defaults=False
118 115 )
@@ -1,41 +1,32 b''
1 1 """The base Controller API
2 2
3 3 Provides the BaseController class for subclassing.
4 4 """
5 from beaker.cache import cache_region
6 5 from pylons import config, tmpl_context as c, request, session
7 6 from pylons.controllers import WSGIController
8 7 from pylons.templating import render_mako as render
9 8 from pylons_app.lib.auth import LoginRequired, AuthUser
10 9 from pylons_app.lib.utils import get_repo_slug
11 10 from pylons_app.model import meta
12 from pylons_app.model.hg_model import HgModel
11 from pylons_app.model.hg_model import _get_repos_cached
13 12 from pylons_app import __version__
14 13
15 @cache_region('long_term', 'cached_repo_list')
16 def _get_repos_cached():
17 return [rep for rep in HgModel().get_repos()]
18
19 @cache_region('long_term', 'full_changelog')
20 def _full_changelog_cached(repo_name):
21 return list(reversed(list(HgModel().get_repo(repo_name))))
22
23 14 class BaseController(WSGIController):
24 15
25 16 def __before__(self):
26 17 c.hg_app_version = __version__
27 18 c.repos_prefix = config['hg_app_name']
28 19 c.repo_name = get_repo_slug(request)
29 20 c.hg_app_user = session.get('hg_app_user', AuthUser())
30 21 c.cached_repo_list = _get_repos_cached()
31 22 self.sa = meta.Session
32 23
33 24 def __call__(self, environ, start_response):
34 25 """Invoke the Controller"""
35 26 # WSGIController.__call__ dispatches to the Controller method
36 27 # the request is routed to. This routing information is
37 28 # available in environ['pylons.routes_dict']
38 29 try:
39 30 return WSGIController.__call__(self, environ, start_response)
40 31 finally:
41 32 meta.Session.remove()
@@ -1,130 +1,144 b''
1 1 import os
2 2 import logging
3 3 from mercurial import ui, config, hg
4 4 from mercurial.error import RepoError
5 5 log = logging.getLogger(__name__)
6 6
7 7
8 8 def get_repo_slug(request):
9 9 path_info = request.environ.get('PATH_INFO')
10 10 uri_lst = path_info.split('/')
11 11 repo_name = uri_lst[1]
12 12 return repo_name
13 13
14 14 def is_mercurial(environ):
15 15 """
16 16 Returns True if request's target is mercurial server - header
17 17 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
18 18 """
19 19 http_accept = environ.get('HTTP_ACCEPT')
20 20 if http_accept and http_accept.startswith('application/mercurial'):
21 21 return True
22 22 return False
23 23
24 24 def check_repo_dir(paths):
25 25 repos_path = paths[0][1].split('/')
26 26 if repos_path[-1] in ['*', '**']:
27 27 repos_path = repos_path[:-1]
28 28 if repos_path[0] != '/':
29 29 repos_path[0] = '/'
30 30 if not os.path.isdir(os.path.join(*repos_path)):
31 31 raise Exception('Not a valid repository in %s' % paths[0][1])
32 32
33 33 def check_repo(repo_name, base_path):
34 34
35 35 repo_path = os.path.join(base_path, repo_name)
36 36
37 37 try:
38 38 r = hg.repository(ui.ui(), repo_path)
39 39 hg.verify(r)
40 40 #here we hnow that repo exists it was verified
41 41 log.info('%s repo is already created', repo_name)
42 42 return False
43 43 #raise Exception('Repo exists')
44 44 except RepoError:
45 45 log.info('%s repo is free for creation', repo_name)
46 46 #it means that there is no valid repo there...
47 47 return True
48 48
49 49 def make_ui(path=None, checkpaths=True):
50 50 """
51 51 A funcion that will read python rc files and make an ui from read options
52 52
53 53 @param path: path to mercurial config file
54 54 """
55 55 if not path:
56 56 log.error('repos config path is empty !')
57 57
58 58 if not os.path.isfile(path):
59 59 log.warning('Unable to read config file %s' % path)
60 60 return False
61 61 #propagated from mercurial documentation
62 62 sections = [
63 63 'alias',
64 64 'auth',
65 65 'decode/encode',
66 66 'defaults',
67 67 'diff',
68 68 'email',
69 69 'extensions',
70 70 'format',
71 71 'merge-patterns',
72 72 'merge-tools',
73 73 'hooks',
74 74 'http_proxy',
75 75 'smtp',
76 76 'patch',
77 77 'paths',
78 78 'profiling',
79 79 'server',
80 80 'trusted',
81 81 'ui',
82 82 'web',
83 83 ]
84 84
85 85 baseui = ui.ui()
86 86 cfg = config.config()
87 87 cfg.read(path)
88 88 if checkpaths:check_repo_dir(cfg.items('paths'))
89 89
90 90 for section in sections:
91 91 for k, v in cfg.items(section):
92 92 baseui.setconfig(section, k, v)
93 93
94 94 return baseui
95 95
96 96 def invalidate_cache(name, *args):
97 97 """Invalidates given name cache"""
98 98
99 99 from beaker.cache import region_invalidate
100 100 log.info('INVALIDATING CACHE FOR %s', name)
101 101
102 102 """propagate our arguments to make sure invalidation works. First
103 103 argument has to be the name of cached func name give to cache decorator
104 104 without that the invalidation would not work"""
105 105 tmp = [name]
106 106 tmp.extend(args)
107 107 args = tuple(tmp)
108 108
109 109 if name == 'cached_repo_list':
110 from pylons_app.lib.base import _get_repos_cached
110 from pylons_app.model.hg_model import _get_repos_cached
111 111 region_invalidate(_get_repos_cached, None, *args)
112 112
113 113 if name == 'full_changelog':
114 from pylons_app.lib.base import _full_changelog_cached
114 from pylons_app.model.hg_model import _full_changelog_cached
115 115 region_invalidate(_full_changelog_cached, None, *args)
116 116
117 117 from vcs.backends.base import BaseChangeset
118 118 from vcs.utils.lazy import LazyProperty
119 119 class EmptyChangeset(BaseChangeset):
120 120
121 121 revision = -1
122 122
123 123 @LazyProperty
124 124 def raw_id(self):
125 125 """
126 126 Returns raw string identifing this changeset, useful for web
127 127 representation.
128 128 """
129 129 return '0' * 12
130 130
131
132 def repo2db_mapper():
133 """
134 put !
135 """
136 pass
137 #scann all dirs for .hgdbid
138 #if some dir doesn't have one generate one.
139 #
140
141
142
143
144
@@ -1,67 +1,120 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 #
4 4 # Copyright (c) 2010 marcink. All rights reserved.
5 5 #
6 from vcs.exceptions import RepositoryError
7 6 '''
8 7 Created on Apr 9, 2010
9 8
10 9 @author: marcink
11 10 '''
11
12 from beaker.cache import cache_region
13 from mercurial import ui
14 from mercurial.hgweb.hgwebdir_mod import findrepos
15 from pylons import app_globals as g
16 from vcs.exceptions import RepositoryError, VCSError
17 import logging
12 18 import os
13 from pylons import tmpl_context as c, app_globals as g, session, request, config
14 from pylons.controllers.util import abort
15 19 import sys
20 log = logging.getLogger(__name__)
21
16 22 try:
17 from vcs.backends.hg import get_repositories, MercurialRepository
23 from vcs.backends.hg import MercurialRepository
18 24 except ImportError:
19 25 sys.stderr.write('You have to import vcs module')
20 26 raise Exception('Unable to import vcs')
21 27
28
29 @cache_region('long_term', 'cached_repo_list')
30 def _get_repos_cached():
31 """
32 return cached dict with repos
33 """
34 return HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui)
35
36 @cache_region('long_term', 'full_changelog')
37 def _full_changelog_cached(repo_name):
38 log.info('getting full changelog for %s', repo_name)
39 return list(reversed(list(HgModel().get_repo(repo_name))))
40
22 41 class HgModel(object):
23 42 """
24 43 Mercurial Model
25 44 """
26 45
27
28 46 def __init__(self):
29 47 """
30 48 Constructor
31 49 """
32 50 pass
33
51
52 @staticmethod
53 def repo_scan(repos_prefix, repos_path, baseui):
54 """
55 Listing of repositories in given path. This path should not be a
56 repository itself. Return a dictionary of repository objects
57 :param repos_path: path to directory it could take syntax with
58 * or ** for deep recursive displaying repositories
59 """
60 def check_repo_dir(path):
61 """
62 Checks the repository
63 :param path:
64 """
65 repos_path = path.split('/')
66 if repos_path[-1] in ['*', '**']:
67 repos_path = repos_path[:-1]
68 if repos_path[0] != '/':
69 repos_path[0] = '/'
70 if not os.path.isdir(os.path.join(*repos_path)):
71 raise RepositoryError('Not a valid repository in %s' % path[0][1])
72 if not repos_path.endswith('*'):
73 raise VCSError('You need to specify * or ** at the end of path '
74 'for recursive scanning')
75
76 check_repo_dir(repos_path)
77 log.info('scanning for repositories in %s', repos_path)
78 repos = findrepos([(repos_prefix, repos_path)])
79 if not isinstance(baseui, ui.ui):
80 baseui = ui.ui()
81
82 repos_list = {}
83 for name, path in repos:
84 try:
85 repos_list[name] = MercurialRepository(path, baseui=baseui)
86 except OSError:
87 continue
88 return repos_list
89
34 90 def get_repos(self):
35 for mercurial_repo in get_repositories(g.paths[0][0], g.paths[0][1], g.baseui):
36
37 if mercurial_repo._get_hidden():
91 for name, repo in _get_repos_cached().items():
92 if repo._get_hidden():
38 93 #skip hidden web repository
39 94 continue
40 95
41 last_change = mercurial_repo.last_change
96 last_change = repo.last_change
42 97 try:
43 tip = mercurial_repo.get_changeset('tip')
98 tip = repo.get_changeset('tip')
44 99 except RepositoryError:
45 100 from pylons_app.lib.utils import EmptyChangeset
46 101 tip = EmptyChangeset()
47 102
48 103 tmp_d = {}
49 tmp_d['name'] = mercurial_repo.name
104 tmp_d['name'] = repo.name
50 105 tmp_d['name_sort'] = tmp_d['name'].lower()
51 tmp_d['description'] = mercurial_repo.description
106 tmp_d['description'] = repo.description
52 107 tmp_d['description_sort'] = tmp_d['description']
53 108 tmp_d['last_change'] = last_change
54 109 tmp_d['last_change_sort'] = last_change[1] - last_change[0]
55 110 tmp_d['tip'] = tip.raw_id
56 111 tmp_d['tip_sort'] = tip.revision
57 112 tmp_d['rev'] = tip.revision
58 tmp_d['contact'] = mercurial_repo.contact
113 tmp_d['contact'] = repo.contact
59 114 tmp_d['contact_sort'] = tmp_d['contact']
60 tmp_d['repo_archives'] = list(mercurial_repo._get_archives())
115 tmp_d['repo_archives'] = list(repo._get_archives())
61 116
62 117 yield tmp_d
63 118
64 119 def get_repo(self, repo_name):
65 path = g.paths[0][1].replace('*', '')
66 repo = MercurialRepository(os.path.join(path, repo_name), baseui=g.baseui)
67 return repo
120 return _get_repos_cached()[repo_name]
@@ -1,153 +1,153 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3 3 <html xmlns="http://www.w3.org/1999/xhtml" id="mainhtml">
4 4 <head>
5 5 <link rel="icon" href="/images/hgicon.png" type="image/png" />
6 6 <meta name="robots" content="index, nofollow"/>
7 7 <title>${next.title()}</title>
8 8 ##For future use yui reset for cross browser compatability.
9 9 ##<link rel="stylesheet" href="/js/yui/reset-fonts-grids/reset-fonts-grids.css" type="text/css" />
10 10 ${self.css()}
11 11 ${self.js()}
12 12 </head>
13 13
14 14 <body class="mainbody">
15 15 <div id="container">
16 16 <div class="page-header">
17 17 <h1>${next.breadcrumbs()}</h1>
18 18 ${self.page_nav()}
19 19 <div class="flash_msg">
20 20 <% messages = h.flash.pop_messages() %>
21 21 % if messages:
22 22 <ul id="flash-messages">
23 23 % for message in messages:
24 24 <li class="${message.category}_msg">${message}</li>
25 25 % endfor
26 26 </ul>
27 27 % endif
28 28 </div>
29 29 <div id="main">
30 30 ${next.main()}
31 31 </div>
32 32 <div class="page-footer">
33 33 Hg App ${c.hg_app_version} &copy; 2010 by Marcin Kuzminski
34 34 </div>
35 35
36 36 <div id="powered-by">
37 37 <p>
38 38 <a href="http://mercurial.selenic.com/" title="Mercurial">
39 39 <img src="/images/hglogo.png" width="75" height="90" alt="mercurial"/></a>
40 40 </p>
41 41 </div>
42 42
43 43 <div id="corner-top-left"></div>
44 44 <div id="corner-top-right"></div>
45 45 <div id="corner-bottom-left"></div>
46 46 <div id="corner-bottom-right"></div>
47 47
48 48 </div>
49 49 </body>
50 50 </html>
51 51
52 52 ### MAKO DEFS ###
53 53
54 54 <%def name="page_nav()">
55 55 ${self.menu()}
56 56 ${self.submenu()}
57 57 </%def>
58 58
59 59 <%def name="menu(current)">
60 60 <%
61 61 def is_current(selected):
62 62 if selected == current:
63 63 return "class='current'"
64 64 %>
65 65 %if current not in ['home','admin']:
66 66 ##regular menu
67 67 <script type="text/javascript">
68 68 YAHOO.util.Event.onDOMReady(function(){
69 69 YAHOO.util.Event.addListener('repo_switcher','click',function(){
70 70 if(YAHOO.util.Dom.hasClass('repo_switcher','selected')){
71 71 YAHOO.util.Dom.setStyle('switch_repos','display','none');
72 72 YAHOO.util.Dom.setStyle('repo_switcher','background','');
73 73 YAHOO.util.Dom.removeClass('repo_switcher','selected');
74 74 }
75 75 else{
76 76 YAHOO.util.Dom.setStyle('switch_repos','display','');
77 77 YAHOO.util.Dom.setStyle('repo_switcher','background','#FFFFFF');
78 78 YAHOO.util.Dom.setStyle('repo_switcher','color','#556CB5');
79 79 YAHOO.util.Dom.addClass('repo_switcher','selected');
80 80 }
81 81 });
82 82 YAHOO.util.Event.addListener('repos_list','change',function(e){
83 83 var wa = YAHOO.util.Dom.get('repos_list').value;
84 84
85 85 var url = "${h.url('summary_home',repo_name='__REPLACE__')}".replace('__REPLACE__',wa);
86 86 window.location = url;
87 87 })
88 88 });
89 89 </script>
90 90 <ul class="page-nav">
91 91 <li>
92 92 <a id="repo_switcher" title="${_('Switch repository')}" href="#">&darr;</a>
93 93 <div id="switch_repos" style="display:none;position: absolute;width: 150px;height: 25px">
94 94 <select id="repos_list" size="=10">
95 %for repo in c.cached_repo_list:
96 <option value="${repo['name']}">${repo['name']}</option>
95 %for repo in c.cached_repo_list.values():
96 <option value="${repo.name}">${repo.name}</option>
97 97 %endfor
98 98 </select>
99 99 </div>
100 100 </li>
101 101 <li ${is_current('summary')}>${h.link_to(_('summary'),h.url('summary_home',repo_name=c.repo_name))}</li>
102 102 <li ${is_current('shortlog')}>${h.link_to(_('shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}</li>
103 103 <li ${is_current('changelog')}>${h.link_to(_('changelog'),h.url('changelog_home',repo_name=c.repo_name))}</li>
104 104 <li ${is_current('branches')}>${h.link_to(_('branches'),h.url('branches_home',repo_name=c.repo_name))}</li>
105 105 <li ${is_current('tags')}>${h.link_to(_('tags'),h.url('tags_home',repo_name=c.repo_name))}</li>
106 106 <li ${is_current('files')}>${h.link_to(_('files'),h.url('files_home',repo_name=c.repo_name))}</li>
107 107 </ul>
108 108 %else:
109 109 ##Root menu
110 110 <ul class="page-nav">
111 111 <li ${is_current('home')}>${h.link_to(_('Home'),h.url('/'))}</li>
112 112 <li ${is_current('admin')}>${h.link_to(_('Admin'),h.url('admin_home'))}</li>
113 113 <li class="logout">${h.link_to(u'Logout',h.url('logout_home'))}</li>
114 114 </ul>
115 115 %endif
116 116 </div>
117 117 </%def>
118 118 <%def name="submenu(current=None)">
119 119 <%
120 120 def is_current(selected):
121 121 if selected == current:
122 122 return "class='current_submenu'"
123 123 %>
124 124 %if current != None:
125 125 <div>
126 126 <ul class="submenu">
127 127 <li ${is_current('repos')}>${h.link_to(u'repos',h.url('repos'),class_='repos')}</li>
128 128 <li ${is_current('users')}>${h.link_to(u'users',h.url('users'),class_='users')}</li>
129 129 <li ${is_current('permissions')}>${h.link_to(u'permissions',h.url('permissions'),class_='permissions')}</li>
130 130 </ul>
131 131 </div>
132 132 %endif
133 133 </%def>
134 134
135 135
136 136 <%def name="css()">
137 137 <link rel="stylesheet" href="/css/monoblue_custom.css" type="text/css" />
138 138 </%def>
139 139
140 140 <%def name="js()">
141 141 <script type="text/javascript" src="/js/yui/utilities/utilities.js"></script>
142 142 </%def>
143 143
144 144 <!-- DEFINITION OF FORM ERROR FETCHER -->
145 145 <%def name="get_form_error(element)">
146 146 %if hasattr(c,'form_errors') and type(c.form_errors) == dict:
147 147 %if c.form_errors.get(element,False):
148 148 <span class="error-message">
149 149 ${c.form_errors.get(element,'')}
150 150 </span>
151 151 %endif
152 152 %endif
153 153 </%def> No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now