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