Show More
@@ -1,46 +1,46 b'' | |||||
1 | #!/usr/bin/env python |
|
1 | #!/usr/bin/env python | |
2 | # encoding: utf-8 |
|
2 | # encoding: utf-8 | |
3 | # branches controller for pylons |
|
3 | # branches controller for pylons | |
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> |
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |
5 |
|
5 | |||
6 | # This program is free software; you can redistribute it and/or |
|
6 | # This program is free software; you can redistribute it and/or | |
7 | # modify it under the terms of the GNU General Public License |
|
7 | # modify it under the terms of the GNU General Public License | |
8 | # as published by the Free Software Foundation; version 2 |
|
8 | # as published by the Free Software Foundation; version 2 | |
9 | # of the License or (at your opinion) any later version of the license. |
|
9 | # of the License or (at your opinion) any later version of the license. | |
10 | # |
|
10 | # | |
11 | # This program is distributed in the hope that it will be useful, |
|
11 | # This program is distributed in the hope that it will be useful, | |
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | # GNU General Public License for more details. |
|
14 | # GNU General Public License for more details. | |
15 | # |
|
15 | # | |
16 | # You should have received a copy of the GNU General Public License |
|
16 | # You should have received a copy of the GNU General Public License | |
17 | # along with this program; if not, write to the Free Software |
|
17 | # along with this program; if not, write to the Free Software | |
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |
19 | # MA 02110-1301, USA. |
|
19 | # MA 02110-1301, USA. | |
20 | """ |
|
20 | """ | |
21 | Created on April 21, 2010 |
|
21 | Created on April 21, 2010 | |
22 | branches controller for pylons |
|
22 | branches controller for pylons | |
23 | @author: marcink |
|
23 | @author: marcink | |
24 | """ |
|
24 | """ | |
25 | from pylons import tmpl_context as c |
|
25 | from pylons import tmpl_context as c, request | |
26 | from pylons_app.lib.auth import LoginRequired |
|
26 | from pylons_app.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator | |
27 | from pylons_app.lib.base import BaseController, render |
|
27 | from pylons_app.lib.base import BaseController, render | |
28 | from pylons_app.model.hg_model import HgModel |
|
28 | from pylons_app.model.hg_model import HgModel | |
29 | import logging |
|
29 | import logging | |
30 |
|
||||
31 | log = logging.getLogger(__name__) |
|
30 | log = logging.getLogger(__name__) | |
32 |
|
31 | |||
33 | class BranchesController(BaseController): |
|
32 | class BranchesController(BaseController): | |
34 |
|
33 | |||
35 | @LoginRequired() |
|
34 | @LoginRequired() | |
|
35 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', 'repository.admin') | |||
36 | def __before__(self): |
|
36 | def __before__(self): | |
37 | super(BranchesController, self).__before__() |
|
37 | super(BranchesController, self).__before__() | |
38 |
|
38 | |||
39 | def index(self): |
|
39 | def index(self): | |
40 | hg_model = HgModel() |
|
40 | hg_model = HgModel() | |
41 | c.repo_info = hg_model.get_repo(c.repo_name) |
|
41 | c.repo_info = hg_model.get_repo(c.repo_name) | |
42 | c.repo_branches = {} |
|
42 | c.repo_branches = {} | |
43 | for name, hash in c.repo_info.branches.items(): |
|
43 | for name, hash_ in c.repo_info.branches.items(): | |
44 | c.repo_branches[name] = c.repo_info.get_changeset(hash) |
|
44 | c.repo_branches[name] = c.repo_info.get_changeset(hash_) | |
45 |
|
45 | |||
46 | return render('branches/branches.html') |
|
46 | return render('branches/branches.html') |
@@ -1,88 +1,90 b'' | |||||
1 | #!/usr/bin/env python |
|
1 | #!/usr/bin/env python | |
2 | # encoding: utf-8 |
|
2 | # encoding: utf-8 | |
3 | # changelog controller for pylons |
|
3 | # changelog controller for pylons | |
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> |
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |
5 | from json import dumps |
|
|||
6 | from mercurial.graphmod import colored, CHANGESET, revisions as graph_rev |
|
|||
7 | from pylons import request, session, tmpl_context as c |
|
|||
8 | from pylons_app.lib.auth import LoginRequired |
|
|||
9 | from pylons_app.lib.base import BaseController, render |
|
|||
10 | from pylons_app.model.hg_model import HgModel |
|
|||
11 | from webhelpers.paginate import Page |
|
|||
12 | import logging |
|
|||
13 |
|
5 | |||
14 | # This program is free software; you can redistribute it and/or |
|
6 | # This program is free software; you can redistribute it and/or | |
15 | # modify it under the terms of the GNU General Public License |
|
7 | # modify it under the terms of the GNU General Public License | |
16 | # as published by the Free Software Foundation; version 2 |
|
8 | # as published by the Free Software Foundation; version 2 | |
17 | # of the License or (at your opinion) any later version of the license. |
|
9 | # of the License or (at your opinion) any later version of the license. | |
18 | # |
|
10 | # | |
19 | # This program is distributed in the hope that it will be useful, |
|
11 | # This program is distributed in the hope that it will be useful, | |
20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
22 | # GNU General Public License for more details. |
|
14 | # GNU General Public License for more details. | |
23 | # |
|
15 | # | |
24 | # You should have received a copy of the GNU General Public License |
|
16 | # You should have received a copy of the GNU General Public License | |
25 | # along with this program; if not, write to the Free Software |
|
17 | # along with this program; if not, write to the Free Software | |
26 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |
27 | # MA 02110-1301, USA. |
|
19 | # MA 02110-1301, USA. | |
28 | """ |
|
20 | """ | |
29 | Created on April 21, 2010 |
|
21 | Created on April 21, 2010 | |
30 | changelog controller for pylons |
|
22 | changelog controller for pylons | |
31 | @author: marcink |
|
23 | @author: marcink | |
32 | """ |
|
24 | """ | |
33 | log = logging.getLogger(__name__) |
|
25 | from json import dumps | |
|
26 | from mercurial.graphmod import colored, CHANGESET, revisions as graph_rev | |||
|
27 | from pylons import request, session, tmpl_context as c | |||
|
28 | from pylons_app.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator | |||
|
29 | from pylons_app.lib.base import BaseController, render | |||
|
30 | from pylons_app.model.hg_model import HgModel | |||
|
31 | from webhelpers.paginate import Page | |||
|
32 | import logging | |||
|
33 | log = logging.getLogger(__name__) | |||
34 |
|
34 | |||
35 | class ChangelogController(BaseController): |
|
35 | class ChangelogController(BaseController): | |
36 |
|
36 | |||
37 | @LoginRequired() |
|
37 | @LoginRequired() | |
|
38 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', | |||
|
39 | 'repository.admin') | |||
38 | def __before__(self): |
|
40 | def __before__(self): | |
39 | super(ChangelogController, self).__before__() |
|
41 | super(ChangelogController, self).__before__() | |
40 |
|
42 | |||
41 | def index(self): |
|
43 | def index(self): | |
42 | limit = 100 |
|
44 | limit = 100 | |
43 | default = 20 |
|
45 | default = 20 | |
44 | if request.params.get('size'): |
|
46 | if request.params.get('size'): | |
45 | try: |
|
47 | try: | |
46 | int_size = int(request.params.get('size')) |
|
48 | int_size = int(request.params.get('size')) | |
47 | except ValueError: |
|
49 | except ValueError: | |
48 | int_size = default |
|
50 | int_size = default | |
49 | int_size = int_size if int_size <= limit else limit |
|
51 | int_size = int_size if int_size <= limit else limit | |
50 | c.size = int_size |
|
52 | c.size = int_size | |
51 | session['changelog_size'] = c.size |
|
53 | session['changelog_size'] = c.size | |
52 | session.save() |
|
54 | session.save() | |
53 | else: |
|
55 | else: | |
54 | c.size = int(session.get('changelog_size', default)) |
|
56 | c.size = int(session.get('changelog_size', default)) | |
55 |
|
57 | |||
56 | changesets = HgModel().get_repo(c.repo_name) |
|
58 | changesets = HgModel().get_repo(c.repo_name) | |
57 |
|
59 | |||
58 | p = int(request.params.get('page', 1)) |
|
60 | p = int(request.params.get('page', 1)) | |
59 | c.total_cs = len(changesets) |
|
61 | c.total_cs = len(changesets) | |
60 | c.pagination = Page(changesets, page=p, item_count=c.total_cs, |
|
62 | c.pagination = Page(changesets, page=p, item_count=c.total_cs, | |
61 | items_per_page=c.size) |
|
63 | items_per_page=c.size) | |
62 |
|
64 | |||
63 | self._graph(changesets, c.size, p) |
|
65 | self._graph(changesets, c.size, p) | |
64 |
|
66 | |||
65 | return render('changelog/changelog.html') |
|
67 | return render('changelog/changelog.html') | |
66 |
|
68 | |||
67 |
|
69 | |||
68 | def _graph(self, repo, size, p): |
|
70 | def _graph(self, repo, size, p): | |
69 | revcount = size |
|
71 | revcount = size | |
70 | if not repo.revisions:return dumps([]), 0 |
|
72 | if not repo.revisions:return dumps([]), 0 | |
71 |
|
73 | |||
72 | max_rev = repo.revisions[-1] |
|
74 | max_rev = repo.revisions[-1] | |
73 | offset = 1 if p == 1 else ((p - 1) * revcount) |
|
75 | offset = 1 if p == 1 else ((p - 1) * revcount) | |
74 | rev_start = repo.revisions[(-1 * offset)] |
|
76 | rev_start = repo.revisions[(-1 * offset)] | |
75 |
|
77 | |||
76 | revcount = min(max_rev, revcount) |
|
78 | revcount = min(max_rev, revcount) | |
77 | rev_end = max(0, rev_start - revcount) |
|
79 | rev_end = max(0, rev_start - revcount) | |
78 | dag = graph_rev(repo.repo, rev_start, rev_end) |
|
80 | dag = graph_rev(repo.repo, rev_start, rev_end) | |
79 |
|
81 | |||
80 | c.dag = tree = list(colored(dag)) |
|
82 | c.dag = tree = list(colored(dag)) | |
81 | data = [] |
|
83 | data = [] | |
82 | for (id, type, ctx, vtx, edges) in tree: |
|
84 | for (id, type, ctx, vtx, edges) in tree: | |
83 | if type != CHANGESET: |
|
85 | if type != CHANGESET: | |
84 | continue |
|
86 | continue | |
85 | data.append(('', vtx, edges)) |
|
87 | data.append(('', vtx, edges)) | |
86 |
|
88 | |||
87 | c.jsdata = dumps(data) |
|
89 | c.jsdata = dumps(data) | |
88 |
|
90 |
@@ -1,96 +1,97 b'' | |||||
1 | #!/usr/bin/env python |
|
1 | #!/usr/bin/env python | |
2 | # encoding: utf-8 |
|
2 | # encoding: utf-8 | |
3 | # changeset controller for pylons |
|
3 | # changeset controller for pylons | |
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> |
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |
5 | from pylons import tmpl_context as c, url |
|
|||
6 | from pylons.controllers.util import redirect |
|
|||
7 | from pylons_app.lib.auth import LoginRequired |
|
|||
8 | from pylons_app.lib.base import BaseController, render |
|
|||
9 | from pylons_app.model.hg_model import HgModel |
|
|||
10 | from vcs.exceptions import RepositoryError |
|
|||
11 | from vcs.nodes import FileNode |
|
|||
12 | from vcs.utils import diffs as differ |
|
|||
13 | import logging |
|
|||
14 | import traceback |
|
|||
15 |
|
5 | |||
16 | # This program is free software; you can redistribute it and/or |
|
6 | # This program is free software; you can redistribute it and/or | |
17 | # modify it under the terms of the GNU General Public License |
|
7 | # modify it under the terms of the GNU General Public License | |
18 | # as published by the Free Software Foundation; version 2 |
|
8 | # as published by the Free Software Foundation; version 2 | |
19 | # of the License or (at your opinion) any later version of the license. |
|
9 | # of the License or (at your opinion) any later version of the license. | |
20 | # |
|
10 | # | |
21 | # This program is distributed in the hope that it will be useful, |
|
11 | # This program is distributed in the hope that it will be useful, | |
22 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
23 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
24 | # GNU General Public License for more details. |
|
14 | # GNU General Public License for more details. | |
25 | # |
|
15 | # | |
26 | # You should have received a copy of the GNU General Public License |
|
16 | # You should have received a copy of the GNU General Public License | |
27 | # along with this program; if not, write to the Free Software |
|
17 | # along with this program; if not, write to the Free Software | |
28 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |
29 | # MA 02110-1301, USA. |
|
19 | # MA 02110-1301, USA. | |
30 | """ |
|
20 | """ | |
31 | Created on April 25, 2010 |
|
21 | Created on April 25, 2010 | |
32 | changeset controller for pylons |
|
22 | changeset controller for pylons | |
33 | @author: marcink |
|
23 | @author: marcink | |
34 | """ |
|
24 | """ | |
35 |
|
25 | from pylons import tmpl_context as c, url, request | ||
|
26 | from pylons.controllers.util import redirect | |||
|
27 | from pylons_app.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator | |||
|
28 | from pylons_app.lib.base import BaseController, render | |||
|
29 | from pylons_app.model.hg_model import HgModel | |||
|
30 | from vcs.exceptions import RepositoryError | |||
|
31 | from vcs.nodes import FileNode | |||
|
32 | from vcs.utils import diffs as differ | |||
|
33 | import logging | |||
|
34 | import traceback | |||
36 |
|
35 | |||
37 | log = logging.getLogger(__name__) |
|
36 | log = logging.getLogger(__name__) | |
38 |
|
37 | |||
39 | class ChangesetController(BaseController): |
|
38 | class ChangesetController(BaseController): | |
40 |
|
39 | |||
41 | @LoginRequired() |
|
40 | @LoginRequired() | |
|
41 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', | |||
|
42 | 'repository.admin') | |||
42 | def __before__(self): |
|
43 | def __before__(self): | |
43 | super(ChangesetController, self).__before__() |
|
44 | super(ChangesetController, self).__before__() | |
44 |
|
45 | |||
45 | def index(self, revision): |
|
46 | def index(self, revision): | |
46 | hg_model = HgModel() |
|
47 | hg_model = HgModel() | |
47 | try: |
|
48 | try: | |
48 | c.changeset = hg_model.get_repo(c.repo_name).get_changeset(revision) |
|
49 | c.changeset = hg_model.get_repo(c.repo_name).get_changeset(revision) | |
49 | except RepositoryError: |
|
50 | except RepositoryError: | |
50 | log.error(traceback.format_exc()) |
|
51 | log.error(traceback.format_exc()) | |
51 | return redirect(url('hg_home')) |
|
52 | return redirect(url('hg_home')) | |
52 | else: |
|
53 | else: | |
53 | try: |
|
54 | try: | |
54 | c.changeset_old = c.changeset.parents[0] |
|
55 | c.changeset_old = c.changeset.parents[0] | |
55 | except IndexError: |
|
56 | except IndexError: | |
56 | c.changeset_old = None |
|
57 | c.changeset_old = None | |
57 | c.changes = [] |
|
58 | c.changes = [] | |
58 |
|
59 | |||
59 | for node in c.changeset.added: |
|
60 | for node in c.changeset.added: | |
60 | filenode_old = FileNode(node.path, '') |
|
61 | filenode_old = FileNode(node.path, '') | |
61 | if filenode_old.is_binary or node.is_binary: |
|
62 | if filenode_old.is_binary or node.is_binary: | |
62 | diff = 'binary file' |
|
63 | diff = 'binary file' | |
63 | else: |
|
64 | else: | |
64 | f_udiff = differ.get_udiff(filenode_old, node) |
|
65 | f_udiff = differ.get_udiff(filenode_old, node) | |
65 | diff = differ.DiffProcessor(f_udiff).as_html() |
|
66 | diff = differ.DiffProcessor(f_udiff).as_html() | |
66 | try: |
|
67 | try: | |
67 | diff = unicode(diff) |
|
68 | diff = unicode(diff) | |
68 | except: |
|
69 | except: | |
69 | log.warning('Decoding failed of %s', filenode_old) |
|
70 | log.warning('Decoding failed of %s', filenode_old) | |
70 | log.warning('Decoding failed of %s', node) |
|
71 | log.warning('Decoding failed of %s', node) | |
71 | diff = 'unsupported type' |
|
72 | diff = 'unsupported type' | |
72 | cs1 = None |
|
73 | cs1 = None | |
73 | cs2 = node.last_changeset.raw_id |
|
74 | cs2 = node.last_changeset.raw_id | |
74 | c.changes.append(('added', node, diff, cs1, cs2)) |
|
75 | c.changes.append(('added', node, diff, cs1, cs2)) | |
75 |
|
76 | |||
76 | for node in c.changeset.changed: |
|
77 | for node in c.changeset.changed: | |
77 | filenode_old = c.changeset_old.get_node(node.path) |
|
78 | filenode_old = c.changeset_old.get_node(node.path) | |
78 | if filenode_old.is_binary or node.is_binary: |
|
79 | if filenode_old.is_binary or node.is_binary: | |
79 | diff = 'binary file' |
|
80 | diff = 'binary file' | |
80 | else: |
|
81 | else: | |
81 | f_udiff = differ.get_udiff(filenode_old, node) |
|
82 | f_udiff = differ.get_udiff(filenode_old, node) | |
82 | diff = differ.DiffProcessor(f_udiff).as_html() |
|
83 | diff = differ.DiffProcessor(f_udiff).as_html() | |
83 | try: |
|
84 | try: | |
84 | diff = unicode(diff) |
|
85 | diff = unicode(diff) | |
85 | except: |
|
86 | except: | |
86 | log.warning('Decoding failed of %s', filenode_old) |
|
87 | log.warning('Decoding failed of %s', filenode_old) | |
87 | log.warning('Decoding failed of %s', node) |
|
88 | log.warning('Decoding failed of %s', node) | |
88 | diff = 'unsupported type' |
|
89 | diff = 'unsupported type' | |
89 | cs1 = filenode_old.last_changeset.raw_id |
|
90 | cs1 = filenode_old.last_changeset.raw_id | |
90 | cs2 = node.last_changeset.raw_id |
|
91 | cs2 = node.last_changeset.raw_id | |
91 | c.changes.append(('changed', node, diff, cs1, cs2)) |
|
92 | c.changes.append(('changed', node, diff, cs1, cs2)) | |
92 |
|
93 | |||
93 | for node in c.changeset.removed: |
|
94 | for node in c.changeset.removed: | |
94 | c.changes.append(('removed', node, None, None, None)) |
|
95 | c.changes.append(('removed', node, None, None, None)) | |
95 |
|
96 | |||
96 | return render('changeset/changeset.html') |
|
97 | return render('changeset/changeset.html') |
@@ -1,193 +1,194 b'' | |||||
1 | #!/usr/bin/env python |
|
1 | #!/usr/bin/env python | |
2 | # encoding: utf-8 |
|
2 | # encoding: utf-8 | |
3 | # files controller for pylons |
|
3 | # files controller for pylons | |
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> |
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |
5 | from mercurial import archival |
|
5 | ||
6 | from pylons import request, response, session, tmpl_context as c, url |
|
|||
7 | from pylons.controllers.util import redirect |
|
|||
8 | from pylons_app.lib.auth import LoginRequired |
|
|||
9 | from pylons_app.lib.base import BaseController, render |
|
|||
10 | from pylons_app.lib.utils import EmptyChangeset |
|
|||
11 | from pylons_app.model.hg_model import HgModel |
|
|||
12 | from vcs.exceptions import RepositoryError, ChangesetError |
|
|||
13 | from vcs.nodes import FileNode |
|
|||
14 | from vcs.utils import diffs as differ |
|
|||
15 | import logging |
|
|||
16 | import pylons_app.lib.helpers as h |
|
|||
17 | import tempfile |
|
|||
18 |
|
||||
19 | # This program is free software; you can redistribute it and/or |
|
6 | # This program is free software; you can redistribute it and/or | |
20 | # modify it under the terms of the GNU General Public License |
|
7 | # modify it under the terms of the GNU General Public License | |
21 | # as published by the Free Software Foundation; version 2 |
|
8 | # as published by the Free Software Foundation; version 2 | |
22 | # of the License or (at your opinion) any later version of the license. |
|
9 | # of the License or (at your opinion) any later version of the license. | |
23 | # |
|
10 | # | |
24 | # This program is distributed in the hope that it will be useful, |
|
11 | # This program is distributed in the hope that it will be useful, | |
25 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
26 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
27 | # GNU General Public License for more details. |
|
14 | # GNU General Public License for more details. | |
28 | # |
|
15 | # | |
29 | # You should have received a copy of the GNU General Public License |
|
16 | # You should have received a copy of the GNU General Public License | |
30 | # along with this program; if not, write to the Free Software |
|
17 | # along with this program; if not, write to the Free Software | |
31 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |
32 | # MA 02110-1301, USA. |
|
19 | # MA 02110-1301, USA. | |
33 | """ |
|
20 | """ | |
34 | Created on April 21, 2010 |
|
21 | Created on April 21, 2010 | |
35 | files controller for pylons |
|
22 | files controller for pylons | |
36 | @author: marcink |
|
23 | @author: marcink | |
37 | """ |
|
24 | """ | |
38 |
|
25 | from mercurial import archival | ||
|
26 | from pylons import request, response, session, tmpl_context as c, url | |||
|
27 | from pylons.controllers.util import redirect | |||
|
28 | from pylons_app.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator | |||
|
29 | from pylons_app.lib.base import BaseController, render | |||
|
30 | from pylons_app.lib.utils import EmptyChangeset, get_repo_slug | |||
|
31 | from pylons_app.model.hg_model import HgModel | |||
|
32 | from vcs.exceptions import RepositoryError, ChangesetError | |||
|
33 | from vcs.nodes import FileNode | |||
|
34 | from vcs.utils import diffs as differ | |||
|
35 | import logging | |||
|
36 | import pylons_app.lib.helpers as h | |||
|
37 | import tempfile | |||
39 |
|
38 | |||
40 | log = logging.getLogger(__name__) |
|
39 | log = logging.getLogger(__name__) | |
41 |
|
40 | |||
42 | class FilesController(BaseController): |
|
41 | class FilesController(BaseController): | |
43 |
|
42 | |||
44 | @LoginRequired() |
|
43 | @LoginRequired() | |
|
44 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', | |||
|
45 | 'repository.admin') | |||
45 | def __before__(self): |
|
46 | def __before__(self): | |
46 | super(FilesController, self).__before__() |
|
47 | super(FilesController, self).__before__() | |
47 |
|
48 | |||
48 | def index(self, repo_name, revision, f_path): |
|
49 | def index(self, repo_name, revision, f_path): | |
49 | hg_model = HgModel() |
|
50 | hg_model = HgModel() | |
50 | c.repo = repo = hg_model.get_repo(c.repo_name) |
|
51 | c.repo = repo = hg_model.get_repo(c.repo_name) | |
51 | revision = request.POST.get('at_rev', None) or revision |
|
52 | revision = request.POST.get('at_rev', None) or revision | |
52 |
|
53 | |||
53 | def get_next_rev(cur): |
|
54 | def get_next_rev(cur): | |
54 | max_rev = len(c.repo.revisions) - 1 |
|
55 | max_rev = len(c.repo.revisions) - 1 | |
55 | r = cur + 1 |
|
56 | r = cur + 1 | |
56 | if r > max_rev: |
|
57 | if r > max_rev: | |
57 | r = max_rev |
|
58 | r = max_rev | |
58 | return r |
|
59 | return r | |
59 |
|
60 | |||
60 | def get_prev_rev(cur): |
|
61 | def get_prev_rev(cur): | |
61 | r = cur - 1 |
|
62 | r = cur - 1 | |
62 | return r |
|
63 | return r | |
63 |
|
64 | |||
64 | c.f_path = f_path |
|
65 | c.f_path = f_path | |
65 |
|
66 | |||
66 |
|
67 | |||
67 | try: |
|
68 | try: | |
68 | cur_rev = repo.get_changeset(revision).revision |
|
69 | cur_rev = repo.get_changeset(revision).revision | |
69 | prev_rev = repo.get_changeset(get_prev_rev(cur_rev)).raw_id |
|
70 | prev_rev = repo.get_changeset(get_prev_rev(cur_rev)).raw_id | |
70 | next_rev = repo.get_changeset(get_next_rev(cur_rev)).raw_id |
|
71 | next_rev = repo.get_changeset(get_next_rev(cur_rev)).raw_id | |
71 |
|
72 | |||
72 | c.url_prev = url('files_home', repo_name=c.repo_name, |
|
73 | c.url_prev = url('files_home', repo_name=c.repo_name, | |
73 | revision=prev_rev, f_path=f_path) |
|
74 | revision=prev_rev, f_path=f_path) | |
74 | c.url_next = url('files_home', repo_name=c.repo_name, |
|
75 | c.url_next = url('files_home', repo_name=c.repo_name, | |
75 | revision=next_rev, f_path=f_path) |
|
76 | revision=next_rev, f_path=f_path) | |
76 |
|
77 | |||
77 | c.changeset = repo.get_changeset(revision) |
|
78 | c.changeset = repo.get_changeset(revision) | |
78 |
|
79 | |||
79 |
|
80 | |||
80 | c.cur_rev = c.changeset.raw_id |
|
81 | c.cur_rev = c.changeset.raw_id | |
81 | c.rev_nr = c.changeset.revision |
|
82 | c.rev_nr = c.changeset.revision | |
82 | c.files_list = c.changeset.get_node(f_path) |
|
83 | c.files_list = c.changeset.get_node(f_path) | |
83 | c.file_history = self._get_history(repo, c.files_list, f_path) |
|
84 | c.file_history = self._get_history(repo, c.files_list, f_path) | |
84 |
|
85 | |||
85 | except (RepositoryError, ChangesetError): |
|
86 | except (RepositoryError, ChangesetError): | |
86 | c.files_list = None |
|
87 | c.files_list = None | |
87 |
|
88 | |||
88 | return render('files/files.html') |
|
89 | return render('files/files.html') | |
89 |
|
90 | |||
90 | def rawfile(self, repo_name, revision, f_path): |
|
91 | def rawfile(self, repo_name, revision, f_path): | |
91 | hg_model = HgModel() |
|
92 | hg_model = HgModel() | |
92 | c.repo = hg_model.get_repo(c.repo_name) |
|
93 | c.repo = hg_model.get_repo(c.repo_name) | |
93 | file_node = c.repo.get_changeset(revision).get_node(f_path) |
|
94 | file_node = c.repo.get_changeset(revision).get_node(f_path) | |
94 | response.content_type = file_node.mimetype |
|
95 | response.content_type = file_node.mimetype | |
95 | response.content_disposition = 'attachment; filename=%s' \ |
|
96 | response.content_disposition = 'attachment; filename=%s' \ | |
96 | % f_path.split('/')[-1] |
|
97 | % f_path.split('/')[-1] | |
97 | return file_node.content |
|
98 | return file_node.content | |
98 |
|
99 | |||
99 | def annotate(self, repo_name, revision, f_path): |
|
100 | def annotate(self, repo_name, revision, f_path): | |
100 | hg_model = HgModel() |
|
101 | hg_model = HgModel() | |
101 | c.repo = hg_model.get_repo(c.repo_name) |
|
102 | c.repo = hg_model.get_repo(c.repo_name) | |
102 | cs = c.repo.get_changeset(revision) |
|
103 | cs = c.repo.get_changeset(revision) | |
103 | c.file = cs.get_node(f_path) |
|
104 | c.file = cs.get_node(f_path) | |
104 | c.file_msg = cs.get_file_message(f_path) |
|
105 | c.file_msg = cs.get_file_message(f_path) | |
105 | c.cur_rev = cs.raw_id |
|
106 | c.cur_rev = cs.raw_id | |
106 | c.f_path = f_path |
|
107 | c.f_path = f_path | |
107 | c.annotate = cs.get_file_annotate(f_path) |
|
108 | c.annotate = cs.get_file_annotate(f_path) | |
108 | return render('files/files_annotate.html') |
|
109 | return render('files/files_annotate.html') | |
109 |
|
110 | |||
110 | def archivefile(self, repo_name, revision, fileformat): |
|
111 | def archivefile(self, repo_name, revision, fileformat): | |
111 | archive_specs = { |
|
112 | archive_specs = { | |
112 | '.tar.bz2': ('application/x-tar', 'tbz2'), |
|
113 | '.tar.bz2': ('application/x-tar', 'tbz2'), | |
113 | '.tar.gz': ('application/x-tar', 'tgz'), |
|
114 | '.tar.gz': ('application/x-tar', 'tgz'), | |
114 | '.zip': ('application/zip', 'zip'), |
|
115 | '.zip': ('application/zip', 'zip'), | |
115 | } |
|
116 | } | |
116 | if not archive_specs.has_key(fileformat): |
|
117 | if not archive_specs.has_key(fileformat): | |
117 | return 'Unknown archive type %s' % fileformat |
|
118 | return 'Unknown archive type %s' % fileformat | |
118 |
|
119 | |||
119 | def read_in_chunks(file_object, chunk_size=1024 * 40): |
|
120 | def read_in_chunks(file_object, chunk_size=1024 * 40): | |
120 | """Lazy function (generator) to read a file piece by piece. |
|
121 | """Lazy function (generator) to read a file piece by piece. | |
121 | Default chunk size: 40k.""" |
|
122 | Default chunk size: 40k.""" | |
122 | while True: |
|
123 | while True: | |
123 | data = file_object.read(chunk_size) |
|
124 | data = file_object.read(chunk_size) | |
124 | if not data: |
|
125 | if not data: | |
125 | break |
|
126 | break | |
126 | yield data |
|
127 | yield data | |
127 |
|
128 | |||
128 | archive = tempfile.TemporaryFile() |
|
129 | archive = tempfile.TemporaryFile() | |
129 | repo = HgModel().get_repo(repo_name).repo |
|
130 | repo = HgModel().get_repo(repo_name).repo | |
130 | fname = '%s-%s%s' % (repo_name, revision, fileformat) |
|
131 | fname = '%s-%s%s' % (repo_name, revision, fileformat) | |
131 | archival.archive(repo, archive, revision, archive_specs[fileformat][1], |
|
132 | archival.archive(repo, archive, revision, archive_specs[fileformat][1], | |
132 | prefix='%s-%s' % (repo_name, revision)) |
|
133 | prefix='%s-%s' % (repo_name, revision)) | |
133 | response.content_type = archive_specs[fileformat][0] |
|
134 | response.content_type = archive_specs[fileformat][0] | |
134 | response.content_disposition = 'attachment; filename=%s' % fname |
|
135 | response.content_disposition = 'attachment; filename=%s' % fname | |
135 | archive.seek(0) |
|
136 | archive.seek(0) | |
136 | return read_in_chunks(archive) |
|
137 | return read_in_chunks(archive) | |
137 |
|
138 | |||
138 | def diff(self, repo_name, f_path): |
|
139 | def diff(self, repo_name, f_path): | |
139 | hg_model = HgModel() |
|
140 | hg_model = HgModel() | |
140 | diff1 = request.GET.get('diff1') |
|
141 | diff1 = request.GET.get('diff1') | |
141 | diff2 = request.GET.get('diff2') |
|
142 | diff2 = request.GET.get('diff2') | |
142 | c.action = action = request.GET.get('diff') |
|
143 | c.action = action = request.GET.get('diff') | |
143 | c.no_changes = diff1 == diff2 |
|
144 | c.no_changes = diff1 == diff2 | |
144 | c.f_path = f_path |
|
145 | c.f_path = f_path | |
145 | c.repo = hg_model.get_repo(c.repo_name) |
|
146 | c.repo = hg_model.get_repo(c.repo_name) | |
146 |
|
147 | |||
147 | try: |
|
148 | try: | |
148 | if diff1 not in ['', None, 'None', '0' * 12]: |
|
149 | if diff1 not in ['', None, 'None', '0' * 12]: | |
149 | c.changeset_1 = c.repo.get_changeset(diff1) |
|
150 | c.changeset_1 = c.repo.get_changeset(diff1) | |
150 | node1 = c.changeset_1.get_node(f_path) |
|
151 | node1 = c.changeset_1.get_node(f_path) | |
151 | else: |
|
152 | else: | |
152 | c.changeset_1 = EmptyChangeset() |
|
153 | c.changeset_1 = EmptyChangeset() | |
153 | node1 = FileNode('.', '') |
|
154 | node1 = FileNode('.', '') | |
154 | if diff2 not in ['', None, 'None', '0' * 12]: |
|
155 | if diff2 not in ['', None, 'None', '0' * 12]: | |
155 | c.changeset_2 = c.repo.get_changeset(diff2) |
|
156 | c.changeset_2 = c.repo.get_changeset(diff2) | |
156 | node2 = c.changeset_2.get_node(f_path) |
|
157 | node2 = c.changeset_2.get_node(f_path) | |
157 | else: |
|
158 | else: | |
158 | c.changeset_2 = EmptyChangeset() |
|
159 | c.changeset_2 = EmptyChangeset() | |
159 | node2 = FileNode('.', '') |
|
160 | node2 = FileNode('.', '') | |
160 | except RepositoryError: |
|
161 | except RepositoryError: | |
161 | return redirect(url('files_home', |
|
162 | return redirect(url('files_home', | |
162 | repo_name=c.repo_name, f_path=f_path)) |
|
163 | repo_name=c.repo_name, f_path=f_path)) | |
163 |
|
164 | |||
164 | c.diff1 = 'r%s:%s' % (c.changeset_1.revision, c.changeset_1.raw_id) |
|
165 | c.diff1 = 'r%s:%s' % (c.changeset_1.revision, c.changeset_1.raw_id) | |
165 | c.diff2 = 'r%s:%s' % (c.changeset_2.revision, c.changeset_2.raw_id) |
|
166 | c.diff2 = 'r%s:%s' % (c.changeset_2.revision, c.changeset_2.raw_id) | |
166 | f_udiff = differ.get_udiff(node1, node2) |
|
167 | f_udiff = differ.get_udiff(node1, node2) | |
167 |
|
168 | |||
168 | diff = differ.DiffProcessor(f_udiff) |
|
169 | diff = differ.DiffProcessor(f_udiff) | |
169 |
|
170 | |||
170 | if action == 'download': |
|
171 | if action == 'download': | |
171 | diff_name = '%s_vs_%s.diff' % (diff1, diff2) |
|
172 | diff_name = '%s_vs_%s.diff' % (diff1, diff2) | |
172 | response.content_type = 'text/plain' |
|
173 | response.content_type = 'text/plain' | |
173 | response.content_disposition = 'attachment; filename=%s' \ |
|
174 | response.content_disposition = 'attachment; filename=%s' \ | |
174 | % diff_name |
|
175 | % diff_name | |
175 | return diff.raw_diff() |
|
176 | return diff.raw_diff() | |
176 |
|
177 | |||
177 | elif action == 'raw': |
|
178 | elif action == 'raw': | |
178 | c.cur_diff = '<pre class="raw">%s</pre>' % h.escape(diff.raw_diff()) |
|
179 | c.cur_diff = '<pre class="raw">%s</pre>' % h.escape(diff.raw_diff()) | |
179 | elif action == 'diff': |
|
180 | elif action == 'diff': | |
180 | c.cur_diff = diff.as_html() |
|
181 | c.cur_diff = diff.as_html() | |
181 |
|
182 | |||
182 | return render('files/file_diff.html') |
|
183 | return render('files/file_diff.html') | |
183 |
|
184 | |||
184 | def _get_history(self, repo, node, f_path): |
|
185 | def _get_history(self, repo, node, f_path): | |
185 | from vcs.nodes import NodeKind |
|
186 | from vcs.nodes import NodeKind | |
186 | if not node.kind is NodeKind.FILE: |
|
187 | if not node.kind is NodeKind.FILE: | |
187 | return [] |
|
188 | return [] | |
188 | changesets = node.history |
|
189 | changesets = node.history | |
189 | hist_l = [] |
|
190 | hist_l = [] | |
190 | for chs in changesets: |
|
191 | for chs in changesets: | |
191 | n_desc = 'r%s:%s' % (chs.revision, chs._short) |
|
192 | n_desc = 'r%s:%s' % (chs.revision, chs._short) | |
192 | hist_l.append((chs._short, n_desc,)) |
|
193 | hist_l.append((chs._short, n_desc,)) | |
193 | return hist_l |
|
194 | return hist_l |
@@ -1,90 +1,90 b'' | |||||
1 | #!/usr/bin/env python |
|
1 | #!/usr/bin/env python | |
2 | # encoding: utf-8 |
|
2 | # encoding: utf-8 | |
3 | # permissions controller for pylons |
|
3 | # permissions controller for pylons | |
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> |
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |
5 |
|
5 | |||
6 | # This program is free software; you can redistribute it and/or |
|
6 | # This program is free software; you can redistribute it and/or | |
7 | # modify it under the terms of the GNU General Public License |
|
7 | # modify it under the terms of the GNU General Public License | |
8 | # as published by the Free Software Foundation; version 2 |
|
8 | # as published by the Free Software Foundation; version 2 | |
9 | # of the License or (at your opinion) any later version of the license. |
|
9 | # of the License or (at your opinion) any later version of the license. | |
10 | # |
|
10 | # | |
11 | # This program is distributed in the hope that it will be useful, |
|
11 | # This program is distributed in the hope that it will be useful, | |
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | # GNU General Public License for more details. |
|
14 | # GNU General Public License for more details. | |
15 | # |
|
15 | # | |
16 | # You should have received a copy of the GNU General Public License |
|
16 | # You should have received a copy of the GNU General Public License | |
17 | # along with this program; if not, write to the Free Software |
|
17 | # along with this program; if not, write to the Free Software | |
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |
19 | # MA 02110-1301, USA. |
|
19 | # MA 02110-1301, USA. | |
20 | """ |
|
20 | """ | |
21 | Created on April 27, 2010 |
|
21 | Created on April 27, 2010 | |
22 | permissions controller for pylons |
|
22 | permissions controller for pylons | |
23 | @author: marcink |
|
23 | @author: marcink | |
24 | """ |
|
24 | """ | |
25 | from formencode import htmlfill |
|
25 | from formencode import htmlfill | |
26 | from pylons import request, session, tmpl_context as c, url |
|
26 | from pylons import request, session, tmpl_context as c, url | |
27 | from pylons.controllers.util import abort, redirect |
|
27 | from pylons.controllers.util import abort, redirect | |
28 | from pylons.i18n.translation import _ |
|
28 | from pylons.i18n.translation import _ | |
29 | from pylons_app.lib import helpers as h |
|
29 | from pylons_app.lib import helpers as h | |
30 | from pylons_app.lib.auth import LoginRequired, HasPermissionAllDecorator |
|
30 | from pylons_app.lib.auth import LoginRequired, HasPermissionAllDecorator | |
31 | from pylons_app.lib.base import BaseController, render |
|
31 | from pylons_app.lib.base import BaseController, render | |
32 | from pylons_app.model.db import User, UserLog |
|
32 | from pylons_app.model.db import User, UserLog | |
33 | from pylons_app.model.forms import UserForm |
|
33 | from pylons_app.model.forms import UserForm | |
34 | from pylons_app.model.user_model import UserModel |
|
34 | from pylons_app.model.user_model import UserModel | |
35 | import formencode |
|
35 | import formencode | |
36 | import logging |
|
36 | import logging | |
37 |
|
37 | |||
38 | log = logging.getLogger(__name__) |
|
38 | log = logging.getLogger(__name__) | |
39 |
|
39 | |||
40 | class PermissionsController(BaseController): |
|
40 | class PermissionsController(BaseController): | |
41 | """REST Controller styled on the Atom Publishing Protocol""" |
|
41 | """REST Controller styled on the Atom Publishing Protocol""" | |
42 | # To properly map this controller, ensure your config/routing.py |
|
42 | # To properly map this controller, ensure your config/routing.py | |
43 | # file has a resource setup: |
|
43 | # file has a resource setup: | |
44 | # map.resource('permission', 'permissions') |
|
44 | # map.resource('permission', 'permissions') | |
45 |
|
45 | |||
46 | @LoginRequired() |
|
46 | @LoginRequired() | |
47 | @HasPermissionAllDecorator('hg.admin') |
|
47 | #@HasPermissionAllDecorator('hg.admin') | |
48 | def __before__(self): |
|
48 | def __before__(self): | |
49 | c.admin_user = session.get('admin_user') |
|
49 | c.admin_user = session.get('admin_user') | |
50 | c.admin_username = session.get('admin_username') |
|
50 | c.admin_username = session.get('admin_username') | |
51 | super(PermissionsController, self).__before__() |
|
51 | super(PermissionsController, self).__before__() | |
52 |
|
52 | |||
53 | def index(self, format='html'): |
|
53 | def index(self, format='html'): | |
54 | """GET /permissions: All items in the collection""" |
|
54 | """GET /permissions: All items in the collection""" | |
55 | # url('permissions') |
|
55 | # url('permissions') | |
56 | return render('admin/permissions/permissions.html') |
|
56 | return render('admin/permissions/permissions.html') | |
57 |
|
57 | |||
58 | def create(self): |
|
58 | def create(self): | |
59 | """POST /permissions: Create a new item""" |
|
59 | """POST /permissions: Create a new item""" | |
60 | # url('permissions') |
|
60 | # url('permissions') | |
61 |
|
61 | |||
62 | def new(self, format='html'): |
|
62 | def new(self, format='html'): | |
63 | """GET /permissions/new: Form to create a new item""" |
|
63 | """GET /permissions/new: Form to create a new item""" | |
64 | # url('new_permission') |
|
64 | # url('new_permission') | |
65 |
|
65 | |||
66 | def update(self, id): |
|
66 | def update(self, id): | |
67 | """PUT /permissions/id: Update an existing item""" |
|
67 | """PUT /permissions/id: Update an existing item""" | |
68 | # Forms posted to this method should contain a hidden field: |
|
68 | # Forms posted to this method should contain a hidden field: | |
69 | # <input type="hidden" name="_method" value="PUT" /> |
|
69 | # <input type="hidden" name="_method" value="PUT" /> | |
70 | # Or using helpers: |
|
70 | # Or using helpers: | |
71 | # h.form(url('permission', id=ID), |
|
71 | # h.form(url('permission', id=ID), | |
72 | # method='put') |
|
72 | # method='put') | |
73 | # url('permission', id=ID) |
|
73 | # url('permission', id=ID) | |
74 |
|
74 | |||
75 | def delete(self, id): |
|
75 | def delete(self, id): | |
76 | """DELETE /permissions/id: Delete an existing item""" |
|
76 | """DELETE /permissions/id: Delete an existing item""" | |
77 | # Forms posted to this method should contain a hidden field: |
|
77 | # Forms posted to this method should contain a hidden field: | |
78 | # <input type="hidden" name="_method" value="DELETE" /> |
|
78 | # <input type="hidden" name="_method" value="DELETE" /> | |
79 | # Or using helpers: |
|
79 | # Or using helpers: | |
80 | # h.form(url('permission', id=ID), |
|
80 | # h.form(url('permission', id=ID), | |
81 | # method='delete') |
|
81 | # method='delete') | |
82 | # url('permission', id=ID) |
|
82 | # url('permission', id=ID) | |
83 |
|
83 | |||
84 | def show(self, id, format='html'): |
|
84 | def show(self, id, format='html'): | |
85 | """GET /permissions/id: Show a specific item""" |
|
85 | """GET /permissions/id: Show a specific item""" | |
86 | # url('permission', id=ID) |
|
86 | # url('permission', id=ID) | |
87 |
|
87 | |||
88 | def edit(self, id, format='html'): |
|
88 | def edit(self, id, format='html'): | |
89 | """GET /permissions/id/edit: Form to edit an existing item""" |
|
89 | """GET /permissions/id/edit: Form to edit an existing item""" | |
90 | # url('edit_permission', id=ID) |
|
90 | # url('edit_permission', id=ID) |
@@ -1,194 +1,197 b'' | |||||
1 | #!/usr/bin/env python |
|
1 | #!/usr/bin/env python | |
2 | # encoding: utf-8 |
|
2 | # encoding: utf-8 | |
3 | # repos controller for pylons |
|
3 | # repos controller for pylons | |
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> |
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |
5 | # This program is free software; you can redistribute it and/or |
|
5 | # This program is free software; you can redistribute it and/or | |
6 | # modify it under the terms of the GNU General Public License |
|
6 | # modify it under the terms of the GNU General Public License | |
7 | # as published by the Free Software Foundation; version 2 |
|
7 | # as published by the Free Software Foundation; version 2 | |
8 | # of the License or (at your opinion) any later version of the license. |
|
8 | # of the License or (at your opinion) any later version of the license. | |
9 | # |
|
9 | # | |
10 | # This program is distributed in the hope that it will be useful, |
|
10 | # This program is distributed in the hope that it will be useful, | |
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | # GNU General Public License for more details. |
|
13 | # GNU General Public License for more details. | |
14 | # |
|
14 | # | |
15 | # You should have received a copy of the GNU General Public License |
|
15 | # You should have received a copy of the GNU General Public License | |
16 | # along with this program; if not, write to the Free Software |
|
16 | # along with this program; if not, write to the Free Software | |
17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
|
17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |
18 | # MA 02110-1301, USA. |
|
18 | # MA 02110-1301, USA. | |
19 | """ |
|
19 | """ | |
20 | Created on April 7, 2010 |
|
20 | Created on April 7, 2010 | |
21 | admin controller for pylons |
|
21 | admin controller for pylons | |
22 | @author: marcink |
|
22 | @author: marcink | |
23 | """ |
|
23 | """ | |
|
24 | from formencode import htmlfill | |||
24 | from operator import itemgetter |
|
25 | from operator import itemgetter | |
25 |
from pylons import request, response, session, tmpl_context as c, url |
|
26 | from pylons import request, response, session, tmpl_context as c, url | |
26 | app_globals as g |
|
|||
27 | from pylons.controllers.util import abort, redirect |
|
27 | from pylons.controllers.util import abort, redirect | |
28 | from pylons.i18n.translation import _ |
|
28 | from pylons.i18n.translation import _ | |
29 | from pylons_app.lib import helpers as h |
|
29 | from pylons_app.lib import helpers as h | |
30 | from pylons_app.lib.auth import LoginRequired |
|
30 | from pylons_app.lib.auth import LoginRequired, HasPermissionAllDecorator | |
31 | from pylons_app.lib.base import BaseController, render |
|
31 | from pylons_app.lib.base import BaseController, render | |
32 | from pylons_app.lib.utils import invalidate_cache |
|
32 | from pylons_app.lib.utils import invalidate_cache | |
33 |
from pylons_app.model. |
|
33 | from pylons_app.model.forms import RepoForm | |
34 | from pylons_app.model.hg_model import HgModel |
|
34 | from pylons_app.model.hg_model import HgModel | |
35 |
from pylons_app.model. |
|
35 | from pylons_app.model.repo_model import RepoModel | |
36 | from pylons_app.model.meta import Session |
|
|||
37 | import formencode |
|
36 | import formencode | |
38 | from formencode import htmlfill |
|
|||
39 | import logging |
|
37 | import logging | |
40 | log = logging.getLogger(__name__) |
|
38 | log = logging.getLogger(__name__) | |
41 |
|
39 | |||
42 | class ReposController(BaseController): |
|
40 | class ReposController(BaseController): | |
43 | """REST Controller styled on the Atom Publishing Protocol""" |
|
41 | """REST Controller styled on the Atom Publishing Protocol""" | |
44 | # To properly map this controller, ensure your config/routing.py |
|
42 | # To properly map this controller, ensure your config/routing.py | |
45 | # file has a resource setup: |
|
43 | # file has a resource setup: | |
46 | # map.resource('repo', 'repos') |
|
44 | # map.resource('repo', 'repos') | |
|
45 | ||||
47 | @LoginRequired() |
|
46 | @LoginRequired() | |
|
47 | @HasPermissionAllDecorator('hg.admin') | |||
48 | def __before__(self): |
|
48 | def __before__(self): | |
49 | c.admin_user = session.get('admin_user') |
|
49 | c.admin_user = session.get('admin_user') | |
50 | c.admin_username = session.get('admin_username') |
|
50 | c.admin_username = session.get('admin_username') | |
51 | super(ReposController, self).__before__() |
|
51 | super(ReposController, self).__before__() | |
52 |
|
52 | |||
53 | def index(self, format='html'): |
|
53 | def index(self, format='html'): | |
54 | """GET /repos: All items in the collection""" |
|
54 | """GET /repos: All items in the collection""" | |
55 | # url('repos') |
|
55 | # url('repos') | |
56 | cached_repo_list = HgModel().get_repos() |
|
56 | cached_repo_list = HgModel().get_repos() | |
57 | c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort')) |
|
57 | c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort')) | |
58 | return render('admin/repos/repos.html') |
|
58 | return render('admin/repos/repos.html') | |
59 |
|
59 | |||
60 | def create(self): |
|
60 | def create(self): | |
61 | """POST /repos: Create a new item""" |
|
61 | """POST /repos: Create a new item""" | |
62 | # url('repos') |
|
62 | # url('repos') | |
63 | repo_model = RepoModel() |
|
63 | repo_model = RepoModel() | |
64 | _form = RepoForm()() |
|
64 | _form = RepoForm()() | |
65 | try: |
|
65 | try: | |
66 | form_result = _form.to_python(dict(request.POST)) |
|
66 | form_result = _form.to_python(dict(request.POST)) | |
67 | repo_model.create(form_result, c.hg_app_user) |
|
67 | repo_model.create(form_result, c.hg_app_user) | |
68 | invalidate_cache('cached_repo_list') |
|
68 | invalidate_cache('cached_repo_list') | |
69 | h.flash(_('created repository %s') % form_result['repo_name'], |
|
69 | h.flash(_('created repository %s') % form_result['repo_name'], | |
70 | category='success') |
|
70 | category='success') | |
71 |
|
71 | |||
72 | except formencode.Invalid as errors: |
|
72 | except formencode.Invalid as errors: | |
73 | c.form_errors = errors.error_dict |
|
73 | c.form_errors = errors.error_dict | |
74 | c.new_repo = errors.value['repo_name'] |
|
74 | c.new_repo = errors.value['repo_name'] | |
75 | return htmlfill.render( |
|
75 | return htmlfill.render( | |
76 | render('admin/repos/repo_add.html'), |
|
76 | render('admin/repos/repo_add.html'), | |
77 | defaults=errors.value, |
|
77 | defaults=errors.value, | |
78 | encoding="UTF-8") |
|
78 | encoding="UTF-8") | |
79 |
|
79 | |||
80 | except Exception: |
|
80 | except Exception: | |
81 | h.flash(_('error occured during creation of repository %s') \ |
|
81 | h.flash(_('error occured during creation of repository %s') \ | |
82 | % form_result['repo_name'], category='error') |
|
82 | % form_result['repo_name'], category='error') | |
83 |
|
83 | |||
84 | return redirect('repos') |
|
84 | return redirect('repos') | |
85 |
|
85 | |||
86 | def new(self, format='html'): |
|
86 | def new(self, format='html'): | |
87 | """GET /repos/new: Form to create a new item""" |
|
87 | """GET /repos/new: Form to create a new item""" | |
88 | new_repo = request.GET.get('repo', '') |
|
88 | new_repo = request.GET.get('repo', '') | |
89 | c.new_repo = h.repo_name_slug(new_repo) |
|
89 | c.new_repo = h.repo_name_slug(new_repo) | |
90 |
|
90 | |||
91 | return render('admin/repos/repo_add.html') |
|
91 | return render('admin/repos/repo_add.html') | |
92 |
|
92 | |||
93 | def update(self, repo_name): |
|
93 | def update(self, repo_name): | |
94 | """PUT /repos/repo_name: Update an existing item""" |
|
94 | """PUT /repos/repo_name: Update an existing item""" | |
95 | # Forms posted to this method should contain a hidden field: |
|
95 | # Forms posted to this method should contain a hidden field: | |
96 | # <input type="hidden" name="_method" value="PUT" /> |
|
96 | # <input type="hidden" name="_method" value="PUT" /> | |
97 | # Or using helpers: |
|
97 | # Or using helpers: | |
98 | # h.form(url('repo', repo_name=ID), |
|
98 | # h.form(url('repo', repo_name=ID), | |
99 | # method='put') |
|
99 | # method='put') | |
100 | # url('repo', repo_name=ID) |
|
100 | # url('repo', repo_name=ID) | |
101 | repo_model = RepoModel() |
|
101 | repo_model = RepoModel() | |
102 | _form = RepoForm(edit=True)() |
|
102 | _form = RepoForm(edit=True)() | |
103 | try: |
|
103 | try: | |
104 | form_result = _form.to_python(dict(request.POST)) |
|
104 | form_result = _form.to_python(dict(request.POST)) | |
105 | repo_model.update(repo_name, form_result) |
|
105 | repo_model.update(repo_name, form_result) | |
106 | invalidate_cache('cached_repo_list') |
|
106 | invalidate_cache('cached_repo_list') | |
107 |
h.flash(_('Repository %s updated succesfully' % repo_name), |
|
107 | h.flash(_('Repository %s updated succesfully' % repo_name), | |
|
108 | category='success') | |||
108 |
|
109 | |||
109 | except formencode.Invalid as errors: |
|
110 | except formencode.Invalid as errors: | |
110 | c.repo_info = repo_model.get(repo_name) |
|
111 | c.repo_info = repo_model.get(repo_name) | |
111 | c.users_array = repo_model.get_users_js() |
|
112 | c.users_array = repo_model.get_users_js() | |
112 | errors.value.update({'user':c.repo_info.user.username}) |
|
113 | errors.value.update({'user':c.repo_info.user.username}) | |
113 | c.form_errors = errors.error_dict |
|
114 | c.form_errors = errors.error_dict | |
114 | return htmlfill.render( |
|
115 | return htmlfill.render( | |
115 | render('admin/repos/repo_edit.html'), |
|
116 | render('admin/repos/repo_edit.html'), | |
116 | defaults=errors.value, |
|
117 | defaults=errors.value, | |
117 | encoding="UTF-8") |
|
118 | encoding="UTF-8") | |
118 | except Exception: |
|
119 | except Exception: | |
119 | h.flash(_('error occured during update of repository %s') \ |
|
120 | h.flash(_('error occured during update of repository %s') \ | |
120 | % form_result['repo_name'], category='error') |
|
121 | % form_result['repo_name'], category='error') | |
121 | return redirect(url('repos')) |
|
122 | return redirect(url('repos')) | |
122 |
|
123 | |||
123 | def delete(self, repo_name): |
|
124 | def delete(self, repo_name): | |
124 | """DELETE /repos/repo_name: Delete an existing item""" |
|
125 | """DELETE /repos/repo_name: Delete an existing item""" | |
125 | # Forms posted to this method should contain a hidden field: |
|
126 | # Forms posted to this method should contain a hidden field: | |
126 | # <input type="hidden" name="_method" value="DELETE" /> |
|
127 | # <input type="hidden" name="_method" value="DELETE" /> | |
127 | # Or using helpers: |
|
128 | # Or using helpers: | |
128 | # h.form(url('repo', repo_name=ID), |
|
129 | # h.form(url('repo', repo_name=ID), | |
129 | # method='delete') |
|
130 | # method='delete') | |
130 | # url('repo', repo_name=ID) |
|
131 | # url('repo', repo_name=ID) | |
131 |
|
132 | |||
132 | repo_model = RepoModel() |
|
133 | repo_model = RepoModel() | |
133 | repo = repo_model.get(repo_name) |
|
134 | repo = repo_model.get(repo_name) | |
134 | if not repo: |
|
135 | if not repo: | |
135 | h.flash(_('%s repository is not mapped to db perhaps' |
|
136 | h.flash(_('%s repository is not mapped to db perhaps' | |
136 | ' it was moved or renamed from the filesystem' |
|
137 | ' it was moved or renamed from the filesystem' | |
137 | ' please run the application again' |
|
138 | ' please run the application again' | |
138 |
' in order to rescan repositories') % repo_name, |
|
139 | ' in order to rescan repositories') % repo_name, | |
|
140 | category='error') | |||
139 |
|
141 | |||
140 | return redirect(url('repos')) |
|
142 | return redirect(url('repos')) | |
141 | try: |
|
143 | try: | |
142 | repo_model.delete(repo) |
|
144 | repo_model.delete(repo) | |
143 | invalidate_cache('cached_repo_list') |
|
145 | invalidate_cache('cached_repo_list') | |
144 | h.flash(_('deleted repository %s') % repo_name, category='success') |
|
146 | h.flash(_('deleted repository %s') % repo_name, category='success') | |
145 | except Exception: |
|
147 | except Exception: | |
146 | h.flash(_('An error occured during deletion of %s') % repo_name, |
|
148 | h.flash(_('An error occured during deletion of %s') % repo_name, | |
147 | category='error') |
|
149 | category='error') | |
148 |
|
150 | |||
149 | return redirect(url('repos')) |
|
151 | return redirect(url('repos')) | |
150 |
|
152 | |||
151 | def delete_perm_user(self, repo_name): |
|
153 | def delete_perm_user(self, repo_name): | |
152 | """ |
|
154 | """ | |
153 | DELETE an existing repository permission user |
|
155 | DELETE an existing repository permission user | |
154 | @param repo_name: |
|
156 | @param repo_name: | |
155 | """ |
|
157 | """ | |
156 |
|
158 | |||
157 | try: |
|
159 | try: | |
158 | repo_model = RepoModel() |
|
160 | repo_model = RepoModel() | |
159 | repo_model.delete_perm_user(request.POST, repo_name) |
|
161 | repo_model.delete_perm_user(request.POST, repo_name) | |
160 | except Exception as e: |
|
162 | except Exception as e: | |
161 | h.flash(_('An error occured during deletion of repository user'), |
|
163 | h.flash(_('An error occured during deletion of repository user'), | |
162 | category='error') |
|
164 | category='error') | |
163 |
|
165 | |||
164 |
|
166 | |||
165 | def show(self, repo_name, format='html'): |
|
167 | def show(self, repo_name, format='html'): | |
166 | """GET /repos/repo_name: Show a specific item""" |
|
168 | """GET /repos/repo_name: Show a specific item""" | |
167 | # url('repo', repo_name=ID) |
|
169 | # url('repo', repo_name=ID) | |
168 |
|
170 | |||
169 | def edit(self, repo_name, format='html'): |
|
171 | def edit(self, repo_name, format='html'): | |
170 | """GET /repos/repo_name/edit: Form to edit an existing item""" |
|
172 | """GET /repos/repo_name/edit: Form to edit an existing item""" | |
171 | # url('edit_repo', repo_name=ID) |
|
173 | # url('edit_repo', repo_name=ID) | |
172 | repo_model = RepoModel() |
|
174 | repo_model = RepoModel() | |
173 | c.repo_info = repo = repo_model.get(repo_name) |
|
175 | c.repo_info = repo = repo_model.get(repo_name) | |
174 | if not repo: |
|
176 | if not repo: | |
175 | h.flash(_('%s repository is not mapped to db perhaps' |
|
177 | h.flash(_('%s repository is not mapped to db perhaps' | |
176 | ' it was created or renamed from the filesystem' |
|
178 | ' it was created or renamed from the filesystem' | |
177 | ' please run the application again' |
|
179 | ' please run the application again' | |
178 |
' in order to rescan repositories') % repo_name, |
|
180 | ' in order to rescan repositories') % repo_name, | |
|
181 | category='error') | |||
179 |
|
182 | |||
180 | return redirect(url('repos')) |
|
183 | return redirect(url('repos')) | |
181 | defaults = c.repo_info.__dict__ |
|
184 | defaults = c.repo_info.__dict__ | |
182 | defaults.update({'user':c.repo_info.user.username}) |
|
185 | defaults.update({'user':c.repo_info.user.username}) | |
183 | c.users_array = repo_model.get_users_js() |
|
186 | c.users_array = repo_model.get_users_js() | |
184 |
|
187 | |||
185 | for p in c.repo_info.repo2perm: |
|
188 | for p in c.repo_info.repo2perm: | |
186 | defaults.update({'perm_%s' % p.user.username: |
|
189 | defaults.update({'perm_%s' % p.user.username: | |
187 | p.permission.permission_name}) |
|
190 | p.permission.permission_name}) | |
188 |
|
191 | |||
189 | return htmlfill.render( |
|
192 | return htmlfill.render( | |
190 | render('admin/repos/repo_edit.html'), |
|
193 | render('admin/repos/repo_edit.html'), | |
191 | defaults=defaults, |
|
194 | defaults=defaults, | |
192 | encoding="UTF-8", |
|
195 | encoding="UTF-8", | |
193 | force_defaults=False |
|
196 | force_defaults=False | |
194 | ) |
|
197 | ) |
@@ -1,48 +1,49 b'' | |||||
1 | #!/usr/bin/env python |
|
1 | #!/usr/bin/env python | |
2 | # encoding: utf-8 |
|
2 | # encoding: utf-8 | |
3 | # shortlog controller for pylons |
|
3 | # shortlog controller for pylons | |
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> |
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |
5 |
|
5 | |||
6 | # This program is free software; you can redistribute it and/or |
|
6 | # This program is free software; you can redistribute it and/or | |
7 | # modify it under the terms of the GNU General Public License |
|
7 | # modify it under the terms of the GNU General Public License | |
8 | # as published by the Free Software Foundation; version 2 |
|
8 | # as published by the Free Software Foundation; version 2 | |
9 | # of the License or (at your opinion) any later version of the license. |
|
9 | # of the License or (at your opinion) any later version of the license. | |
10 | # |
|
10 | # | |
11 | # This program is distributed in the hope that it will be useful, |
|
11 | # This program is distributed in the hope that it will be useful, | |
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | # GNU General Public License for more details. |
|
14 | # GNU General Public License for more details. | |
15 | # |
|
15 | # | |
16 | # You should have received a copy of the GNU General Public License |
|
16 | # You should have received a copy of the GNU General Public License | |
17 | # along with this program; if not, write to the Free Software |
|
17 | # along with this program; if not, write to the Free Software | |
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |
19 | # MA 02110-1301, USA. |
|
19 | # MA 02110-1301, USA. | |
20 | """ |
|
20 | """ | |
21 | Created on April 18, 2010 |
|
21 | Created on April 18, 2010 | |
22 | shortlog controller for pylons |
|
22 | shortlog controller for pylons | |
23 | @author: marcink |
|
23 | @author: marcink | |
24 | """ |
|
24 | """ | |
25 | from pylons import tmpl_context as c, request |
|
25 | from pylons import tmpl_context as c, request | |
26 | from pylons_app.lib.auth import LoginRequired |
|
26 | from pylons_app.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator | |
27 | from pylons_app.lib.base import BaseController, render |
|
27 | from pylons_app.lib.base import BaseController, render | |
28 | from pylons_app.model.hg_model import HgModel |
|
28 | from pylons_app.model.hg_model import HgModel | |
29 | from webhelpers.paginate import Page |
|
29 | from webhelpers.paginate import Page | |
30 | import logging |
|
30 | import logging | |
31 |
|
||||
32 | log = logging.getLogger(__name__) |
|
31 | log = logging.getLogger(__name__) | |
33 |
|
32 | |||
34 | class ShortlogController(BaseController): |
|
33 | class ShortlogController(BaseController): | |
35 |
|
34 | |||
36 | @LoginRequired() |
|
35 | @LoginRequired() | |
|
36 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', | |||
|
37 | 'repository.admin') | |||
37 | def __before__(self): |
|
38 | def __before__(self): | |
38 | super(ShortlogController, self).__before__() |
|
39 | super(ShortlogController, self).__before__() | |
39 |
|
40 | |||
40 | def index(self): |
|
41 | def index(self): | |
41 | p = int(request.params.get('page', 1)) |
|
42 | p = int(request.params.get('page', 1)) | |
42 | repo = HgModel().get_repo(c.repo_name) |
|
43 | repo = HgModel().get_repo(c.repo_name) | |
43 | c.repo_changesets = Page(repo, page=p, items_per_page=20) |
|
44 | c.repo_changesets = Page(repo, page=p, items_per_page=20) | |
44 | c.shortlog_data = render('shortlog/shortlog_data.html') |
|
45 | c.shortlog_data = render('shortlog/shortlog_data.html') | |
45 | if request.params.get('partial'): |
|
46 | if request.params.get('partial'): | |
46 | return c.shortlog_data |
|
47 | return c.shortlog_data | |
47 | r = render('shortlog/shortlog.html') |
|
48 | r = render('shortlog/shortlog.html') | |
48 | return r |
|
49 | return r |
@@ -1,59 +1,60 b'' | |||||
1 | #!/usr/bin/env python |
|
1 | #!/usr/bin/env python | |
2 | # encoding: utf-8 |
|
2 | # encoding: utf-8 | |
3 | # summary controller for pylons |
|
3 | # summary controller for pylons | |
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> |
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |
5 |
|
5 | |||
6 | # This program is free software; you can redistribute it and/or |
|
6 | # This program is free software; you can redistribute it and/or | |
7 | # modify it under the terms of the GNU General Public License |
|
7 | # modify it under the terms of the GNU General Public License | |
8 | # as published by the Free Software Foundation; version 2 |
|
8 | # as published by the Free Software Foundation; version 2 | |
9 | # of the License or (at your opinion) any later version of the license. |
|
9 | # of the License or (at your opinion) any later version of the license. | |
10 | # |
|
10 | # | |
11 | # This program is distributed in the hope that it will be useful, |
|
11 | # This program is distributed in the hope that it will be useful, | |
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | # GNU General Public License for more details. |
|
14 | # GNU General Public License for more details. | |
15 | # |
|
15 | # | |
16 | # You should have received a copy of the GNU General Public License |
|
16 | # You should have received a copy of the GNU General Public License | |
17 | # along with this program; if not, write to the Free Software |
|
17 | # along with this program; if not, write to the Free Software | |
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |
19 | # MA 02110-1301, USA. |
|
19 | # MA 02110-1301, USA. | |
20 | """ |
|
20 | """ | |
21 | Created on April 18, 2010 |
|
21 | Created on April 18, 2010 | |
22 | summary controller for pylons |
|
22 | summary controller for pylons | |
23 | @author: marcink |
|
23 | @author: marcink | |
24 | """ |
|
24 | """ | |
25 | from pylons import tmpl_context as c, request |
|
25 | from pylons import tmpl_context as c, request | |
26 | from pylons_app.lib.auth import LoginRequired |
|
26 | from pylons_app.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator | |
27 | from pylons_app.lib.base import BaseController, render |
|
27 | from pylons_app.lib.base import BaseController, render | |
28 | from pylons_app.model.hg_model import HgModel |
|
28 | from pylons_app.model.hg_model import HgModel | |
29 | from webhelpers.paginate import Page |
|
29 | from webhelpers.paginate import Page | |
30 | import logging |
|
30 | import logging | |
31 |
|
||||
32 | log = logging.getLogger(__name__) |
|
31 | log = logging.getLogger(__name__) | |
33 |
|
32 | |||
34 | class SummaryController(BaseController): |
|
33 | class SummaryController(BaseController): | |
35 |
|
34 | |||
36 | @LoginRequired() |
|
35 | @LoginRequired() | |
|
36 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', | |||
|
37 | 'repository.admin') | |||
37 | def __before__(self): |
|
38 | def __before__(self): | |
38 | super(SummaryController, self).__before__() |
|
39 | super(SummaryController, self).__before__() | |
39 |
|
40 | |||
40 | def index(self): |
|
41 | def index(self): | |
41 | hg_model = HgModel() |
|
42 | hg_model = HgModel() | |
42 | c.repo_info = hg_model.get_repo(c.repo_name) |
|
43 | c.repo_info = hg_model.get_repo(c.repo_name) | |
43 | c.repo_changesets = Page(list(c.repo_info[:10]), page=1, items_per_page=20) |
|
44 | c.repo_changesets = Page(list(c.repo_info[:10]), page=1, items_per_page=20) | |
44 | e = request.environ |
|
45 | e = request.environ | |
45 | uri = u'%(protocol)s://%(user)s@%(host)s/%(repo_name)s' % { |
|
46 | uri = u'%(protocol)s://%(user)s@%(host)s/%(repo_name)s' % { | |
46 | 'protocol': e.get('wsgi.url_scheme'), |
|
47 | 'protocol': e.get('wsgi.url_scheme'), | |
47 | 'user':str(c.hg_app_user.username), |
|
48 | 'user':str(c.hg_app_user.username), | |
48 | 'host':e.get('HTTP_HOST'), |
|
49 | 'host':e.get('HTTP_HOST'), | |
49 | 'repo_name':c.repo_name, } |
|
50 | 'repo_name':c.repo_name, } | |
50 | c.clone_repo_url = uri |
|
51 | c.clone_repo_url = uri | |
51 | c.repo_tags = {} |
|
52 | c.repo_tags = {} | |
52 | for name, hash in c.repo_info.tags.items()[:10]: |
|
53 | for name, hash in c.repo_info.tags.items()[:10]: | |
53 | c.repo_tags[name] = c.repo_info.get_changeset(hash) |
|
54 | c.repo_tags[name] = c.repo_info.get_changeset(hash) | |
54 |
|
55 | |||
55 | c.repo_branches = {} |
|
56 | c.repo_branches = {} | |
56 | for name, hash in c.repo_info.branches.items()[:10]: |
|
57 | for name, hash in c.repo_info.branches.items()[:10]: | |
57 | c.repo_branches[name] = c.repo_info.get_changeset(hash) |
|
58 | c.repo_branches[name] = c.repo_info.get_changeset(hash) | |
58 |
|
59 | |||
59 | return render('summary/summary.html') |
|
60 | return render('summary/summary.html') |
@@ -1,46 +1,46 b'' | |||||
1 | #!/usr/bin/env python |
|
1 | #!/usr/bin/env python | |
2 | # encoding: utf-8 |
|
2 | # encoding: utf-8 | |
3 | # tags controller for pylons |
|
3 | # tags controller for pylons | |
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> |
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |
5 |
|
5 | |||
6 | # This program is free software; you can redistribute it and/or |
|
6 | # This program is free software; you can redistribute it and/or | |
7 | # modify it under the terms of the GNU General Public License |
|
7 | # modify it under the terms of the GNU General Public License | |
8 | # as published by the Free Software Foundation; version 2 |
|
8 | # as published by the Free Software Foundation; version 2 | |
9 | # of the License or (at your opinion) any later version of the license. |
|
9 | # of the License or (at your opinion) any later version of the license. | |
10 | # |
|
10 | # | |
11 | # This program is distributed in the hope that it will be useful, |
|
11 | # This program is distributed in the hope that it will be useful, | |
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | # GNU General Public License for more details. |
|
14 | # GNU General Public License for more details. | |
15 | # |
|
15 | # | |
16 | # You should have received a copy of the GNU General Public License |
|
16 | # You should have received a copy of the GNU General Public License | |
17 | # along with this program; if not, write to the Free Software |
|
17 | # along with this program; if not, write to the Free Software | |
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |
19 | # MA 02110-1301, USA. |
|
19 | # MA 02110-1301, USA. | |
20 | """ |
|
20 | """ | |
21 | Created on April 21, 2010 |
|
21 | Created on April 21, 2010 | |
22 | tags controller for pylons |
|
22 | tags controller for pylons | |
23 | @author: marcink |
|
23 | @author: marcink | |
24 | """ |
|
24 | """ | |
25 | from pylons import tmpl_context as c |
|
25 | from pylons import tmpl_context as c, request | |
26 | from pylons_app.lib.auth import LoginRequired |
|
26 | from pylons_app.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator | |
27 | from pylons_app.lib.base import BaseController, render |
|
27 | from pylons_app.lib.base import BaseController, render | |
28 | from pylons_app.model.hg_model import HgModel |
|
28 | from pylons_app.model.hg_model import HgModel | |
29 | import logging |
|
29 | import logging | |
30 |
|
||||
31 | log = logging.getLogger(__name__) |
|
30 | log = logging.getLogger(__name__) | |
32 |
|
31 | |||
33 | class TagsController(BaseController): |
|
32 | class TagsController(BaseController): | |
34 |
|
33 | |||
35 | @LoginRequired() |
|
34 | @LoginRequired() | |
|
35 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', 'repository.admin') | |||
36 | def __before__(self): |
|
36 | def __before__(self): | |
37 | super(TagsController, self).__before__() |
|
37 | super(TagsController, self).__before__() | |
38 |
|
38 | |||
39 | def index(self): |
|
39 | def index(self): | |
40 | hg_model = HgModel() |
|
40 | hg_model = HgModel() | |
41 | c.repo_info = hg_model.get_repo(c.repo_name) |
|
41 | c.repo_info = hg_model.get_repo(c.repo_name) | |
42 | c.repo_tags = {} |
|
42 | c.repo_tags = {} | |
43 | for name, hash in c.repo_info.tags.items(): |
|
43 | for name, hash in c.repo_info.tags.items(): | |
44 | c.repo_tags[name] = c.repo_info.get_changeset(hash) |
|
44 | c.repo_tags[name] = c.repo_info.get_changeset(hash) | |
45 |
|
45 | |||
46 | return render('tags/tags.html') |
|
46 | return render('tags/tags.html') |
@@ -1,149 +1,155 b'' | |||||
1 | #!/usr/bin/env python |
|
1 | #!/usr/bin/env python | |
2 | # encoding: utf-8 |
|
2 | # encoding: utf-8 | |
3 | # users controller for pylons |
|
3 | # users controller for pylons | |
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> |
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |
5 |
|
5 | |||
6 | # This program is free software; you can redistribute it and/or |
|
6 | # This program is free software; you can redistribute it and/or | |
7 | # modify it under the terms of the GNU General Public License |
|
7 | # modify it under the terms of the GNU General Public License | |
8 | # as published by the Free Software Foundation; version 2 |
|
8 | # as published by the Free Software Foundation; version 2 | |
9 | # of the License or (at your opinion) any later version of the license. |
|
9 | # of the License or (at your opinion) any later version of the license. | |
10 | # |
|
10 | # | |
11 | # This program is distributed in the hope that it will be useful, |
|
11 | # This program is distributed in the hope that it will be useful, | |
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | # GNU General Public License for more details. |
|
14 | # GNU General Public License for more details. | |
15 | # |
|
15 | # | |
16 | # You should have received a copy of the GNU General Public License |
|
16 | # You should have received a copy of the GNU General Public License | |
17 | # along with this program; if not, write to the Free Software |
|
17 | # along with this program; if not, write to the Free Software | |
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |
19 | # MA 02110-1301, USA. |
|
19 | # MA 02110-1301, USA. | |
20 | """ |
|
20 | """ | |
21 | Created on April 4, 2010 |
|
21 | Created on April 4, 2010 | |
22 | users controller for pylons |
|
22 | users controller for pylons | |
23 | @author: marcink |
|
23 | @author: marcink | |
24 | """ |
|
24 | """ | |
25 | from formencode import htmlfill |
|
25 | from formencode import htmlfill | |
26 | from pylons import request, session, tmpl_context as c, url |
|
26 | from pylons import request, session, tmpl_context as c, url | |
27 | from pylons.controllers.util import abort, redirect |
|
27 | from pylons.controllers.util import abort, redirect | |
28 | from pylons.i18n.translation import _ |
|
28 | from pylons.i18n.translation import _ | |
29 | from pylons_app.lib import helpers as h |
|
29 | from pylons_app.lib import helpers as h | |
30 | from pylons_app.lib.auth import LoginRequired, HasPermissionAllDecorator |
|
30 | from pylons_app.lib.auth import LoginRequired, HasPermissionAllDecorator | |
31 | from pylons_app.lib.base import BaseController, render |
|
31 | from pylons_app.lib.base import BaseController, render | |
32 | from pylons_app.model.db import User, UserLog |
|
32 | from pylons_app.model.db import User, UserLog | |
33 | from pylons_app.model.forms import UserForm |
|
33 | from pylons_app.model.forms import UserForm | |
34 | from pylons_app.model.user_model import UserModel |
|
34 | from pylons_app.model.user_model import UserModel, DefaultUserException | |
35 | import formencode |
|
35 | import formencode | |
36 | import logging |
|
36 | import logging | |
37 |
|
37 | |||
38 | log = logging.getLogger(__name__) |
|
38 | log = logging.getLogger(__name__) | |
39 |
|
39 | |||
40 | class UsersController(BaseController): |
|
40 | class UsersController(BaseController): | |
41 | """REST Controller styled on the Atom Publishing Protocol""" |
|
41 | """REST Controller styled on the Atom Publishing Protocol""" | |
42 | # To properly map this controller, ensure your config/routing.py |
|
42 | # To properly map this controller, ensure your config/routing.py | |
43 | # file has a resource setup: |
|
43 | # file has a resource setup: | |
44 | # map.resource('user', 'users') |
|
44 | # map.resource('user', 'users') | |
45 |
|
45 | |||
46 | @LoginRequired() |
|
46 | @LoginRequired() | |
47 | @HasPermissionAllDecorator('hg.admin') |
|
47 | @HasPermissionAllDecorator('hg.admin') | |
48 | def __before__(self): |
|
48 | def __before__(self): | |
49 | c.admin_user = session.get('admin_user') |
|
49 | c.admin_user = session.get('admin_user') | |
50 | c.admin_username = session.get('admin_username') |
|
50 | c.admin_username = session.get('admin_username') | |
51 | super(UsersController, self).__before__() |
|
51 | super(UsersController, self).__before__() | |
52 |
|
52 | |||
53 |
|
53 | |||
54 | def index(self, format='html'): |
|
54 | def index(self, format='html'): | |
55 | """GET /users: All items in the collection""" |
|
55 | """GET /users: All items in the collection""" | |
56 | # url('users') |
|
56 | # url('users') | |
57 |
|
57 | |||
58 | c.users_list = self.sa.query(User).all() |
|
58 | c.users_list = self.sa.query(User).all() | |
59 | return render('admin/users/users.html') |
|
59 | return render('admin/users/users.html') | |
60 |
|
60 | |||
61 | def create(self): |
|
61 | def create(self): | |
62 | """POST /users: Create a new item""" |
|
62 | """POST /users: Create a new item""" | |
63 | # url('users') |
|
63 | # url('users') | |
64 |
|
64 | |||
65 | user_model = UserModel() |
|
65 | user_model = UserModel() | |
66 | login_form = UserForm()() |
|
66 | login_form = UserForm()() | |
67 | try: |
|
67 | try: | |
68 | form_result = login_form.to_python(dict(request.POST)) |
|
68 | form_result = login_form.to_python(dict(request.POST)) | |
69 | user_model.create(form_result) |
|
69 | user_model.create(form_result) | |
70 | h.flash(_('created user %s') % form_result['username'], |
|
70 | h.flash(_('created user %s') % form_result['username'], | |
71 | category='success') |
|
71 | category='success') | |
72 | except formencode.Invalid as errors: |
|
72 | except formencode.Invalid as errors: | |
73 | c.form_errors = errors.error_dict |
|
73 | c.form_errors = errors.error_dict | |
74 | return htmlfill.render( |
|
74 | return htmlfill.render( | |
75 | render('admin/users/user_add.html'), |
|
75 | render('admin/users/user_add.html'), | |
76 | defaults=errors.value, |
|
76 | defaults=errors.value, | |
77 | encoding="UTF-8") |
|
77 | encoding="UTF-8") | |
78 | except Exception: |
|
78 | except Exception: | |
79 | h.flash(_('error occured during creation of user %s') \ |
|
79 | h.flash(_('error occured during creation of user %s') \ | |
80 | % form_result['username'], category='error') |
|
80 | % form_result['username'], category='error') | |
81 | return redirect(url('users')) |
|
81 | return redirect(url('users')) | |
82 |
|
82 | |||
83 | def new(self, format='html'): |
|
83 | def new(self, format='html'): | |
84 | """GET /users/new: Form to create a new item""" |
|
84 | """GET /users/new: Form to create a new item""" | |
85 | # url('new_user') |
|
85 | # url('new_user') | |
86 | return render('admin/users/user_add.html') |
|
86 | return render('admin/users/user_add.html') | |
87 |
|
87 | |||
88 | def update(self, id): |
|
88 | def update(self, id): | |
89 | """PUT /users/id: Update an existing item""" |
|
89 | """PUT /users/id: Update an existing item""" | |
90 | # Forms posted to this method should contain a hidden field: |
|
90 | # Forms posted to this method should contain a hidden field: | |
91 | # <input type="hidden" name="_method" value="PUT" /> |
|
91 | # <input type="hidden" name="_method" value="PUT" /> | |
92 | # Or using helpers: |
|
92 | # Or using helpers: | |
93 | # h.form(url('user', id=ID), |
|
93 | # h.form(url('user', id=ID), | |
94 | # method='put') |
|
94 | # method='put') | |
95 | # url('user', id=ID) |
|
95 | # url('user', id=ID) | |
96 | user_model = UserModel() |
|
96 | user_model = UserModel() | |
97 | _form = UserForm(edit=True)() |
|
97 | _form = UserForm(edit=True)() | |
98 | try: |
|
98 | try: | |
99 | form_result = _form.to_python(dict(request.POST)) |
|
99 | form_result = _form.to_python(dict(request.POST)) | |
100 | user_model.update(id, form_result) |
|
100 | user_model.update(id, form_result) | |
101 | h.flash(_('User updated succesfully'), category='success') |
|
101 | h.flash(_('User updated succesfully'), category='success') | |
102 |
|
102 | |||
103 | except formencode.Invalid as errors: |
|
103 | except formencode.Invalid as errors: | |
104 | c.user = user_model.get_user(id) |
|
104 | c.user = user_model.get_user(id) | |
105 | c.form_errors = errors.error_dict |
|
105 | c.form_errors = errors.error_dict | |
106 | return htmlfill.render( |
|
106 | return htmlfill.render( | |
107 | render('admin/users/user_edit.html'), |
|
107 | render('admin/users/user_edit.html'), | |
108 | defaults=errors.value, |
|
108 | defaults=errors.value, | |
109 | encoding="UTF-8") |
|
109 | encoding="UTF-8") | |
110 | except Exception: |
|
110 | except Exception: | |
111 | h.flash(_('error occured during update of user %s') \ |
|
111 | h.flash(_('error occured during update of user %s') \ | |
112 | % form_result['username'], category='error') |
|
112 | % form_result['username'], category='error') | |
113 |
|
113 | |||
114 | return redirect(url('users')) |
|
114 | return redirect(url('users')) | |
115 |
|
115 | |||
116 | def delete(self, id): |
|
116 | def delete(self, id): | |
117 | """DELETE /users/id: Delete an existing item""" |
|
117 | """DELETE /users/id: Delete an existing item""" | |
118 | # Forms posted to this method should contain a hidden field: |
|
118 | # Forms posted to this method should contain a hidden field: | |
119 | # <input type="hidden" name="_method" value="DELETE" /> |
|
119 | # <input type="hidden" name="_method" value="DELETE" /> | |
120 | # Or using helpers: |
|
120 | # Or using helpers: | |
121 | # h.form(url('user', id=ID), |
|
121 | # h.form(url('user', id=ID), | |
122 | # method='delete') |
|
122 | # method='delete') | |
123 | # url('user', id=ID) |
|
123 | # url('user', id=ID) | |
124 | user_model = UserModel() |
|
124 | user_model = UserModel() | |
125 | try: |
|
125 | try: | |
126 | user_model.delete(id) |
|
126 | user_model.delete(id) | |
127 | h.flash(_('sucessfully deleted user'), category='success') |
|
127 | h.flash(_('sucessfully deleted user'), category='success') | |
|
128 | except DefaultUserException as e: | |||
|
129 | h.flash(str(e), category='warning') | |||
128 | except Exception: |
|
130 | except Exception: | |
129 | h.flash(_('An error occured during deletion of user'), |
|
131 | h.flash(_('An error occured during deletion of user'), | |
130 | category='error') |
|
132 | category='error') | |
131 |
|
||||
132 | return redirect(url('users')) |
|
133 | return redirect(url('users')) | |
133 |
|
134 | |||
134 | def show(self, id, format='html'): |
|
135 | def show(self, id, format='html'): | |
135 | """GET /users/id: Show a specific item""" |
|
136 | """GET /users/id: Show a specific item""" | |
136 | # url('user', id=ID) |
|
137 | # url('user', id=ID) | |
137 |
|
138 | |||
138 |
|
139 | |||
139 | def edit(self, id, format='html'): |
|
140 | def edit(self, id, format='html'): | |
140 | """GET /users/id/edit: Form to edit an existing item""" |
|
141 | """GET /users/id/edit: Form to edit an existing item""" | |
141 | # url('edit_user', id=ID) |
|
142 | # url('edit_user', id=ID) | |
142 | c.user = self.sa.query(User).get(id) |
|
143 | c.user = self.sa.query(User).get(id) | |
|
144 | if c.user.username == 'default': | |||
|
145 | h.flash(_("You can't edit this user since it's" | |||
|
146 | " crucial for entire application"), category='warning') | |||
|
147 | return redirect(url('users')) | |||
|
148 | ||||
143 | defaults = c.user.__dict__ |
|
149 | defaults = c.user.__dict__ | |
144 | return htmlfill.render( |
|
150 | return htmlfill.render( | |
145 | render('admin/users/user_edit.html'), |
|
151 | render('admin/users/user_edit.html'), | |
146 | defaults=defaults, |
|
152 | defaults=defaults, | |
147 | encoding="UTF-8", |
|
153 | encoding="UTF-8", | |
148 | force_defaults=False |
|
154 | force_defaults=False | |
149 | ) |
|
155 | ) |
@@ -1,159 +1,163 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 class="breadcrumbs">${next.breadcrumbs()}</h1> |
|
17 | <h1 class="breadcrumbs">${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 | <script type="text/javascript">${h.tooltip.activate()}</script> |
|
31 | <script type="text/javascript">${h.tooltip.activate()}</script> | |
32 | </div> |
|
32 | </div> | |
33 | <div class="page-footer"> |
|
33 | <div class="page-footer"> | |
34 | Hg App ${c.hg_app_version} © 2010 by Marcin Kuzminski |
|
34 | Hg App ${c.hg_app_version} © 2010 by Marcin Kuzminski | |
35 | </div> |
|
35 | </div> | |
36 |
|
36 | |||
37 | <div id="powered-by"> |
|
37 | <div id="powered-by"> | |
38 | <p> |
|
38 | <p> | |
39 | <a href="http://mercurial.selenic.com/" title="Mercurial"> |
|
39 | <a href="http://mercurial.selenic.com/" title="Mercurial"> | |
40 | <img src="/images/hglogo.png" width="75" height="90" alt="mercurial"/></a> |
|
40 | <img src="/images/hglogo.png" width="75" height="90" alt="mercurial"/></a> | |
41 | </p> |
|
41 | </p> | |
42 | </div> |
|
42 | </div> | |
43 |
|
43 | |||
44 | <div id="corner-top-left"></div> |
|
44 | <div id="corner-top-left"></div> | |
45 | <div id="corner-top-right"></div> |
|
45 | <div id="corner-top-right"></div> | |
46 | <div id="corner-bottom-left"></div> |
|
46 | <div id="corner-bottom-left"></div> | |
47 | <div id="corner-bottom-right"></div> |
|
47 | <div id="corner-bottom-right"></div> | |
48 |
|
48 | |||
49 | </div> |
|
49 | </div> | |
50 | </body> |
|
50 | </body> | |
51 | </html> |
|
51 | </html> | |
52 |
|
52 | |||
53 | ### MAKO DEFS ### |
|
53 | ### MAKO DEFS ### | |
54 |
|
54 | |||
55 | <%def name="page_nav()"> |
|
55 | <%def name="page_nav()"> | |
56 | ${self.menu()} |
|
56 | ${self.menu()} | |
57 | ${self.submenu()} |
|
57 | ${self.submenu()} | |
58 | </%def> |
|
58 | </%def> | |
59 |
|
59 | |||
60 | <%def name="menu(current)"> |
|
60 | <%def name="menu(current)"> | |
61 | <% |
|
61 | <% | |
62 | def is_current(selected): |
|
62 | def is_current(selected): | |
63 | if selected == current: |
|
63 | if selected == current: | |
64 | return "class='current'" |
|
64 | return "class='current'" | |
65 | %> |
|
65 | %> | |
66 | %if current not in ['home','admin']: |
|
66 | %if current not in ['home','admin']: | |
67 | ##regular menu |
|
67 | ##regular menu | |
68 | <script type="text/javascript"> |
|
68 | <script type="text/javascript"> | |
69 | YAHOO.util.Event.onDOMReady(function(){ |
|
69 | YAHOO.util.Event.onDOMReady(function(){ | |
70 | YAHOO.util.Event.addListener('repo_switcher','click',function(){ |
|
70 | YAHOO.util.Event.addListener('repo_switcher','click',function(){ | |
71 | if(YAHOO.util.Dom.hasClass('repo_switcher','selected')){ |
|
71 | if(YAHOO.util.Dom.hasClass('repo_switcher','selected')){ | |
72 | YAHOO.util.Dom.setStyle('switch_repos','display','none'); |
|
72 | YAHOO.util.Dom.setStyle('switch_repos','display','none'); | |
73 | YAHOO.util.Dom.setStyle('repo_switcher','background',''); |
|
73 | YAHOO.util.Dom.setStyle('repo_switcher','background',''); | |
74 | YAHOO.util.Dom.removeClass('repo_switcher','selected'); |
|
74 | YAHOO.util.Dom.removeClass('repo_switcher','selected'); | |
75 | YAHOO.util.Dom.get('repo_switcher').removeAttribute('style'); |
|
75 | YAHOO.util.Dom.get('repo_switcher').removeAttribute('style'); | |
76 | } |
|
76 | } | |
77 | else{ |
|
77 | else{ | |
78 | YAHOO.util.Dom.setStyle('switch_repos','display',''); |
|
78 | YAHOO.util.Dom.setStyle('switch_repos','display',''); | |
79 | YAHOO.util.Dom.setStyle('repo_switcher','background','#FFFFFF'); |
|
79 | YAHOO.util.Dom.setStyle('repo_switcher','background','#FFFFFF'); | |
80 | YAHOO.util.Dom.setStyle('repo_switcher','color','#556CB5'); |
|
80 | YAHOO.util.Dom.setStyle('repo_switcher','color','#556CB5'); | |
81 | YAHOO.util.Dom.addClass('repo_switcher','selected'); |
|
81 | YAHOO.util.Dom.addClass('repo_switcher','selected'); | |
82 | } |
|
82 | } | |
83 | }); |
|
83 | }); | |
84 | YAHOO.util.Event.addListener('repos_list','change',function(e){ |
|
84 | YAHOO.util.Event.addListener('repos_list','change',function(e){ | |
85 | var wa = YAHOO.util.Dom.get('repos_list').value; |
|
85 | var wa = YAHOO.util.Dom.get('repos_list').value; | |
86 |
|
86 | |||
87 | var url = "${h.url('summary_home',repo_name='__REPLACE__')}".replace('__REPLACE__',wa); |
|
87 | var url = "${h.url('summary_home',repo_name='__REPLACE__')}".replace('__REPLACE__',wa); | |
88 | window.location = url; |
|
88 | window.location = url; | |
89 | }) |
|
89 | }) | |
90 | }); |
|
90 | }); | |
91 | </script> |
|
91 | </script> | |
92 | <ul class="page-nav"> |
|
92 | <ul class="page-nav"> | |
93 | <li> |
|
93 | <li> | |
94 | <a id="repo_switcher" title="${_('Switch repository')}" href="#">↓</a> |
|
94 | <a id="repo_switcher" title="${_('Switch repository')}" href="#">↓</a> | |
95 | <div id="switch_repos" style="display:none;position: absolute;height: 25px"> |
|
95 | <div id="switch_repos" style="display:none;position: absolute;height: 25px"> | |
96 | <select id="repos_list" size="=10" style="min-width: 150px"> |
|
96 | <select id="repos_list" size="=10" style="min-width: 150px"> | |
97 | %for repo in sorted(x.name.lower() for x in c.cached_repo_list.values()): |
|
97 | %for repo in sorted(x.name.lower() for x in c.cached_repo_list.values()): | |
98 | <option value="${repo}">${repo}</option> |
|
98 | <option value="${repo}">${repo}</option> | |
99 | %endfor |
|
99 | %endfor | |
100 | </select> |
|
100 | </select> | |
101 | </div> |
|
101 | </div> | |
102 | </li> |
|
102 | </li> | |
103 | <li ${is_current('summary')}>${h.link_to(_('summary'),h.url('summary_home',repo_name=c.repo_name))}</li> |
|
103 | <li ${is_current('summary')}>${h.link_to(_('summary'),h.url('summary_home',repo_name=c.repo_name))}</li> | |
104 | <li ${is_current('shortlog')}>${h.link_to(_('shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}</li> |
|
104 | <li ${is_current('shortlog')}>${h.link_to(_('shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}</li> | |
105 | <li ${is_current('changelog')}>${h.link_to(_('changelog'),h.url('changelog_home',repo_name=c.repo_name))}</li> |
|
105 | <li ${is_current('changelog')}>${h.link_to(_('changelog'),h.url('changelog_home',repo_name=c.repo_name))}</li> | |
106 | <li ${is_current('branches')}>${h.link_to(_('branches'),h.url('branches_home',repo_name=c.repo_name))}</li> |
|
106 | <li ${is_current('branches')}>${h.link_to(_('branches'),h.url('branches_home',repo_name=c.repo_name))}</li> | |
107 | <li ${is_current('tags')}>${h.link_to(_('tags'),h.url('tags_home',repo_name=c.repo_name))}</li> |
|
107 | <li ${is_current('tags')}>${h.link_to(_('tags'),h.url('tags_home',repo_name=c.repo_name))}</li> | |
108 | <li ${is_current('files')}>${h.link_to(_('files'),h.url('files_home',repo_name=c.repo_name))}</li> |
|
108 | <li ${is_current('files')}>${h.link_to(_('files'),h.url('files_home',repo_name=c.repo_name))}</li> | |
109 | <li>${h.link_to(_('settings'),h.url('edit_repo',repo_name=c.repo_name))}</li> |
|
109 | %if h.HasRepoPermissionAll('repository.admin')(c.repo_name): | |
|
110 | <li>${h.link_to(_('settings'),h.url('edit_repo',repo_name=c.repo_name))}</li> | |||
|
111 | %endif | |||
110 | </ul> |
|
112 | </ul> | |
111 | %else: |
|
113 | %else: | |
112 | ##Root menu |
|
114 | ##Root menu | |
113 | <ul class="page-nav"> |
|
115 | <ul class="page-nav"> | |
114 | <li ${is_current('home')}>${h.link_to(_('Home'),h.url('/'))}</li> |
|
116 | <li ${is_current('home')}>${h.link_to(_('Home'),h.url('/'))}</li> | |
115 | <li ${is_current('admin')}>${h.link_to(_('Admin'),h.url('admin_home'))}</li> |
|
117 | %if h.HasPermissionAll('hg.admin')('access admin main page'): | |
|
118 | <li ${is_current('admin')}>${h.link_to(_('Admin'),h.url('admin_home'))}</li> | |||
|
119 | %endif | |||
116 | <li class="logout">${h.link_to(u'Logout',h.url('logout_home'))}</li> |
|
120 | <li class="logout">${h.link_to(u'Logout',h.url('logout_home'))}</li> | |
117 | </ul> |
|
121 | </ul> | |
118 | %endif |
|
122 | %endif | |
119 | </div> |
|
123 | </div> | |
120 | </%def> |
|
124 | </%def> | |
121 | <%def name="submenu(current=None)"> |
|
125 | <%def name="submenu(current=None)"> | |
122 | <% |
|
126 | <% | |
123 | def is_current(selected): |
|
127 | def is_current(selected): | |
124 | if selected == current: |
|
128 | if selected == current: | |
125 | return "class='current_submenu'" |
|
129 | return "class='current_submenu'" | |
126 | %> |
|
130 | %> | |
127 | %if current != None: |
|
131 | %if current != None: | |
128 | <div> |
|
132 | <div> | |
129 | <ul class="submenu"> |
|
133 | <ul class="submenu"> | |
130 | <li ${is_current('repos')}>${h.link_to(u'repos',h.url('repos'),class_='repos')}</li> |
|
134 | <li ${is_current('repos')}>${h.link_to(u'repos',h.url('repos'),class_='repos')}</li> | |
131 | <li ${is_current('users')}>${h.link_to(u'users',h.url('users'),class_='users')}</li> |
|
135 | <li ${is_current('users')}>${h.link_to(u'users',h.url('users'),class_='users')}</li> | |
132 | <li ${is_current('permissions')}>${h.link_to(u'permissions',h.url('permissions'),class_='permissions')}</li> |
|
136 | ##comented for now<li ${is_current('permissions')}>${h.link_to(u'permissions',h.url('permissions'),class_='permissions')}</li> | |
133 | </ul> |
|
137 | </ul> | |
134 | </div> |
|
138 | </div> | |
135 | %endif |
|
139 | %endif | |
136 | </%def> |
|
140 | </%def> | |
137 |
|
141 | |||
138 |
|
142 | |||
139 | <%def name="css()"> |
|
143 | <%def name="css()"> | |
140 | <link rel="stylesheet" href="/css/monoblue_custom.css" type="text/css" /> |
|
144 | <link rel="stylesheet" href="/css/monoblue_custom.css" type="text/css" /> | |
141 | </%def> |
|
145 | </%def> | |
142 |
|
146 | |||
143 | <%def name="js()"> |
|
147 | <%def name="js()"> | |
144 | <script type="text/javascript" src="/js/yui/utilities/utilities.js"></script> |
|
148 | <script type="text/javascript" src="/js/yui/utilities/utilities.js"></script> | |
145 | <script type="text/javascript" src="/js/yui/container/container-min.js"></script> |
|
149 | <script type="text/javascript" src="/js/yui/container/container-min.js"></script> | |
146 | <script type="text/javascript" src="/js/yui/datasource/datasource-min.js"></script> |
|
150 | <script type="text/javascript" src="/js/yui/datasource/datasource-min.js"></script> | |
147 | <script type="text/javascript" src="/js/yui/autocomplete/autocomplete-min.js"></script> |
|
151 | <script type="text/javascript" src="/js/yui/autocomplete/autocomplete-min.js"></script> | |
148 | </%def> |
|
152 | </%def> | |
149 |
|
153 | |||
150 | <!-- DEFINITION OF FORM ERROR FETCHER --> |
|
154 | <!-- DEFINITION OF FORM ERROR FETCHER --> | |
151 | <%def name="get_form_error(element)"> |
|
155 | <%def name="get_form_error(element)"> | |
152 | %if hasattr(c,'form_errors') and type(c.form_errors) == dict: |
|
156 | %if hasattr(c,'form_errors') and type(c.form_errors) == dict: | |
153 | %if c.form_errors.get(element,False): |
|
157 | %if c.form_errors.get(element,False): | |
154 | <span class="error-message"> |
|
158 | <span class="error-message"> | |
155 | ${c.form_errors.get(element,'')} |
|
159 | ${c.form_errors.get(element,'')} | |
156 | </span> |
|
160 | </span> | |
157 | %endif |
|
161 | %endif | |
158 | %endif |
|
162 | %endif | |
159 | </%def> No newline at end of file |
|
163 | </%def> |
@@ -1,53 +1,55 b'' | |||||
1 | ## -*- coding: utf-8 -*- |
|
1 | ## -*- coding: utf-8 -*- | |
2 | <%inherit file="base/base.html"/> |
|
2 | <%inherit file="base/base.html"/> | |
3 | <%def name="title()"> |
|
3 | <%def name="title()"> | |
4 | ${c.repos_prefix} Mercurial Repositories |
|
4 | ${c.repos_prefix} Mercurial Repositories | |
5 | </%def> |
|
5 | </%def> | |
6 | <%def name="breadcrumbs()"> |
|
6 | <%def name="breadcrumbs()"> | |
7 | ${c.repos_prefix} Mercurial Repositories |
|
7 | ${c.repos_prefix} Mercurial Repositories | |
8 | </%def> |
|
8 | </%def> | |
9 | <%def name="page_nav()"> |
|
9 | <%def name="page_nav()"> | |
10 | ${self.menu('home')} |
|
10 | ${self.menu('home')} | |
11 | </%def> |
|
11 | </%def> | |
12 | <%def name="main()"> |
|
12 | <%def name="main()"> | |
13 | <%def name="get_sort(name)"> |
|
13 | <%def name="get_sort(name)"> | |
14 | <%name_slug = name.lower().replace(' ','_') %> |
|
14 | <%name_slug = name.lower().replace(' ','_') %> | |
15 | %if name_slug == c.cs_slug: |
|
15 | %if name_slug == c.cs_slug: | |
16 | <span style="font-weight: bold;text-decoration: underline;">${name}</span> |
|
16 | <span style="font-weight: bold;text-decoration: underline;">${name}</span> | |
17 | %else: |
|
17 | %else: | |
18 | <span style="font-weight: bold">${name}</span> |
|
18 | <span style="font-weight: bold">${name}</span> | |
19 | %endif |
|
19 | %endif | |
20 | <a style="color:#FFF" href="?sort=${name_slug}">↓</a> |
|
20 | <a style="color:#FFF" href="?sort=${name_slug}">↓</a> | |
21 | <a style="color:#FFF" href="?sort=-${name_slug}">↑</a> |
|
21 | <a style="color:#FFF" href="?sort=-${name_slug}">↑</a> | |
22 | </%def> |
|
22 | </%def> | |
23 | <table class="table_disp"> |
|
23 | <table class="table_disp"> | |
24 | <tr class="header"> |
|
24 | <tr class="header"> | |
25 | <td>${get_sort(_('Name'))}</td> |
|
25 | <td>${get_sort(_('Name'))}</td> | |
26 | <td>${get_sort(_('Description'))}</td> |
|
26 | <td>${get_sort(_('Description'))}</td> | |
27 | <td>${get_sort(_('Last change'))}</td> |
|
27 | <td>${get_sort(_('Last change'))}</td> | |
28 | <td>${get_sort(_('Tip'))}</td> |
|
28 | <td>${get_sort(_('Tip'))}</td> | |
29 | <td>${get_sort(_('Contact'))}</td> |
|
29 | <td>${get_sort(_('Contact'))}</td> | |
30 | <td>${_('RSS')}</td> |
|
30 | <td>${_('RSS')}</td> | |
31 | <td>${_('Atom')}</td> |
|
31 | <td>${_('Atom')}</td> | |
32 | </tr> |
|
32 | </tr> | |
33 | %for cnt,repo in enumerate(c.repos_list): |
|
33 | %for cnt,repo in enumerate(c.repos_list): | |
|
34 | %if h.HasRepoPermissionAny('repository.write','repository.read','repository.admin')(repo['name'],'main page check'): | |||
34 | <tr class="parity${cnt%2}"> |
|
35 | <tr class="parity${cnt%2}"> | |
35 | <td>${h.link_to(repo['name'], |
|
36 | <td>${h.link_to(repo['name'], | |
36 | h.url('summary_home',repo_name=repo['name']))}</td> |
|
37 | h.url('summary_home',repo_name=repo['name']))}</td> | |
37 | <td title="${repo['description']}">${h.truncate(repo['description'],60)}</td> |
|
38 | <td title="${repo['description']}">${h.truncate(repo['description'],60)}</td> | |
38 | <td>${h.age(repo['last_change'])}</td> |
|
39 | <td>${h.age(repo['last_change'])}</td> | |
39 | <td>${h.link_to_if(repo['rev']>=0,'r%s:%s' % (repo['rev'],repo['tip']), |
|
40 | <td>${h.link_to_if(repo['rev']>=0,'r%s:%s' % (repo['rev'],repo['tip']), | |
40 | h.url('changeset_home',repo_name=repo['name'],revision=repo['tip']), |
|
41 | h.url('changeset_home',repo_name=repo['name'],revision=repo['tip']), | |
41 | class_="tooltip", |
|
42 | class_="tooltip", | |
42 | tooltip_title=h.tooltip(repo['last_msg']))}</td> |
|
43 | tooltip_title=h.tooltip(repo['last_msg']))}</td> | |
43 | <td title="${repo['contact']}">${h.person(repo['contact'])}</td> |
|
44 | <td title="${repo['contact']}">${h.person(repo['contact'])}</td> | |
44 | <td> |
|
45 | <td> | |
45 | <a title="${_('Subscribe to %s rss feed')%repo['name']}" class="rss_logo" href="${h.url('rss_feed_home',repo_name=repo['name'])}"></a> |
|
46 | <a title="${_('Subscribe to %s rss feed')%repo['name']}" class="rss_logo" href="${h.url('rss_feed_home',repo_name=repo['name'])}"></a> | |
46 | </td> |
|
47 | </td> | |
47 | <td> |
|
48 | <td> | |
48 | <a title="${_('Subscribe to %s atom feed')%repo['name']}" class="atom_logo" href="${h.url('atom_feed_home',repo_name=repo['name'])}"></a> |
|
49 | <a title="${_('Subscribe to %s atom feed')%repo['name']}" class="atom_logo" href="${h.url('atom_feed_home',repo_name=repo['name'])}"></a> | |
49 | </td> |
|
50 | </td> | |
50 | </tr> |
|
51 | </tr> | |
|
52 | %endif | |||
51 | %endfor |
|
53 | %endfor | |
52 | </table> |
|
54 | </table> | |
53 | </%def> |
|
55 | </%def> |
General Comments 0
You need to be logged in to leave comments.
Login now