Show More
@@ -1,479 +1,479 | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """ |
|
2 | """ | |
3 | rhodecode.controllers.pullrequests |
|
3 | rhodecode.controllers.pullrequests | |
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
5 |
|
5 | |||
6 | pull requests controller for rhodecode for initializing pull requests |
|
6 | pull requests controller for rhodecode for initializing pull requests | |
7 |
|
7 | |||
8 | :created_on: May 7, 2012 |
|
8 | :created_on: May 7, 2012 | |
9 | :author: marcink |
|
9 | :author: marcink | |
10 | :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com> |
|
10 | :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com> | |
11 | :license: GPLv3, see COPYING for more details. |
|
11 | :license: GPLv3, see COPYING for more details. | |
12 | """ |
|
12 | """ | |
13 | # This program is free software: you can redistribute it and/or modify |
|
13 | # This program is free software: you can redistribute it and/or modify | |
14 | # it under the terms of the GNU General Public License as published by |
|
14 | # it under the terms of the GNU General Public License as published by | |
15 | # the Free Software Foundation, either version 3 of the License, or |
|
15 | # the Free Software Foundation, either version 3 of the License, or | |
16 | # (at your option) any later version. |
|
16 | # (at your option) any later version. | |
17 | # |
|
17 | # | |
18 | # This program is distributed in the hope that it will be useful, |
|
18 | # This program is distributed in the hope that it will be useful, | |
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 | # GNU General Public License for more details. |
|
21 | # GNU General Public License for more details. | |
22 | # |
|
22 | # | |
23 | # You should have received a copy of the GNU General Public License |
|
23 | # You should have received a copy of the GNU General Public License | |
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
25 | import logging |
|
25 | import logging | |
26 | import traceback |
|
26 | import traceback | |
27 | import formencode |
|
27 | import formencode | |
28 |
|
28 | |||
29 | from webob.exc import HTTPNotFound, HTTPForbidden |
|
29 | from webob.exc import HTTPNotFound, HTTPForbidden | |
30 | from collections import defaultdict |
|
30 | from collections import defaultdict | |
31 | from itertools import groupby |
|
31 | from itertools import groupby | |
32 |
|
32 | |||
33 | from pylons import request, response, session, tmpl_context as c, url |
|
33 | from pylons import request, response, session, tmpl_context as c, url | |
34 | from pylons.controllers.util import abort, redirect |
|
34 | from pylons.controllers.util import abort, redirect | |
35 | from pylons.i18n.translation import _ |
|
35 | from pylons.i18n.translation import _ | |
36 |
|
36 | |||
37 | from rhodecode.lib.compat import json |
|
37 | from rhodecode.lib.compat import json | |
38 | from rhodecode.lib.base import BaseRepoController, render |
|
38 | from rhodecode.lib.base import BaseRepoController, render | |
39 | from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\ |
|
39 | from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\ | |
40 | NotAnonymous |
|
40 | NotAnonymous | |
41 | from rhodecode.lib import helpers as h |
|
41 | from rhodecode.lib import helpers as h | |
42 | from rhodecode.lib import diffs |
|
42 | from rhodecode.lib import diffs | |
43 | from rhodecode.lib.utils import action_logger, jsonify |
|
43 | from rhodecode.lib.utils import action_logger, jsonify | |
44 | from rhodecode.lib.vcs.exceptions import EmptyRepositoryError |
|
44 | from rhodecode.lib.vcs.exceptions import EmptyRepositoryError | |
45 | from rhodecode.lib.vcs.backends.base import EmptyChangeset |
|
45 | from rhodecode.lib.vcs.backends.base import EmptyChangeset | |
46 | from rhodecode.lib.diffs import LimitedDiffContainer |
|
46 | from rhodecode.lib.diffs import LimitedDiffContainer | |
47 | from rhodecode.model.db import User, PullRequest, ChangesetStatus,\ |
|
47 | from rhodecode.model.db import User, PullRequest, ChangesetStatus,\ | |
48 | ChangesetComment |
|
48 | ChangesetComment | |
49 | from rhodecode.model.pull_request import PullRequestModel |
|
49 | from rhodecode.model.pull_request import PullRequestModel | |
50 | from rhodecode.model.meta import Session |
|
50 | from rhodecode.model.meta import Session | |
51 | from rhodecode.model.repo import RepoModel |
|
51 | from rhodecode.model.repo import RepoModel | |
52 | from rhodecode.model.comment import ChangesetCommentsModel |
|
52 | from rhodecode.model.comment import ChangesetCommentsModel | |
53 | from rhodecode.model.changeset_status import ChangesetStatusModel |
|
53 | from rhodecode.model.changeset_status import ChangesetStatusModel | |
54 | from rhodecode.model.forms import PullRequestForm |
|
54 | from rhodecode.model.forms import PullRequestForm | |
55 |
|
55 | |||
56 | log = logging.getLogger(__name__) |
|
56 | log = logging.getLogger(__name__) | |
57 |
|
57 | |||
58 |
|
58 | |||
59 | class PullrequestsController(BaseRepoController): |
|
59 | class PullrequestsController(BaseRepoController): | |
60 |
|
60 | |||
61 | @LoginRequired() |
|
61 | @LoginRequired() | |
62 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
62 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', | |
63 | 'repository.admin') |
|
63 | 'repository.admin') | |
64 | def __before__(self): |
|
64 | def __before__(self): | |
65 | super(PullrequestsController, self).__before__() |
|
65 | super(PullrequestsController, self).__before__() | |
66 | repo_model = RepoModel() |
|
66 | repo_model = RepoModel() | |
67 | c.users_array = repo_model.get_users_js() |
|
67 | c.users_array = repo_model.get_users_js() | |
68 | c.users_groups_array = repo_model.get_users_groups_js() |
|
68 | c.users_groups_array = repo_model.get_users_groups_js() | |
69 |
|
69 | |||
70 | def _get_repo_refs(self, repo): |
|
70 | def _get_repo_refs(self, repo): | |
71 | hist_l = [] |
|
71 | hist_l = [] | |
72 |
|
72 | |||
73 | branches_group = ([('branch:%s:%s' % (k, v), k) for |
|
73 | branches_group = ([('branch:%s:%s' % (k, v), k) for | |
74 | k, v in repo.branches.iteritems()], _("Branches")) |
|
74 | k, v in repo.branches.iteritems()], _("Branches")) | |
75 | bookmarks_group = ([('book:%s:%s' % (k, v), k) for |
|
75 | bookmarks_group = ([('book:%s:%s' % (k, v), k) for | |
76 | k, v in repo.bookmarks.iteritems()], _("Bookmarks")) |
|
76 | k, v in repo.bookmarks.iteritems()], _("Bookmarks")) | |
77 | tags_group = ([('tag:%s:%s' % (k, v), k) for |
|
77 | tags_group = ([('tag:%s:%s' % (k, v), k) for | |
78 | k, v in repo.tags.iteritems() |
|
78 | k, v in repo.tags.iteritems() | |
79 | if k != 'tip'], _("Tags")) |
|
79 | if k != 'tip'], _("Tags")) | |
80 |
|
80 | |||
81 | tip = repo.tags['tip'] |
|
81 | tip = repo.tags['tip'] | |
82 | tipref = 'tag:tip:%s' % tip |
|
82 | tipref = 'tag:tip:%s' % tip | |
83 | colontip = ':' + tip |
|
83 | colontip = ':' + tip | |
84 | tips = [x[1] for x in branches_group[0] + bookmarks_group[0] + tags_group[0] |
|
84 | tips = [x[1] for x in branches_group[0] + bookmarks_group[0] + tags_group[0] | |
85 | if x[0].endswith(colontip)] |
|
85 | if x[0].endswith(colontip)] | |
86 | tags_group[0].append((tipref, 'tip (%s)' % ', '.join(tips))) |
|
86 | tags_group[0].append((tipref, 'tip (%s)' % ', '.join(tips))) | |
87 |
|
87 | |||
88 | hist_l.append(bookmarks_group) |
|
88 | hist_l.append(bookmarks_group) | |
89 | hist_l.append(branches_group) |
|
89 | hist_l.append(branches_group) | |
90 | hist_l.append(tags_group) |
|
90 | hist_l.append(tags_group) | |
91 |
|
91 | |||
92 | return hist_l, tipref |
|
92 | return hist_l, tipref | |
93 |
|
93 | |||
94 | def _get_is_allowed_change_status(self, pull_request): |
|
94 | def _get_is_allowed_change_status(self, pull_request): | |
95 | owner = self.rhodecode_user.user_id == pull_request.user_id |
|
95 | owner = self.rhodecode_user.user_id == pull_request.user_id | |
96 | reviewer = self.rhodecode_user.user_id in [x.user_id for x in |
|
96 | reviewer = self.rhodecode_user.user_id in [x.user_id for x in | |
97 | pull_request.reviewers] |
|
97 | pull_request.reviewers] | |
98 | return (self.rhodecode_user.admin or owner or reviewer) |
|
98 | return (self.rhodecode_user.admin or owner or reviewer) | |
99 |
|
99 | |||
100 | def show_all(self, repo_name): |
|
100 | def show_all(self, repo_name): | |
101 | c.pull_requests = PullRequestModel().get_all(repo_name) |
|
101 | c.pull_requests = PullRequestModel().get_all(repo_name) | |
102 | c.repo_name = repo_name |
|
102 | c.repo_name = repo_name | |
103 | return render('/pullrequests/pullrequest_show_all.html') |
|
103 | return render('/pullrequests/pullrequest_show_all.html') | |
104 |
|
104 | |||
105 | @NotAnonymous() |
|
105 | @NotAnonymous() | |
106 | def index(self): |
|
106 | def index(self): | |
107 | org_repo = c.rhodecode_db_repo |
|
107 | org_repo = c.rhodecode_db_repo | |
108 |
|
108 | |||
109 | if org_repo.scm_instance.alias != 'hg': |
|
109 | if org_repo.scm_instance.alias != 'hg': | |
110 | log.error('Review not available for GIT REPOS') |
|
110 | log.error('Review not available for GIT REPOS') | |
111 | raise HTTPNotFound |
|
111 | raise HTTPNotFound | |
112 |
|
112 | |||
113 | try: |
|
113 | try: | |
114 | org_repo.scm_instance.get_changeset() |
|
114 | org_repo.scm_instance.get_changeset() | |
115 | except EmptyRepositoryError, e: |
|
115 | except EmptyRepositoryError, e: | |
116 | h.flash(h.literal(_('There are no changesets yet')), |
|
116 | h.flash(h.literal(_('There are no changesets yet')), | |
117 | category='warning') |
|
117 | category='warning') | |
118 | redirect(url('summary_home', repo_name=org_repo.repo_name)) |
|
118 | redirect(url('summary_home', repo_name=org_repo.repo_name)) | |
119 |
|
119 | |||
120 | other_repos_info = {} |
|
120 | other_repos_info = {} | |
121 |
|
121 | |||
122 | c.org_repos = [] |
|
122 | c.org_repos = [] | |
123 | c.org_repos.append((org_repo.repo_name, org_repo.repo_name)) |
|
123 | c.org_repos.append((org_repo.repo_name, org_repo.repo_name)) | |
124 | c.default_org_repo = org_repo.repo_name |
|
124 | c.default_org_repo = org_repo.repo_name | |
125 | c.org_refs, c.default_org_ref = self._get_repo_refs(org_repo.scm_instance) |
|
125 | c.org_refs, c.default_org_ref = self._get_repo_refs(org_repo.scm_instance) | |
126 |
|
126 | |||
127 | c.other_repos = [] |
|
127 | c.other_repos = [] | |
128 | # add org repo to other so we can open pull request against itself |
|
128 | # add org repo to other so we can open pull request against itself | |
129 | c.other_repos.extend(c.org_repos) |
|
129 | c.other_repos.extend(c.org_repos) | |
130 | c.default_other_repo = org_repo.repo_name |
|
130 | c.default_other_repo = org_repo.repo_name | |
131 | c.default_other_refs, c.default_other_ref = self._get_repo_refs(org_repo.scm_instance) |
|
131 | c.default_other_refs, c.default_other_ref = self._get_repo_refs(org_repo.scm_instance) | |
132 | usr_data = lambda usr: dict(user_id=usr.user_id, |
|
132 | usr_data = lambda usr: dict(user_id=usr.user_id, | |
133 | username=usr.username, |
|
133 | username=usr.username, | |
134 | firstname=usr.firstname, |
|
134 | firstname=usr.firstname, | |
135 | lastname=usr.lastname, |
|
135 | lastname=usr.lastname, | |
136 | gravatar_link=h.gravatar_url(usr.email, 14)) |
|
136 | gravatar_link=h.gravatar_url(usr.email, 14)) | |
137 | other_repos_info[org_repo.repo_name] = { |
|
137 | other_repos_info[org_repo.repo_name] = { | |
138 | 'user': usr_data(org_repo.user), |
|
138 | 'user': usr_data(org_repo.user), | |
139 | 'description': org_repo.description, |
|
139 | 'description': org_repo.description, | |
140 | 'revs': h.select('other_ref', c.default_other_ref, |
|
140 | 'revs': h.select('other_ref', c.default_other_ref, | |
141 | c.default_other_refs, class_='refs') |
|
141 | c.default_other_refs, class_='refs') | |
142 | } |
|
142 | } | |
143 |
|
143 | |||
144 |
# gather forks and add to this list ... even though it is rare to |
|
144 | # gather forks and add to this list ... even though it is rare to | |
145 | # request forks to pull their parent |
|
145 | # request forks to pull their parent | |
146 | for fork in org_repo.forks: |
|
146 | for fork in org_repo.forks: | |
147 | c.other_repos.append((fork.repo_name, fork.repo_name)) |
|
147 | c.other_repos.append((fork.repo_name, fork.repo_name)) | |
148 | refs, default_ref = self._get_repo_refs(fork.scm_instance) |
|
148 | refs, default_ref = self._get_repo_refs(fork.scm_instance) | |
149 | other_repos_info[fork.repo_name] = { |
|
149 | other_repos_info[fork.repo_name] = { | |
150 | 'user': usr_data(fork.user), |
|
150 | 'user': usr_data(fork.user), | |
151 | 'description': fork.description, |
|
151 | 'description': fork.description, | |
152 | 'revs': h.select('other_ref', default_ref, refs, class_='refs') |
|
152 | 'revs': h.select('other_ref', default_ref, refs, class_='refs') | |
153 | } |
|
153 | } | |
154 |
|
154 | |||
155 | # add parents of this fork also, but only if it's not empty |
|
155 | # add parents of this fork also, but only if it's not empty | |
156 | if org_repo.parent and org_repo.parent.scm_instance.revisions: |
|
156 | if org_repo.parent and org_repo.parent.scm_instance.revisions: | |
157 | c.default_other_repo = org_repo.parent.repo_name |
|
157 | c.default_other_repo = org_repo.parent.repo_name | |
158 | c.default_other_refs, c.default_other_ref = self._get_repo_refs(org_repo.parent.scm_instance) |
|
158 | c.default_other_refs, c.default_other_ref = self._get_repo_refs(org_repo.parent.scm_instance) | |
159 | c.other_repos.append((org_repo.parent.repo_name, org_repo.parent.repo_name)) |
|
159 | c.other_repos.append((org_repo.parent.repo_name, org_repo.parent.repo_name)) | |
160 | other_repos_info[org_repo.parent.repo_name] = { |
|
160 | other_repos_info[org_repo.parent.repo_name] = { | |
161 | 'user': usr_data(org_repo.parent.user), |
|
161 | 'user': usr_data(org_repo.parent.user), | |
162 | 'description': org_repo.parent.description, |
|
162 | 'description': org_repo.parent.description, | |
163 | 'revs': h.select('other_ref', c.default_other_ref, |
|
163 | 'revs': h.select('other_ref', c.default_other_ref, | |
164 | c.default_other_refs, class_='refs') |
|
164 | c.default_other_refs, class_='refs') | |
165 | } |
|
165 | } | |
166 |
|
166 | |||
167 | c.other_repos_info = json.dumps(other_repos_info) |
|
167 | c.other_repos_info = json.dumps(other_repos_info) | |
168 | # other repo owner |
|
168 | # other repo owner | |
169 | c.review_members = [] |
|
169 | c.review_members = [] | |
170 | return render('/pullrequests/pullrequest.html') |
|
170 | return render('/pullrequests/pullrequest.html') | |
171 |
|
171 | |||
172 | @NotAnonymous() |
|
172 | @NotAnonymous() | |
173 | def create(self, repo_name): |
|
173 | def create(self, repo_name): | |
174 | repo = RepoModel()._get_repo(repo_name) |
|
174 | repo = RepoModel()._get_repo(repo_name) | |
175 | try: |
|
175 | try: | |
176 | _form = PullRequestForm(repo.repo_id)().to_python(request.POST) |
|
176 | _form = PullRequestForm(repo.repo_id)().to_python(request.POST) | |
177 | except formencode.Invalid, errors: |
|
177 | except formencode.Invalid, errors: | |
178 | log.error(traceback.format_exc()) |
|
178 | log.error(traceback.format_exc()) | |
179 | if errors.error_dict.get('revisions'): |
|
179 | if errors.error_dict.get('revisions'): | |
180 | msg = 'Revisions: %s' % errors.error_dict['revisions'] |
|
180 | msg = 'Revisions: %s' % errors.error_dict['revisions'] | |
181 | elif errors.error_dict.get('pullrequest_title'): |
|
181 | elif errors.error_dict.get('pullrequest_title'): | |
182 | msg = _('Pull request requires a title with min. 3 chars') |
|
182 | msg = _('Pull request requires a title with min. 3 chars') | |
183 | else: |
|
183 | else: | |
184 | msg = _('error during creation of pull request') |
|
184 | msg = _('error during creation of pull request') | |
185 |
|
185 | |||
186 | h.flash(msg, 'error') |
|
186 | h.flash(msg, 'error') | |
187 | return redirect(url('pullrequest_home', repo_name=repo_name)) |
|
187 | return redirect(url('pullrequest_home', repo_name=repo_name)) | |
188 |
|
188 | |||
189 | org_repo = _form['org_repo'] |
|
189 | org_repo = _form['org_repo'] | |
190 | org_ref = _form['org_ref'] |
|
190 | org_ref = _form['org_ref'] | |
191 | other_repo = _form['other_repo'] |
|
191 | other_repo = _form['other_repo'] | |
192 | other_ref = _form['other_ref'] |
|
192 | other_ref = _form['other_ref'] | |
193 | revisions = _form['revisions'] |
|
193 | revisions = _form['revisions'] | |
194 | reviewers = _form['review_members'] |
|
194 | reviewers = _form['review_members'] | |
195 |
|
195 | |||
196 | # if we have cherry picked pull request we don't care what is in |
|
196 | # if we have cherry picked pull request we don't care what is in | |
197 | # org_ref/other_ref |
|
197 | # org_ref/other_ref | |
198 | rev_start = request.POST.get('rev_start') |
|
198 | rev_start = request.POST.get('rev_start') | |
199 | rev_end = request.POST.get('rev_end') |
|
199 | rev_end = request.POST.get('rev_end') | |
200 |
|
200 | |||
201 | if rev_start and rev_end: |
|
201 | if rev_start and rev_end: | |
202 | # this is swapped to simulate that rev_end is a revision from |
|
202 | # this is swapped to simulate that rev_end is a revision from | |
203 | # parent of the fork |
|
203 | # parent of the fork | |
204 | org_ref = 'rev:%s:%s' % (rev_end, rev_end) |
|
204 | org_ref = 'rev:%s:%s' % (rev_end, rev_end) | |
205 | other_ref = 'rev:%s:%s' % (rev_start, rev_start) |
|
205 | other_ref = 'rev:%s:%s' % (rev_start, rev_start) | |
206 |
|
206 | |||
207 | title = _form['pullrequest_title'] |
|
207 | title = _form['pullrequest_title'] | |
208 | description = _form['pullrequest_desc'] |
|
208 | description = _form['pullrequest_desc'] | |
209 |
|
209 | |||
210 | try: |
|
210 | try: | |
211 | pull_request = PullRequestModel().create( |
|
211 | pull_request = PullRequestModel().create( | |
212 | self.rhodecode_user.user_id, org_repo, org_ref, other_repo, |
|
212 | self.rhodecode_user.user_id, org_repo, org_ref, other_repo, | |
213 | other_ref, revisions, reviewers, title, description |
|
213 | other_ref, revisions, reviewers, title, description | |
214 | ) |
|
214 | ) | |
215 | Session().commit() |
|
215 | Session().commit() | |
216 | h.flash(_('Successfully opened new pull request'), |
|
216 | h.flash(_('Successfully opened new pull request'), | |
217 | category='success') |
|
217 | category='success') | |
218 | except Exception: |
|
218 | except Exception: | |
219 | h.flash(_('Error occurred during sending pull request'), |
|
219 | h.flash(_('Error occurred during sending pull request'), | |
220 | category='error') |
|
220 | category='error') | |
221 | log.error(traceback.format_exc()) |
|
221 | log.error(traceback.format_exc()) | |
222 | return redirect(url('pullrequest_home', repo_name=repo_name)) |
|
222 | return redirect(url('pullrequest_home', repo_name=repo_name)) | |
223 |
|
223 | |||
224 | return redirect(url('pullrequest_show', repo_name=other_repo, |
|
224 | return redirect(url('pullrequest_show', repo_name=other_repo, | |
225 | pull_request_id=pull_request.pull_request_id)) |
|
225 | pull_request_id=pull_request.pull_request_id)) | |
226 |
|
226 | |||
227 | @NotAnonymous() |
|
227 | @NotAnonymous() | |
228 | @jsonify |
|
228 | @jsonify | |
229 | def update(self, repo_name, pull_request_id): |
|
229 | def update(self, repo_name, pull_request_id): | |
230 | pull_request = PullRequest.get_or_404(pull_request_id) |
|
230 | pull_request = PullRequest.get_or_404(pull_request_id) | |
231 | if pull_request.is_closed(): |
|
231 | if pull_request.is_closed(): | |
232 | raise HTTPForbidden() |
|
232 | raise HTTPForbidden() | |
233 | #only owner or admin can update it |
|
233 | #only owner or admin can update it | |
234 | owner = pull_request.author.user_id == c.rhodecode_user.user_id |
|
234 | owner = pull_request.author.user_id == c.rhodecode_user.user_id | |
235 | if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner: |
|
235 | if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner: | |
236 | reviewers_ids = map(int, filter(lambda v: v not in [None, ''], |
|
236 | reviewers_ids = map(int, filter(lambda v: v not in [None, ''], | |
237 | request.POST.get('reviewers_ids', '').split(','))) |
|
237 | request.POST.get('reviewers_ids', '').split(','))) | |
238 |
|
238 | |||
239 | PullRequestModel().update_reviewers(pull_request_id, reviewers_ids) |
|
239 | PullRequestModel().update_reviewers(pull_request_id, reviewers_ids) | |
240 | Session().commit() |
|
240 | Session().commit() | |
241 | return True |
|
241 | return True | |
242 | raise HTTPForbidden() |
|
242 | raise HTTPForbidden() | |
243 |
|
243 | |||
244 | @NotAnonymous() |
|
244 | @NotAnonymous() | |
245 | @jsonify |
|
245 | @jsonify | |
246 | def delete(self, repo_name, pull_request_id): |
|
246 | def delete(self, repo_name, pull_request_id): | |
247 | pull_request = PullRequest.get_or_404(pull_request_id) |
|
247 | pull_request = PullRequest.get_or_404(pull_request_id) | |
248 | #only owner can delete it ! |
|
248 | #only owner can delete it ! | |
249 | if pull_request.author.user_id == c.rhodecode_user.user_id: |
|
249 | if pull_request.author.user_id == c.rhodecode_user.user_id: | |
250 | PullRequestModel().delete(pull_request) |
|
250 | PullRequestModel().delete(pull_request) | |
251 | Session().commit() |
|
251 | Session().commit() | |
252 | h.flash(_('Successfully deleted pull request'), |
|
252 | h.flash(_('Successfully deleted pull request'), | |
253 | category='success') |
|
253 | category='success') | |
254 | return redirect(url('admin_settings_my_account', anchor='pullrequests')) |
|
254 | return redirect(url('admin_settings_my_account', anchor='pullrequests')) | |
255 | raise HTTPForbidden() |
|
255 | raise HTTPForbidden() | |
256 |
|
256 | |||
257 | def _load_compare_data(self, pull_request, enable_comments=True): |
|
257 | def _load_compare_data(self, pull_request, enable_comments=True): | |
258 | """ |
|
258 | """ | |
259 | Load context data needed for generating compare diff |
|
259 | Load context data needed for generating compare diff | |
260 |
|
260 | |||
261 | :param pull_request: |
|
261 | :param pull_request: | |
262 | :type pull_request: |
|
262 | :type pull_request: | |
263 | """ |
|
263 | """ | |
264 | rev_start = request.GET.get('rev_start') |
|
264 | rev_start = request.GET.get('rev_start') | |
265 | rev_end = request.GET.get('rev_end') |
|
265 | rev_end = request.GET.get('rev_end') | |
266 |
|
266 | |||
267 | org_repo = pull_request.org_repo |
|
267 | org_repo = pull_request.org_repo | |
268 | (org_ref_type, |
|
268 | (org_ref_type, | |
269 | org_ref_name, |
|
269 | org_ref_name, | |
270 | org_ref_rev) = pull_request.org_ref.split(':') |
|
270 | org_ref_rev) = pull_request.org_ref.split(':') | |
271 |
|
271 | |||
272 | other_repo = org_repo |
|
272 | other_repo = org_repo | |
273 | (other_ref_type, |
|
273 | (other_ref_type, | |
274 | other_ref_name, |
|
274 | other_ref_name, | |
275 | other_ref_rev) = pull_request.other_ref.split(':') |
|
275 | other_ref_rev) = pull_request.other_ref.split(':') | |
276 |
|
276 | |||
277 | # despite opening revisions for bookmarks/branches/tags, we always |
|
277 | # despite opening revisions for bookmarks/branches/tags, we always | |
278 | # convert this to rev to prevent changes after book or branch change |
|
278 | # convert this to rev to prevent changes after book or branch change | |
279 | org_ref = ('rev', org_ref_rev) |
|
279 | org_ref = ('rev', org_ref_rev) | |
280 | other_ref = ('rev', other_ref_rev) |
|
280 | other_ref = ('rev', other_ref_rev) | |
281 |
|
281 | |||
282 | c.org_repo = org_repo |
|
282 | c.org_repo = org_repo | |
283 | c.other_repo = other_repo |
|
283 | c.other_repo = other_repo | |
284 |
|
284 | |||
285 | c.fulldiff = fulldiff = request.GET.get('fulldiff') |
|
285 | c.fulldiff = fulldiff = request.GET.get('fulldiff') | |
286 |
|
286 | |||
287 | c.cs_ranges = [org_repo.get_changeset(x) for x in pull_request.revisions] |
|
287 | c.cs_ranges = [org_repo.get_changeset(x) for x in pull_request.revisions] | |
288 |
|
288 | |||
289 | other_ref = ('rev', getattr(c.cs_ranges[0].parents[0] |
|
289 | other_ref = ('rev', getattr(c.cs_ranges[0].parents[0] | |
290 | if c.cs_ranges[0].parents |
|
290 | if c.cs_ranges[0].parents | |
291 | else EmptyChangeset(), 'raw_id')) |
|
291 | else EmptyChangeset(), 'raw_id')) | |
292 |
|
292 | |||
293 | c.statuses = org_repo.statuses([x.raw_id for x in c.cs_ranges]) |
|
293 | c.statuses = org_repo.statuses([x.raw_id for x in c.cs_ranges]) | |
294 | # defines that we need hidden inputs with changesets |
|
294 | # defines that we need hidden inputs with changesets | |
295 | c.as_form = request.GET.get('as_form', False) |
|
295 | c.as_form = request.GET.get('as_form', False) | |
296 |
|
296 | |||
297 | c.org_ref = org_ref[1] |
|
297 | c.org_ref = org_ref[1] | |
298 | c.org_ref_type = org_ref[0] |
|
298 | c.org_ref_type = org_ref[0] | |
299 | c.other_ref = other_ref[1] |
|
299 | c.other_ref = other_ref[1] | |
300 | c.other_ref_type = other_ref[0] |
|
300 | c.other_ref_type = other_ref[0] | |
301 |
|
301 | |||
302 | diff_limit = self.cut_off_limit if not fulldiff else None |
|
302 | diff_limit = self.cut_off_limit if not fulldiff else None | |
303 |
|
303 | |||
304 | #we swap org/other ref since we run a simple diff on one repo |
|
304 | #we swap org/other ref since we run a simple diff on one repo | |
305 | _diff = diffs.differ(org_repo, other_ref, other_repo, org_ref) |
|
305 | _diff = diffs.differ(org_repo, other_ref, other_repo, org_ref) | |
306 |
|
306 | |||
307 | diff_processor = diffs.DiffProcessor(_diff or '', format='gitdiff', |
|
307 | diff_processor = diffs.DiffProcessor(_diff or '', format='gitdiff', | |
308 | diff_limit=diff_limit) |
|
308 | diff_limit=diff_limit) | |
309 | _parsed = diff_processor.prepare() |
|
309 | _parsed = diff_processor.prepare() | |
310 |
|
310 | |||
311 | c.limited_diff = False |
|
311 | c.limited_diff = False | |
312 | if isinstance(_parsed, LimitedDiffContainer): |
|
312 | if isinstance(_parsed, LimitedDiffContainer): | |
313 | c.limited_diff = True |
|
313 | c.limited_diff = True | |
314 |
|
314 | |||
315 | c.files = [] |
|
315 | c.files = [] | |
316 | c.changes = {} |
|
316 | c.changes = {} | |
317 | c.lines_added = 0 |
|
317 | c.lines_added = 0 | |
318 | c.lines_deleted = 0 |
|
318 | c.lines_deleted = 0 | |
319 | for f in _parsed: |
|
319 | for f in _parsed: | |
320 | st = f['stats'] |
|
320 | st = f['stats'] | |
321 | if st[0] != 'b': |
|
321 | if st[0] != 'b': | |
322 | c.lines_added += st[0] |
|
322 | c.lines_added += st[0] | |
323 | c.lines_deleted += st[1] |
|
323 | c.lines_deleted += st[1] | |
324 | fid = h.FID('', f['filename']) |
|
324 | fid = h.FID('', f['filename']) | |
325 | c.files.append([fid, f['operation'], f['filename'], f['stats']]) |
|
325 | c.files.append([fid, f['operation'], f['filename'], f['stats']]) | |
326 | diff = diff_processor.as_html(enable_comments=enable_comments, |
|
326 | diff = diff_processor.as_html(enable_comments=enable_comments, | |
327 | parsed_lines=[f]) |
|
327 | parsed_lines=[f]) | |
328 | c.changes[fid] = [f['operation'], f['filename'], diff] |
|
328 | c.changes[fid] = [f['operation'], f['filename'], diff] | |
329 |
|
329 | |||
330 | def show(self, repo_name, pull_request_id): |
|
330 | def show(self, repo_name, pull_request_id): | |
331 | repo_model = RepoModel() |
|
331 | repo_model = RepoModel() | |
332 | c.users_array = repo_model.get_users_js() |
|
332 | c.users_array = repo_model.get_users_js() | |
333 | c.users_groups_array = repo_model.get_users_groups_js() |
|
333 | c.users_groups_array = repo_model.get_users_groups_js() | |
334 | c.pull_request = PullRequest.get_or_404(pull_request_id) |
|
334 | c.pull_request = PullRequest.get_or_404(pull_request_id) | |
335 | c.allowed_to_change_status = self._get_is_allowed_change_status(c.pull_request) |
|
335 | c.allowed_to_change_status = self._get_is_allowed_change_status(c.pull_request) | |
336 | cc_model = ChangesetCommentsModel() |
|
336 | cc_model = ChangesetCommentsModel() | |
337 | cs_model = ChangesetStatusModel() |
|
337 | cs_model = ChangesetStatusModel() | |
338 | _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo, |
|
338 | _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo, | |
339 | pull_request=c.pull_request, |
|
339 | pull_request=c.pull_request, | |
340 | with_revisions=True) |
|
340 | with_revisions=True) | |
341 |
|
341 | |||
342 | cs_statuses = defaultdict(list) |
|
342 | cs_statuses = defaultdict(list) | |
343 | for st in _cs_statuses: |
|
343 | for st in _cs_statuses: | |
344 | cs_statuses[st.author.username] += [st] |
|
344 | cs_statuses[st.author.username] += [st] | |
345 |
|
345 | |||
346 | c.pull_request_reviewers = [] |
|
346 | c.pull_request_reviewers = [] | |
347 | c.pull_request_pending_reviewers = [] |
|
347 | c.pull_request_pending_reviewers = [] | |
348 | for o in c.pull_request.reviewers: |
|
348 | for o in c.pull_request.reviewers: | |
349 | st = cs_statuses.get(o.user.username, None) |
|
349 | st = cs_statuses.get(o.user.username, None) | |
350 | if st: |
|
350 | if st: | |
351 | sorter = lambda k: k.version |
|
351 | sorter = lambda k: k.version | |
352 | st = [(x, list(y)[0]) |
|
352 | st = [(x, list(y)[0]) | |
353 | for x, y in (groupby(sorted(st, key=sorter), sorter))] |
|
353 | for x, y in (groupby(sorted(st, key=sorter), sorter))] | |
354 | else: |
|
354 | else: | |
355 | c.pull_request_pending_reviewers.append(o.user) |
|
355 | c.pull_request_pending_reviewers.append(o.user) | |
356 | c.pull_request_reviewers.append([o.user, st]) |
|
356 | c.pull_request_reviewers.append([o.user, st]) | |
357 |
|
357 | |||
358 | # pull_requests repo_name we opened it against |
|
358 | # pull_requests repo_name we opened it against | |
359 | # ie. other_repo must match |
|
359 | # ie. other_repo must match | |
360 | if repo_name != c.pull_request.other_repo.repo_name: |
|
360 | if repo_name != c.pull_request.other_repo.repo_name: | |
361 | raise HTTPNotFound |
|
361 | raise HTTPNotFound | |
362 |
|
362 | |||
363 | # load compare data into template context |
|
363 | # load compare data into template context | |
364 | enable_comments = not c.pull_request.is_closed() |
|
364 | enable_comments = not c.pull_request.is_closed() | |
365 | self._load_compare_data(c.pull_request, enable_comments=enable_comments) |
|
365 | self._load_compare_data(c.pull_request, enable_comments=enable_comments) | |
366 |
|
366 | |||
367 | # inline comments |
|
367 | # inline comments | |
368 | c.inline_cnt = 0 |
|
368 | c.inline_cnt = 0 | |
369 | c.inline_comments = cc_model.get_inline_comments( |
|
369 | c.inline_comments = cc_model.get_inline_comments( | |
370 | c.rhodecode_db_repo.repo_id, |
|
370 | c.rhodecode_db_repo.repo_id, | |
371 | pull_request=pull_request_id) |
|
371 | pull_request=pull_request_id) | |
372 | # count inline comments |
|
372 | # count inline comments | |
373 | for __, lines in c.inline_comments: |
|
373 | for __, lines in c.inline_comments: | |
374 | for comments in lines.values(): |
|
374 | for comments in lines.values(): | |
375 | c.inline_cnt += len(comments) |
|
375 | c.inline_cnt += len(comments) | |
376 | # comments |
|
376 | # comments | |
377 | c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id, |
|
377 | c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id, | |
378 | pull_request=pull_request_id) |
|
378 | pull_request=pull_request_id) | |
379 |
|
379 | |||
380 | try: |
|
380 | try: | |
381 | cur_status = c.statuses[c.pull_request.revisions[0]][0] |
|
381 | cur_status = c.statuses[c.pull_request.revisions[0]][0] | |
382 | except: |
|
382 | except: | |
383 | log.error(traceback.format_exc()) |
|
383 | log.error(traceback.format_exc()) | |
384 | cur_status = 'undefined' |
|
384 | cur_status = 'undefined' | |
385 | if c.pull_request.is_closed() and 0: |
|
385 | if c.pull_request.is_closed() and 0: | |
386 | c.current_changeset_status = cur_status |
|
386 | c.current_changeset_status = cur_status | |
387 | else: |
|
387 | else: | |
388 | # changeset(pull-request) status calulation based on reviewers |
|
388 | # changeset(pull-request) status calulation based on reviewers | |
389 | c.current_changeset_status = cs_model.calculate_status( |
|
389 | c.current_changeset_status = cs_model.calculate_status( | |
390 | c.pull_request_reviewers, |
|
390 | c.pull_request_reviewers, | |
391 | ) |
|
391 | ) | |
392 | c.changeset_statuses = ChangesetStatus.STATUSES |
|
392 | c.changeset_statuses = ChangesetStatus.STATUSES | |
393 |
|
393 | |||
394 | return render('/pullrequests/pullrequest_show.html') |
|
394 | return render('/pullrequests/pullrequest_show.html') | |
395 |
|
395 | |||
396 | @NotAnonymous() |
|
396 | @NotAnonymous() | |
397 | @jsonify |
|
397 | @jsonify | |
398 | def comment(self, repo_name, pull_request_id): |
|
398 | def comment(self, repo_name, pull_request_id): | |
399 | pull_request = PullRequest.get_or_404(pull_request_id) |
|
399 | pull_request = PullRequest.get_or_404(pull_request_id) | |
400 | if pull_request.is_closed(): |
|
400 | if pull_request.is_closed(): | |
401 | raise HTTPForbidden() |
|
401 | raise HTTPForbidden() | |
402 |
|
402 | |||
403 | status = request.POST.get('changeset_status') |
|
403 | status = request.POST.get('changeset_status') | |
404 | change_status = request.POST.get('change_changeset_status') |
|
404 | change_status = request.POST.get('change_changeset_status') | |
405 | text = request.POST.get('text') |
|
405 | text = request.POST.get('text') | |
406 |
|
406 | |||
407 | allowed_to_change_status = self._get_is_allowed_change_status(pull_request) |
|
407 | allowed_to_change_status = self._get_is_allowed_change_status(pull_request) | |
408 | if status and change_status and allowed_to_change_status: |
|
408 | if status and change_status and allowed_to_change_status: | |
409 | text = text or (_('Status change -> %s') |
|
409 | text = text or (_('Status change -> %s') | |
410 | % ChangesetStatus.get_status_lbl(status)) |
|
410 | % ChangesetStatus.get_status_lbl(status)) | |
411 | comm = ChangesetCommentsModel().create( |
|
411 | comm = ChangesetCommentsModel().create( | |
412 | text=text, |
|
412 | text=text, | |
413 | repo=c.rhodecode_db_repo.repo_id, |
|
413 | repo=c.rhodecode_db_repo.repo_id, | |
414 | user=c.rhodecode_user.user_id, |
|
414 | user=c.rhodecode_user.user_id, | |
415 | pull_request=pull_request_id, |
|
415 | pull_request=pull_request_id, | |
416 | f_path=request.POST.get('f_path'), |
|
416 | f_path=request.POST.get('f_path'), | |
417 | line_no=request.POST.get('line'), |
|
417 | line_no=request.POST.get('line'), | |
418 | status_change=(ChangesetStatus.get_status_lbl(status) |
|
418 | status_change=(ChangesetStatus.get_status_lbl(status) | |
419 | if status and change_status and allowed_to_change_status else None) |
|
419 | if status and change_status and allowed_to_change_status else None) | |
420 | ) |
|
420 | ) | |
421 |
|
421 | |||
422 | action_logger(self.rhodecode_user, |
|
422 | action_logger(self.rhodecode_user, | |
423 | 'user_commented_pull_request:%s' % pull_request_id, |
|
423 | 'user_commented_pull_request:%s' % pull_request_id, | |
424 | c.rhodecode_db_repo, self.ip_addr, self.sa) |
|
424 | c.rhodecode_db_repo, self.ip_addr, self.sa) | |
425 |
|
425 | |||
426 | if allowed_to_change_status: |
|
426 | if allowed_to_change_status: | |
427 | # get status if set ! |
|
427 | # get status if set ! | |
428 | if status and change_status: |
|
428 | if status and change_status: | |
429 | ChangesetStatusModel().set_status( |
|
429 | ChangesetStatusModel().set_status( | |
430 | c.rhodecode_db_repo.repo_id, |
|
430 | c.rhodecode_db_repo.repo_id, | |
431 | status, |
|
431 | status, | |
432 | c.rhodecode_user.user_id, |
|
432 | c.rhodecode_user.user_id, | |
433 | comm, |
|
433 | comm, | |
434 | pull_request=pull_request_id |
|
434 | pull_request=pull_request_id | |
435 | ) |
|
435 | ) | |
436 |
|
436 | |||
437 | if request.POST.get('save_close'): |
|
437 | if request.POST.get('save_close'): | |
438 | if status in ['rejected', 'approved']: |
|
438 | if status in ['rejected', 'approved']: | |
439 | PullRequestModel().close_pull_request(pull_request_id) |
|
439 | PullRequestModel().close_pull_request(pull_request_id) | |
440 | action_logger(self.rhodecode_user, |
|
440 | action_logger(self.rhodecode_user, | |
441 | 'user_closed_pull_request:%s' % pull_request_id, |
|
441 | 'user_closed_pull_request:%s' % pull_request_id, | |
442 | c.rhodecode_db_repo, self.ip_addr, self.sa) |
|
442 | c.rhodecode_db_repo, self.ip_addr, self.sa) | |
443 | else: |
|
443 | else: | |
444 | h.flash(_('Closing pull request on other statuses than ' |
|
444 | h.flash(_('Closing pull request on other statuses than ' | |
445 | 'rejected or approved forbidden'), |
|
445 | 'rejected or approved forbidden'), | |
446 | category='warning') |
|
446 | category='warning') | |
447 |
|
447 | |||
448 | Session().commit() |
|
448 | Session().commit() | |
449 |
|
449 | |||
450 | if not request.environ.get('HTTP_X_PARTIAL_XHR'): |
|
450 | if not request.environ.get('HTTP_X_PARTIAL_XHR'): | |
451 | return redirect(h.url('pullrequest_show', repo_name=repo_name, |
|
451 | return redirect(h.url('pullrequest_show', repo_name=repo_name, | |
452 | pull_request_id=pull_request_id)) |
|
452 | pull_request_id=pull_request_id)) | |
453 |
|
453 | |||
454 | data = { |
|
454 | data = { | |
455 | 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))), |
|
455 | 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))), | |
456 | } |
|
456 | } | |
457 | if comm: |
|
457 | if comm: | |
458 | c.co = comm |
|
458 | c.co = comm | |
459 | data.update(comm.get_dict()) |
|
459 | data.update(comm.get_dict()) | |
460 | data.update({'rendered_text': |
|
460 | data.update({'rendered_text': | |
461 | render('changeset/changeset_comment_block.html')}) |
|
461 | render('changeset/changeset_comment_block.html')}) | |
462 |
|
462 | |||
463 | return data |
|
463 | return data | |
464 |
|
464 | |||
465 | @NotAnonymous() |
|
465 | @NotAnonymous() | |
466 | @jsonify |
|
466 | @jsonify | |
467 | def delete_comment(self, repo_name, comment_id): |
|
467 | def delete_comment(self, repo_name, comment_id): | |
468 | co = ChangesetComment.get(comment_id) |
|
468 | co = ChangesetComment.get(comment_id) | |
469 | if co.pull_request.is_closed(): |
|
469 | if co.pull_request.is_closed(): | |
470 | #don't allow deleting comments on closed pull request |
|
470 | #don't allow deleting comments on closed pull request | |
471 | raise HTTPForbidden() |
|
471 | raise HTTPForbidden() | |
472 |
|
472 | |||
473 | owner = co.author.user_id == c.rhodecode_user.user_id |
|
473 | owner = co.author.user_id == c.rhodecode_user.user_id | |
474 | if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner: |
|
474 | if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner: | |
475 | ChangesetCommentsModel().delete(comment=co) |
|
475 | ChangesetCommentsModel().delete(comment=co) | |
476 | Session().commit() |
|
476 | Session().commit() | |
477 | return True |
|
477 | return True | |
478 | else: |
|
478 | else: | |
479 | raise HTTPForbidden() |
|
479 | raise HTTPForbidden() |
@@ -1,544 +1,544 | |||||
1 | import re |
|
1 | import re | |
2 | from itertools import chain |
|
2 | from itertools import chain | |
3 | from dulwich import objects |
|
3 | from dulwich import objects | |
4 | from subprocess import Popen, PIPE |
|
4 | from subprocess import Popen, PIPE | |
5 | import rhodecode |
|
5 | import rhodecode | |
6 | from rhodecode.lib.vcs.conf import settings |
|
6 | from rhodecode.lib.vcs.conf import settings | |
7 | from rhodecode.lib.vcs.exceptions import RepositoryError |
|
7 | from rhodecode.lib.vcs.exceptions import RepositoryError | |
8 | from rhodecode.lib.vcs.exceptions import ChangesetError |
|
8 | from rhodecode.lib.vcs.exceptions import ChangesetError | |
9 | from rhodecode.lib.vcs.exceptions import NodeDoesNotExistError |
|
9 | from rhodecode.lib.vcs.exceptions import NodeDoesNotExistError | |
10 | from rhodecode.lib.vcs.exceptions import VCSError |
|
10 | from rhodecode.lib.vcs.exceptions import VCSError | |
11 | from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError |
|
11 | from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError | |
12 | from rhodecode.lib.vcs.exceptions import ImproperArchiveTypeError |
|
12 | from rhodecode.lib.vcs.exceptions import ImproperArchiveTypeError | |
13 | from rhodecode.lib.vcs.backends.base import BaseChangeset, EmptyChangeset |
|
13 | from rhodecode.lib.vcs.backends.base import BaseChangeset, EmptyChangeset | |
14 | from rhodecode.lib.vcs.nodes import FileNode, DirNode, NodeKind, RootNode, \ |
|
14 | from rhodecode.lib.vcs.nodes import FileNode, DirNode, NodeKind, RootNode, \ | |
15 | RemovedFileNode, SubModuleNode, ChangedFileNodesGenerator,\ |
|
15 | RemovedFileNode, SubModuleNode, ChangedFileNodesGenerator,\ | |
16 | AddedFileNodesGenerator, RemovedFileNodesGenerator |
|
16 | AddedFileNodesGenerator, RemovedFileNodesGenerator | |
17 | from rhodecode.lib.vcs.utils import safe_unicode |
|
17 | from rhodecode.lib.vcs.utils import safe_unicode | |
18 | from rhodecode.lib.vcs.utils import date_fromtimestamp |
|
18 | from rhodecode.lib.vcs.utils import date_fromtimestamp | |
19 | from rhodecode.lib.vcs.utils.lazy import LazyProperty |
|
19 | from rhodecode.lib.vcs.utils.lazy import LazyProperty | |
20 |
|
20 | |||
21 |
|
21 | |||
22 | class GitChangeset(BaseChangeset): |
|
22 | class GitChangeset(BaseChangeset): | |
23 | """ |
|
23 | """ | |
24 | Represents state of the repository at single revision. |
|
24 | Represents state of the repository at single revision. | |
25 | """ |
|
25 | """ | |
26 |
|
26 | |||
27 | def __init__(self, repository, revision): |
|
27 | def __init__(self, repository, revision): | |
28 | self._stat_modes = {} |
|
28 | self._stat_modes = {} | |
29 | self.repository = repository |
|
29 | self.repository = repository | |
30 |
|
30 | |||
31 | try: |
|
31 | try: | |
32 | commit = self.repository._repo.get_object(revision) |
|
32 | commit = self.repository._repo.get_object(revision) | |
33 | if isinstance(commit, objects.Tag): |
|
33 | if isinstance(commit, objects.Tag): | |
34 | revision = commit.object[1] |
|
34 | revision = commit.object[1] | |
35 | commit = self.repository._repo.get_object(commit.object[1]) |
|
35 | commit = self.repository._repo.get_object(commit.object[1]) | |
36 | except KeyError: |
|
36 | except KeyError: | |
37 | raise RepositoryError("Cannot get object with id %s" % revision) |
|
37 | raise RepositoryError("Cannot get object with id %s" % revision) | |
38 | self.raw_id = revision |
|
38 | self.raw_id = revision | |
39 | self.id = self.raw_id |
|
39 | self.id = self.raw_id | |
40 | self.short_id = self.raw_id[:12] |
|
40 | self.short_id = self.raw_id[:12] | |
41 | self._commit = commit |
|
41 | self._commit = commit | |
42 |
|
42 | |||
43 | self._tree_id = commit.tree |
|
43 | self._tree_id = commit.tree | |
44 | self._commiter_property = 'committer' |
|
44 | self._commiter_property = 'committer' | |
45 | self._author_property = 'author' |
|
45 | self._author_property = 'author' | |
46 | self._date_property = 'commit_time' |
|
46 | self._date_property = 'commit_time' | |
47 | self._date_tz_property = 'commit_timezone' |
|
47 | self._date_tz_property = 'commit_timezone' | |
48 | self.revision = repository.revisions.index(revision) |
|
48 | self.revision = repository.revisions.index(revision) | |
49 |
|
49 | |||
50 | self.message = safe_unicode(commit.message) |
|
50 | self.message = safe_unicode(commit.message) | |
51 |
|
51 | |||
52 | self.nodes = {} |
|
52 | self.nodes = {} | |
53 | self._paths = {} |
|
53 | self._paths = {} | |
54 |
|
54 | |||
55 | @LazyProperty |
|
55 | @LazyProperty | |
56 | def commiter(self): |
|
56 | def commiter(self): | |
57 | return safe_unicode(getattr(self._commit, self._commiter_property)) |
|
57 | return safe_unicode(getattr(self._commit, self._commiter_property)) | |
58 |
|
58 | |||
59 | @LazyProperty |
|
59 | @LazyProperty | |
60 | def author(self): |
|
60 | def author(self): | |
61 | return safe_unicode(getattr(self._commit, self._author_property)) |
|
61 | return safe_unicode(getattr(self._commit, self._author_property)) | |
62 |
|
62 | |||
63 | @LazyProperty |
|
63 | @LazyProperty | |
64 | def date(self): |
|
64 | def date(self): | |
65 | return date_fromtimestamp(getattr(self._commit, self._date_property), |
|
65 | return date_fromtimestamp(getattr(self._commit, self._date_property), | |
66 | getattr(self._commit, self._date_tz_property)) |
|
66 | getattr(self._commit, self._date_tz_property)) | |
67 |
|
67 | |||
68 | @LazyProperty |
|
68 | @LazyProperty | |
69 | def _timestamp(self): |
|
69 | def _timestamp(self): | |
70 | return getattr(self._commit, self._date_property) |
|
70 | return getattr(self._commit, self._date_property) | |
71 |
|
71 | |||
72 | @LazyProperty |
|
72 | @LazyProperty | |
73 | def status(self): |
|
73 | def status(self): | |
74 | """ |
|
74 | """ | |
75 | Returns modified, added, removed, deleted files for current changeset |
|
75 | Returns modified, added, removed, deleted files for current changeset | |
76 | """ |
|
76 | """ | |
77 | return self.changed, self.added, self.removed |
|
77 | return self.changed, self.added, self.removed | |
78 |
|
78 | |||
79 | @LazyProperty |
|
79 | @LazyProperty | |
80 | def tags(self): |
|
80 | def tags(self): | |
81 | _tags = [] |
|
81 | _tags = [] | |
82 | for tname, tsha in self.repository.tags.iteritems(): |
|
82 | for tname, tsha in self.repository.tags.iteritems(): | |
83 | if tsha == self.raw_id: |
|
83 | if tsha == self.raw_id: | |
84 | _tags.append(tname) |
|
84 | _tags.append(tname) | |
85 | return _tags |
|
85 | return _tags | |
86 |
|
86 | |||
87 | @LazyProperty |
|
87 | @LazyProperty | |
88 | def branch(self): |
|
88 | def branch(self): | |
89 |
|
89 | |||
90 | heads = self.repository._heads(reverse=False) |
|
90 | heads = self.repository._heads(reverse=False) | |
91 |
|
91 | |||
92 | ref = heads.get(self.raw_id) |
|
92 | ref = heads.get(self.raw_id) | |
93 | if ref: |
|
93 | if ref: | |
94 | return safe_unicode(ref) |
|
94 | return safe_unicode(ref) | |
95 |
|
95 | |||
96 | def _fix_path(self, path): |
|
96 | def _fix_path(self, path): | |
97 | """ |
|
97 | """ | |
98 | Paths are stored without trailing slash so we need to get rid off it if |
|
98 | Paths are stored without trailing slash so we need to get rid off it if | |
99 | needed. |
|
99 | needed. | |
100 | """ |
|
100 | """ | |
101 | if path.endswith('/'): |
|
101 | if path.endswith('/'): | |
102 | path = path.rstrip('/') |
|
102 | path = path.rstrip('/') | |
103 | return path |
|
103 | return path | |
104 |
|
104 | |||
105 | def _get_id_for_path(self, path): |
|
105 | def _get_id_for_path(self, path): | |
106 |
|
106 | |||
107 | # FIXME: Please, spare a couple of minutes and make those codes cleaner; |
|
107 | # FIXME: Please, spare a couple of minutes and make those codes cleaner; | |
108 | if not path in self._paths: |
|
108 | if not path in self._paths: | |
109 | path = path.strip('/') |
|
109 | path = path.strip('/') | |
110 | # set root tree |
|
110 | # set root tree | |
111 | tree = self.repository._repo[self._tree_id] |
|
111 | tree = self.repository._repo[self._tree_id] | |
112 | if path == '': |
|
112 | if path == '': | |
113 | self._paths[''] = tree.id |
|
113 | self._paths[''] = tree.id | |
114 | return tree.id |
|
114 | return tree.id | |
115 | splitted = path.split('/') |
|
115 | splitted = path.split('/') | |
116 | dirs, name = splitted[:-1], splitted[-1] |
|
116 | dirs, name = splitted[:-1], splitted[-1] | |
117 | curdir = '' |
|
117 | curdir = '' | |
118 |
|
118 | |||
119 | # initially extract things from root dir |
|
119 | # initially extract things from root dir | |
120 | for item, stat, id in tree.iteritems(): |
|
120 | for item, stat, id in tree.iteritems(): | |
121 | if curdir: |
|
121 | if curdir: | |
122 | name = '/'.join((curdir, item)) |
|
122 | name = '/'.join((curdir, item)) | |
123 | else: |
|
123 | else: | |
124 | name = item |
|
124 | name = item | |
125 | self._paths[name] = id |
|
125 | self._paths[name] = id | |
126 | self._stat_modes[name] = stat |
|
126 | self._stat_modes[name] = stat | |
127 |
|
127 | |||
128 | for dir in dirs: |
|
128 | for dir in dirs: | |
129 | if curdir: |
|
129 | if curdir: | |
130 | curdir = '/'.join((curdir, dir)) |
|
130 | curdir = '/'.join((curdir, dir)) | |
131 | else: |
|
131 | else: | |
132 | curdir = dir |
|
132 | curdir = dir | |
133 | dir_id = None |
|
133 | dir_id = None | |
134 | for item, stat, id in tree.iteritems(): |
|
134 | for item, stat, id in tree.iteritems(): | |
135 | if dir == item: |
|
135 | if dir == item: | |
136 | dir_id = id |
|
136 | dir_id = id | |
137 | if dir_id: |
|
137 | if dir_id: | |
138 | # Update tree |
|
138 | # Update tree | |
139 | tree = self.repository._repo[dir_id] |
|
139 | tree = self.repository._repo[dir_id] | |
140 | if not isinstance(tree, objects.Tree): |
|
140 | if not isinstance(tree, objects.Tree): | |
141 | raise ChangesetError('%s is not a directory' % curdir) |
|
141 | raise ChangesetError('%s is not a directory' % curdir) | |
142 | else: |
|
142 | else: | |
143 | raise ChangesetError('%s have not been found' % curdir) |
|
143 | raise ChangesetError('%s have not been found' % curdir) | |
144 |
|
144 | |||
145 | # cache all items from the given traversed tree |
|
145 | # cache all items from the given traversed tree | |
146 | for item, stat, id in tree.iteritems(): |
|
146 | for item, stat, id in tree.iteritems(): | |
147 | if curdir: |
|
147 | if curdir: | |
148 | name = '/'.join((curdir, item)) |
|
148 | name = '/'.join((curdir, item)) | |
149 | else: |
|
149 | else: | |
150 | name = item |
|
150 | name = item | |
151 | self._paths[name] = id |
|
151 | self._paths[name] = id | |
152 | self._stat_modes[name] = stat |
|
152 | self._stat_modes[name] = stat | |
153 | if not path in self._paths: |
|
153 | if not path in self._paths: | |
154 | raise NodeDoesNotExistError("There is no file nor directory " |
|
154 | raise NodeDoesNotExistError("There is no file nor directory " | |
155 | "at the given path %r at revision %r" |
|
155 | "at the given path %r at revision %r" | |
156 | % (path, self.short_id)) |
|
156 | % (path, self.short_id)) | |
157 | return self._paths[path] |
|
157 | return self._paths[path] | |
158 |
|
158 | |||
159 | def _get_kind(self, path): |
|
159 | def _get_kind(self, path): | |
160 | obj = self.repository._repo[self._get_id_for_path(path)] |
|
160 | obj = self.repository._repo[self._get_id_for_path(path)] | |
161 | if isinstance(obj, objects.Blob): |
|
161 | if isinstance(obj, objects.Blob): | |
162 | return NodeKind.FILE |
|
162 | return NodeKind.FILE | |
163 | elif isinstance(obj, objects.Tree): |
|
163 | elif isinstance(obj, objects.Tree): | |
164 | return NodeKind.DIR |
|
164 | return NodeKind.DIR | |
165 |
|
165 | |||
166 | def _get_filectx(self, path): |
|
166 | def _get_filectx(self, path): | |
167 | path = self._fix_path(path) |
|
167 | path = self._fix_path(path) | |
168 | if self._get_kind(path) != NodeKind.FILE: |
|
168 | if self._get_kind(path) != NodeKind.FILE: | |
169 | raise ChangesetError("File does not exist for revision %r at " |
|
169 | raise ChangesetError("File does not exist for revision %r at " | |
170 | " %r" % (self.raw_id, path)) |
|
170 | " %r" % (self.raw_id, path)) | |
171 | return path |
|
171 | return path | |
172 |
|
172 | |||
173 | def _get_file_nodes(self): |
|
173 | def _get_file_nodes(self): | |
174 | return chain(*(t[2] for t in self.walk())) |
|
174 | return chain(*(t[2] for t in self.walk())) | |
175 |
|
175 | |||
176 | @LazyProperty |
|
176 | @LazyProperty | |
177 | def parents(self): |
|
177 | def parents(self): | |
178 | """ |
|
178 | """ | |
179 | Returns list of parents changesets. |
|
179 | Returns list of parents changesets. | |
180 | """ |
|
180 | """ | |
181 | return [self.repository.get_changeset(parent) |
|
181 | return [self.repository.get_changeset(parent) | |
182 | for parent in self._commit.parents] |
|
182 | for parent in self._commit.parents] | |
183 |
|
183 | |||
184 | @LazyProperty |
|
184 | @LazyProperty | |
185 | def children(self): |
|
185 | def children(self): | |
186 | """ |
|
186 | """ | |
187 | Returns list of children changesets. |
|
187 | Returns list of children changesets. | |
188 | """ |
|
188 | """ | |
189 | so, se = self.repository.run_git_command( |
|
189 | so, se = self.repository.run_git_command( | |
190 | "rev-list --all --children | grep '^%s'" % self.raw_id |
|
190 | "rev-list --all --children | grep '^%s'" % self.raw_id | |
191 | ) |
|
191 | ) | |
192 |
|
192 | |||
193 | children = [] |
|
193 | children = [] | |
194 | for l in so.splitlines(): |
|
194 | for l in so.splitlines(): | |
195 | childs = l.split(' ')[1:] |
|
195 | childs = l.split(' ')[1:] | |
196 | children.extend(childs) |
|
196 | children.extend(childs) | |
197 | return [self.repository.get_changeset(cs) for cs in children] |
|
197 | return [self.repository.get_changeset(cs) for cs in children] | |
198 |
|
198 | |||
199 | def next(self, branch=None): |
|
199 | def next(self, branch=None): | |
200 |
|
200 | |||
201 | if branch and self.branch != branch: |
|
201 | if branch and self.branch != branch: | |
202 | raise VCSError('Branch option used on changeset not belonging ' |
|
202 | raise VCSError('Branch option used on changeset not belonging ' | |
203 | 'to that branch') |
|
203 | 'to that branch') | |
204 |
|
204 | |||
205 | def _next(changeset, branch): |
|
205 | def _next(changeset, branch): | |
206 | try: |
|
206 | try: | |
207 | next_ = changeset.revision + 1 |
|
207 | next_ = changeset.revision + 1 | |
208 | next_rev = changeset.repository.revisions[next_] |
|
208 | next_rev = changeset.repository.revisions[next_] | |
209 | except IndexError: |
|
209 | except IndexError: | |
210 | raise ChangesetDoesNotExistError |
|
210 | raise ChangesetDoesNotExistError | |
211 | cs = changeset.repository.get_changeset(next_rev) |
|
211 | cs = changeset.repository.get_changeset(next_rev) | |
212 |
|
212 | |||
213 | if branch and branch != cs.branch: |
|
213 | if branch and branch != cs.branch: | |
214 | return _next(cs, branch) |
|
214 | return _next(cs, branch) | |
215 |
|
215 | |||
216 | return cs |
|
216 | return cs | |
217 |
|
217 | |||
218 | return _next(self, branch) |
|
218 | return _next(self, branch) | |
219 |
|
219 | |||
220 | def prev(self, branch=None): |
|
220 | def prev(self, branch=None): | |
221 | if branch and self.branch != branch: |
|
221 | if branch and self.branch != branch: | |
222 | raise VCSError('Branch option used on changeset not belonging ' |
|
222 | raise VCSError('Branch option used on changeset not belonging ' | |
223 | 'to that branch') |
|
223 | 'to that branch') | |
224 |
|
224 | |||
225 | def _prev(changeset, branch): |
|
225 | def _prev(changeset, branch): | |
226 | try: |
|
226 | try: | |
227 | prev_ = changeset.revision - 1 |
|
227 | prev_ = changeset.revision - 1 | |
228 | if prev_ < 0: |
|
228 | if prev_ < 0: | |
229 | raise IndexError |
|
229 | raise IndexError | |
230 | prev_rev = changeset.repository.revisions[prev_] |
|
230 | prev_rev = changeset.repository.revisions[prev_] | |
231 | except IndexError: |
|
231 | except IndexError: | |
232 | raise ChangesetDoesNotExistError |
|
232 | raise ChangesetDoesNotExistError | |
233 |
|
233 | |||
234 | cs = changeset.repository.get_changeset(prev_rev) |
|
234 | cs = changeset.repository.get_changeset(prev_rev) | |
235 |
|
235 | |||
236 | if branch and branch != cs.branch: |
|
236 | if branch and branch != cs.branch: | |
237 | return _prev(cs, branch) |
|
237 | return _prev(cs, branch) | |
238 |
|
238 | |||
239 | return cs |
|
239 | return cs | |
240 |
|
240 | |||
241 | return _prev(self, branch) |
|
241 | return _prev(self, branch) | |
242 |
|
242 | |||
243 | def diff(self, ignore_whitespace=True, context=3): |
|
243 | def diff(self, ignore_whitespace=True, context=3): | |
244 | rev1 = self.parents[0] if self.parents else self.repository.EMPTY_CHANGESET |
|
244 | rev1 = self.parents[0] if self.parents else self.repository.EMPTY_CHANGESET | |
245 | rev2 = self |
|
245 | rev2 = self | |
246 | return ''.join(self.repository.get_diff(rev1, rev2, |
|
246 | return ''.join(self.repository.get_diff(rev1, rev2, | |
247 | ignore_whitespace=ignore_whitespace, |
|
247 | ignore_whitespace=ignore_whitespace, | |
248 | context=context)) |
|
248 | context=context)) | |
249 |
|
249 | |||
250 | def get_file_mode(self, path): |
|
250 | def get_file_mode(self, path): | |
251 | """ |
|
251 | """ | |
252 | Returns stat mode of the file at the given ``path``. |
|
252 | Returns stat mode of the file at the given ``path``. | |
253 | """ |
|
253 | """ | |
254 | # ensure path is traversed |
|
254 | # ensure path is traversed | |
255 | self._get_id_for_path(path) |
|
255 | self._get_id_for_path(path) | |
256 | return self._stat_modes[path] |
|
256 | return self._stat_modes[path] | |
257 |
|
257 | |||
258 | def get_file_content(self, path): |
|
258 | def get_file_content(self, path): | |
259 | """ |
|
259 | """ | |
260 | Returns content of the file at given ``path``. |
|
260 | Returns content of the file at given ``path``. | |
261 | """ |
|
261 | """ | |
262 | id = self._get_id_for_path(path) |
|
262 | id = self._get_id_for_path(path) | |
263 | blob = self.repository._repo[id] |
|
263 | blob = self.repository._repo[id] | |
264 | return blob.as_pretty_string() |
|
264 | return blob.as_pretty_string() | |
265 |
|
265 | |||
266 | def get_file_size(self, path): |
|
266 | def get_file_size(self, path): | |
267 | """ |
|
267 | """ | |
268 | Returns size of the file at given ``path``. |
|
268 | Returns size of the file at given ``path``. | |
269 | """ |
|
269 | """ | |
270 | id = self._get_id_for_path(path) |
|
270 | id = self._get_id_for_path(path) | |
271 | blob = self.repository._repo[id] |
|
271 | blob = self.repository._repo[id] | |
272 | return blob.raw_length() |
|
272 | return blob.raw_length() | |
273 |
|
273 | |||
274 | def get_file_changeset(self, path): |
|
274 | def get_file_changeset(self, path): | |
275 | """ |
|
275 | """ | |
276 | Returns last commit of the file at the given ``path``. |
|
276 | Returns last commit of the file at the given ``path``. | |
277 | """ |
|
277 | """ | |
278 | node = self.get_node(path) |
|
278 | node = self.get_node(path) | |
279 | return node.history[0] |
|
279 | return node.history[0] | |
280 |
|
280 | |||
281 | def get_file_history(self, path): |
|
281 | def get_file_history(self, path): | |
282 | """ |
|
282 | """ | |
283 | Returns history of file as reversed list of ``Changeset`` objects for |
|
283 | Returns history of file as reversed list of ``Changeset`` objects for | |
284 | which file at given ``path`` has been modified. |
|
284 | which file at given ``path`` has been modified. | |
285 |
|
285 | |||
286 | TODO: This function now uses os underlying 'git' and 'grep' commands |
|
286 | TODO: This function now uses os underlying 'git' and 'grep' commands | |
287 | which is generally not good. Should be replaced with algorithm |
|
287 | which is generally not good. Should be replaced with algorithm | |
288 | iterating commits. |
|
288 | iterating commits. | |
289 | """ |
|
289 | """ | |
290 | self._get_filectx(path) |
|
290 | self._get_filectx(path) | |
291 |
|
291 | |||
292 | cmd = 'log --pretty="format: %%H" -s -p %s -- "%s"' % ( |
|
292 | cmd = 'log --pretty="format: %%H" -s -p %s -- "%s"' % ( | |
293 | self.id, path |
|
293 | self.id, path | |
294 | ) |
|
294 | ) | |
295 | so, se = self.repository.run_git_command(cmd) |
|
295 | so, se = self.repository.run_git_command(cmd) | |
296 | ids = re.findall(r'[0-9a-fA-F]{40}', so) |
|
296 | ids = re.findall(r'[0-9a-fA-F]{40}', so) | |
297 | return [self.repository.get_changeset(id) for id in ids] |
|
297 | return [self.repository.get_changeset(id) for id in ids] | |
298 |
|
298 | |||
299 | def get_file_history_2(self, path): |
|
299 | def get_file_history_2(self, path): | |
300 | """ |
|
300 | """ | |
301 | Returns history of file as reversed list of ``Changeset`` objects for |
|
301 | Returns history of file as reversed list of ``Changeset`` objects for | |
302 | which file at given ``path`` has been modified. |
|
302 | which file at given ``path`` has been modified. | |
303 |
|
303 | |||
304 | """ |
|
304 | """ | |
305 | self._get_filectx(path) |
|
305 | self._get_filectx(path) | |
306 | from dulwich.walk import Walker |
|
306 | from dulwich.walk import Walker | |
307 | include = [self.id] |
|
307 | include = [self.id] | |
308 | walker = Walker(self.repository._repo.object_store, include, |
|
308 | walker = Walker(self.repository._repo.object_store, include, | |
309 | paths=[path], max_entries=1) |
|
309 | paths=[path], max_entries=1) | |
310 | return [self.repository.get_changeset(sha) |
|
310 | return [self.repository.get_changeset(sha) | |
311 | for sha in (x.commit.id for x in walker)] |
|
311 | for sha in (x.commit.id for x in walker)] | |
312 |
|
312 | |||
313 | def get_file_annotate(self, path): |
|
313 | def get_file_annotate(self, path): | |
314 | """ |
|
314 | """ | |
315 | Returns a generator of four element tuples with |
|
315 | Returns a generator of four element tuples with | |
316 | lineno, sha, changeset lazy loader and line |
|
316 | lineno, sha, changeset lazy loader and line | |
317 |
|
317 | |||
318 | TODO: This function now uses os underlying 'git' command which is |
|
318 | TODO: This function now uses os underlying 'git' command which is | |
319 | generally not good. Should be replaced with algorithm iterating |
|
319 | generally not good. Should be replaced with algorithm iterating | |
320 | commits. |
|
320 | commits. | |
321 | """ |
|
321 | """ | |
322 | cmd = 'blame -l --root -r %s -- "%s"' % (self.id, path) |
|
322 | cmd = 'blame -l --root -r %s -- "%s"' % (self.id, path) | |
323 | # -l ==> outputs long shas (and we need all 40 characters) |
|
323 | # -l ==> outputs long shas (and we need all 40 characters) | |
324 | # --root ==> doesn't put '^' character for bounderies |
|
324 | # --root ==> doesn't put '^' character for bounderies | |
325 | # -r sha ==> blames for the given revision |
|
325 | # -r sha ==> blames for the given revision | |
326 | so, se = self.repository.run_git_command(cmd) |
|
326 | so, se = self.repository.run_git_command(cmd) | |
327 |
|
327 | |||
328 | for i, blame_line in enumerate(so.split('\n')[:-1]): |
|
328 | for i, blame_line in enumerate(so.split('\n')[:-1]): | |
329 | ln_no = i + 1 |
|
329 | ln_no = i + 1 | |
330 | sha, line = re.split(r' ', blame_line, 1) |
|
330 | sha, line = re.split(r' ', blame_line, 1) | |
331 | yield (ln_no, sha, lambda: self.repository.get_changeset(sha), line) |
|
331 | yield (ln_no, sha, lambda: self.repository.get_changeset(sha), line) | |
332 |
|
332 | |||
333 | def fill_archive(self, stream=None, kind='tgz', prefix=None, |
|
333 | def fill_archive(self, stream=None, kind='tgz', prefix=None, | |
334 | subrepos=False): |
|
334 | subrepos=False): | |
335 | """ |
|
335 | """ | |
336 | Fills up given stream. |
|
336 | Fills up given stream. | |
337 |
|
337 | |||
338 | :param stream: file like object. |
|
338 | :param stream: file like object. | |
339 | :param kind: one of following: ``zip``, ``tgz`` or ``tbz2``. |
|
339 | :param kind: one of following: ``zip``, ``tgz`` or ``tbz2``. | |
340 | Default: ``tgz``. |
|
340 | Default: ``tgz``. | |
341 | :param prefix: name of root directory in archive. |
|
341 | :param prefix: name of root directory in archive. | |
342 | Default is repository name and changeset's raw_id joined with dash |
|
342 | Default is repository name and changeset's raw_id joined with dash | |
343 | (``repo-tip.<KIND>``). |
|
343 | (``repo-tip.<KIND>``). | |
344 | :param subrepos: include subrepos in this archive. |
|
344 | :param subrepos: include subrepos in this archive. | |
345 |
|
345 | |||
346 | :raise ImproperArchiveTypeError: If given kind is wrong. |
|
346 | :raise ImproperArchiveTypeError: If given kind is wrong. | |
347 | :raise VcsError: If given stream is None |
|
347 | :raise VcsError: If given stream is None | |
348 |
|
348 | |||
349 | """ |
|
349 | """ | |
350 | allowed_kinds = settings.ARCHIVE_SPECS.keys() |
|
350 | allowed_kinds = settings.ARCHIVE_SPECS.keys() | |
351 | if kind not in allowed_kinds: |
|
351 | if kind not in allowed_kinds: | |
352 | raise ImproperArchiveTypeError('Archive kind not supported use one' |
|
352 | raise ImproperArchiveTypeError('Archive kind not supported use one' | |
353 | 'of %s', allowed_kinds) |
|
353 | 'of %s', allowed_kinds) | |
354 |
|
354 | |||
355 | if prefix is None: |
|
355 | if prefix is None: | |
356 | prefix = '%s-%s' % (self.repository.name, self.short_id) |
|
356 | prefix = '%s-%s' % (self.repository.name, self.short_id) | |
357 | elif prefix.startswith('/'): |
|
357 | elif prefix.startswith('/'): | |
358 | raise VCSError("Prefix cannot start with leading slash") |
|
358 | raise VCSError("Prefix cannot start with leading slash") | |
359 | elif prefix.strip() == '': |
|
359 | elif prefix.strip() == '': | |
360 | raise VCSError("Prefix cannot be empty") |
|
360 | raise VCSError("Prefix cannot be empty") | |
361 |
|
361 | |||
362 | if kind == 'zip': |
|
362 | if kind == 'zip': | |
363 | frmt = 'zip' |
|
363 | frmt = 'zip' | |
364 | else: |
|
364 | else: | |
365 | frmt = 'tar' |
|
365 | frmt = 'tar' | |
366 | _git_path = rhodecode.CONFIG.get('git_path', 'git') |
|
366 | _git_path = rhodecode.CONFIG.get('git_path', 'git') | |
367 |
cmd = '%s archive --format=%s --prefix=%s/ %s' % (_git_path, |
|
367 | cmd = '%s archive --format=%s --prefix=%s/ %s' % (_git_path, | |
368 | frmt, prefix, self.raw_id) |
|
368 | frmt, prefix, self.raw_id) | |
369 | if kind == 'tgz': |
|
369 | if kind == 'tgz': | |
370 | cmd += ' | gzip -9' |
|
370 | cmd += ' | gzip -9' | |
371 | elif kind == 'tbz2': |
|
371 | elif kind == 'tbz2': | |
372 | cmd += ' | bzip2 -9' |
|
372 | cmd += ' | bzip2 -9' | |
373 |
|
373 | |||
374 | if stream is None: |
|
374 | if stream is None: | |
375 | raise VCSError('You need to pass in a valid stream for filling' |
|
375 | raise VCSError('You need to pass in a valid stream for filling' | |
376 | ' with archival data') |
|
376 | ' with archival data') | |
377 | popen = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True, |
|
377 | popen = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True, | |
378 | cwd=self.repository.path) |
|
378 | cwd=self.repository.path) | |
379 |
|
379 | |||
380 | buffer_size = 1024 * 8 |
|
380 | buffer_size = 1024 * 8 | |
381 | chunk = popen.stdout.read(buffer_size) |
|
381 | chunk = popen.stdout.read(buffer_size) | |
382 | while chunk: |
|
382 | while chunk: | |
383 | stream.write(chunk) |
|
383 | stream.write(chunk) | |
384 | chunk = popen.stdout.read(buffer_size) |
|
384 | chunk = popen.stdout.read(buffer_size) | |
385 | # Make sure all descriptors would be read |
|
385 | # Make sure all descriptors would be read | |
386 | popen.communicate() |
|
386 | popen.communicate() | |
387 |
|
387 | |||
388 | def get_nodes(self, path): |
|
388 | def get_nodes(self, path): | |
389 | if self._get_kind(path) != NodeKind.DIR: |
|
389 | if self._get_kind(path) != NodeKind.DIR: | |
390 | raise ChangesetError("Directory does not exist for revision %r at " |
|
390 | raise ChangesetError("Directory does not exist for revision %r at " | |
391 | " %r" % (self.revision, path)) |
|
391 | " %r" % (self.revision, path)) | |
392 | path = self._fix_path(path) |
|
392 | path = self._fix_path(path) | |
393 | id = self._get_id_for_path(path) |
|
393 | id = self._get_id_for_path(path) | |
394 | tree = self.repository._repo[id] |
|
394 | tree = self.repository._repo[id] | |
395 | dirnodes = [] |
|
395 | dirnodes = [] | |
396 | filenodes = [] |
|
396 | filenodes = [] | |
397 | als = self.repository.alias |
|
397 | als = self.repository.alias | |
398 | for name, stat, id in tree.iteritems(): |
|
398 | for name, stat, id in tree.iteritems(): | |
399 | if objects.S_ISGITLINK(stat): |
|
399 | if objects.S_ISGITLINK(stat): | |
400 | dirnodes.append(SubModuleNode(name, url=None, changeset=id, |
|
400 | dirnodes.append(SubModuleNode(name, url=None, changeset=id, | |
401 | alias=als)) |
|
401 | alias=als)) | |
402 | continue |
|
402 | continue | |
403 |
|
403 | |||
404 | obj = self.repository._repo.get_object(id) |
|
404 | obj = self.repository._repo.get_object(id) | |
405 | if path != '': |
|
405 | if path != '': | |
406 | obj_path = '/'.join((path, name)) |
|
406 | obj_path = '/'.join((path, name)) | |
407 | else: |
|
407 | else: | |
408 | obj_path = name |
|
408 | obj_path = name | |
409 | if obj_path not in self._stat_modes: |
|
409 | if obj_path not in self._stat_modes: | |
410 | self._stat_modes[obj_path] = stat |
|
410 | self._stat_modes[obj_path] = stat | |
411 | if isinstance(obj, objects.Tree): |
|
411 | if isinstance(obj, objects.Tree): | |
412 | dirnodes.append(DirNode(obj_path, changeset=self)) |
|
412 | dirnodes.append(DirNode(obj_path, changeset=self)) | |
413 | elif isinstance(obj, objects.Blob): |
|
413 | elif isinstance(obj, objects.Blob): | |
414 | filenodes.append(FileNode(obj_path, changeset=self, mode=stat)) |
|
414 | filenodes.append(FileNode(obj_path, changeset=self, mode=stat)) | |
415 | else: |
|
415 | else: | |
416 | raise ChangesetError("Requested object should be Tree " |
|
416 | raise ChangesetError("Requested object should be Tree " | |
417 | "or Blob, is %r" % type(obj)) |
|
417 | "or Blob, is %r" % type(obj)) | |
418 | nodes = dirnodes + filenodes |
|
418 | nodes = dirnodes + filenodes | |
419 | for node in nodes: |
|
419 | for node in nodes: | |
420 | if not node.path in self.nodes: |
|
420 | if not node.path in self.nodes: | |
421 | self.nodes[node.path] = node |
|
421 | self.nodes[node.path] = node | |
422 | nodes.sort() |
|
422 | nodes.sort() | |
423 | return nodes |
|
423 | return nodes | |
424 |
|
424 | |||
425 | def get_node(self, path): |
|
425 | def get_node(self, path): | |
426 | if isinstance(path, unicode): |
|
426 | if isinstance(path, unicode): | |
427 | path = path.encode('utf-8') |
|
427 | path = path.encode('utf-8') | |
428 | path = self._fix_path(path) |
|
428 | path = self._fix_path(path) | |
429 | if not path in self.nodes: |
|
429 | if not path in self.nodes: | |
430 | try: |
|
430 | try: | |
431 | id_ = self._get_id_for_path(path) |
|
431 | id_ = self._get_id_for_path(path) | |
432 | except ChangesetError: |
|
432 | except ChangesetError: | |
433 | raise NodeDoesNotExistError("Cannot find one of parents' " |
|
433 | raise NodeDoesNotExistError("Cannot find one of parents' " | |
434 | "directories for a given path: %s" % path) |
|
434 | "directories for a given path: %s" % path) | |
435 |
|
435 | |||
436 | _GL = lambda m: m and objects.S_ISGITLINK(m) |
|
436 | _GL = lambda m: m and objects.S_ISGITLINK(m) | |
437 | if _GL(self._stat_modes.get(path)): |
|
437 | if _GL(self._stat_modes.get(path)): | |
438 | node = SubModuleNode(path, url=None, changeset=id_, |
|
438 | node = SubModuleNode(path, url=None, changeset=id_, | |
439 | alias=self.repository.alias) |
|
439 | alias=self.repository.alias) | |
440 | else: |
|
440 | else: | |
441 | obj = self.repository._repo.get_object(id_) |
|
441 | obj = self.repository._repo.get_object(id_) | |
442 |
|
442 | |||
443 | if isinstance(obj, objects.Tree): |
|
443 | if isinstance(obj, objects.Tree): | |
444 | if path == '': |
|
444 | if path == '': | |
445 | node = RootNode(changeset=self) |
|
445 | node = RootNode(changeset=self) | |
446 | else: |
|
446 | else: | |
447 | node = DirNode(path, changeset=self) |
|
447 | node = DirNode(path, changeset=self) | |
448 | node._tree = obj |
|
448 | node._tree = obj | |
449 | elif isinstance(obj, objects.Blob): |
|
449 | elif isinstance(obj, objects.Blob): | |
450 | node = FileNode(path, changeset=self) |
|
450 | node = FileNode(path, changeset=self) | |
451 | node._blob = obj |
|
451 | node._blob = obj | |
452 | else: |
|
452 | else: | |
453 | raise NodeDoesNotExistError("There is no file nor directory " |
|
453 | raise NodeDoesNotExistError("There is no file nor directory " | |
454 | "at the given path %r at revision %r" |
|
454 | "at the given path %r at revision %r" | |
455 | % (path, self.short_id)) |
|
455 | % (path, self.short_id)) | |
456 | # cache node |
|
456 | # cache node | |
457 | self.nodes[path] = node |
|
457 | self.nodes[path] = node | |
458 | return self.nodes[path] |
|
458 | return self.nodes[path] | |
459 |
|
459 | |||
460 | @LazyProperty |
|
460 | @LazyProperty | |
461 | def affected_files(self): |
|
461 | def affected_files(self): | |
462 | """ |
|
462 | """ | |
463 | Get's a fast accessible file changes for given changeset |
|
463 | Get's a fast accessible file changes for given changeset | |
464 | """ |
|
464 | """ | |
465 | a, m, d = self._changes_cache |
|
465 | a, m, d = self._changes_cache | |
466 | return list(a.union(m).union(d)) |
|
466 | return list(a.union(m).union(d)) | |
467 |
|
467 | |||
468 | @LazyProperty |
|
468 | @LazyProperty | |
469 | def _diff_name_status(self): |
|
469 | def _diff_name_status(self): | |
470 | output = [] |
|
470 | output = [] | |
471 | for parent in self.parents: |
|
471 | for parent in self.parents: | |
472 | cmd = 'diff --name-status %s %s --encoding=utf8' % (parent.raw_id, |
|
472 | cmd = 'diff --name-status %s %s --encoding=utf8' % (parent.raw_id, | |
473 | self.raw_id) |
|
473 | self.raw_id) | |
474 | so, se = self.repository.run_git_command(cmd) |
|
474 | so, se = self.repository.run_git_command(cmd) | |
475 | output.append(so.strip()) |
|
475 | output.append(so.strip()) | |
476 | return '\n'.join(output) |
|
476 | return '\n'.join(output) | |
477 |
|
477 | |||
478 | @LazyProperty |
|
478 | @LazyProperty | |
479 | def _changes_cache(self): |
|
479 | def _changes_cache(self): | |
480 | added = set() |
|
480 | added = set() | |
481 | modified = set() |
|
481 | modified = set() | |
482 | deleted = set() |
|
482 | deleted = set() | |
483 | _r = self.repository._repo |
|
483 | _r = self.repository._repo | |
484 |
|
484 | |||
485 | parents = self.parents |
|
485 | parents = self.parents | |
486 | if not self.parents: |
|
486 | if not self.parents: | |
487 | parents = [EmptyChangeset()] |
|
487 | parents = [EmptyChangeset()] | |
488 | for parent in parents: |
|
488 | for parent in parents: | |
489 | if isinstance(parent, EmptyChangeset): |
|
489 | if isinstance(parent, EmptyChangeset): | |
490 | oid = None |
|
490 | oid = None | |
491 | else: |
|
491 | else: | |
492 | oid = _r[parent.raw_id].tree |
|
492 | oid = _r[parent.raw_id].tree | |
493 | changes = _r.object_store.tree_changes(oid, _r[self.raw_id].tree) |
|
493 | changes = _r.object_store.tree_changes(oid, _r[self.raw_id].tree) | |
494 | for (oldpath, newpath), (_, _), (_, _) in changes: |
|
494 | for (oldpath, newpath), (_, _), (_, _) in changes: | |
495 | if newpath and oldpath: |
|
495 | if newpath and oldpath: | |
496 | modified.add(newpath) |
|
496 | modified.add(newpath) | |
497 | elif newpath and not oldpath: |
|
497 | elif newpath and not oldpath: | |
498 | added.add(newpath) |
|
498 | added.add(newpath) | |
499 | elif not newpath and oldpath: |
|
499 | elif not newpath and oldpath: | |
500 | deleted.add(oldpath) |
|
500 | deleted.add(oldpath) | |
501 | return added, modified, deleted |
|
501 | return added, modified, deleted | |
502 |
|
502 | |||
503 | def _get_paths_for_status(self, status): |
|
503 | def _get_paths_for_status(self, status): | |
504 | """ |
|
504 | """ | |
505 | Returns sorted list of paths for given ``status``. |
|
505 | Returns sorted list of paths for given ``status``. | |
506 |
|
506 | |||
507 | :param status: one of: *added*, *modified* or *deleted* |
|
507 | :param status: one of: *added*, *modified* or *deleted* | |
508 | """ |
|
508 | """ | |
509 | a, m, d = self._changes_cache |
|
509 | a, m, d = self._changes_cache | |
510 | return sorted({ |
|
510 | return sorted({ | |
511 | 'added': list(a), |
|
511 | 'added': list(a), | |
512 | 'modified': list(m), |
|
512 | 'modified': list(m), | |
513 | 'deleted': list(d)}[status] |
|
513 | 'deleted': list(d)}[status] | |
514 | ) |
|
514 | ) | |
515 |
|
515 | |||
516 | @LazyProperty |
|
516 | @LazyProperty | |
517 | def added(self): |
|
517 | def added(self): | |
518 | """ |
|
518 | """ | |
519 | Returns list of added ``FileNode`` objects. |
|
519 | Returns list of added ``FileNode`` objects. | |
520 | """ |
|
520 | """ | |
521 | if not self.parents: |
|
521 | if not self.parents: | |
522 | return list(self._get_file_nodes()) |
|
522 | return list(self._get_file_nodes()) | |
523 | return AddedFileNodesGenerator([n for n in |
|
523 | return AddedFileNodesGenerator([n for n in | |
524 | self._get_paths_for_status('added')], self) |
|
524 | self._get_paths_for_status('added')], self) | |
525 |
|
525 | |||
526 | @LazyProperty |
|
526 | @LazyProperty | |
527 | def changed(self): |
|
527 | def changed(self): | |
528 | """ |
|
528 | """ | |
529 | Returns list of modified ``FileNode`` objects. |
|
529 | Returns list of modified ``FileNode`` objects. | |
530 | """ |
|
530 | """ | |
531 | if not self.parents: |
|
531 | if not self.parents: | |
532 | return [] |
|
532 | return [] | |
533 | return ChangedFileNodesGenerator([n for n in |
|
533 | return ChangedFileNodesGenerator([n for n in | |
534 | self._get_paths_for_status('modified')], self) |
|
534 | self._get_paths_for_status('modified')], self) | |
535 |
|
535 | |||
536 | @LazyProperty |
|
536 | @LazyProperty | |
537 | def removed(self): |
|
537 | def removed(self): | |
538 | """ |
|
538 | """ | |
539 | Returns list of removed ``FileNode`` objects. |
|
539 | Returns list of removed ``FileNode`` objects. | |
540 | """ |
|
540 | """ | |
541 | if not self.parents: |
|
541 | if not self.parents: | |
542 | return [] |
|
542 | return [] | |
543 | return RemovedFileNodesGenerator([n for n in |
|
543 | return RemovedFileNodesGenerator([n for n in | |
544 | self._get_paths_for_status('deleted')], self) |
|
544 | self._get_paths_for_status('deleted')], self) |
@@ -1,373 +1,373 | |||||
1 | ## -*- coding: utf-8 -*- |
|
1 | ## -*- coding: utf-8 -*- | |
2 | <%inherit file="/base/base.html"/> |
|
2 | <%inherit file="/base/base.html"/> | |
3 |
|
3 | |||
4 | <%def name="title()"> |
|
4 | <%def name="title()"> | |
5 | ${_('Edit repository')} ${c.repo_info.repo_name} - ${c.rhodecode_name} |
|
5 | ${_('Edit repository')} ${c.repo_info.repo_name} - ${c.rhodecode_name} | |
6 | </%def> |
|
6 | </%def> | |
7 |
|
7 | |||
8 | <%def name="breadcrumbs_links()"> |
|
8 | <%def name="breadcrumbs_links()"> | |
9 | ${h.link_to(_(u'Home'),h.url('/'))} |
|
9 | ${h.link_to(_(u'Home'),h.url('/'))} | |
10 | » |
|
10 | » | |
11 | ${h.repo_link(c.rhodecode_db_repo.groups_and_repo)} |
|
11 | ${h.repo_link(c.rhodecode_db_repo.groups_and_repo)} | |
12 | » |
|
12 | » | |
13 | ${_('edit')} |
|
13 | ${_('edit')} | |
14 | </%def> |
|
14 | </%def> | |
15 |
|
15 | |||
16 | <%def name="page_nav()"> |
|
16 | <%def name="page_nav()"> | |
17 | ${self.menu('options')} |
|
17 | ${self.menu('options')} | |
18 | </%def> |
|
18 | </%def> | |
19 |
|
19 | |||
20 | <%def name="main()"> |
|
20 | <%def name="main()"> | |
21 | <div class="box box-left"> |
|
21 | <div class="box box-left"> | |
22 | <!-- box / title --> |
|
22 | <!-- box / title --> | |
23 | <div class="title"> |
|
23 | <div class="title"> | |
24 | ${self.breadcrumbs()} |
|
24 | ${self.breadcrumbs()} | |
25 | </div> |
|
25 | </div> | |
26 | ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='put')} |
|
26 | ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='put')} | |
27 | <div class="form"> |
|
27 | <div class="form"> | |
28 | <!-- fields --> |
|
28 | <!-- fields --> | |
29 | <div class="fields"> |
|
29 | <div class="fields"> | |
30 | <div class="field"> |
|
30 | <div class="field"> | |
31 | <div class="label"> |
|
31 | <div class="label"> | |
32 | <label for="repo_name">${_('Name')}:</label> |
|
32 | <label for="repo_name">${_('Name')}:</label> | |
33 | </div> |
|
33 | </div> | |
34 | <div class="input"> |
|
34 | <div class="input"> | |
35 | ${h.text('repo_name',class_="medium")} |
|
35 | ${h.text('repo_name',class_="medium")} | |
36 | </div> |
|
36 | </div> | |
37 | </div> |
|
37 | </div> | |
38 | <div class="field"> |
|
38 | <div class="field"> | |
39 | <div class="label"> |
|
39 | <div class="label"> | |
40 | <label for="clone_uri">${_('Clone uri')}:</label> |
|
40 | <label for="clone_uri">${_('Clone uri')}:</label> | |
41 | </div> |
|
41 | </div> | |
42 | <div class="input"> |
|
42 | <div class="input"> | |
43 | ${h.text('clone_uri',class_="medium")} |
|
43 | ${h.text('clone_uri',class_="medium")} | |
44 | <span class="help-block">${_('Optional http[s] url from which repository should be cloned.')}</span> |
|
44 | <span class="help-block">${_('Optional http[s] url from which repository should be cloned.')}</span> | |
45 | </div> |
|
45 | </div> | |
46 | </div> |
|
46 | </div> | |
47 | <div class="field"> |
|
47 | <div class="field"> | |
48 | <div class="label"> |
|
48 | <div class="label"> | |
49 | <label for="repo_group">${_('Repository group')}:</label> |
|
49 | <label for="repo_group">${_('Repository group')}:</label> | |
50 | </div> |
|
50 | </div> | |
51 | <div class="input"> |
|
51 | <div class="input"> | |
52 | ${h.select('repo_group','',c.repo_groups,class_="medium")} |
|
52 | ${h.select('repo_group','',c.repo_groups,class_="medium")} | |
53 | <span class="help-block">${_('Optional select a group to put this repository into.')}</span> |
|
53 | <span class="help-block">${_('Optional select a group to put this repository into.')}</span> | |
54 | </div> |
|
54 | </div> | |
55 | </div> |
|
55 | </div> | |
56 | <div class="field"> |
|
56 | <div class="field"> | |
57 | <div class="label"> |
|
57 | <div class="label"> | |
58 | <label for="repo_type">${_('Type')}:</label> |
|
58 | <label for="repo_type">${_('Type')}:</label> | |
59 | </div> |
|
59 | </div> | |
60 | <div class="input"> |
|
60 | <div class="input"> | |
61 | ${h.select('repo_type','hg',c.backends,class_="medium")} |
|
61 | ${h.select('repo_type','hg',c.backends,class_="medium")} | |
62 | </div> |
|
62 | </div> | |
63 | </div> |
|
63 | </div> | |
64 | <div class="field"> |
|
64 | <div class="field"> | |
65 | <div class="label"> |
|
65 | <div class="label"> | |
66 | <label for="repo_landing_rev">${_('Landing revision')}:</label> |
|
66 | <label for="repo_landing_rev">${_('Landing revision')}:</label> | |
67 | </div> |
|
67 | </div> | |
68 | <div class="input"> |
|
68 | <div class="input"> | |
69 | ${h.select('repo_landing_rev','',c.landing_revs,class_="medium")} |
|
69 | ${h.select('repo_landing_rev','',c.landing_revs,class_="medium")} | |
70 | <span class="help-block">${_('Default revision for files page, downloads, whoosh and readme')}</span> |
|
70 | <span class="help-block">${_('Default revision for files page, downloads, whoosh and readme')}</span> | |
71 | </div> |
|
71 | </div> | |
72 | </div> |
|
72 | </div> | |
73 | <div class="field"> |
|
73 | <div class="field"> | |
74 | <div class="label label-textarea"> |
|
74 | <div class="label label-textarea"> | |
75 | <label for="repo_description">${_('Description')}:</label> |
|
75 | <label for="repo_description">${_('Description')}:</label> | |
76 | </div> |
|
76 | </div> | |
77 | <div class="textarea text-area editor"> |
|
77 | <div class="textarea text-area editor"> | |
78 | ${h.textarea('repo_description')} |
|
78 | ${h.textarea('repo_description')} | |
79 | <span class="help-block">${_('Keep it short and to the point. Use a README file for longer descriptions.')}</span> |
|
79 | <span class="help-block">${_('Keep it short and to the point. Use a README file for longer descriptions.')}</span> | |
80 | </div> |
|
80 | </div> | |
81 | </div> |
|
81 | </div> | |
82 |
|
82 | |||
83 | <div class="field"> |
|
83 | <div class="field"> | |
84 | <div class="label label-checkbox"> |
|
84 | <div class="label label-checkbox"> | |
85 | <label for="repo_private">${_('Private repository')}:</label> |
|
85 | <label for="repo_private">${_('Private repository')}:</label> | |
86 | </div> |
|
86 | </div> | |
87 | <div class="checkboxes"> |
|
87 | <div class="checkboxes"> | |
88 | ${h.checkbox('repo_private',value="True")} |
|
88 | ${h.checkbox('repo_private',value="True")} | |
89 | <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span> |
|
89 | <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span> | |
90 | </div> |
|
90 | </div> | |
91 | </div> |
|
91 | </div> | |
92 | <div class="field"> |
|
92 | <div class="field"> | |
93 | <div class="label label-checkbox"> |
|
93 | <div class="label label-checkbox"> | |
94 | <label for="repo_enable_statistics">${_('Enable statistics')}:</label> |
|
94 | <label for="repo_enable_statistics">${_('Enable statistics')}:</label> | |
95 | </div> |
|
95 | </div> | |
96 | <div class="checkboxes"> |
|
96 | <div class="checkboxes"> | |
97 | ${h.checkbox('repo_enable_statistics',value="True")} |
|
97 | ${h.checkbox('repo_enable_statistics',value="True")} | |
98 | <span class="help-block">${_('Enable statistics window on summary page.')}</span> |
|
98 | <span class="help-block">${_('Enable statistics window on summary page.')}</span> | |
99 | </div> |
|
99 | </div> | |
100 | </div> |
|
100 | </div> | |
101 | <div class="field"> |
|
101 | <div class="field"> | |
102 | <div class="label label-checkbox"> |
|
102 | <div class="label label-checkbox"> | |
103 | <label for="repo_enable_downloads">${_('Enable downloads')}:</label> |
|
103 | <label for="repo_enable_downloads">${_('Enable downloads')}:</label> | |
104 | </div> |
|
104 | </div> | |
105 | <div class="checkboxes"> |
|
105 | <div class="checkboxes"> | |
106 | ${h.checkbox('repo_enable_downloads',value="True")} |
|
106 | ${h.checkbox('repo_enable_downloads',value="True")} | |
107 | <span class="help-block">${_('Enable download menu on summary page.')}</span> |
|
107 | <span class="help-block">${_('Enable download menu on summary page.')}</span> | |
108 | </div> |
|
108 | </div> | |
109 | </div> |
|
109 | </div> | |
110 | <div class="field"> |
|
110 | <div class="field"> | |
111 | <div class="label label-checkbox"> |
|
111 | <div class="label label-checkbox"> | |
112 | <label for="repo_enable_locking">${_('Enable locking')}:</label> |
|
112 | <label for="repo_enable_locking">${_('Enable locking')}:</label> | |
113 | </div> |
|
113 | </div> | |
114 | <div class="checkboxes"> |
|
114 | <div class="checkboxes"> | |
115 | ${h.checkbox('repo_enable_locking',value="True")} |
|
115 | ${h.checkbox('repo_enable_locking',value="True")} | |
116 | <span class="help-block">${_('Enable lock-by-pulling on repository.')}</span> |
|
116 | <span class="help-block">${_('Enable lock-by-pulling on repository.')}</span> | |
117 | </div> |
|
117 | </div> | |
118 | </div> |
|
118 | </div> | |
119 | <div class="field"> |
|
119 | <div class="field"> | |
120 | <div class="label"> |
|
120 | <div class="label"> | |
121 | <label for="user">${_('Owner')}:</label> |
|
121 | <label for="user">${_('Owner')}:</label> | |
122 | </div> |
|
122 | </div> | |
123 | <div class="input input-medium ac"> |
|
123 | <div class="input input-medium ac"> | |
124 | <div class="perm_ac"> |
|
124 | <div class="perm_ac"> | |
125 | ${h.text('user',class_='yui-ac-input')} |
|
125 | ${h.text('user',class_='yui-ac-input')} | |
126 | <span class="help-block">${_('Change owner of this repository.')}</span> |
|
126 | <span class="help-block">${_('Change owner of this repository.')}</span> | |
127 | <div id="owner_container"></div> |
|
127 | <div id="owner_container"></div> | |
128 | </div> |
|
128 | </div> | |
129 | </div> |
|
129 | </div> | |
130 | </div> |
|
130 | </div> | |
131 | %if c.visual.repository_fields: |
|
131 | %if c.visual.repository_fields: | |
132 | ## EXTRA FIELDS |
|
132 | ## EXTRA FIELDS | |
133 | %for field in c.repo_fields: |
|
133 | %for field in c.repo_fields: | |
134 | <div class="field"> |
|
134 | <div class="field"> | |
135 | <div class="label"> |
|
135 | <div class="label"> | |
136 | <label for="${field.field_key_prefixed}">${field.field_label} (${field.field_key}):</label> |
|
136 | <label for="${field.field_key_prefixed}">${field.field_label} (${field.field_key}):</label> | |
137 | </div> |
|
137 | </div> | |
138 | <div class="input input-medium"> |
|
138 | <div class="input input-medium"> | |
139 | ${h.text(field.field_key_prefixed, field.field_value, class_='medium')} |
|
139 | ${h.text(field.field_key_prefixed, field.field_value, class_='medium')} | |
140 | %if field.field_desc: |
|
140 | %if field.field_desc: | |
141 | <span class="help-block">${field.field_desc}</span> |
|
141 | <span class="help-block">${field.field_desc}</span> | |
142 | %endif |
|
142 | %endif | |
143 | </div> |
|
143 | </div> | |
144 | </div> |
|
144 | </div> | |
145 | %endfor |
|
145 | %endfor | |
146 | %endif |
|
146 | %endif | |
147 | <div class="field"> |
|
147 | <div class="field"> | |
148 | <div class="label"> |
|
148 | <div class="label"> | |
149 | <label for="input">${_('Permissions')}:</label> |
|
149 | <label for="input">${_('Permissions')}:</label> | |
150 | </div> |
|
150 | </div> | |
151 | <div class="input"> |
|
151 | <div class="input"> | |
152 | <%include file="repo_edit_perms.html"/> |
|
152 | <%include file="repo_edit_perms.html"/> | |
153 | </div> |
|
153 | </div> | |
154 |
|
154 | |||
155 | <div class="buttons"> |
|
155 | <div class="buttons"> | |
156 | ${h.submit('save',_('Save'),class_="ui-btn large")} |
|
156 | ${h.submit('save',_('Save'),class_="ui-btn large")} | |
157 | ${h.reset('reset',_('Reset'),class_="ui-btn large")} |
|
157 | ${h.reset('reset',_('Reset'),class_="ui-btn large")} | |
158 | </div> |
|
158 | </div> | |
159 | </div> |
|
159 | </div> | |
160 | </div> |
|
160 | </div> | |
161 | </div> |
|
161 | </div> | |
162 | ${h.end_form()} |
|
162 | ${h.end_form()} | |
163 | </div> |
|
163 | </div> | |
164 |
|
164 | |||
165 | <div class="box box-right"> |
|
165 | <div class="box box-right"> | |
166 | <div class="title"> |
|
166 | <div class="title"> | |
167 | <h5>${_('Administration')}</h5> |
|
167 | <h5>${_('Administration')}</h5> | |
168 | </div> |
|
168 | </div> | |
169 |
|
169 | |||
170 | <h3>${_('Statistics')}</h3> |
|
170 | <h3>${_('Statistics')}</h3> | |
171 | ${h.form(url('repo_stats', repo_name=c.repo_info.repo_name),method='delete')} |
|
171 | ${h.form(url('repo_stats', repo_name=c.repo_info.repo_name),method='delete')} | |
172 | <div class="form"> |
|
172 | <div class="form"> | |
173 | <div class="fields"> |
|
173 | <div class="fields"> | |
174 | ${h.submit('reset_stats_%s' % c.repo_info.repo_name,_('Reset current statistics'),class_="ui-btn",onclick="return confirm('"+_('Confirm to remove current statistics')+"');")} |
|
174 | ${h.submit('reset_stats_%s' % c.repo_info.repo_name,_('Reset current statistics'),class_="ui-btn",onclick="return confirm('"+_('Confirm to remove current statistics')+"');")} | |
175 | <div class="field" style="border:none;color:#888"> |
|
175 | <div class="field" style="border:none;color:#888"> | |
176 | <ul> |
|
176 | <ul> | |
177 | <li>${_('Fetched to rev')}: ${c.stats_revision}/${c.repo_last_rev}</li> |
|
177 | <li>${_('Fetched to rev')}: ${c.stats_revision}/${c.repo_last_rev}</li> | |
178 | <li>${_('Stats gathered')}: ${c.stats_percentage}%</li> |
|
178 | <li>${_('Stats gathered')}: ${c.stats_percentage}%</li> | |
179 | </ul> |
|
179 | </ul> | |
180 | </div> |
|
180 | </div> | |
181 | </div> |
|
181 | </div> | |
182 | </div> |
|
182 | </div> | |
183 | ${h.end_form()} |
|
183 | ${h.end_form()} | |
184 |
|
184 | |||
185 | %if c.repo_info.clone_uri: |
|
185 | %if c.repo_info.clone_uri: | |
186 | <h3>${_('Remote')}</h3> |
|
186 | <h3>${_('Remote')}</h3> | |
187 | ${h.form(url('repo_pull', repo_name=c.repo_info.repo_name),method='put')} |
|
187 | ${h.form(url('repo_pull', repo_name=c.repo_info.repo_name),method='put')} | |
188 | <div class="form"> |
|
188 | <div class="form"> | |
189 | <div class="fields"> |
|
189 | <div class="fields"> | |
190 | ${h.submit('remote_pull_%s' % c.repo_info.repo_name,_('Pull changes from remote location'),class_="ui-btn",onclick="return confirm('"+_('Confirm to pull changes from remote side')+"');")} |
|
190 | ${h.submit('remote_pull_%s' % c.repo_info.repo_name,_('Pull changes from remote location'),class_="ui-btn",onclick="return confirm('"+_('Confirm to pull changes from remote side')+"');")} | |
191 | <div class="field" style="border:none"> |
|
191 | <div class="field" style="border:none"> | |
192 | <ul> |
|
192 | <ul> | |
193 | <li><a href="${c.repo_info.clone_uri}">${c.repo_info.clone_uri}</a></li> |
|
193 | <li><a href="${c.repo_info.clone_uri}">${c.repo_info.clone_uri}</a></li> | |
194 | </ul> |
|
194 | </ul> | |
195 | </div> |
|
195 | </div> | |
196 | </div> |
|
196 | </div> | |
197 | </div> |
|
197 | </div> | |
198 | ${h.end_form()} |
|
198 | ${h.end_form()} | |
199 | %endif |
|
199 | %endif | |
200 |
|
200 | |||
201 | <h3>${_('Cache')}</h3> |
|
201 | <h3>${_('Cache')}</h3> | |
202 | ${h.form(url('repo_cache', repo_name=c.repo_info.repo_name),method='delete')} |
|
202 | ${h.form(url('repo_cache', repo_name=c.repo_info.repo_name),method='delete')} | |
203 | <div class="form"> |
|
203 | <div class="form"> | |
204 | <div class="fields"> |
|
204 | <div class="fields"> | |
205 | ${h.submit('reset_cache_%s' % c.repo_info.repo_name,_('Invalidate repository cache'),class_="ui-btn",onclick="return confirm('"+_('Confirm to invalidate repository cache')+"');")} |
|
205 | ${h.submit('reset_cache_%s' % c.repo_info.repo_name,_('Invalidate repository cache'),class_="ui-btn",onclick="return confirm('"+_('Confirm to invalidate repository cache')+"');")} | |
206 | <div class="field" style="border:none;color:#888"> |
|
206 | <div class="field" style="border:none;color:#888"> | |
207 | <ul> |
|
207 | <ul> | |
208 | <li>${_('Manually invalidate cache for this repository. On first access repository will be cached again')} |
|
208 | <li>${_('Manually invalidate cache for this repository. On first access repository will be cached again')} | |
209 | </li> |
|
209 | </li> | |
210 | </ul> |
|
210 | </ul> | |
211 | </div> |
|
211 | </div> | |
212 | <div class="field" style="border:none;"> |
|
212 | <div class="field" style="border:none;"> | |
213 | ${_('List of cached values')} |
|
213 | ${_('List of cached values')} | |
214 | <table> |
|
214 | <table> | |
215 | <tr> |
|
215 | <tr> | |
216 | <th>${_('Prefix')}</th> |
|
216 | <th>${_('Prefix')}</th> | |
217 | <th>${_('Key')}</th> |
|
217 | <th>${_('Key')}</th> | |
218 | <th>${_('Active')}</th> |
|
218 | <th>${_('Active')}</th> | |
219 | </tr> |
|
219 | </tr> | |
220 | %for cache in c.repo_info.cache_keys: |
|
220 | %for cache in c.repo_info.cache_keys: | |
221 | <tr> |
|
221 | <tr> | |
222 | <td>${cache.prefix or '-'}</td> |
|
222 | <td>${cache.prefix or '-'}</td> | |
223 | <td>${cache.cache_key}</td> |
|
223 | <td>${cache.cache_key}</td> | |
224 | <td>${h.bool2icon(cache.cache_active)}</td> |
|
224 | <td>${h.bool2icon(cache.cache_active)}</td> | |
225 | </tr> |
|
225 | </tr> | |
226 | %endfor |
|
226 | %endfor | |
227 | </table> |
|
227 | </table> | |
228 | </div> |
|
228 | </div> | |
229 | </div> |
|
229 | </div> | |
230 | </div> |
|
230 | </div> | |
231 | ${h.end_form()} |
|
231 | ${h.end_form()} | |
232 |
|
232 | |||
233 | <h3>${_('Public journal')}</h3> |
|
233 | <h3>${_('Public journal')}</h3> | |
234 | ${h.form(url('repo_public_journal', repo_name=c.repo_info.repo_name),method='put')} |
|
234 | ${h.form(url('repo_public_journal', repo_name=c.repo_info.repo_name),method='put')} | |
235 | <div class="form"> |
|
235 | <div class="form"> | |
236 | ${h.hidden('auth_token',str(h.get_token()))} |
|
236 | ${h.hidden('auth_token',str(h.get_token()))} | |
237 | <div class="field"> |
|
237 | <div class="field"> | |
238 | %if c.in_public_journal: |
|
238 | %if c.in_public_journal: | |
239 | ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Remove from public journal'),class_="ui-btn")} |
|
239 | ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Remove from public journal'),class_="ui-btn")} | |
240 | %else: |
|
240 | %else: | |
241 | ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Add to public journal'),class_="ui-btn")} |
|
241 | ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Add to public journal'),class_="ui-btn")} | |
242 | %endif |
|
242 | %endif | |
243 | </div> |
|
243 | </div> | |
244 | <div class="field" style="border:none;color:#888"> |
|
244 | <div class="field" style="border:none;color:#888"> | |
245 | <ul> |
|
245 | <ul> | |
246 | <li>${_('All actions made on this repository will be accessible to everyone in public journal')} |
|
246 | <li>${_('All actions made on this repository will be accessible to everyone in public journal')} | |
247 | </li> |
|
247 | </li> | |
248 | </ul> |
|
248 | </ul> | |
249 | </div> |
|
249 | </div> | |
250 | </div> |
|
250 | </div> | |
251 | ${h.end_form()} |
|
251 | ${h.end_form()} | |
252 |
|
252 | |||
253 | <h3>${_('Locking')}</h3> |
|
253 | <h3>${_('Locking')}</h3> | |
254 | ${h.form(url('repo_locking', repo_name=c.repo_info.repo_name),method='put')} |
|
254 | ${h.form(url('repo_locking', repo_name=c.repo_info.repo_name),method='put')} | |
255 | <div class="form"> |
|
255 | <div class="form"> | |
256 | <div class="fields"> |
|
256 | <div class="fields"> | |
257 | %if c.repo_info.locked[0]: |
|
257 | %if c.repo_info.locked[0]: | |
258 | ${h.submit('set_unlock' ,_('Unlock locked repo'),class_="ui-btn",onclick="return confirm('"+_('Confirm to unlock repository')+"');")} |
|
258 | ${h.submit('set_unlock' ,_('Unlock locked repo'),class_="ui-btn",onclick="return confirm('"+_('Confirm to unlock repository')+"');")} | |
259 | ${'Locked by %s on %s' % (h.person_by_id(c.repo_info.locked[0]),h.fmt_date(h.time_to_datetime(c.repo_info.locked[1])))} |
|
259 | ${'Locked by %s on %s' % (h.person_by_id(c.repo_info.locked[0]),h.fmt_date(h.time_to_datetime(c.repo_info.locked[1])))} | |
260 | %else: |
|
260 | %else: | |
261 | ${h.submit('set_lock',_('lock repo'),class_="ui-btn",onclick="return confirm('"+_('Confirm to lock repository')+"');")} |
|
261 | ${h.submit('set_lock',_('lock repo'),class_="ui-btn",onclick="return confirm('"+_('Confirm to lock repository')+"');")} | |
262 | ${_('Repository is not locked')} |
|
262 | ${_('Repository is not locked')} | |
263 | %endif |
|
263 | %endif | |
264 | </div> |
|
264 | </div> | |
265 | <div class="field" style="border:none;color:#888"> |
|
265 | <div class="field" style="border:none;color:#888"> | |
266 | <ul> |
|
266 | <ul> | |
267 | <li>${_('Force locking on repository. Works only when anonymous access is disabled')} |
|
267 | <li>${_('Force locking on repository. Works only when anonymous access is disabled')} | |
268 | </li> |
|
268 | </li> | |
269 | </ul> |
|
269 | </ul> | |
270 | </div> |
|
270 | </div> | |
271 | </div> |
|
271 | </div> | |
272 | ${h.end_form()} |
|
272 | ${h.end_form()} | |
273 |
|
273 | |||
274 | <h3>${_('Set as fork of')}</h3> |
|
274 | <h3>${_('Set as fork of')}</h3> | |
275 | ${h.form(url('repo_as_fork', repo_name=c.repo_info.repo_name),method='put')} |
|
275 | ${h.form(url('repo_as_fork', repo_name=c.repo_info.repo_name),method='put')} | |
276 | <div class="form"> |
|
276 | <div class="form"> | |
277 | <div class="fields"> |
|
277 | <div class="fields"> | |
278 | ${h.select('id_fork_of','',c.repos_list,class_="medium")} |
|
278 | ${h.select('id_fork_of','',c.repos_list,class_="medium")} | |
279 | ${h.submit('set_as_fork_%s' % c.repo_info.repo_name,_('set'),class_="ui-btn",)} |
|
279 | ${h.submit('set_as_fork_%s' % c.repo_info.repo_name,_('set'),class_="ui-btn",)} | |
280 | </div> |
|
280 | </div> | |
281 | <div class="field" style="border:none;color:#888"> |
|
281 | <div class="field" style="border:none;color:#888"> | |
282 | <ul> |
|
282 | <ul> | |
283 | <li>${_('''Manually set this repository as a fork of another from the list''')}</li> |
|
283 | <li>${_('''Manually set this repository as a fork of another from the list''')}</li> | |
284 | </ul> |
|
284 | </ul> | |
285 | </div> |
|
285 | </div> | |
286 | </div> |
|
286 | </div> | |
287 | ${h.end_form()} |
|
287 | ${h.end_form()} | |
288 |
|
288 | |||
289 | <h3>${_('Delete')}</h3> |
|
289 | <h3>${_('Delete')}</h3> | |
290 | ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='delete')} |
|
290 | ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='delete')} | |
291 | <div class="form"> |
|
291 | <div class="form"> | |
292 | <div class="fields"> |
|
292 | <div class="fields"> | |
293 | ${h.submit('remove_%s' % c.repo_info.repo_name,_('Remove this repository'),class_="ui-btn red",onclick="return confirm('"+_('Confirm to delete this repository')+"');")} |
|
293 | ${h.submit('remove_%s' % c.repo_info.repo_name,_('Remove this repository'),class_="ui-btn red",onclick="return confirm('"+_('Confirm to delete this repository')+"');")} | |
294 | %if c.repo_info.forks.count(): |
|
294 | %if c.repo_info.forks.count(): | |
295 |
- ${ungettext('this repository has %s fork', 'this repository has %s forks', c.repo_info.forks.count()) % c.repo_info.forks.count()} |
|
295 | - ${ungettext('this repository has %s fork', 'this repository has %s forks', c.repo_info.forks.count()) % c.repo_info.forks.count()} | |
296 | <input type="radio" name="forks" value="detach_forks" checked="checked"/> <label for="forks">${_('Detach forks')}</label> |
|
296 | <input type="radio" name="forks" value="detach_forks" checked="checked"/> <label for="forks">${_('Detach forks')}</label> | |
297 | <input type="radio" name="forks" value="delete_forks" /> <label for="forks">${_('Delete forks')}</label> |
|
297 | <input type="radio" name="forks" value="delete_forks" /> <label for="forks">${_('Delete forks')}</label> | |
298 |
%endif |
|
298 | %endif | |
299 | </div> |
|
299 | </div> | |
300 | <div class="field" style="border:none;color:#888"> |
|
300 | <div class="field" style="border:none;color:#888"> | |
301 | <ul> |
|
301 | <ul> | |
302 | <li>${_('This repository will be renamed in a special way in order to be unaccesible for RhodeCode and VCS systems. If you need to fully delete it from file system please do it manually')}</li> |
|
302 | <li>${_('This repository will be renamed in a special way in order to be unaccesible for RhodeCode and VCS systems. If you need to fully delete it from file system please do it manually')}</li> | |
303 | </ul> |
|
303 | </ul> | |
304 | </div> |
|
304 | </div> | |
305 | </div> |
|
305 | </div> | |
306 | ${h.end_form()} |
|
306 | ${h.end_form()} | |
307 | </div> |
|
307 | </div> | |
308 |
|
308 | |||
309 | ##TODO: this should be controlled by the VISUAL setting |
|
309 | ##TODO: this should be controlled by the VISUAL setting | |
310 | %if c.visual.repository_fields: |
|
310 | %if c.visual.repository_fields: | |
311 | <div class="box box-left" style="clear:left"> |
|
311 | <div class="box box-left" style="clear:left"> | |
312 | <!-- box / title --> |
|
312 | <!-- box / title --> | |
313 | <div class="title"> |
|
313 | <div class="title"> | |
314 | <h5>${_('Extra fields')}</h5> |
|
314 | <h5>${_('Extra fields')}</h5> | |
315 | </div> |
|
315 | </div> | |
316 |
|
316 | |||
317 | <div class="emails_wrap"> |
|
317 | <div class="emails_wrap"> | |
318 | <table class="noborder"> |
|
318 | <table class="noborder"> | |
319 | %for field in c.repo_fields: |
|
319 | %for field in c.repo_fields: | |
320 | <tr> |
|
320 | <tr> | |
321 | <td>${field.field_label} (${field.field_key})</td> |
|
321 | <td>${field.field_label} (${field.field_key})</td> | |
322 | <td>${field.field_type}</td> |
|
322 | <td>${field.field_type}</td> | |
323 | <td> |
|
323 | <td> | |
324 | ${h.form(url('delete_repo_fields', repo_name=c.repo_info.repo_name, field_id=field.repo_field_id),method='delete')} |
|
324 | ${h.form(url('delete_repo_fields', repo_name=c.repo_info.repo_name, field_id=field.repo_field_id),method='delete')} | |
325 | ${h.submit('remove_%s' % field.repo_field_id, _('delete'), id="remove_field_%s" % field.repo_field_id, |
|
325 | ${h.submit('remove_%s' % field.repo_field_id, _('delete'), id="remove_field_%s" % field.repo_field_id, | |
326 | class_="delete_icon action_button", onclick="return confirm('"+_('Confirm to delete this field: %s') % field.field_key+"');")} |
|
326 | class_="delete_icon action_button", onclick="return confirm('"+_('Confirm to delete this field: %s') % field.field_key+"');")} | |
327 | ${h.end_form()} |
|
327 | ${h.end_form()} | |
328 | </td> |
|
328 | </td> | |
329 | </tr> |
|
329 | </tr> | |
330 | %endfor |
|
330 | %endfor | |
331 | </table> |
|
331 | </table> | |
332 | </div> |
|
332 | </div> | |
333 |
|
333 | |||
334 | ${h.form(url('create_repo_fields', repo_name=c.repo_info.repo_name),method='put')} |
|
334 | ${h.form(url('create_repo_fields', repo_name=c.repo_info.repo_name),method='put')} | |
335 | <div class="form"> |
|
335 | <div class="form"> | |
336 | <!-- fields --> |
|
336 | <!-- fields --> | |
337 | <div class="fields"> |
|
337 | <div class="fields"> | |
338 | <div class="field"> |
|
338 | <div class="field"> | |
339 | <div class="label"> |
|
339 | <div class="label"> | |
340 | <label for="new_field_key">${_('New field key')}:</label> |
|
340 | <label for="new_field_key">${_('New field key')}:</label> | |
341 | </div> |
|
341 | </div> | |
342 | <div class="input"> |
|
342 | <div class="input"> | |
343 | ${h.text('new_field_key', class_='small')} |
|
343 | ${h.text('new_field_key', class_='small')} | |
344 | </div> |
|
344 | </div> | |
345 | </div> |
|
345 | </div> | |
346 | <div class="field"> |
|
346 | <div class="field"> | |
347 | <div class="label"> |
|
347 | <div class="label"> | |
348 | <label for="new_field_label">${_('New field label')}:</label> |
|
348 | <label for="new_field_label">${_('New field label')}:</label> | |
349 | </div> |
|
349 | </div> | |
350 | <div class="input"> |
|
350 | <div class="input"> | |
351 | ${h.text('new_field_label', class_='small', placeholder=_('Enter short label'))} |
|
351 | ${h.text('new_field_label', class_='small', placeholder=_('Enter short label'))} | |
352 | </div> |
|
352 | </div> | |
353 | </div> |
|
353 | </div> | |
354 |
|
354 | |||
355 | <div class="field"> |
|
355 | <div class="field"> | |
356 | <div class="label"> |
|
356 | <div class="label"> | |
357 | <label for="new_field_desc">${_('New field description')}:</label> |
|
357 | <label for="new_field_desc">${_('New field description')}:</label> | |
358 | </div> |
|
358 | </div> | |
359 | <div class="input"> |
|
359 | <div class="input"> | |
360 | ${h.text('new_field_desc', class_='small', placeholder=_('Enter description of a field'))} |
|
360 | ${h.text('new_field_desc', class_='small', placeholder=_('Enter description of a field'))} | |
361 | </div> |
|
361 | </div> | |
362 | </div> |
|
362 | </div> | |
363 |
|
363 | |||
364 | <div class="buttons"> |
|
364 | <div class="buttons"> | |
365 | ${h.submit('save',_('Add'),class_="ui-btn large")} |
|
365 | ${h.submit('save',_('Add'),class_="ui-btn large")} | |
366 | ${h.reset('reset',_('Reset'),class_="ui-btn large")} |
|
366 | ${h.reset('reset',_('Reset'),class_="ui-btn large")} | |
367 | </div> |
|
367 | </div> | |
368 | </div> |
|
368 | </div> | |
369 | </div> |
|
369 | </div> | |
370 | ${h.end_form()} |
|
370 | ${h.end_form()} | |
371 | </div> |
|
371 | </div> | |
372 | %endif |
|
372 | %endif | |
373 | </%def> |
|
373 | </%def> |
@@ -1,49 +1,49 | |||||
1 |
|
1 | |||
2 | <div class="pullrequests_section_head">${_('Opened by me')}</div> |
|
2 | <div class="pullrequests_section_head">${_('Opened by me')}</div> | |
3 | <ul> |
|
3 | <ul> | |
4 | %if c.my_pull_requests: |
|
4 | %if c.my_pull_requests: | |
5 | %for pull_request in c.my_pull_requests: |
|
5 | %for pull_request in c.my_pull_requests: | |
6 | <li> |
|
6 | <li> | |
7 | <div style="height: 12px"> |
|
7 | <div style="height: 12px"> | |
8 | <div style="float:left"> |
|
8 | <div style="float:left"> | |
9 | %if pull_request.is_closed(): |
|
9 | %if pull_request.is_closed(): | |
10 | <img src="${h.url('/images/icons/lock_go.png')}" title="${_('Closed')}"/> |
|
10 | <img src="${h.url('/images/icons/lock_go.png')}" title="${_('Closed')}"/> | |
11 | %endif |
|
11 | %endif | |
12 |
<img src="${h.url('/images/icons/flag_status_%s.png' % str(pull_request.last_review_status))}" /> |
|
12 | <img src="${h.url('/images/icons/flag_status_%s.png' % str(pull_request.last_review_status))}" /> | |
13 | <a href="${h.url('pullrequest_show',repo_name=pull_request.other_repo.repo_name,pull_request_id=pull_request.pull_request_id)}"> |
|
13 | <a href="${h.url('pullrequest_show',repo_name=pull_request.other_repo.repo_name,pull_request_id=pull_request.pull_request_id)}"> | |
14 | ${_('Pull request #%s opened on %s') % (pull_request.pull_request_id, h.fmt_date(pull_request.created_on))} |
|
14 | ${_('Pull request #%s opened on %s') % (pull_request.pull_request_id, h.fmt_date(pull_request.created_on))} | |
15 | </a> |
|
15 | </a> | |
16 | </div> |
|
16 | </div> | |
17 | <div style="float:left;margin-top: -5px"> |
|
17 | <div style="float:left;margin-top: -5px"> | |
18 | ${h.form(url('pullrequest_delete', repo_name=pull_request.other_repo.repo_name, pull_request_id=pull_request.pull_request_id),method='delete')} |
|
18 | ${h.form(url('pullrequest_delete', repo_name=pull_request.other_repo.repo_name, pull_request_id=pull_request.pull_request_id),method='delete')} | |
19 | ${h.submit('remove_%s' % pull_request.pull_request_id,'',class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this pull request')+"');")} |
|
19 | ${h.submit('remove_%s' % pull_request.pull_request_id,'',class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this pull request')+"');")} | |
20 | ${h.end_form()} |
|
20 | ${h.end_form()} | |
21 | </div> |
|
21 | </div> | |
22 | </div> |
|
22 | </div> | |
23 | </li> |
|
23 | </li> | |
24 | %endfor |
|
24 | %endfor | |
25 | %else: |
|
25 | %else: | |
26 | <li><span class="empty_data">${_('Nothing here yet')}</span></li> |
|
26 | <li><span class="empty_data">${_('Nothing here yet')}</span></li> | |
27 | %endif |
|
27 | %endif | |
28 | </ul> |
|
28 | </ul> | |
29 |
|
29 | |||
30 | <div class="pullrequests_section_head" style="clear:both">${_('I participate in')}</div> |
|
30 | <div class="pullrequests_section_head" style="clear:both">${_('I participate in')}</div> | |
31 | <ul> |
|
31 | <ul> | |
32 | %if c.participate_in_pull_requests: |
|
32 | %if c.participate_in_pull_requests: | |
33 | %for pull_request in c.participate_in_pull_requests: |
|
33 | %for pull_request in c.participate_in_pull_requests: | |
34 | <li> |
|
34 | <li> | |
35 | <div style="height: 12px"> |
|
35 | <div style="height: 12px"> | |
36 | %if pull_request.is_closed(): |
|
36 | %if pull_request.is_closed(): | |
37 | <img src="${h.url('/images/icons/lock_go.png')}" title="${_('Closed')}"/> |
|
37 | <img src="${h.url('/images/icons/lock_go.png')}" title="${_('Closed')}"/> | |
38 | %endif |
|
38 | %endif | |
39 |
<img src="${h.url('/images/icons/flag_status_%s.png' % str(pull_request.last_review_status))}" /> |
|
39 | <img src="${h.url('/images/icons/flag_status_%s.png' % str(pull_request.last_review_status))}" /> | |
40 | <a href="${h.url('pullrequest_show',repo_name=pull_request.other_repo.repo_name,pull_request_id=pull_request.pull_request_id)}"> |
|
40 | <a href="${h.url('pullrequest_show',repo_name=pull_request.other_repo.repo_name,pull_request_id=pull_request.pull_request_id)}"> | |
41 | ${_('Pull request #%s opened by %s on %s') % (pull_request.pull_request_id, pull_request.author.full_name, h.fmt_date(pull_request.created_on))} |
|
41 | ${_('Pull request #%s opened by %s on %s') % (pull_request.pull_request_id, pull_request.author.full_name, h.fmt_date(pull_request.created_on))} | |
42 | </a> |
|
42 | </a> | |
43 | </div> |
|
43 | </div> | |
44 | </li> |
|
44 | </li> | |
45 | %endfor |
|
45 | %endfor | |
46 | %else: |
|
46 | %else: | |
47 | <li><span class="empty_data">${_('Nothing here yet')}</span></li> |
|
47 | <li><span class="empty_data">${_('Nothing here yet')}</span></li> | |
48 | %endif |
|
48 | %endif | |
49 | </ul> |
|
49 | </ul> |
@@ -1,390 +1,390 | |||||
1 | ## -*- coding: utf-8 -*- |
|
1 | ## -*- coding: utf-8 -*- | |
2 | <%inherit file="root.html"/> |
|
2 | <%inherit file="root.html"/> | |
3 |
|
3 | |||
4 | <!-- HEADER --> |
|
4 | <!-- HEADER --> | |
5 | <div id="header-dd"></div> |
|
5 | <div id="header-dd"></div> | |
6 | <div id="header"> |
|
6 | <div id="header"> | |
7 | <div id="header-inner" class="title"> |
|
7 | <div id="header-inner" class="title"> | |
8 | <div id="logo"> |
|
8 | <div id="logo"> | |
9 | <h1><a href="${h.url('home')}">${c.rhodecode_name}</a></h1> |
|
9 | <h1><a href="${h.url('home')}">${c.rhodecode_name}</a></h1> | |
10 | </div> |
|
10 | </div> | |
11 | <!-- MENU --> |
|
11 | <!-- MENU --> | |
12 | ${self.page_nav()} |
|
12 | ${self.page_nav()} | |
13 | <!-- END MENU --> |
|
13 | <!-- END MENU --> | |
14 | ${self.body()} |
|
14 | ${self.body()} | |
15 | </div> |
|
15 | </div> | |
16 | </div> |
|
16 | </div> | |
17 | <!-- END HEADER --> |
|
17 | <!-- END HEADER --> | |
18 |
|
18 | |||
19 | <!-- CONTENT --> |
|
19 | <!-- CONTENT --> | |
20 | <div id="content"> |
|
20 | <div id="content"> | |
21 | <div class="flash_msg"> |
|
21 | <div class="flash_msg"> | |
22 | <% messages = h.flash.pop_messages() %> |
|
22 | <% messages = h.flash.pop_messages() %> | |
23 | % if messages: |
|
23 | % if messages: | |
24 | <ul id="flash-messages"> |
|
24 | <ul id="flash-messages"> | |
25 | % for message in messages: |
|
25 | % for message in messages: | |
26 | <li class="${message.category}_msg">${message}</li> |
|
26 | <li class="${message.category}_msg">${message}</li> | |
27 | % endfor |
|
27 | % endfor | |
28 | </ul> |
|
28 | </ul> | |
29 | % endif |
|
29 | % endif | |
30 | </div> |
|
30 | </div> | |
31 | <div id="main"> |
|
31 | <div id="main"> | |
32 | ${next.main()} |
|
32 | ${next.main()} | |
33 | </div> |
|
33 | </div> | |
34 | </div> |
|
34 | </div> | |
35 | <!-- END CONTENT --> |
|
35 | <!-- END CONTENT --> | |
36 |
|
36 | |||
37 | <!-- FOOTER --> |
|
37 | <!-- FOOTER --> | |
38 | <div id="footer"> |
|
38 | <div id="footer"> | |
39 | <div id="footer-inner" class="title"> |
|
39 | <div id="footer-inner" class="title"> | |
40 | <div> |
|
40 | <div> | |
41 | <p class="footer-link"> |
|
41 | <p class="footer-link"> | |
42 | <a href="${h.url('bugtracker')}">${_('Submit a bug')}</a> |
|
42 | <a href="${h.url('bugtracker')}">${_('Submit a bug')}</a> | |
43 | </p> |
|
43 | </p> | |
44 | <p class="footer-link-right"> |
|
44 | <p class="footer-link-right"> | |
45 | <a href="${h.url('rhodecode_official')}">RhodeCode${'-%s' % c.rhodecode_instanceid if c.rhodecode_instanceid else ''}</a> |
|
45 | <a href="${h.url('rhodecode_official')}">RhodeCode${'-%s' % c.rhodecode_instanceid if c.rhodecode_instanceid else ''}</a> | |
46 | ${c.rhodecode_version} © 2010-${h.datetime.today().year} by Marcin Kuzminski |
|
46 | ${c.rhodecode_version} © 2010-${h.datetime.today().year} by Marcin Kuzminski | |
47 | </p> |
|
47 | </p> | |
48 | </div> |
|
48 | </div> | |
49 | </div> |
|
49 | </div> | |
50 | </div> |
|
50 | </div> | |
51 | <!-- END FOOTER --> |
|
51 | <!-- END FOOTER --> | |
52 |
|
52 | |||
53 | ### MAKO DEFS ### |
|
53 | ### MAKO DEFS ### | |
54 | <%def name="page_nav()"> |
|
54 | <%def name="page_nav()"> | |
55 | ${self.menu()} |
|
55 | ${self.menu()} | |
56 | </%def> |
|
56 | </%def> | |
57 |
|
57 | |||
58 | <%def name="breadcrumbs()"> |
|
58 | <%def name="breadcrumbs()"> | |
59 | <div class="breadcrumbs"> |
|
59 | <div class="breadcrumbs"> | |
60 | ${self.breadcrumbs_links()} |
|
60 | ${self.breadcrumbs_links()} | |
61 | </div> |
|
61 | </div> | |
62 | </%def> |
|
62 | </%def> | |
63 |
|
63 | |||
64 | <%def name="usermenu()"> |
|
64 | <%def name="usermenu()"> | |
65 | ## USER MENU |
|
65 | ## USER MENU | |
66 | <li> |
|
66 | <li> | |
67 | <a class="menu_link" id="quick_login_link"> |
|
67 | <a class="menu_link" id="quick_login_link"> | |
68 | <span class="icon" style="padding:5px 5px 0px 5px"> |
|
68 | <span class="icon" style="padding:5px 5px 0px 5px"> | |
69 | <img src="${h.gravatar_url(c.rhodecode_user.email,20)}" alt="avatar"> |
|
69 | <img src="${h.gravatar_url(c.rhodecode_user.email,20)}" alt="avatar"> | |
70 | </span> |
|
70 | </span> | |
71 | %if c.rhodecode_user.username != 'default': |
|
71 | %if c.rhodecode_user.username != 'default': | |
72 | <span class="menu_link_user">${c.rhodecode_user.username}</span> |
|
72 | <span class="menu_link_user">${c.rhodecode_user.username}</span> | |
73 | %if c.unread_notifications != 0: |
|
73 | %if c.unread_notifications != 0: | |
74 | <span class="menu_link_notifications">${c.unread_notifications}</span> |
|
74 | <span class="menu_link_notifications">${c.unread_notifications}</span> | |
75 | %endif |
|
75 | %endif | |
76 | %else: |
|
76 | %else: | |
77 | <span>${_('Not logged in')}</span> |
|
77 | <span>${_('Not logged in')}</span> | |
78 | %endif |
|
78 | %endif | |
79 | </a> |
|
79 | </a> | |
80 |
|
80 | |||
81 | <div class="user-menu"> |
|
81 | <div class="user-menu"> | |
82 | <div id="quick_login"> |
|
82 | <div id="quick_login"> | |
83 | %if c.rhodecode_user.username == 'default': |
|
83 | %if c.rhodecode_user.username == 'default': | |
84 | <h4>${_('Login to your account')}</h4> |
|
84 | <h4>${_('Login to your account')}</h4> | |
85 | ${h.form(h.url('login_home',came_from=h.url.current()))} |
|
85 | ${h.form(h.url('login_home',came_from=h.url.current()))} | |
86 | <div class="form"> |
|
86 | <div class="form"> | |
87 | <div class="fields"> |
|
87 | <div class="fields"> | |
88 | <div class="field"> |
|
88 | <div class="field"> | |
89 | <div class="label"> |
|
89 | <div class="label"> | |
90 | <label for="username">${_('Username')}:</label> |
|
90 | <label for="username">${_('Username')}:</label> | |
91 | </div> |
|
91 | </div> | |
92 | <div class="input"> |
|
92 | <div class="input"> | |
93 | ${h.text('username',class_='focus',size=40)} |
|
93 | ${h.text('username',class_='focus',size=40)} | |
94 | </div> |
|
94 | </div> | |
95 |
|
95 | |||
96 | </div> |
|
96 | </div> | |
97 | <div class="field"> |
|
97 | <div class="field"> | |
98 | <div class="label"> |
|
98 | <div class="label"> | |
99 | <label for="password">${_('Password')}:</label> |
|
99 | <label for="password">${_('Password')}:</label> | |
100 | </div> |
|
100 | </div> | |
101 | <div class="input"> |
|
101 | <div class="input"> | |
102 | ${h.password('password',class_='focus',size=40)} |
|
102 | ${h.password('password',class_='focus',size=40)} | |
103 | </div> |
|
103 | </div> | |
104 |
|
104 | |||
105 | </div> |
|
105 | </div> | |
106 | <div class="buttons"> |
|
106 | <div class="buttons"> | |
107 | <div class="password_forgoten">${h.link_to(_('Forgot password ?'),h.url('reset_password'))}</div> |
|
107 | <div class="password_forgoten">${h.link_to(_('Forgot password ?'),h.url('reset_password'))}</div> | |
108 | <div class="register"> |
|
108 | <div class="register"> | |
109 | %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')(): |
|
109 | %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')(): | |
110 | ${h.link_to(_("Don't have an account ?"),h.url('register'))} |
|
110 | ${h.link_to(_("Don't have an account ?"),h.url('register'))} | |
111 | %endif |
|
111 | %endif | |
112 | </div> |
|
112 | </div> | |
113 | <div class="submit"> |
|
113 | <div class="submit"> | |
114 | ${h.submit('sign_in',_('Log In'),class_="ui-btn xsmall")} |
|
114 | ${h.submit('sign_in',_('Log In'),class_="ui-btn xsmall")} | |
115 | </div> |
|
115 | </div> | |
116 | </div> |
|
116 | </div> | |
117 | </div> |
|
117 | </div> | |
118 | </div> |
|
118 | </div> | |
119 | ${h.end_form()} |
|
119 | ${h.end_form()} | |
120 | %else: |
|
120 | %else: | |
121 | <div class="links_left"> |
|
121 | <div class="links_left"> | |
122 | <div class="full_name">${c.rhodecode_user.full_name_or_username}</div> |
|
122 | <div class="full_name">${c.rhodecode_user.full_name_or_username}</div> | |
123 | <div class="email">${c.rhodecode_user.email}</div> |
|
123 | <div class="email">${c.rhodecode_user.email}</div> | |
124 | <div class="big_gravatar"><img alt="gravatar" src="${h.gravatar_url(c.rhodecode_user.email,48)}" /></div> |
|
124 | <div class="big_gravatar"><img alt="gravatar" src="${h.gravatar_url(c.rhodecode_user.email,48)}" /></div> | |
125 | <div class="notifications"><a href="${h.url('notifications')}">${_('Notifications')}</a></div> |
|
125 | <div class="notifications"><a href="${h.url('notifications')}">${_('Notifications')}</a></div> | |
126 | <div class="unread"><a href="${h.url('notifications')}">${_('Unread')}: ${c.unread_notifications}</a></div> |
|
126 | <div class="unread"><a href="${h.url('notifications')}">${_('Unread')}: ${c.unread_notifications}</a></div> | |
127 | </div> |
|
127 | </div> | |
128 | <div class="links_right"> |
|
128 | <div class="links_right"> | |
129 | <ol class="links"> |
|
129 | <ol class="links"> | |
130 | <li>${h.link_to(_(u'Home'),h.url('home'))}</li> |
|
130 | <li>${h.link_to(_(u'Home'),h.url('home'))}</li> | |
131 | <li>${h.link_to(_(u'Journal'),h.url('journal'))}</li> |
|
131 | <li>${h.link_to(_(u'Journal'),h.url('journal'))}</li> | |
132 | <li>${h.link_to(_(u'My account'),h.url('admin_settings_my_account'))}</li> |
|
132 | <li>${h.link_to(_(u'My account'),h.url('admin_settings_my_account'))}</li> | |
133 | <li class="logout">${h.link_to(_(u'Log Out'),h.url('logout_home'))}</li> |
|
133 | <li class="logout">${h.link_to(_(u'Log Out'),h.url('logout_home'))}</li> | |
134 | </ol> |
|
134 | </ol> | |
135 | </div> |
|
135 | </div> | |
136 | %endif |
|
136 | %endif | |
137 | </div> |
|
137 | </div> | |
138 | </div> |
|
138 | </div> | |
139 |
|
139 | |||
140 | </li> |
|
140 | </li> | |
141 | </%def> |
|
141 | </%def> | |
142 |
|
142 | |||
143 | <%def name="menu(current=None)"> |
|
143 | <%def name="menu(current=None)"> | |
144 | <% |
|
144 | <% | |
145 | def is_current(selected): |
|
145 | def is_current(selected): | |
146 | if selected == current: |
|
146 | if selected == current: | |
147 | return h.literal('class="current"') |
|
147 | return h.literal('class="current"') | |
148 | %> |
|
148 | %> | |
149 | <ul id="quick"> |
|
149 | <ul id="quick"> | |
150 | <!-- repo switcher --> |
|
150 | <!-- repo switcher --> | |
151 | <li ${is_current('home')}> |
|
151 | <li ${is_current('home')}> | |
152 | <a class="menu_link" id="repo_switcher" title="${_('Switch repository')}" href="${h.url('home')}"> |
|
152 | <a class="menu_link" id="repo_switcher" title="${_('Switch repository')}" href="${h.url('home')}"> | |
153 | <span class="icon"> |
|
153 | <span class="icon"> | |
154 | <img src="${h.url('/images/icons/database.png')}" alt="${_('Products')}" /> |
|
154 | <img src="${h.url('/images/icons/database.png')}" alt="${_('Products')}" /> | |
155 | </span> |
|
155 | </span> | |
156 | <span>${_('Repositories')}</span> |
|
156 | <span>${_('Repositories')}</span> | |
157 | </a> |
|
157 | </a> | |
158 | <ul id="repo_switcher_list" class="repo_switcher"> |
|
158 | <ul id="repo_switcher_list" class="repo_switcher"> | |
159 | <li> |
|
159 | <li> | |
160 | <a href="#">${_('loading...')}</a> |
|
160 | <a href="#">${_('loading...')}</a> | |
161 | </li> |
|
161 | </li> | |
162 | </ul> |
|
162 | </ul> | |
163 | </li> |
|
163 | </li> | |
164 | ## we render this menu only not for those pages |
|
164 | ## we render this menu only not for those pages | |
165 | %if current not in ['home','admin', 'search', 'journal']: |
|
165 | %if current not in ['home','admin', 'search', 'journal']: | |
166 | ##REGULAR MENU |
|
166 | ##REGULAR MENU | |
167 | <li ${is_current('summary')}> |
|
167 | <li ${is_current('summary')}> | |
168 | <a class="menu_link" title="${_('Summary page')}" href="${h.url('summary_home',repo_name=c.repo_name)}"> |
|
168 | <a class="menu_link" title="${_('Summary page')}" href="${h.url('summary_home',repo_name=c.repo_name)}"> | |
169 | <span class="icon"> |
|
169 | <span class="icon"> | |
170 | <img src="${h.url('/images/icons/clipboard_16.png')}" alt="${_('Summary')}" /> |
|
170 | <img src="${h.url('/images/icons/clipboard_16.png')}" alt="${_('Summary')}" /> | |
171 | </span> |
|
171 | </span> | |
172 | <span>${_('Summary')}</span> |
|
172 | <span>${_('Summary')}</span> | |
173 | </a> |
|
173 | </a> | |
174 | </li> |
|
174 | </li> | |
175 | <li ${is_current('changelog')}> |
|
175 | <li ${is_current('changelog')}> | |
176 | <a class="menu_link" title="${_('Changeset list')}" href="${h.url('changelog_home',repo_name=c.repo_name)}"> |
|
176 | <a class="menu_link" title="${_('Changeset list')}" href="${h.url('changelog_home',repo_name=c.repo_name)}"> | |
177 | <span class="icon"> |
|
177 | <span class="icon"> | |
178 | <img src="${h.url('/images/icons/time.png')}" alt="${_('Changelog')}" /> |
|
178 | <img src="${h.url('/images/icons/time.png')}" alt="${_('Changelog')}" /> | |
179 | </span> |
|
179 | </span> | |
180 | <span>${_('Changelog')}</span> |
|
180 | <span>${_('Changelog')}</span> | |
181 | </a> |
|
181 | </a> | |
182 | </li> |
|
182 | </li> | |
183 | <li ${is_current('switch_to')}> |
|
183 | <li ${is_current('switch_to')}> | |
184 | <a class="menu_link" id="branch_tag_switcher" title="${_('Switch to')}" href="#"> |
|
184 | <a class="menu_link" id="branch_tag_switcher" title="${_('Switch to')}" href="#"> | |
185 | <span class="icon"> |
|
185 | <span class="icon"> | |
186 | <img src="${h.url('/images/icons/arrow_switch.png')}" alt="${_('Switch to')}" /> |
|
186 | <img src="${h.url('/images/icons/arrow_switch.png')}" alt="${_('Switch to')}" /> | |
187 | </span> |
|
187 | </span> | |
188 | <span>${_('Switch to')}</span> |
|
188 | <span>${_('Switch to')}</span> | |
189 | </a> |
|
189 | </a> | |
190 | <ul id="switch_to_list" class="switch_to"> |
|
190 | <ul id="switch_to_list" class="switch_to"> | |
191 | <li><a href="#">${_('loading...')}</a></li> |
|
191 | <li><a href="#">${_('loading...')}</a></li> | |
192 | </ul> |
|
192 | </ul> | |
193 | </li> |
|
193 | </li> | |
194 | <li ${is_current('files')}> |
|
194 | <li ${is_current('files')}> | |
195 | <a class="menu_link" title="${_('Show repository content')}" href="${h.url('files_home',repo_name=c.repo_name)}"> |
|
195 | <a class="menu_link" title="${_('Show repository content')}" href="${h.url('files_home',repo_name=c.repo_name)}"> | |
196 | <span class="icon"> |
|
196 | <span class="icon"> | |
197 | <img src="${h.url('/images/icons/file.png')}" alt="${_('Files')}" /> |
|
197 | <img src="${h.url('/images/icons/file.png')}" alt="${_('Files')}" /> | |
198 | </span> |
|
198 | </span> | |
199 | <span>${_('Files')}</span> |
|
199 | <span>${_('Files')}</span> | |
200 | </a> |
|
200 | </a> | |
201 | </li> |
|
201 | </li> | |
202 | <li ${is_current('options')}> |
|
202 | <li ${is_current('options')}> | |
203 | <a class="menu_link" title="${_('Options')}" href="#"> |
|
203 | <a class="menu_link" title="${_('Options')}" href="#"> | |
204 | <span class="icon"> |
|
204 | <span class="icon"> | |
205 | <img src="${h.url('/images/icons/table_gear.png')}" alt="${_('Admin')}" /> |
|
205 | <img src="${h.url('/images/icons/table_gear.png')}" alt="${_('Admin')}" /> | |
206 | </span> |
|
206 | </span> | |
207 | <span>${_('Options')}</span> |
|
207 | <span>${_('Options')}</span> | |
208 | </a> |
|
208 | </a> | |
209 | <ul> |
|
209 | <ul> | |
210 | %if h.HasRepoPermissionAll('repository.admin')(c.repo_name): |
|
210 | %if h.HasRepoPermissionAll('repository.admin')(c.repo_name): | |
211 | %if h.HasPermissionAll('hg.admin')('access settings on repository'): |
|
211 | %if h.HasPermissionAll('hg.admin')('access settings on repository'): | |
212 | <li>${h.link_to(_('repository settings'),h.url('edit_repo',repo_name=c.repo_name),class_='settings')}</li> |
|
212 | <li>${h.link_to(_('repository settings'),h.url('edit_repo',repo_name=c.repo_name),class_='settings')}</li> | |
213 | %else: |
|
213 | %else: | |
214 | <li>${h.link_to(_('repository settings'),h.url('repo_settings_home',repo_name=c.repo_name),class_='settings')}</li> |
|
214 | <li>${h.link_to(_('repository settings'),h.url('repo_settings_home',repo_name=c.repo_name),class_='settings')}</li> | |
215 | %endif |
|
215 | %endif | |
216 | %endif |
|
216 | %endif | |
217 |
|
217 | |||
218 | <li>${h.link_to(_('fork'),h.url('repo_fork_home',repo_name=c.repo_name),class_='fork')}</li> |
|
218 | <li>${h.link_to(_('fork'),h.url('repo_fork_home',repo_name=c.repo_name),class_='fork')}</li> | |
219 | %if h.is_hg(c.rhodecode_repo): |
|
219 | %if h.is_hg(c.rhodecode_repo): | |
220 | <li>${h.link_to(_('open new pull request'),h.url('pullrequest_home',repo_name=c.repo_name),class_='pull_request')}</li> |
|
220 | <li>${h.link_to(_('open new pull request'),h.url('pullrequest_home',repo_name=c.repo_name),class_='pull_request')}</li> | |
221 | %endif |
|
221 | %endif | |
222 | %if c.rhodecode_db_repo.fork: |
|
222 | %if c.rhodecode_db_repo.fork: | |
223 | <li>${h.link_to(_('compare fork'),h.url('compare_url',repo_name=c.rhodecode_db_repo.fork.repo_name,org_ref_type='branch',org_ref='default',other_repo=c.repo_name,other_ref_type='branch',other_ref=request.GET.get('branch') or 'default'),class_='compare_request')}</li> |
|
223 | <li>${h.link_to(_('compare fork'),h.url('compare_url',repo_name=c.rhodecode_db_repo.fork.repo_name,org_ref_type='branch',org_ref='default',other_repo=c.repo_name,other_ref_type='branch',other_ref=request.GET.get('branch') or 'default'),class_='compare_request')}</li> | |
224 | %endif |
|
224 | %endif | |
225 | <li>${h.link_to(_('lightweight changelog'),h.url('shortlog_home',repo_name=c.repo_name),class_='shortlog')}</li> |
|
225 | <li>${h.link_to(_('lightweight changelog'),h.url('shortlog_home',repo_name=c.repo_name),class_='shortlog')}</li> | |
226 | <li>${h.link_to(_('search'),h.url('search_repo',repo_name=c.repo_name),class_='search')}</li> |
|
226 | <li>${h.link_to(_('search'),h.url('search_repo',repo_name=c.repo_name),class_='search')}</li> | |
227 |
|
227 | |||
228 | %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking: |
|
228 | %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking: | |
229 | %if c.rhodecode_db_repo.locked[0]: |
|
229 | %if c.rhodecode_db_repo.locked[0]: | |
230 | <li>${h.link_to(_('unlock'), h.url('toggle_locking',repo_name=c.repo_name),class_='locking_del')}</li> |
|
230 | <li>${h.link_to(_('unlock'), h.url('toggle_locking',repo_name=c.repo_name),class_='locking_del')}</li> | |
231 | %else: |
|
231 | %else: | |
232 | <li>${h.link_to(_('lock'), h.url('toggle_locking',repo_name=c.repo_name),class_='locking_add')}</li> |
|
232 | <li>${h.link_to(_('lock'), h.url('toggle_locking',repo_name=c.repo_name),class_='locking_add')}</li> | |
233 | %endif |
|
233 | %endif | |
234 | %endif |
|
234 | %endif | |
235 |
|
235 | |||
236 | % if h.HasPermissionAll('hg.admin')('access admin main page'): |
|
236 | % if h.HasPermissionAll('hg.admin')('access admin main page'): | |
237 | <li> |
|
237 | <li> | |
238 | ${h.link_to(_('admin'),h.url('admin_home'),class_='admin')} |
|
238 | ${h.link_to(_('admin'),h.url('admin_home'),class_='admin')} | |
239 | <%def name="admin_menu()"> |
|
239 | <%def name="admin_menu()"> | |
240 | <ul> |
|
240 | <ul> | |
241 | <li>${h.link_to(_('admin journal'),h.url('admin_home'),class_='journal')}</li> |
|
241 | <li>${h.link_to(_('admin journal'),h.url('admin_home'),class_='journal')}</li> | |
242 | <li>${h.link_to(_('repositories'),h.url('repos'),class_='repos')}</li> |
|
242 | <li>${h.link_to(_('repositories'),h.url('repos'),class_='repos')}</li> | |
243 | <li>${h.link_to(_('repositories groups'),h.url('repos_groups'),class_='repos_groups')}</li> |
|
243 | <li>${h.link_to(_('repositories groups'),h.url('repos_groups'),class_='repos_groups')}</li> | |
244 | <li>${h.link_to(_('users'),h.url('users'),class_='users')}</li> |
|
244 | <li>${h.link_to(_('users'),h.url('users'),class_='users')}</li> | |
245 | <li>${h.link_to(_('users groups'),h.url('users_groups'),class_='groups')}</li> |
|
245 | <li>${h.link_to(_('users groups'),h.url('users_groups'),class_='groups')}</li> | |
246 | <li>${h.link_to(_('permissions'),h.url('edit_permission',id='default'),class_='permissions')}</li> |
|
246 | <li>${h.link_to(_('permissions'),h.url('edit_permission',id='default'),class_='permissions')}</li> | |
247 | <li>${h.link_to(_('ldap'),h.url('ldap_home'),class_='ldap')}</li> |
|
247 | <li>${h.link_to(_('ldap'),h.url('ldap_home'),class_='ldap')}</li> | |
248 | <li>${h.link_to(_('defaults'),h.url('defaults'),class_='defaults')}</li> |
|
248 | <li>${h.link_to(_('defaults'),h.url('defaults'),class_='defaults')}</li> | |
249 | <li class="last">${h.link_to(_('settings'),h.url('admin_settings'),class_='settings')}</li> |
|
249 | <li class="last">${h.link_to(_('settings'),h.url('admin_settings'),class_='settings')}</li> | |
250 | </ul> |
|
250 | </ul> | |
251 | </%def> |
|
251 | </%def> | |
252 | ## ADMIN MENU |
|
252 | ## ADMIN MENU | |
253 | ${admin_menu()} |
|
253 | ${admin_menu()} | |
254 | </li> |
|
254 | </li> | |
255 | ## if you're a admin of any groups, show admin menu for it |
|
255 | ## if you're a admin of any groups, show admin menu for it | |
256 | % elif c.rhodecode_user.groups_admin: |
|
256 | % elif c.rhodecode_user.groups_admin: | |
257 | <li> |
|
257 | <li> | |
258 | ${h.link_to(_('admin'),h.url('admin_home'),class_='admin')} |
|
258 | ${h.link_to(_('admin'),h.url('admin_home'),class_='admin')} | |
259 | <%def name="admin_menu_simple()"> |
|
259 | <%def name="admin_menu_simple()"> | |
260 | <ul> |
|
260 | <ul> | |
261 | <li>${h.link_to(_('repositories groups'),h.url('repos_groups'),class_='repos_groups')}</li> |
|
261 | <li>${h.link_to(_('repositories groups'),h.url('repos_groups'),class_='repos_groups')}</li> | |
262 | </ul> |
|
262 | </ul> | |
263 | </%def> |
|
263 | </%def> | |
264 | ## ADMIN MENU |
|
264 | ## ADMIN MENU | |
265 | ${admin_menu_simple()} |
|
265 | ${admin_menu_simple()} | |
266 | </li> |
|
266 | </li> | |
267 | % endif |
|
267 | % endif | |
268 | </ul> |
|
268 | </ul> | |
269 | </li> |
|
269 | </li> | |
270 | <li> |
|
270 | <li> | |
271 | <a class="menu_link" title="${_('Followers')}" href="${h.url('repo_followers_home',repo_name=c.repo_name)}"> |
|
271 | <a class="menu_link" title="${_('Followers')}" href="${h.url('repo_followers_home',repo_name=c.repo_name)}"> | |
272 | <span class="icon_short"> |
|
272 | <span class="icon_short"> | |
273 | <img src="${h.url('/images/icons/heart.png')}" alt="${_('Followers')}" /> |
|
273 | <img src="${h.url('/images/icons/heart.png')}" alt="${_('Followers')}" /> | |
274 | </span> |
|
274 | </span> | |
275 | <span id="current_followers_count" class="short">${c.repository_followers}</span> |
|
275 | <span id="current_followers_count" class="short">${c.repository_followers}</span> | |
276 | </a> |
|
276 | </a> | |
277 | </li> |
|
277 | </li> | |
278 | <li> |
|
278 | <li> | |
279 | <a class="menu_link" title="${_('Forks')}" href="${h.url('repo_forks_home',repo_name=c.repo_name)}"> |
|
279 | <a class="menu_link" title="${_('Forks')}" href="${h.url('repo_forks_home',repo_name=c.repo_name)}"> | |
280 | <span class="icon_short"> |
|
280 | <span class="icon_short"> | |
281 | <img src="${h.url('/images/icons/arrow_divide.png')}" alt="${_('Forks')}" /> |
|
281 | <img src="${h.url('/images/icons/arrow_divide.png')}" alt="${_('Forks')}" /> | |
282 | </span> |
|
282 | </span> | |
283 | <span class="short">${c.repository_forks}</span> |
|
283 | <span class="short">${c.repository_forks}</span> | |
284 | </a> |
|
284 | </a> | |
285 | </li> |
|
285 | </li> | |
286 | <li> |
|
286 | <li> | |
287 | <a class="menu_link" title="${_('Pull requests')}" href="${h.url('pullrequest_show_all',repo_name=c.repo_name)}"> |
|
287 | <a class="menu_link" title="${_('Pull requests')}" href="${h.url('pullrequest_show_all',repo_name=c.repo_name)}"> | |
288 | <span class="icon_short"> |
|
288 | <span class="icon_short"> | |
289 | <img src="${h.url('/images/icons/arrow_join.png')}" alt="${_('Pull requests')}" /> |
|
289 | <img src="${h.url('/images/icons/arrow_join.png')}" alt="${_('Pull requests')}" /> | |
290 | </span> |
|
290 | </span> | |
291 | <span class="short">${c.repository_pull_requests}</span> |
|
291 | <span class="short">${c.repository_pull_requests}</span> | |
292 | </a> |
|
292 | </a> | |
293 | </li> |
|
293 | </li> | |
294 | ${usermenu()} |
|
294 | ${usermenu()} | |
295 | <script type="text/javascript"> |
|
295 | <script type="text/javascript"> | |
296 | YUE.on('branch_tag_switcher','mouseover',function(){ |
|
296 | YUE.on('branch_tag_switcher','mouseover',function(){ | |
297 | var loaded = YUD.hasClass('branch_tag_switcher','loaded'); |
|
297 | var loaded = YUD.hasClass('branch_tag_switcher','loaded'); | |
298 | if(!loaded){ |
|
298 | if(!loaded){ | |
299 | YUD.addClass('branch_tag_switcher','loaded'); |
|
299 | YUD.addClass('branch_tag_switcher','loaded'); | |
300 | ypjax("${h.url('branch_tag_switcher',repo_name=c.repo_name)}",'switch_to_list', |
|
300 | ypjax("${h.url('branch_tag_switcher',repo_name=c.repo_name)}",'switch_to_list', | |
301 | function(o){}, |
|
301 | function(o){}, | |
302 | function(o){YUD.removeClass('branch_tag_switcher','loaded');} |
|
302 | function(o){YUD.removeClass('branch_tag_switcher','loaded');} | |
303 | ,null); |
|
303 | ,null); | |
304 | } |
|
304 | } | |
305 | return false; |
|
305 | return false; | |
306 | }); |
|
306 | }); | |
307 | </script> |
|
307 | </script> | |
308 | %else: |
|
308 | %else: | |
309 | ##ROOT MENU |
|
309 | ##ROOT MENU | |
310 | %if c.rhodecode_user.username != 'default': |
|
310 | %if c.rhodecode_user.username != 'default': | |
311 | <li ${is_current('journal')}> |
|
311 | <li ${is_current('journal')}> | |
312 | <a class="menu_link" title="${_('Show recent activity')}" href="${h.url('journal')}"> |
|
312 | <a class="menu_link" title="${_('Show recent activity')}" href="${h.url('journal')}"> | |
313 | <span class="icon"> |
|
313 | <span class="icon"> | |
314 | <img src="${h.url('/images/icons/book.png')}" alt="${_('Journal')}" /> |
|
314 | <img src="${h.url('/images/icons/book.png')}" alt="${_('Journal')}" /> | |
315 | </span> |
|
315 | </span> | |
316 | <span>${_('Journal')}</span> |
|
316 | <span>${_('Journal')}</span> | |
317 | </a> |
|
317 | </a> | |
318 | </li> |
|
318 | </li> | |
319 | %else: |
|
319 | %else: | |
320 | <li ${is_current('journal')}> |
|
320 | <li ${is_current('journal')}> | |
321 | <a class="menu_link" title="${_('Public journal')}" href="${h.url('public_journal')}"> |
|
321 | <a class="menu_link" title="${_('Public journal')}" href="${h.url('public_journal')}"> | |
322 | <span class="icon"> |
|
322 | <span class="icon"> | |
323 | <img src="${h.url('/images/icons/book.png')}" alt="${_('Public journal')}" /> |
|
323 | <img src="${h.url('/images/icons/book.png')}" alt="${_('Public journal')}" /> | |
324 | </span> |
|
324 | </span> | |
325 | <span>${_('Public journal')}</span> |
|
325 | <span>${_('Public journal')}</span> | |
326 | </a> |
|
326 | </a> | |
327 | </li> |
|
327 | </li> | |
328 | %endif |
|
328 | %endif | |
329 | <li ${is_current('search')}> |
|
329 | <li ${is_current('search')}> | |
330 | <a class="menu_link" title="${_('Search in repositories')}" href="${h.url('search')}"> |
|
330 | <a class="menu_link" title="${_('Search in repositories')}" href="${h.url('search')}"> | |
331 | <span class="icon"> |
|
331 | <span class="icon"> | |
332 | <img src="${h.url('/images/icons/search_16.png')}" alt="${_('Search')}" /> |
|
332 | <img src="${h.url('/images/icons/search_16.png')}" alt="${_('Search')}" /> | |
333 | </span> |
|
333 | </span> | |
334 | <span>${_('Search')}</span> |
|
334 | <span>${_('Search')}</span> | |
335 | </a> |
|
335 | </a> | |
336 | </li> |
|
336 | </li> | |
337 | % if h.HasPermissionAll('hg.admin')('access admin main page'): |
|
337 | % if h.HasPermissionAll('hg.admin')('access admin main page'): | |
338 | <li ${is_current('admin')}> |
|
338 | <li ${is_current('admin')}> | |
339 | <a class="menu_link" title="${_('Admin')}" href="${h.url('admin_home')}"> |
|
339 | <a class="menu_link" title="${_('Admin')}" href="${h.url('admin_home')}"> | |
340 | <span class="icon"> |
|
340 | <span class="icon"> | |
341 | <img src="${h.url('/images/icons/cog_edit.png')}" alt="${_('Admin')}" /> |
|
341 | <img src="${h.url('/images/icons/cog_edit.png')}" alt="${_('Admin')}" /> | |
342 | </span> |
|
342 | </span> | |
343 | <span>${_('Admin')}</span> |
|
343 | <span>${_('Admin')}</span> | |
344 | </a> |
|
344 | </a> | |
345 | ${admin_menu()} |
|
345 | ${admin_menu()} | |
346 | </li> |
|
346 | </li> | |
347 | % elif c.rhodecode_user.groups_admin: |
|
347 | % elif c.rhodecode_user.groups_admin: | |
348 | <li ${is_current('admin')}> |
|
348 | <li ${is_current('admin')}> | |
349 | <a class="menu_link" title="${_('Admin')}" href="${h.url('admin_home')}"> |
|
349 | <a class="menu_link" title="${_('Admin')}" href="${h.url('admin_home')}"> | |
350 | <span class="icon"> |
|
350 | <span class="icon"> | |
351 | <img src="${h.url('/images/icons/cog_edit.png')}" alt="${_('Admin')}" /> |
|
351 | <img src="${h.url('/images/icons/cog_edit.png')}" alt="${_('Admin')}" /> | |
352 | </span> |
|
352 | </span> | |
353 | <span>${_('Admin')}</span> |
|
353 | <span>${_('Admin')}</span> | |
354 | </a> |
|
354 | </a> | |
355 | ${admin_menu_simple()} |
|
355 | ${admin_menu_simple()} | |
356 |
</li> |
|
356 | </li> | |
357 | % endif |
|
357 | % endif | |
358 | ${usermenu()} |
|
358 | ${usermenu()} | |
359 | %endif |
|
359 | %endif | |
360 | <script type="text/javascript"> |
|
360 | <script type="text/javascript"> | |
361 | YUE.on('repo_switcher','mouseover',function(){ |
|
361 | YUE.on('repo_switcher','mouseover',function(){ | |
362 | var target = 'q_filter_rs'; |
|
362 | var target = 'q_filter_rs'; | |
363 | var qfilter_activate = function(){ |
|
363 | var qfilter_activate = function(){ | |
364 | var nodes = YUQ('ul#repo_switcher_list li a.repo_name'); |
|
364 | var nodes = YUQ('ul#repo_switcher_list li a.repo_name'); | |
365 | var func = function(node){ |
|
365 | var func = function(node){ | |
366 | return node.parentNode; |
|
366 | return node.parentNode; | |
367 | } |
|
367 | } | |
368 | q_filter(target,nodes,func); |
|
368 | q_filter(target,nodes,func); | |
369 | } |
|
369 | } | |
370 |
|
370 | |||
371 | var loaded = YUD.hasClass('repo_switcher','loaded'); |
|
371 | var loaded = YUD.hasClass('repo_switcher','loaded'); | |
372 | if(!loaded){ |
|
372 | if(!loaded){ | |
373 | YUD.addClass('repo_switcher','loaded'); |
|
373 | YUD.addClass('repo_switcher','loaded'); | |
374 | ypjax("${h.url('repo_switcher')}",'repo_switcher_list', |
|
374 | ypjax("${h.url('repo_switcher')}",'repo_switcher_list', | |
375 | function(o){qfilter_activate();YUD.get(target).focus()}, |
|
375 | function(o){qfilter_activate();YUD.get(target).focus()}, | |
376 | function(o){YUD.removeClass('repo_switcher','loaded');} |
|
376 | function(o){YUD.removeClass('repo_switcher','loaded');} | |
377 | ,null); |
|
377 | ,null); | |
378 | }else{ |
|
378 | }else{ | |
379 | YUD.get(target).focus(); |
|
379 | YUD.get(target).focus(); | |
380 | } |
|
380 | } | |
381 | return false; |
|
381 | return false; | |
382 | }); |
|
382 | }); | |
383 |
|
383 | |||
384 | YUE.on('header-dd', 'click',function(e){ |
|
384 | YUE.on('header-dd', 'click',function(e){ | |
385 | YUD.addClass('header-inner', 'hover'); |
|
385 | YUD.addClass('header-inner', 'hover'); | |
386 | YUD.addClass('content', 'hover'); |
|
386 | YUD.addClass('content', 'hover'); | |
387 | }); |
|
387 | }); | |
388 |
|
388 | |||
389 | </script> |
|
389 | </script> | |
390 | </%def> |
|
390 | </%def> |
@@ -1,111 +1,111 | |||||
1 | ## -*- coding: utf-8 -*- |
|
1 | ## -*- coding: utf-8 -*- | |
2 | <!DOCTYPE html> |
|
2 | <!DOCTYPE html> | |
3 | <html xmlns="http://www.w3.org/1999/xhtml"> |
|
3 | <html xmlns="http://www.w3.org/1999/xhtml"> | |
4 | <head> |
|
4 | <head> | |
5 | <title>${self.title()}</title> |
|
5 | <title>${self.title()}</title> | |
6 | <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> |
|
6 | <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> | |
7 | <meta name="robots" content="index, nofollow"/> |
|
7 | <meta name="robots" content="index, nofollow"/> | |
8 | <link rel="icon" href="${h.url('/images/icons/database_gear.png')}" type="image/png" /> |
|
8 | <link rel="icon" href="${h.url('/images/icons/database_gear.png')}" type="image/png" /> | |
9 |
|
9 | |||
10 | ## CSS ### |
|
10 | ## CSS ### | |
11 | <%def name="css()"> |
|
11 | <%def name="css()"> | |
12 | <link rel="stylesheet" type="text/css" href="${h.url('/css/style.css', ver=c.rhodecode_version)}" media="screen"/> |
|
12 | <link rel="stylesheet" type="text/css" href="${h.url('/css/style.css', ver=c.rhodecode_version)}" media="screen"/> | |
13 | <link rel="stylesheet" type="text/css" href="${h.url('/css/pygments.css', ver=c.rhodecode_version)}"/> |
|
13 | <link rel="stylesheet" type="text/css" href="${h.url('/css/pygments.css', ver=c.rhodecode_version)}"/> | |
14 | ## EXTRA FOR CSS |
|
14 | ## EXTRA FOR CSS | |
15 | ${self.css_extra()} |
|
15 | ${self.css_extra()} | |
16 | </%def> |
|
16 | </%def> | |
17 | <%def name="css_extra()"> |
|
17 | <%def name="css_extra()"> | |
18 | </%def> |
|
18 | </%def> | |
19 |
|
19 | |||
20 | ${self.css()} |
|
20 | ${self.css()} | |
21 |
|
21 | |||
22 | %if c.ga_code: |
|
22 | %if c.ga_code: | |
23 | <!-- Analytics --> |
|
23 | <!-- Analytics --> | |
24 | <script type="text/javascript"> |
|
24 | <script type="text/javascript"> | |
25 | var _gaq = _gaq || []; |
|
25 | var _gaq = _gaq || []; | |
26 | _gaq.push(['_setAccount', '${c.ga_code}']); |
|
26 | _gaq.push(['_setAccount', '${c.ga_code}']); | |
27 | _gaq.push(['_trackPageview']); |
|
27 | _gaq.push(['_trackPageview']); | |
28 |
|
28 | |||
29 | (function() { |
|
29 | (function() { | |
30 | var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; |
|
30 | var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; | |
31 | ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; |
|
31 | ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; | |
32 | var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); |
|
32 | var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); | |
33 | })(); |
|
33 | })(); | |
34 | </script> |
|
34 | </script> | |
35 | %endif |
|
35 | %endif | |
36 |
|
36 | |||
37 | ## JAVASCRIPT ## |
|
37 | ## JAVASCRIPT ## | |
38 | <%def name="js()"> |
|
38 | <%def name="js()"> | |
39 | <script type="text/javascript"> |
|
39 | <script type="text/javascript"> | |
40 | //JS translations map |
|
40 | //JS translations map | |
41 | var TRANSLATION_MAP = { |
|
41 | var TRANSLATION_MAP = { | |
42 | 'add another comment':'${_("add another comment")}', |
|
42 | 'add another comment':'${_("add another comment")}', | |
43 | 'Stop following this repository':"${_('Stop following this repository')}", |
|
43 | 'Stop following this repository':"${_('Stop following this repository')}", | |
44 | 'Start following this repository':"${_('Start following this repository')}", |
|
44 | 'Start following this repository':"${_('Start following this repository')}", | |
45 | 'Group':"${_('Group')}", |
|
45 | 'Group':"${_('Group')}", | |
46 | 'members':"${_('members')}", |
|
46 | 'members':"${_('members')}", | |
47 | 'loading...':"${_('loading...')}", |
|
47 | 'loading...':"${_('loading...')}", | |
48 | 'search truncated': "${_('search truncated')}", |
|
48 | 'search truncated': "${_('search truncated')}", | |
49 | 'no matching files': "${_('no matching files')}", |
|
49 | 'no matching files': "${_('no matching files')}", | |
50 | 'Open new pull request': "${_('Open new pull request')}", |
|
50 | 'Open new pull request': "${_('Open new pull request')}", | |
51 | 'Open new pull request for selected changesets': "${_('Open new pull request for selected changesets')}", |
|
51 | 'Open new pull request for selected changesets': "${_('Open new pull request for selected changesets')}", | |
52 | 'Show selected changes __S -> __E': "${_('Show selected changes __S -> __E')}", |
|
52 | 'Show selected changes __S -> __E': "${_('Show selected changes __S -> __E')}", | |
53 | 'Selection link': "${_('Selection link')}", |
|
53 | 'Selection link': "${_('Selection link')}", | |
54 | }; |
|
54 | }; | |
55 | var _TM = TRANSLATION_MAP; |
|
55 | var _TM = TRANSLATION_MAP; | |
56 |
|
56 | |||
57 | var TOGGLE_FOLLOW_URL = "${h.url('toggle_following')}"; |
|
57 | var TOGGLE_FOLLOW_URL = "${h.url('toggle_following')}"; | |
58 |
|
58 | |||
59 | </script> |
|
59 | </script> | |
60 | <script type="text/javascript" src="${h.url('/js/yui.2.9.js', ver=c.rhodecode_version)}"></script> |
|
60 | <script type="text/javascript" src="${h.url('/js/yui.2.9.js', ver=c.rhodecode_version)}"></script> | |
61 | <!--[if lt IE 9]> |
|
61 | <!--[if lt IE 9]> | |
62 | <script language="javascript" type="text/javascript" src="${h.url('/js/excanvas.min.js')}"></script> |
|
62 | <script language="javascript" type="text/javascript" src="${h.url('/js/excanvas.min.js')}"></script> | |
63 | <![endif]--> |
|
63 | <![endif]--> | |
64 | <script type="text/javascript" src="${h.url('/js/yui.flot.js', ver=c.rhodecode_version)}"></script> |
|
64 | <script type="text/javascript" src="${h.url('/js/yui.flot.js', ver=c.rhodecode_version)}"></script> | |
65 | <script type="text/javascript" src="${h.url('/js/native.history.js', ver=c.rhodecode_version)}"></script> |
|
65 | <script type="text/javascript" src="${h.url('/js/native.history.js', ver=c.rhodecode_version)}"></script> | |
66 | <script type="text/javascript" src="${h.url('/js/pyroutes_map.js', ver=c.rhodecode_version)}"></script> |
|
66 | <script type="text/javascript" src="${h.url('/js/pyroutes_map.js', ver=c.rhodecode_version)}"></script> | |
67 | <script type="text/javascript" src="${h.url('/js/rhodecode.js', ver=c.rhodecode_version)}"></script> |
|
67 | <script type="text/javascript" src="${h.url('/js/rhodecode.js', ver=c.rhodecode_version)}"></script> | |
68 | ## EXTRA FOR JS |
|
68 | ## EXTRA FOR JS | |
69 | ${self.js_extra()} |
|
69 | ${self.js_extra()} | |
70 | <script type="text/javascript"> |
|
70 | <script type="text/javascript"> | |
71 | (function(window,undefined){ |
|
71 | (function(window,undefined){ | |
72 | // Prepare |
|
72 | // Prepare | |
73 | var History = window.History; // Note: We are using a capital H instead of a lower h |
|
73 | var History = window.History; // Note: We are using a capital H instead of a lower h | |
74 | if ( !History.enabled ) { |
|
74 | if ( !History.enabled ) { | |
75 | // History.js is disabled for this browser. |
|
75 | // History.js is disabled for this browser. | |
76 | // This is because we can optionally choose to support HTML4 browsers or not. |
|
76 | // This is because we can optionally choose to support HTML4 browsers or not. | |
77 | return false; |
|
77 | return false; | |
78 | } |
|
78 | } | |
79 | })(window); |
|
79 | })(window); | |
80 |
|
80 | |||
81 | YUE.onDOMReady(function(){ |
|
81 | YUE.onDOMReady(function(){ | |
82 | tooltip_activate(); |
|
82 | tooltip_activate(); | |
83 | show_more_event(); |
|
83 | show_more_event(); | |
84 | show_changeset_tooltip(); |
|
84 | show_changeset_tooltip(); | |
85 | // routes registration |
|
85 | // routes registration | |
86 | pyroutes.register('toggle_following', "${h.url('toggle_following')}"); |
|
86 | pyroutes.register('toggle_following', "${h.url('toggle_following')}"); | |
87 | pyroutes.register('changeset_info', "${h.url('changeset_info', repo_name='%(repo_name)s', revision='%(revision)s')}", ['repo_name', 'revision']); |
|
87 | pyroutes.register('changeset_info', "${h.url('changeset_info', repo_name='%(repo_name)s', revision='%(revision)s')}", ['repo_name', 'revision']); | |
88 |
pyroutes.register('repo_size', "${h.url('repo_size', repo_name='%(repo_name)s')}", ['repo_name']); |
|
88 | pyroutes.register('repo_size', "${h.url('repo_size', repo_name='%(repo_name)s')}", ['repo_name']); | |
89 | }) |
|
89 | }) | |
90 | </script> |
|
90 | </script> | |
91 | </%def> |
|
91 | </%def> | |
92 | <%def name="js_extra()"></%def> |
|
92 | <%def name="js_extra()"></%def> | |
93 | ${self.js()} |
|
93 | ${self.js()} | |
94 | <%def name="head_extra()"></%def> |
|
94 | <%def name="head_extra()"></%def> | |
95 | ${self.head_extra()} |
|
95 | ${self.head_extra()} | |
96 | </head> |
|
96 | </head> | |
97 | <body id="body"> |
|
97 | <body id="body"> | |
98 | ## IE hacks |
|
98 | ## IE hacks | |
99 | <!--[if IE 7]> |
|
99 | <!--[if IE 7]> | |
100 | <script>YUD.addClass(document.body,'ie7')</script> |
|
100 | <script>YUD.addClass(document.body,'ie7')</script> | |
101 | <![endif]--> |
|
101 | <![endif]--> | |
102 | <!--[if IE 8]> |
|
102 | <!--[if IE 8]> | |
103 | <script>YUD.addClass(document.body,'ie8')</script> |
|
103 | <script>YUD.addClass(document.body,'ie8')</script> | |
104 | <![endif]--> |
|
104 | <![endif]--> | |
105 | <!--[if IE 9]> |
|
105 | <!--[if IE 9]> | |
106 | <script>YUD.addClass(document.body,'ie9')</script> |
|
106 | <script>YUD.addClass(document.body,'ie9')</script> | |
107 | <![endif]--> |
|
107 | <![endif]--> | |
108 |
|
108 | |||
109 | ${next.body()} |
|
109 | ${next.body()} | |
110 | </body> |
|
110 | </body> | |
111 | </html> |
|
111 | </html> |
@@ -1,340 +1,340 | |||||
1 | <%page args="parent" /> |
|
1 | <%page args="parent" /> | |
2 | <div class="box"> |
|
2 | <div class="box"> | |
3 | <!-- box / title --> |
|
3 | <!-- box / title --> | |
4 | <div class="title"> |
|
4 | <div class="title"> | |
5 | <h5> |
|
5 | <h5> | |
6 | <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}"/> ${parent.breadcrumbs()} <span id="repo_count">0</span> ${_('repositories')} |
|
6 | <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}"/> ${parent.breadcrumbs()} <span id="repo_count">0</span> ${_('repositories')} | |
7 | </h5> |
|
7 | </h5> | |
8 | %if c.rhodecode_user.username != 'default': |
|
8 | %if c.rhodecode_user.username != 'default': | |
9 | <ul class="links"> |
|
9 | <ul class="links"> | |
10 | %if h.HasPermissionAny('hg.admin','hg.create.repository')() or h.HasReposGroupPermissionAny('group.write', 'group.admin')(c.group.group_name if c.group else None): |
|
10 | %if h.HasPermissionAny('hg.admin','hg.create.repository')() or h.HasReposGroupPermissionAny('group.write', 'group.admin')(c.group.group_name if c.group else None): | |
11 | <li> |
|
11 | <li> | |
12 | %if c.group: |
|
12 | %if c.group: | |
13 | <span>${h.link_to(_('Add repository'),h.url('admin_settings_create_repository',parent_group=c.group.group_id))}</span> |
|
13 | <span>${h.link_to(_('Add repository'),h.url('admin_settings_create_repository',parent_group=c.group.group_id))}</span> | |
14 | %if h.HasPermissionAny('hg.admin')() or h.HasReposGroupPermissionAny('group.admin')(c.group.group_name): |
|
14 | %if h.HasPermissionAny('hg.admin')() or h.HasReposGroupPermissionAny('group.admin')(c.group.group_name): | |
15 | <span>${h.link_to(_(u'Add group'),h.url('new_repos_group', parent_group=c.group.group_id))}</span> |
|
15 | <span>${h.link_to(_(u'Add group'),h.url('new_repos_group', parent_group=c.group.group_id))}</span> | |
16 |
%endif |
|
16 | %endif | |
17 | %else: |
|
17 | %else: | |
18 | <span>${h.link_to(_('Add repository'),h.url('admin_settings_create_repository'))}</span> |
|
18 | <span>${h.link_to(_('Add repository'),h.url('admin_settings_create_repository'))}</span> | |
19 | %if h.HasPermissionAny('hg.admin')(): |
|
19 | %if h.HasPermissionAny('hg.admin')(): | |
20 | <span>${h.link_to(_(u'Add group'),h.url('new_repos_group'))}</span> |
|
20 | <span>${h.link_to(_(u'Add group'),h.url('new_repos_group'))}</span> | |
21 | %endif |
|
21 | %endif | |
22 | %endif |
|
22 | %endif | |
23 | </li> |
|
23 | </li> | |
24 | %endif |
|
24 | %endif | |
25 | %if c.group and h.HasReposGroupPermissionAny('group.admin')(c.group.group_name): |
|
25 | %if c.group and h.HasReposGroupPermissionAny('group.admin')(c.group.group_name): | |
26 | <li> |
|
26 | <li> | |
27 | <span>${h.link_to(_('Edit group'),h.url('edit_repos_group',group_name=c.group.group_name), title=_('You have admin right to this group, and can edit it'))}</span> |
|
27 | <span>${h.link_to(_('Edit group'),h.url('edit_repos_group',group_name=c.group.group_name), title=_('You have admin right to this group, and can edit it'))}</span> | |
28 | </li> |
|
28 | </li> | |
29 | %endif |
|
29 | %endif | |
30 | </ul> |
|
30 | </ul> | |
31 | %endif |
|
31 | %endif | |
32 | </div> |
|
32 | </div> | |
33 | <!-- end box / title --> |
|
33 | <!-- end box / title --> | |
34 | <div class="table"> |
|
34 | <div class="table"> | |
35 | % if c.groups: |
|
35 | % if c.groups: | |
36 | <div id='groups_list_wrap' class="yui-skin-sam"> |
|
36 | <div id='groups_list_wrap' class="yui-skin-sam"> | |
37 | <table id="groups_list"> |
|
37 | <table id="groups_list"> | |
38 | <thead> |
|
38 | <thead> | |
39 | <tr> |
|
39 | <tr> | |
40 | <th class="left"><a href="#">${_('Group name')}</a></th> |
|
40 | <th class="left"><a href="#">${_('Group name')}</a></th> | |
41 | <th class="left"><a href="#">${_('Description')}</a></th> |
|
41 | <th class="left"><a href="#">${_('Description')}</a></th> | |
42 | ##<th class="left"><a href="#">${_('Number of repositories')}</a></th> |
|
42 | ##<th class="left"><a href="#">${_('Number of repositories')}</a></th> | |
43 | </tr> |
|
43 | </tr> | |
44 | </thead> |
|
44 | </thead> | |
45 |
|
45 | |||
46 | ## REPO GROUPS |
|
46 | ## REPO GROUPS | |
47 | % for gr in c.groups: |
|
47 | % for gr in c.groups: | |
48 | <tr> |
|
48 | <tr> | |
49 | <td> |
|
49 | <td> | |
50 | <div style="white-space: nowrap"> |
|
50 | <div style="white-space: nowrap"> | |
51 | <img class="icon" alt="${_('Repositories group')}" src="${h.url('/images/icons/database_link.png')}"/> |
|
51 | <img class="icon" alt="${_('Repositories group')}" src="${h.url('/images/icons/database_link.png')}"/> | |
52 | ${h.link_to(gr.name,url('repos_group_home',group_name=gr.group_name))} |
|
52 | ${h.link_to(gr.name,url('repos_group_home',group_name=gr.group_name))} | |
53 | </div> |
|
53 | </div> | |
54 | </td> |
|
54 | </td> | |
55 | %if c.visual.stylify_metatags: |
|
55 | %if c.visual.stylify_metatags: | |
56 | <td>${h.urlify_text(h.desc_stylize(gr.group_description))}</td> |
|
56 | <td>${h.urlify_text(h.desc_stylize(gr.group_description))}</td> | |
57 | %else: |
|
57 | %else: | |
58 | <td>${gr.group_description}</td> |
|
58 | <td>${gr.group_description}</td> | |
59 | %endif |
|
59 | %endif | |
60 | ## this is commented out since for multi nested repos can be HEAVY! |
|
60 | ## this is commented out since for multi nested repos can be HEAVY! | |
61 | ## in number of executed queries during traversing uncomment at will |
|
61 | ## in number of executed queries during traversing uncomment at will | |
62 | ##<td><b>${gr.repositories_recursive_count}</b></td> |
|
62 | ##<td><b>${gr.repositories_recursive_count}</b></td> | |
63 | </tr> |
|
63 | </tr> | |
64 | % endfor |
|
64 | % endfor | |
65 | </table> |
|
65 | </table> | |
66 | </div> |
|
66 | </div> | |
67 | <div id="group-user-paginator" style="padding: 0px 0px 0px 0px"></div> |
|
67 | <div id="group-user-paginator" style="padding: 0px 0px 0px 0px"></div> | |
68 | <div style="height: 20px"></div> |
|
68 | <div style="height: 20px"></div> | |
69 | % endif |
|
69 | % endif | |
70 | <div id="welcome" style="display:none;text-align:center"> |
|
70 | <div id="welcome" style="display:none;text-align:center"> | |
71 | <h1><a href="${h.url('home')}">${c.rhodecode_name} ${c.rhodecode_version}</a></h1> |
|
71 | <h1><a href="${h.url('home')}">${c.rhodecode_name} ${c.rhodecode_version}</a></h1> | |
72 | </div> |
|
72 | </div> | |
73 | <%cnt=0%> |
|
73 | <%cnt=0%> | |
74 | <%namespace name="dt" file="/data_table/_dt_elements.html"/> |
|
74 | <%namespace name="dt" file="/data_table/_dt_elements.html"/> | |
75 | % if c.visual.lightweight_dashboard is False: |
|
75 | % if c.visual.lightweight_dashboard is False: | |
76 | ## old full detailed version |
|
76 | ## old full detailed version | |
77 | <div id='repos_list_wrap' class="yui-skin-sam"> |
|
77 | <div id='repos_list_wrap' class="yui-skin-sam"> | |
78 | <table id="repos_list"> |
|
78 | <table id="repos_list"> | |
79 | <thead> |
|
79 | <thead> | |
80 | <tr> |
|
80 | <tr> | |
81 | <th class="left"></th> |
|
81 | <th class="left"></th> | |
82 | <th class="left">${_('Name')}</th> |
|
82 | <th class="left">${_('Name')}</th> | |
83 | <th class="left">${_('Description')}</th> |
|
83 | <th class="left">${_('Description')}</th> | |
84 | <th class="left">${_('Last change')}</th> |
|
84 | <th class="left">${_('Last change')}</th> | |
85 | <th class="left">${_('Tip')}</th> |
|
85 | <th class="left">${_('Tip')}</th> | |
86 | <th class="left">${_('Owner')}</th> |
|
86 | <th class="left">${_('Owner')}</th> | |
87 | <th class="left">${_('Atom')}</th> |
|
87 | <th class="left">${_('Atom')}</th> | |
88 | </tr> |
|
88 | </tr> | |
89 | </thead> |
|
89 | </thead> | |
90 | <tbody> |
|
90 | <tbody> | |
91 | %for cnt,repo in enumerate(c.repos_list): |
|
91 | %for cnt,repo in enumerate(c.repos_list): | |
92 | <tr class="parity${(cnt+1)%2}"> |
|
92 | <tr class="parity${(cnt+1)%2}"> | |
93 | ##QUICK MENU |
|
93 | ##QUICK MENU | |
94 | <td class="quick_repo_menu"> |
|
94 | <td class="quick_repo_menu"> | |
95 | ${dt.quick_menu(repo['name'])} |
|
95 | ${dt.quick_menu(repo['name'])} | |
96 | </td> |
|
96 | </td> | |
97 | ##REPO NAME AND ICONS |
|
97 | ##REPO NAME AND ICONS | |
98 | <td class="reponame"> |
|
98 | <td class="reponame"> | |
99 | ${dt.repo_name(repo['name'],repo['dbrepo']['repo_type'],repo['dbrepo']['private'],h.AttributeDict(repo['dbrepo_fork']),pageargs.get('short_repo_names'))} |
|
99 | ${dt.repo_name(repo['name'],repo['dbrepo']['repo_type'],repo['dbrepo']['private'],h.AttributeDict(repo['dbrepo_fork']),pageargs.get('short_repo_names'))} | |
100 | </td> |
|
100 | </td> | |
101 | ##DESCRIPTION |
|
101 | ##DESCRIPTION | |
102 | <td><span class="tooltip" title="${h.tooltip(repo['description'])}"> |
|
102 | <td><span class="tooltip" title="${h.tooltip(repo['description'])}"> | |
103 | %if c.visual.stylify_metatags: |
|
103 | %if c.visual.stylify_metatags: | |
104 | ${h.urlify_text(h.desc_stylize(h.truncate(repo['description'],60)))}</span> |
|
104 | ${h.urlify_text(h.desc_stylize(h.truncate(repo['description'],60)))}</span> | |
105 | %else: |
|
105 | %else: | |
106 | ${h.truncate(repo['description'],60)}</span> |
|
106 | ${h.truncate(repo['description'],60)}</span> | |
107 | %endif |
|
107 | %endif | |
108 | </td> |
|
108 | </td> | |
109 | ##LAST CHANGE DATE |
|
109 | ##LAST CHANGE DATE | |
110 | <td> |
|
110 | <td> | |
111 | ${dt.last_change(repo['last_change'])} |
|
111 | ${dt.last_change(repo['last_change'])} | |
112 | </td> |
|
112 | </td> | |
113 | ##LAST REVISION |
|
113 | ##LAST REVISION | |
114 | <td> |
|
114 | <td> | |
115 | ${dt.revision(repo['name'],repo['rev'],repo['tip'],repo['author'],repo['last_msg'])} |
|
115 | ${dt.revision(repo['name'],repo['rev'],repo['tip'],repo['author'],repo['last_msg'])} | |
116 | </td> |
|
116 | </td> | |
117 | ## |
|
117 | ## | |
118 | <td title="${repo['contact']}">${h.person(repo['contact'])}</td> |
|
118 | <td title="${repo['contact']}">${h.person(repo['contact'])}</td> | |
119 | <td> |
|
119 | <td> | |
120 | ${dt.atom(repo['name'])} |
|
120 | ${dt.atom(repo['name'])} | |
121 | </td> |
|
121 | </td> | |
122 | </tr> |
|
122 | </tr> | |
123 | %endfor |
|
123 | %endfor | |
124 | </tbody> |
|
124 | </tbody> | |
125 | </table> |
|
125 | </table> | |
126 | </div> |
|
126 | </div> | |
127 | % else: |
|
127 | % else: | |
128 | ## lightweight version |
|
128 | ## lightweight version | |
129 | <div class="yui-skin-sam" id="repos_list_wrap"></div> |
|
129 | <div class="yui-skin-sam" id="repos_list_wrap"></div> | |
130 | <div id="user-paginator" style="padding: 0px 0px 0px 0px"></div> |
|
130 | <div id="user-paginator" style="padding: 0px 0px 0px 0px"></div> | |
131 | % endif |
|
131 | % endif | |
132 | </div> |
|
132 | </div> | |
133 | </div> |
|
133 | </div> | |
134 | % if c.visual.lightweight_dashboard is False: |
|
134 | % if c.visual.lightweight_dashboard is False: | |
135 | <script> |
|
135 | <script> | |
136 | YUD.get('repo_count').innerHTML = ${cnt+1 if cnt else 0}; |
|
136 | YUD.get('repo_count').innerHTML = ${cnt+1 if cnt else 0}; | |
137 |
|
137 | |||
138 | // groups table sorting |
|
138 | // groups table sorting | |
139 | var myColumnDefs = [ |
|
139 | var myColumnDefs = [ | |
140 | {key:"name",label:"${_('Group name')}",sortable:true, |
|
140 | {key:"name",label:"${_('Group name')}",sortable:true, | |
141 | sortOptions: { sortFunction: groupNameSort }}, |
|
141 | sortOptions: { sortFunction: groupNameSort }}, | |
142 | {key:"desc",label:"${_('Description')}",sortable:true}, |
|
142 | {key:"desc",label:"${_('Description')}",sortable:true}, | |
143 | ]; |
|
143 | ]; | |
144 |
|
144 | |||
145 | var myDataSource = new YAHOO.util.DataSource(YUD.get("groups_list")); |
|
145 | var myDataSource = new YAHOO.util.DataSource(YUD.get("groups_list")); | |
146 |
|
146 | |||
147 | myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE; |
|
147 | myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE; | |
148 | myDataSource.responseSchema = { |
|
148 | myDataSource.responseSchema = { | |
149 | fields: [ |
|
149 | fields: [ | |
150 | {key:"name"}, |
|
150 | {key:"name"}, | |
151 | {key:"desc"}, |
|
151 | {key:"desc"}, | |
152 | ] |
|
152 | ] | |
153 | }; |
|
153 | }; | |
154 |
|
154 | |||
155 | var myDataTable = new YAHOO.widget.DataTable("groups_list_wrap", myColumnDefs, myDataSource,{ |
|
155 | var myDataTable = new YAHOO.widget.DataTable("groups_list_wrap", myColumnDefs, myDataSource,{ | |
156 | sortedBy:{key:"name",dir:"asc"}, |
|
156 | sortedBy:{key:"name",dir:"asc"}, | |
157 | paginator: new YAHOO.widget.Paginator({ |
|
157 | paginator: new YAHOO.widget.Paginator({ | |
158 | rowsPerPage: 50, |
|
158 | rowsPerPage: 50, | |
159 | alwaysVisible: false, |
|
159 | alwaysVisible: false, | |
160 | template : "{PreviousPageLink} {FirstPageLink} {PageLinks} {LastPageLink} {NextPageLink}", |
|
160 | template : "{PreviousPageLink} {FirstPageLink} {PageLinks} {LastPageLink} {NextPageLink}", | |
161 | pageLinks: 5, |
|
161 | pageLinks: 5, | |
162 | containerClass: 'pagination-wh', |
|
162 | containerClass: 'pagination-wh', | |
163 | currentPageClass: 'pager_curpage', |
|
163 | currentPageClass: 'pager_curpage', | |
164 | pageLinkClass: 'pager_link', |
|
164 | pageLinkClass: 'pager_link', | |
165 | nextPageLinkLabel: '>', |
|
165 | nextPageLinkLabel: '>', | |
166 | previousPageLinkLabel: '<', |
|
166 | previousPageLinkLabel: '<', | |
167 | firstPageLinkLabel: '<<', |
|
167 | firstPageLinkLabel: '<<', | |
168 | lastPageLinkLabel: '>>', |
|
168 | lastPageLinkLabel: '>>', | |
169 | containers:['group-user-paginator'] |
|
169 | containers:['group-user-paginator'] | |
170 | }), |
|
170 | }), | |
171 | MSG_SORTASC:"${_('Click to sort ascending')}", |
|
171 | MSG_SORTASC:"${_('Click to sort ascending')}", | |
172 | MSG_SORTDESC:"${_('Click to sort descending')}" |
|
172 | MSG_SORTDESC:"${_('Click to sort descending')}" | |
173 | }); |
|
173 | }); | |
174 |
|
174 | |||
175 | // main table sorting |
|
175 | // main table sorting | |
176 | var myColumnDefs = [ |
|
176 | var myColumnDefs = [ | |
177 | {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"}, |
|
177 | {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"}, | |
178 | {key:"name",label:"${_('Name')}",sortable:true, |
|
178 | {key:"name",label:"${_('Name')}",sortable:true, | |
179 | sortOptions: { sortFunction: nameSort }}, |
|
179 | sortOptions: { sortFunction: nameSort }}, | |
180 | {key:"desc",label:"${_('Description')}",sortable:true}, |
|
180 | {key:"desc",label:"${_('Description')}",sortable:true}, | |
181 | {key:"last_change",label:"${_('Last Change')}",sortable:true, |
|
181 | {key:"last_change",label:"${_('Last Change')}",sortable:true, | |
182 | sortOptions: { sortFunction: ageSort }}, |
|
182 | sortOptions: { sortFunction: ageSort }}, | |
183 | {key:"tip",label:"${_('Tip')}",sortable:true, |
|
183 | {key:"tip",label:"${_('Tip')}",sortable:true, | |
184 | sortOptions: { sortFunction: revisionSort }}, |
|
184 | sortOptions: { sortFunction: revisionSort }}, | |
185 | {key:"owner",label:"${_('Owner')}",sortable:true}, |
|
185 | {key:"owner",label:"${_('Owner')}",sortable:true}, | |
186 | {key:"atom",label:"",sortable:false}, |
|
186 | {key:"atom",label:"",sortable:false}, | |
187 | ]; |
|
187 | ]; | |
188 |
|
188 | |||
189 | var myDataSource = new YAHOO.util.DataSource(YUD.get("repos_list")); |
|
189 | var myDataSource = new YAHOO.util.DataSource(YUD.get("repos_list")); | |
190 |
|
190 | |||
191 | myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE; |
|
191 | myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE; | |
192 |
|
192 | |||
193 | myDataSource.responseSchema = { |
|
193 | myDataSource.responseSchema = { | |
194 | fields: [ |
|
194 | fields: [ | |
195 | {key:"menu"}, |
|
195 | {key:"menu"}, | |
196 | //{key:"raw_name"}, |
|
196 | //{key:"raw_name"}, | |
197 | {key:"name"}, |
|
197 | {key:"name"}, | |
198 | {key:"desc"}, |
|
198 | {key:"desc"}, | |
199 | {key:"last_change"}, |
|
199 | {key:"last_change"}, | |
200 | {key:"tip"}, |
|
200 | {key:"tip"}, | |
201 | {key:"owner"}, |
|
201 | {key:"owner"}, | |
202 | {key:"atom"}, |
|
202 | {key:"atom"}, | |
203 | ] |
|
203 | ] | |
204 | }; |
|
204 | }; | |
205 |
|
205 | |||
206 | var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource, |
|
206 | var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource, | |
207 | { |
|
207 | { | |
208 | sortedBy:{key:"name",dir:"asc"}, |
|
208 | sortedBy:{key:"name",dir:"asc"}, | |
209 | MSG_SORTASC:"${_('Click to sort ascending')}", |
|
209 | MSG_SORTASC:"${_('Click to sort ascending')}", | |
210 | MSG_SORTDESC:"${_('Click to sort descending')}", |
|
210 | MSG_SORTDESC:"${_('Click to sort descending')}", | |
211 | MSG_EMPTY:"${_('No records found.')}", |
|
211 | MSG_EMPTY:"${_('No records found.')}", | |
212 | MSG_ERROR:"${_('Data error.')}", |
|
212 | MSG_ERROR:"${_('Data error.')}", | |
213 | MSG_LOADING:"${_('Loading...')}", |
|
213 | MSG_LOADING:"${_('Loading...')}", | |
214 | } |
|
214 | } | |
215 | ); |
|
215 | ); | |
216 | myDataTable.subscribe('postRenderEvent',function(oArgs) { |
|
216 | myDataTable.subscribe('postRenderEvent',function(oArgs) { | |
217 | tooltip_activate(); |
|
217 | tooltip_activate(); | |
218 | quick_repo_menu(); |
|
218 | quick_repo_menu(); | |
219 | var func = function(node){ |
|
219 | var func = function(node){ | |
220 | return node.parentNode.parentNode.parentNode.parentNode; |
|
220 | return node.parentNode.parentNode.parentNode.parentNode; | |
221 | } |
|
221 | } | |
222 | q_filter('q_filter',YUQ('div.table tr td a.repo_name'),func); |
|
222 | q_filter('q_filter',YUQ('div.table tr td a.repo_name'),func); | |
223 | }); |
|
223 | }); | |
224 |
|
224 | |||
225 | </script> |
|
225 | </script> | |
226 | % else: |
|
226 | % else: | |
227 | <script> |
|
227 | <script> | |
228 | var data = ${c.data|n}; |
|
228 | var data = ${c.data|n}; | |
229 | var myDataSource = new YAHOO.util.DataSource(data); |
|
229 | var myDataSource = new YAHOO.util.DataSource(data); | |
230 | myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON; |
|
230 | myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON; | |
231 |
|
231 | |||
232 | myDataSource.responseSchema = { |
|
232 | myDataSource.responseSchema = { | |
233 | resultsList: "records", |
|
233 | resultsList: "records", | |
234 | fields: [ |
|
234 | fields: [ | |
235 | {key:"menu"}, |
|
235 | {key:"menu"}, | |
236 | {key:"raw_name"}, |
|
236 | {key:"raw_name"}, | |
237 | {key:"name"}, |
|
237 | {key:"name"}, | |
238 | {key:"desc"}, |
|
238 | {key:"desc"}, | |
239 | {key:"last_change"}, |
|
239 | {key:"last_change"}, | |
240 | {key:"last_changeset"}, |
|
240 | {key:"last_changeset"}, | |
241 | {key:"owner"}, |
|
241 | {key:"owner"}, | |
242 | {key:"atom"}, |
|
242 | {key:"atom"}, | |
243 | ] |
|
243 | ] | |
244 | }; |
|
244 | }; | |
245 | myDataSource.doBeforeCallback = function(req,raw,res,cb) { |
|
245 | myDataSource.doBeforeCallback = function(req,raw,res,cb) { | |
246 | // This is the filter function |
|
246 | // This is the filter function | |
247 | var data = res.results || [], |
|
247 | var data = res.results || [], | |
248 | filtered = [], |
|
248 | filtered = [], | |
249 | i,l; |
|
249 | i,l; | |
250 |
|
250 | |||
251 | if (req) { |
|
251 | if (req) { | |
252 | req = req.toLowerCase(); |
|
252 | req = req.toLowerCase(); | |
253 | for (i = 0; i<data.length; i++) { |
|
253 | for (i = 0; i<data.length; i++) { | |
254 | var pos = data[i].raw_name.toLowerCase().indexOf(req) |
|
254 | var pos = data[i].raw_name.toLowerCase().indexOf(req) | |
255 | if (pos != -1) { |
|
255 | if (pos != -1) { | |
256 | filtered.push(data[i]); |
|
256 | filtered.push(data[i]); | |
257 | } |
|
257 | } | |
258 | } |
|
258 | } | |
259 | res.results = filtered; |
|
259 | res.results = filtered; | |
260 | } |
|
260 | } | |
261 | YUD.get('repo_count').innerHTML = res.results.length; |
|
261 | YUD.get('repo_count').innerHTML = res.results.length; | |
262 | return res; |
|
262 | return res; | |
263 | } |
|
263 | } | |
264 |
|
264 | |||
265 | // main table sorting |
|
265 | // main table sorting | |
266 | var myColumnDefs = [ |
|
266 | var myColumnDefs = [ | |
267 | {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"}, |
|
267 | {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"}, | |
268 | {key:"name",label:"${_('Name')}",sortable:true, |
|
268 | {key:"name",label:"${_('Name')}",sortable:true, | |
269 | sortOptions: { sortFunction: nameSort }}, |
|
269 | sortOptions: { sortFunction: nameSort }}, | |
270 | {key:"desc",label:"${_('Description')}",sortable:true}, |
|
270 | {key:"desc",label:"${_('Description')}",sortable:true}, | |
271 | {key:"last_change",label:"${_('Last Change')}",sortable:true, |
|
271 | {key:"last_change",label:"${_('Last Change')}",sortable:true, | |
272 | sortOptions: { sortFunction: ageSort }}, |
|
272 | sortOptions: { sortFunction: ageSort }}, | |
273 | {key:"last_changeset",label:"${_('Tip')}",sortable:true, |
|
273 | {key:"last_changeset",label:"${_('Tip')}",sortable:true, | |
274 | sortOptions: { sortFunction: revisionSort }}, |
|
274 | sortOptions: { sortFunction: revisionSort }}, | |
275 | {key:"owner",label:"${_('Owner')}",sortable:true}, |
|
275 | {key:"owner",label:"${_('Owner')}",sortable:true}, | |
276 | {key:"atom",label:"",sortable:false}, |
|
276 | {key:"atom",label:"",sortable:false}, | |
277 | ]; |
|
277 | ]; | |
278 |
|
278 | |||
279 | var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,{ |
|
279 | var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,{ | |
280 | sortedBy:{key:"name",dir:"asc"}, |
|
280 | sortedBy:{key:"name",dir:"asc"}, | |
281 | paginator: new YAHOO.widget.Paginator({ |
|
281 | paginator: new YAHOO.widget.Paginator({ | |
282 | rowsPerPage: ${c.visual.lightweight_dashboard_items}, |
|
282 | rowsPerPage: ${c.visual.lightweight_dashboard_items}, | |
283 | alwaysVisible: false, |
|
283 | alwaysVisible: false, | |
284 | template : "{PreviousPageLink} {FirstPageLink} {PageLinks} {LastPageLink} {NextPageLink}", |
|
284 | template : "{PreviousPageLink} {FirstPageLink} {PageLinks} {LastPageLink} {NextPageLink}", | |
285 | pageLinks: 5, |
|
285 | pageLinks: 5, | |
286 | containerClass: 'pagination-wh', |
|
286 | containerClass: 'pagination-wh', | |
287 | currentPageClass: 'pager_curpage', |
|
287 | currentPageClass: 'pager_curpage', | |
288 | pageLinkClass: 'pager_link', |
|
288 | pageLinkClass: 'pager_link', | |
289 | nextPageLinkLabel: '>', |
|
289 | nextPageLinkLabel: '>', | |
290 | previousPageLinkLabel: '<', |
|
290 | previousPageLinkLabel: '<', | |
291 | firstPageLinkLabel: '<<', |
|
291 | firstPageLinkLabel: '<<', | |
292 | lastPageLinkLabel: '>>', |
|
292 | lastPageLinkLabel: '>>', | |
293 | containers:['user-paginator'] |
|
293 | containers:['user-paginator'] | |
294 | }), |
|
294 | }), | |
295 |
|
295 | |||
296 | MSG_SORTASC:"${_('Click to sort ascending')}", |
|
296 | MSG_SORTASC:"${_('Click to sort ascending')}", | |
297 | MSG_SORTDESC:"${_('Click to sort descending')}", |
|
297 | MSG_SORTDESC:"${_('Click to sort descending')}", | |
298 | MSG_EMPTY:"${_('No records found.')}", |
|
298 | MSG_EMPTY:"${_('No records found.')}", | |
299 | MSG_ERROR:"${_('Data error.')}", |
|
299 | MSG_ERROR:"${_('Data error.')}", | |
300 | MSG_LOADING:"${_('Loading...')}", |
|
300 | MSG_LOADING:"${_('Loading...')}", | |
301 | } |
|
301 | } | |
302 | ); |
|
302 | ); | |
303 | myDataTable.subscribe('postRenderEvent',function(oArgs) { |
|
303 | myDataTable.subscribe('postRenderEvent',function(oArgs) { | |
304 | tooltip_activate(); |
|
304 | tooltip_activate(); | |
305 | quick_repo_menu(); |
|
305 | quick_repo_menu(); | |
306 | }); |
|
306 | }); | |
307 |
|
307 | |||
308 | var filterTimeout = null; |
|
308 | var filterTimeout = null; | |
309 |
|
309 | |||
310 | updateFilter = function () { |
|
310 | updateFilter = function () { | |
311 | // Reset timeout |
|
311 | // Reset timeout | |
312 | filterTimeout = null; |
|
312 | filterTimeout = null; | |
313 |
|
313 | |||
314 | // Reset sort |
|
314 | // Reset sort | |
315 | var state = myDataTable.getState(); |
|
315 | var state = myDataTable.getState(); | |
316 | state.sortedBy = {key:'name', dir:YAHOO.widget.DataTable.CLASS_ASC}; |
|
316 | state.sortedBy = {key:'name', dir:YAHOO.widget.DataTable.CLASS_ASC}; | |
317 |
|
317 | |||
318 | // Get filtered data |
|
318 | // Get filtered data | |
319 | myDataSource.sendRequest(YUD.get('q_filter').value,{ |
|
319 | myDataSource.sendRequest(YUD.get('q_filter').value,{ | |
320 | success : myDataTable.onDataReturnInitializeTable, |
|
320 | success : myDataTable.onDataReturnInitializeTable, | |
321 | failure : myDataTable.onDataReturnInitializeTable, |
|
321 | failure : myDataTable.onDataReturnInitializeTable, | |
322 | scope : myDataTable, |
|
322 | scope : myDataTable, | |
323 | argument: state |
|
323 | argument: state | |
324 | }); |
|
324 | }); | |
325 |
|
325 | |||
326 | }; |
|
326 | }; | |
327 | YUE.on('q_filter','click',function(){ |
|
327 | YUE.on('q_filter','click',function(){ | |
328 | if(!YUD.hasClass('q_filter', 'loaded')){ |
|
328 | if(!YUD.hasClass('q_filter', 'loaded')){ | |
329 | YUD.get('q_filter').value = ''; |
|
329 | YUD.get('q_filter').value = ''; | |
330 | //TODO: load here full list later to do search within groups |
|
330 | //TODO: load here full list later to do search within groups | |
331 | YUD.addClass('q_filter', 'loaded'); |
|
331 | YUD.addClass('q_filter', 'loaded'); | |
332 | } |
|
332 | } | |
333 | }); |
|
333 | }); | |
334 |
|
334 | |||
335 | YUE.on('q_filter','keyup',function (e) { |
|
335 | YUE.on('q_filter','keyup',function (e) { | |
336 | clearTimeout(filterTimeout); |
|
336 | clearTimeout(filterTimeout); | |
337 | filterTimeout = setTimeout(updateFilter,600); |
|
337 | filterTimeout = setTimeout(updateFilter,600); | |
338 | }); |
|
338 | }); | |
339 | </script> |
|
339 | </script> | |
340 | % endif |
|
340 | % endif |
@@ -1,243 +1,243 | |||||
1 | <%inherit file="/base/base.html"/> |
|
1 | <%inherit file="/base/base.html"/> | |
2 |
|
2 | |||
3 | <%def name="title()"> |
|
3 | <%def name="title()"> | |
4 | ${c.repo_name} ${_('Pull request #%s') % c.pull_request.pull_request_id} |
|
4 | ${c.repo_name} ${_('Pull request #%s') % c.pull_request.pull_request_id} | |
5 | </%def> |
|
5 | </%def> | |
6 |
|
6 | |||
7 | <%def name="breadcrumbs_links()"> |
|
7 | <%def name="breadcrumbs_links()"> | |
8 | ${h.link_to(_(u'Home'),h.url('/'))} |
|
8 | ${h.link_to(_(u'Home'),h.url('/'))} | |
9 | » |
|
9 | » | |
10 | ${h.link_to(c.repo_name,h.url('changelog_home',repo_name=c.repo_name))} |
|
10 | ${h.link_to(c.repo_name,h.url('changelog_home',repo_name=c.repo_name))} | |
11 | » |
|
11 | » | |
12 | ${_('Pull request #%s') % c.pull_request.pull_request_id} |
|
12 | ${_('Pull request #%s') % c.pull_request.pull_request_id} | |
13 | </%def> |
|
13 | </%def> | |
14 |
|
14 | |||
15 | <%def name="main()"> |
|
15 | <%def name="main()"> | |
16 |
|
16 | |||
17 | <div class="box"> |
|
17 | <div class="box"> | |
18 | <!-- box / title --> |
|
18 | <!-- box / title --> | |
19 | <div class="title"> |
|
19 | <div class="title"> | |
20 | ${self.breadcrumbs()} |
|
20 | ${self.breadcrumbs()} | |
21 | </div> |
|
21 | </div> | |
22 | %if c.pull_request.is_closed(): |
|
22 | %if c.pull_request.is_closed(): | |
23 | <div style="padding:10px; font-size:22px;width:100%;text-align: center; color:#88D882">${_('Closed %s') % (h.age(c.pull_request.updated_on))} ${_('with status %s') % h.changeset_status_lbl(c.current_changeset_status)}</div> |
|
23 | <div style="padding:10px; font-size:22px;width:100%;text-align: center; color:#88D882">${_('Closed %s') % (h.age(c.pull_request.updated_on))} ${_('with status %s') % h.changeset_status_lbl(c.current_changeset_status)}</div> | |
24 | %endif |
|
24 | %endif | |
25 | <h3>${_('Title')}: ${c.pull_request.title}</h3> |
|
25 | <h3>${_('Title')}: ${c.pull_request.title}</h3> | |
26 |
|
26 | |||
27 | <div class="form"> |
|
27 | <div class="form"> | |
28 | <div id="summary" class="fields"> |
|
28 | <div id="summary" class="fields"> | |
29 | <div class="field"> |
|
29 | <div class="field"> | |
30 | <div class="label-summary"> |
|
30 | <div class="label-summary"> | |
31 | <label>${_('Review status')}:</label> |
|
31 | <label>${_('Review status')}:</label> | |
32 | </div> |
|
32 | </div> | |
33 | <div class="input"> |
|
33 | <div class="input"> | |
34 | <div class="changeset-status-container" style="float:none;clear:both"> |
|
34 | <div class="changeset-status-container" style="float:none;clear:both"> | |
35 | %if c.current_changeset_status: |
|
35 | %if c.current_changeset_status: | |
36 | <div title="${_('Pull request status')}" class="changeset-status-lbl">${h.changeset_status_lbl(c.current_changeset_status)}</div> |
|
36 | <div title="${_('Pull request status')}" class="changeset-status-lbl">${h.changeset_status_lbl(c.current_changeset_status)}</div> | |
37 | <div class="changeset-status-ico" style="padding:1px 4px"><img src="${h.url('/images/icons/flag_status_%s.png' % c.current_changeset_status)}" /></div> |
|
37 | <div class="changeset-status-ico" style="padding:1px 4px"><img src="${h.url('/images/icons/flag_status_%s.png' % c.current_changeset_status)}" /></div> | |
38 | %endif |
|
38 | %endif | |
39 | </div> |
|
39 | </div> | |
40 | </div> |
|
40 | </div> | |
41 | </div> |
|
41 | </div> | |
42 | <div class="field"> |
|
42 | <div class="field"> | |
43 | <div class="label-summary"> |
|
43 | <div class="label-summary"> | |
44 | <label>${_('Still not reviewed by')}:</label> |
|
44 | <label>${_('Still not reviewed by')}:</label> | |
45 | </div> |
|
45 | </div> | |
46 | <div class="input"> |
|
46 | <div class="input"> | |
47 | % if len(c.pull_request_pending_reviewers) > 0: |
|
47 | % if len(c.pull_request_pending_reviewers) > 0: | |
48 | <div class="tooltip" title="${h.tooltip(','.join([x.username for x in c.pull_request_pending_reviewers]))}">${ungettext('%d reviewer', '%d reviewers',len(c.pull_request_pending_reviewers)) % len(c.pull_request_pending_reviewers)}</div> |
|
48 | <div class="tooltip" title="${h.tooltip(','.join([x.username for x in c.pull_request_pending_reviewers]))}">${ungettext('%d reviewer', '%d reviewers',len(c.pull_request_pending_reviewers)) % len(c.pull_request_pending_reviewers)}</div> | |
49 | %else: |
|
49 | %else: | |
50 | <div>${_('pull request was reviewed by all reviewers')}</div> |
|
50 | <div>${_('pull request was reviewed by all reviewers')}</div> | |
51 | %endif |
|
51 | %endif | |
52 | </div> |
|
52 | </div> | |
53 | </div> |
|
53 | </div> | |
54 | <div class="field"> |
|
54 | <div class="field"> | |
55 | <div class="label-summary"> |
|
55 | <div class="label-summary"> | |
56 | <label>${_('Origin repository')}:</label> |
|
56 | <label>${_('Origin repository')}:</label> | |
57 | </div> |
|
57 | </div> | |
58 | <div class="input"> |
|
58 | <div class="input"> | |
59 | <div> |
|
59 | <div> | |
60 | ##%if h.is_hg(c.pull_request.org_repo): |
|
60 | ##%if h.is_hg(c.pull_request.org_repo): | |
61 | ## <img class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url('/images/icons/hgicon.png')}"/> |
|
61 | ## <img class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url('/images/icons/hgicon.png')}"/> | |
62 | ##%elif h.is_git(c.pull_request.org_repo): |
|
62 | ##%elif h.is_git(c.pull_request.org_repo): | |
63 | ## <img class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url('/images/icons/giticon.png')}"/> |
|
63 | ## <img class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url('/images/icons/giticon.png')}"/> | |
64 | ##%endif |
|
64 | ##%endif | |
65 | <span class="spantag">${c.pull_request.org_ref_parts[0]}: ${c.pull_request.org_ref_parts[1]}</span> |
|
65 | <span class="spantag">${c.pull_request.org_ref_parts[0]}: ${c.pull_request.org_ref_parts[1]}</span> | |
66 | <span><a href="${h.url('summary_home', repo_name=c.pull_request.org_repo.repo_name)}">${c.pull_request.org_repo.clone_url()}</a></span> |
|
66 | <span><a href="${h.url('summary_home', repo_name=c.pull_request.org_repo.repo_name)}">${c.pull_request.org_repo.clone_url()}</a></span> | |
67 | </div> |
|
67 | </div> | |
68 | </div> |
|
68 | </div> | |
69 | </div> |
|
69 | </div> | |
70 | <div class="field"> |
|
70 | <div class="field"> | |
71 | <div class="label-summary"> |
|
71 | <div class="label-summary"> | |
72 | <label>${_('Summary')}:</label> |
|
72 | <label>${_('Summary')}:</label> | |
73 | </div> |
|
73 | </div> | |
74 | <div class="input"> |
|
74 | <div class="input"> | |
75 | <div style="white-space:pre-wrap">${h.literal(c.pull_request.description)}</div> |
|
75 | <div style="white-space:pre-wrap">${h.literal(c.pull_request.description)}</div> | |
76 | </div> |
|
76 | </div> | |
77 | </div> |
|
77 | </div> | |
78 | <div class="field"> |
|
78 | <div class="field"> | |
79 | <div class="label-summary"> |
|
79 | <div class="label-summary"> | |
80 | <label>${_('Created on')}:</label> |
|
80 | <label>${_('Created on')}:</label> | |
81 | </div> |
|
81 | </div> | |
82 | <div class="input"> |
|
82 | <div class="input"> | |
83 | <div>${h.fmt_date(c.pull_request.created_on)}</div> |
|
83 | <div>${h.fmt_date(c.pull_request.created_on)}</div> | |
84 | </div> |
|
84 | </div> | |
85 | </div> |
|
85 | </div> | |
86 | </div> |
|
86 | </div> | |
87 | </div> |
|
87 | </div> | |
88 |
|
88 | |||
89 | <div style="overflow: auto;"> |
|
89 | <div style="overflow: auto;"> | |
90 | ##DIFF |
|
90 | ##DIFF | |
91 | <div class="table" style="float:left;clear:none"> |
|
91 | <div class="table" style="float:left;clear:none"> | |
92 | <div id="body" class="diffblock"> |
|
92 | <div id="body" class="diffblock"> | |
93 | <div style="white-space:pre-wrap;padding:5px">${_('Compare view')}</div> |
|
93 | <div style="white-space:pre-wrap;padding:5px">${_('Compare view')}</div> | |
94 | </div> |
|
94 | </div> | |
95 | <div id="changeset_compare_view_content"> |
|
95 | <div id="changeset_compare_view_content"> | |
96 | ##CS |
|
96 | ##CS | |
97 | <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${ungettext('Showing %s commit','Showing %s commits', len(c.cs_ranges)) % len(c.cs_ranges)}</div> |
|
97 | <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${ungettext('Showing %s commit','Showing %s commits', len(c.cs_ranges)) % len(c.cs_ranges)}</div> | |
98 | <%include file="/compare/compare_cs.html" /> |
|
98 | <%include file="/compare/compare_cs.html" /> | |
99 |
|
99 | |||
100 | ## FILES |
|
100 | ## FILES | |
101 | <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px"> |
|
101 | <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px"> | |
102 |
|
102 | |||
103 | % if c.limited_diff: |
|
103 | % if c.limited_diff: | |
104 | ${ungettext('%s file changed', '%s files changed', len(c.files)) % len(c.files)} |
|
104 | ${ungettext('%s file changed', '%s files changed', len(c.files)) % len(c.files)} | |
105 | % else: |
|
105 | % else: | |
106 | ${ungettext('%s file changed with %s insertions and %s deletions','%s files changed with %s insertions and %s deletions', len(c.files)) % (len(c.files),c.lines_added,c.lines_deleted)}: |
|
106 | ${ungettext('%s file changed with %s insertions and %s deletions','%s files changed with %s insertions and %s deletions', len(c.files)) % (len(c.files),c.lines_added,c.lines_deleted)}: | |
107 | %endif |
|
107 | %endif | |
108 |
|
108 | |||
109 | </div> |
|
109 | </div> | |
110 | <div class="cs_files"> |
|
110 | <div class="cs_files"> | |
111 | %if not c.files: |
|
111 | %if not c.files: | |
112 | <span class="empty_data">${_('No files')}</span> |
|
112 | <span class="empty_data">${_('No files')}</span> | |
113 | %endif |
|
113 | %endif | |
114 | %for fid, change, f, stat in c.files: |
|
114 | %for fid, change, f, stat in c.files: | |
115 | <div class="cs_${change}"> |
|
115 | <div class="cs_${change}"> | |
116 | <div class="node">${h.link_to(h.safe_unicode(f),h.url.current(anchor=fid))}</div> |
|
116 | <div class="node">${h.link_to(h.safe_unicode(f),h.url.current(anchor=fid))}</div> | |
117 | <div class="changes">${h.fancy_file_stats(stat)}</div> |
|
117 | <div class="changes">${h.fancy_file_stats(stat)}</div> | |
118 | </div> |
|
118 | </div> | |
119 | %endfor |
|
119 | %endfor | |
120 | </div> |
|
120 | </div> | |
121 | % if c.limited_diff: |
|
121 | % if c.limited_diff: | |
122 | <h5>${_('Changeset was too big and was cut off...')} <a href="${h.url.current(fulldiff=1, **request.GET.mixed())}" onclick="return confirm('${_("confirm to show potentially huge diff")}')">${_('Show full diff')}</a></h5> |
|
122 | <h5>${_('Changeset was too big and was cut off...')} <a href="${h.url.current(fulldiff=1, **request.GET.mixed())}" onclick="return confirm('${_("confirm to show potentially huge diff")}')">${_('Show full diff')}</a></h5> | |
123 | % endif |
|
123 | % endif | |
124 | </div> |
|
124 | </div> | |
125 | </div> |
|
125 | </div> | |
126 | ## REVIEWERS |
|
126 | ## REVIEWERS | |
127 | <div style="float:left; border-left:1px dashed #eee"> |
|
127 | <div style="float:left; border-left:1px dashed #eee"> | |
128 | <h4>${_('Pull request reviewers')}</h4> |
|
128 | <h4>${_('Pull request reviewers')}</h4> | |
129 | <div id="reviewers" style="padding:0px 0px 5px 10px"> |
|
129 | <div id="reviewers" style="padding:0px 0px 5px 10px"> | |
130 | ## members goes here ! |
|
130 | ## members goes here ! | |
131 | <div class="group_members_wrap" style="min-height:45px"> |
|
131 | <div class="group_members_wrap" style="min-height:45px"> | |
132 | <ul id="review_members" class="group_members"> |
|
132 | <ul id="review_members" class="group_members"> | |
133 | %for member,status in c.pull_request_reviewers: |
|
133 | %for member,status in c.pull_request_reviewers: | |
134 | <li id="reviewer_${member.user_id}"> |
|
134 | <li id="reviewer_${member.user_id}"> | |
135 | <div class="reviewers_member"> |
|
135 | <div class="reviewers_member"> | |
136 | <div style="float:left;padding:0px 3px 0px 0px" class="tooltip" title="${h.tooltip(h.changeset_status_lbl(status[0][1].status if status else 'not_reviewed'))}"> |
|
136 | <div style="float:left;padding:0px 3px 0px 0px" class="tooltip" title="${h.tooltip(h.changeset_status_lbl(status[0][1].status if status else 'not_reviewed'))}"> | |
137 | <img src="${h.url(str('/images/icons/flag_status_%s.png' % (status[0][1].status if status else 'not_reviewed')))}"/> |
|
137 | <img src="${h.url(str('/images/icons/flag_status_%s.png' % (status[0][1].status if status else 'not_reviewed')))}"/> | |
138 | </div> |
|
138 | </div> | |
139 | <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(member.email,14)}"/> </div> |
|
139 | <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(member.email,14)}"/> </div> | |
140 | <div style="float:left">${member.full_name} (${_('owner') if c.pull_request.user_id == member.user_id else _('reviewer')})</div> |
|
140 | <div style="float:left">${member.full_name} (${_('owner') if c.pull_request.user_id == member.user_id else _('reviewer')})</div> | |
141 | <input type="hidden" value="${member.user_id}" name="review_members" /> |
|
141 | <input type="hidden" value="${member.user_id}" name="review_members" /> | |
142 | %if not c.pull_request.is_closed() and (h.HasPermissionAny('hg.admin', 'repository.admin')() or c.pull_request.user_id == c.rhodecode_user.user_id): |
|
142 | %if not c.pull_request.is_closed() and (h.HasPermissionAny('hg.admin', 'repository.admin')() or c.pull_request.user_id == c.rhodecode_user.user_id): | |
143 | <span class="delete_icon action_button" onclick="removeReviewMember(${member.user_id})"></span> |
|
143 | <span class="delete_icon action_button" onclick="removeReviewMember(${member.user_id})"></span> | |
144 | %endif |
|
144 | %endif | |
145 | </div> |
|
145 | </div> | |
146 | </li> |
|
146 | </li> | |
147 | %endfor |
|
147 | %endfor | |
148 | </ul> |
|
148 | </ul> | |
149 | </div> |
|
149 | </div> | |
150 | %if not c.pull_request.is_closed(): |
|
150 | %if not c.pull_request.is_closed(): | |
151 | <div class='ac'> |
|
151 | <div class='ac'> | |
152 | %if h.HasPermissionAny('hg.admin', 'repository.admin')() or c.pull_request.author.user_id == c.rhodecode_user.user_id: |
|
152 | %if h.HasPermissionAny('hg.admin', 'repository.admin')() or c.pull_request.author.user_id == c.rhodecode_user.user_id: | |
153 | <div class="reviewer_ac"> |
|
153 | <div class="reviewer_ac"> | |
154 | ${h.text('user', class_='yui-ac-input')} |
|
154 | ${h.text('user', class_='yui-ac-input')} | |
155 | <span class="help-block">${_('Add or remove reviewer to this pull request.')}</span> |
|
155 | <span class="help-block">${_('Add or remove reviewer to this pull request.')}</span> | |
156 | <div id="reviewers_container"></div> |
|
156 | <div id="reviewers_container"></div> | |
157 | </div> |
|
157 | </div> | |
158 | <div style="padding:0px 10px"> |
|
158 | <div style="padding:0px 10px"> | |
159 | <span id="update_pull_request" class="ui-btn xsmall">${_('save changes')}</span> |
|
159 | <span id="update_pull_request" class="ui-btn xsmall">${_('save changes')}</span> | |
160 | </div> |
|
160 | </div> | |
161 | %endif |
|
161 | %endif | |
162 | </div> |
|
162 | </div> | |
163 | %endif |
|
163 | %endif | |
164 | </div> |
|
164 | </div> | |
165 | </div> |
|
165 | </div> | |
166 | </div> |
|
166 | </div> | |
167 | <script> |
|
167 | <script> | |
168 | var _USERS_AC_DATA = ${c.users_array|n}; |
|
168 | var _USERS_AC_DATA = ${c.users_array|n}; | |
169 | var _GROUPS_AC_DATA = ${c.users_groups_array|n}; |
|
169 | var _GROUPS_AC_DATA = ${c.users_groups_array|n}; | |
170 | // TODO: switch this to pyroutes |
|
170 | // TODO: switch this to pyroutes | |
171 | AJAX_COMMENT_URL = "${url('pullrequest_comment',repo_name=c.repo_name,pull_request_id=c.pull_request.pull_request_id)}"; |
|
171 | AJAX_COMMENT_URL = "${url('pullrequest_comment',repo_name=c.repo_name,pull_request_id=c.pull_request.pull_request_id)}"; | |
172 | AJAX_COMMENT_DELETE_URL = "${url('pullrequest_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}"; |
|
172 | AJAX_COMMENT_DELETE_URL = "${url('pullrequest_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}"; | |
173 |
|
173 | |||
174 | pyroutes.register('pullrequest_comment', "${url('pullrequest_comment',repo_name='%(repo_name)s',pull_request_id='%(pull_request_id)s')}", ['repo_name', 'pull_request_id']); |
|
174 | pyroutes.register('pullrequest_comment', "${url('pullrequest_comment',repo_name='%(repo_name)s',pull_request_id='%(pull_request_id)s')}", ['repo_name', 'pull_request_id']); | |
175 | pyroutes.register('pullrequest_comment_delete', "${url('pullrequest_comment_delete',repo_name='%(repo_name)s',comment_id='%(comment_id)s')}", ['repo_name', 'comment_id']); |
|
175 | pyroutes.register('pullrequest_comment_delete', "${url('pullrequest_comment_delete',repo_name='%(repo_name)s',comment_id='%(comment_id)s')}", ['repo_name', 'comment_id']); | |
176 | pyroutes.register('pullrequest_update', "${url('pullrequest_update',repo_name='%(repo_name)s',pull_request_id='%(pull_request_id)s')}", ['repo_name', 'pull_request_id']); |
|
176 | pyroutes.register('pullrequest_update', "${url('pullrequest_update',repo_name='%(repo_name)s',pull_request_id='%(pull_request_id)s')}", ['repo_name', 'pull_request_id']); | |
177 |
|
177 | |||
178 | </script> |
|
178 | </script> | |
179 |
|
179 | |||
180 | ## diff block |
|
180 | ## diff block | |
181 | <%namespace name="diff_block" file="/changeset/diff_block.html"/> |
|
181 | <%namespace name="diff_block" file="/changeset/diff_block.html"/> | |
182 | %for fid, change, f, stat in c.files: |
|
182 | %for fid, change, f, stat in c.files: | |
183 | ${diff_block.diff_block_simple([c.changes[fid]])} |
|
183 | ${diff_block.diff_block_simple([c.changes[fid]])} | |
184 | %endfor |
|
184 | %endfor | |
185 | % if c.limited_diff: |
|
185 | % if c.limited_diff: | |
186 | <h4>${_('Changeset was too big and was cut off...')} <a href="${h.url.current(fulldiff=1, **request.GET.mixed())}" onclick="return confirm('${_("confirm to show potentially huge diff")}')">${_('Show full diff')}</a></h4> |
|
186 | <h4>${_('Changeset was too big and was cut off...')} <a href="${h.url.current(fulldiff=1, **request.GET.mixed())}" onclick="return confirm('${_("confirm to show potentially huge diff")}')">${_('Show full diff')}</a></h4> | |
187 | % endif |
|
187 | % endif | |
188 |
|
188 | |||
189 |
|
189 | |||
190 | ## template for inline comment form |
|
190 | ## template for inline comment form | |
191 | <%namespace name="comment" file="/changeset/changeset_file_comment.html"/> |
|
191 | <%namespace name="comment" file="/changeset/changeset_file_comment.html"/> | |
192 | ${comment.comment_inline_form()} |
|
192 | ${comment.comment_inline_form()} | |
193 |
|
193 | |||
194 | ## render comments and inlines |
|
194 | ## render comments and inlines | |
195 | ${comment.generate_comments(include_pr=True)} |
|
195 | ${comment.generate_comments(include_pr=True)} | |
196 |
|
196 | |||
197 | % if not c.pull_request.is_closed(): |
|
197 | % if not c.pull_request.is_closed(): | |
198 | ## main comment form and it status |
|
198 | ## main comment form and it status | |
199 | ${comment.comments(h.url('pullrequest_comment', repo_name=c.repo_name, |
|
199 | ${comment.comments(h.url('pullrequest_comment', repo_name=c.repo_name, | |
200 | pull_request_id=c.pull_request.pull_request_id), |
|
200 | pull_request_id=c.pull_request.pull_request_id), | |
201 | c.current_changeset_status, |
|
201 | c.current_changeset_status, | |
202 | close_btn=True, change_status=c.allowed_to_change_status)} |
|
202 | close_btn=True, change_status=c.allowed_to_change_status)} | |
203 | %endif |
|
203 | %endif | |
204 |
|
204 | |||
205 | <script type="text/javascript"> |
|
205 | <script type="text/javascript"> | |
206 | YUE.onDOMReady(function(){ |
|
206 | YUE.onDOMReady(function(){ | |
207 | PullRequestAutoComplete('user', 'reviewers_container', _USERS_AC_DATA, _GROUPS_AC_DATA); |
|
207 | PullRequestAutoComplete('user', 'reviewers_container', _USERS_AC_DATA, _GROUPS_AC_DATA); | |
208 |
|
208 | |||
209 | YUE.on(YUQ('.show-inline-comments'),'change',function(e){ |
|
209 | YUE.on(YUQ('.show-inline-comments'),'change',function(e){ | |
210 | var show = 'none'; |
|
210 | var show = 'none'; | |
211 | var target = e.currentTarget; |
|
211 | var target = e.currentTarget; | |
212 | if(target.checked){ |
|
212 | if(target.checked){ | |
213 | var show = '' |
|
213 | var show = '' | |
214 | } |
|
214 | } | |
215 | var boxid = YUD.getAttribute(target,'id_for'); |
|
215 | var boxid = YUD.getAttribute(target,'id_for'); | |
216 | var comments = YUQ('#{0} .inline-comments'.format(boxid)); |
|
216 | var comments = YUQ('#{0} .inline-comments'.format(boxid)); | |
217 | for(c in comments){ |
|
217 | for(c in comments){ | |
218 | YUD.setStyle(comments[c],'display',show); |
|
218 | YUD.setStyle(comments[c],'display',show); | |
219 | } |
|
219 | } | |
220 | var btns = YUQ('#{0} .inline-comments-button'.format(boxid)); |
|
220 | var btns = YUQ('#{0} .inline-comments-button'.format(boxid)); | |
221 | for(c in btns){ |
|
221 | for(c in btns){ | |
222 | YUD.setStyle(btns[c],'display',show); |
|
222 | YUD.setStyle(btns[c],'display',show); | |
223 | } |
|
223 | } | |
224 | }) |
|
224 | }) | |
225 |
|
225 | |||
226 | YUE.on(YUQ('.line'),'click',function(e){ |
|
226 | YUE.on(YUQ('.line'),'click',function(e){ | |
227 | var tr = e.currentTarget; |
|
227 | var tr = e.currentTarget; | |
228 | injectInlineForm(tr); |
|
228 | injectInlineForm(tr); | |
229 | }); |
|
229 | }); | |
230 |
|
230 | |||
231 | // inject comments into they proper positions |
|
231 | // inject comments into they proper positions | |
232 | var file_comments = YUQ('.inline-comment-placeholder'); |
|
232 | var file_comments = YUQ('.inline-comment-placeholder'); | |
233 | renderInlineComments(file_comments); |
|
233 | renderInlineComments(file_comments); | |
234 |
|
234 | |||
235 | YUE.on(YUD.get('update_pull_request'),'click',function(e){ |
|
235 | YUE.on(YUD.get('update_pull_request'),'click',function(e){ | |
236 | updateReviewers(undefined, "${c.repo_name}", "${c.pull_request.pull_request_id}"); |
|
236 | updateReviewers(undefined, "${c.repo_name}", "${c.pull_request.pull_request_id}"); | |
237 | }) |
|
237 | }) | |
238 | }) |
|
238 | }) | |
239 | </script> |
|
239 | </script> | |
240 |
|
240 | |||
241 | </div> |
|
241 | </div> | |
242 |
|
242 | |||
243 | </%def> |
|
243 | </%def> |
@@ -1,254 +1,254 | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """ |
|
2 | """ | |
3 | rhodecode.tests.test_libs |
|
3 | rhodecode.tests.test_libs | |
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~ | |
5 |
|
5 | |||
6 |
|
6 | |||
7 | Package for testing various lib/helper functions in rhodecode |
|
7 | Package for testing various lib/helper functions in rhodecode | |
8 |
|
8 | |||
9 | :created_on: Jun 9, 2011 |
|
9 | :created_on: Jun 9, 2011 | |
10 | :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com> |
|
10 | :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com> | |
11 | :license: GPLv3, see COPYING for more details. |
|
11 | :license: GPLv3, see COPYING for more details. | |
12 | """ |
|
12 | """ | |
13 | # This program is free software: you can redistribute it and/or modify |
|
13 | # This program is free software: you can redistribute it and/or modify | |
14 | # it under the terms of the GNU General Public License as published by |
|
14 | # it under the terms of the GNU General Public License as published by | |
15 | # the Free Software Foundation, either version 3 of the License, or |
|
15 | # the Free Software Foundation, either version 3 of the License, or | |
16 | # (at your option) any later version. |
|
16 | # (at your option) any later version. | |
17 | # |
|
17 | # | |
18 | # This program is distributed in the hope that it will be useful, |
|
18 | # This program is distributed in the hope that it will be useful, | |
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 | # GNU General Public License for more details. |
|
21 | # GNU General Public License for more details. | |
22 | # |
|
22 | # | |
23 | # You should have received a copy of the GNU General Public License |
|
23 | # You should have received a copy of the GNU General Public License | |
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
25 | from __future__ import with_statement |
|
25 | from __future__ import with_statement | |
26 | import unittest |
|
26 | import unittest | |
27 | import datetime |
|
27 | import datetime | |
28 | import hashlib |
|
28 | import hashlib | |
29 | import mock |
|
29 | import mock | |
30 | from rhodecode.tests import * |
|
30 | from rhodecode.tests import * | |
31 |
|
31 | |||
32 | proto = 'http' |
|
32 | proto = 'http' | |
33 | TEST_URLS = [ |
|
33 | TEST_URLS = [ | |
34 | ('%s://127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'], |
|
34 | ('%s://127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'], | |
35 | '%s://127.0.0.1' % proto), |
|
35 | '%s://127.0.0.1' % proto), | |
36 | ('%s://marcink@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'], |
|
36 | ('%s://marcink@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'], | |
37 | '%s://127.0.0.1' % proto), |
|
37 | '%s://127.0.0.1' % proto), | |
38 | ('%s://marcink:pass@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'], |
|
38 | ('%s://marcink:pass@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'], | |
39 | '%s://127.0.0.1' % proto), |
|
39 | '%s://127.0.0.1' % proto), | |
40 | ('%s://127.0.0.1:8080' % proto, ['%s://' % proto, '127.0.0.1', '8080'], |
|
40 | ('%s://127.0.0.1:8080' % proto, ['%s://' % proto, '127.0.0.1', '8080'], | |
41 | '%s://127.0.0.1:8080' % proto), |
|
41 | '%s://127.0.0.1:8080' % proto), | |
42 | ('%s://domain.org' % proto, ['%s://' % proto, 'domain.org'], |
|
42 | ('%s://domain.org' % proto, ['%s://' % proto, 'domain.org'], | |
43 | '%s://domain.org' % proto), |
|
43 | '%s://domain.org' % proto), | |
44 | ('%s://user:pass@domain.org:8080' % proto, ['%s://' % proto, 'domain.org', |
|
44 | ('%s://user:pass@domain.org:8080' % proto, ['%s://' % proto, 'domain.org', | |
45 | '8080'], |
|
45 | '8080'], | |
46 | '%s://domain.org:8080' % proto), |
|
46 | '%s://domain.org:8080' % proto), | |
47 | ] |
|
47 | ] | |
48 |
|
48 | |||
49 | proto = 'https' |
|
49 | proto = 'https' | |
50 | TEST_URLS += [ |
|
50 | TEST_URLS += [ | |
51 | ('%s://127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'], |
|
51 | ('%s://127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'], | |
52 | '%s://127.0.0.1' % proto), |
|
52 | '%s://127.0.0.1' % proto), | |
53 | ('%s://marcink@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'], |
|
53 | ('%s://marcink@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'], | |
54 | '%s://127.0.0.1' % proto), |
|
54 | '%s://127.0.0.1' % proto), | |
55 | ('%s://marcink:pass@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'], |
|
55 | ('%s://marcink:pass@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'], | |
56 | '%s://127.0.0.1' % proto), |
|
56 | '%s://127.0.0.1' % proto), | |
57 | ('%s://127.0.0.1:8080' % proto, ['%s://' % proto, '127.0.0.1', '8080'], |
|
57 | ('%s://127.0.0.1:8080' % proto, ['%s://' % proto, '127.0.0.1', '8080'], | |
58 | '%s://127.0.0.1:8080' % proto), |
|
58 | '%s://127.0.0.1:8080' % proto), | |
59 | ('%s://domain.org' % proto, ['%s://' % proto, 'domain.org'], |
|
59 | ('%s://domain.org' % proto, ['%s://' % proto, 'domain.org'], | |
60 | '%s://domain.org' % proto), |
|
60 | '%s://domain.org' % proto), | |
61 | ('%s://user:pass@domain.org:8080' % proto, ['%s://' % proto, 'domain.org', |
|
61 | ('%s://user:pass@domain.org:8080' % proto, ['%s://' % proto, 'domain.org', | |
62 | '8080'], |
|
62 | '8080'], | |
63 | '%s://domain.org:8080' % proto), |
|
63 | '%s://domain.org:8080' % proto), | |
64 | ] |
|
64 | ] | |
65 |
|
65 | |||
66 |
|
66 | |||
67 | class TestLibs(unittest.TestCase): |
|
67 | class TestLibs(unittest.TestCase): | |
68 |
|
68 | |||
69 | @parameterized.expand(TEST_URLS) |
|
69 | @parameterized.expand(TEST_URLS) | |
70 | def test_uri_filter(self, test_url, expected, expected_creds): |
|
70 | def test_uri_filter(self, test_url, expected, expected_creds): | |
71 | from rhodecode.lib.utils2 import uri_filter |
|
71 | from rhodecode.lib.utils2 import uri_filter | |
72 | self.assertEqual(uri_filter(test_url), expected) |
|
72 | self.assertEqual(uri_filter(test_url), expected) | |
73 |
|
73 | |||
74 | @parameterized.expand(TEST_URLS) |
|
74 | @parameterized.expand(TEST_URLS) | |
75 | def test_credentials_filter(self, test_url, expected, expected_creds): |
|
75 | def test_credentials_filter(self, test_url, expected, expected_creds): | |
76 | from rhodecode.lib.utils2 import credentials_filter |
|
76 | from rhodecode.lib.utils2 import credentials_filter | |
77 | self.assertEqual(credentials_filter(test_url), expected_creds) |
|
77 | self.assertEqual(credentials_filter(test_url), expected_creds) | |
78 |
|
78 | |||
79 | @parameterized.expand([('t', True), |
|
79 | @parameterized.expand([('t', True), | |
80 | ('true', True), |
|
80 | ('true', True), | |
81 | ('y', True), |
|
81 | ('y', True), | |
82 | ('yes', True), |
|
82 | ('yes', True), | |
83 | ('on', True), |
|
83 | ('on', True), | |
84 | ('1', True), |
|
84 | ('1', True), | |
85 | ('Y', True), |
|
85 | ('Y', True), | |
86 | ('yeS', True), |
|
86 | ('yeS', True), | |
87 | ('Y', True), |
|
87 | ('Y', True), | |
88 | ('TRUE', True), |
|
88 | ('TRUE', True), | |
89 | ('T', True), |
|
89 | ('T', True), | |
90 | ('False', False), |
|
90 | ('False', False), | |
91 | ('F', False), |
|
91 | ('F', False), | |
92 | ('FALSE', False), |
|
92 | ('FALSE', False), | |
93 | ('0', False), |
|
93 | ('0', False), | |
94 | ('-1', False), |
|
94 | ('-1', False), | |
95 | ('', False) |
|
95 | ('', False) | |
96 | ]) |
|
96 | ]) | |
97 | def test_str2bool(self, str_bool, expected): |
|
97 | def test_str2bool(self, str_bool, expected): | |
98 | from rhodecode.lib.utils2 import str2bool |
|
98 | from rhodecode.lib.utils2 import str2bool | |
99 | self.assertEqual(str2bool(str_bool), expected) |
|
99 | self.assertEqual(str2bool(str_bool), expected) | |
100 |
|
100 | |||
101 | def test_mention_extractor(self): |
|
101 | def test_mention_extractor(self): | |
102 | from rhodecode.lib.utils2 import extract_mentioned_users |
|
102 | from rhodecode.lib.utils2 import extract_mentioned_users | |
103 | sample = ( |
|
103 | sample = ( | |
104 | "@first hi there @marcink here's my email marcin@email.com " |
|
104 | "@first hi there @marcink here's my email marcin@email.com " | |
105 | "@lukaszb check @one_more22 it pls @ ttwelve @D[] @one@two@three " |
|
105 | "@lukaszb check @one_more22 it pls @ ttwelve @D[] @one@two@three " | |
106 | "@MARCIN @maRCiN @2one_more22 @john please see this http://org.pl " |
|
106 | "@MARCIN @maRCiN @2one_more22 @john please see this http://org.pl " | |
107 | "@marian.user just do it @marco-polo and next extract @marco_polo " |
|
107 | "@marian.user just do it @marco-polo and next extract @marco_polo " | |
108 | "user.dot hej ! not-needed maril@domain.org" |
|
108 | "user.dot hej ! not-needed maril@domain.org" | |
109 | ) |
|
109 | ) | |
110 |
|
110 | |||
111 | s = sorted([ |
|
111 | s = sorted([ | |
112 | 'first', 'marcink', 'lukaszb', 'one_more22', 'MARCIN', 'maRCiN', 'john', |
|
112 | 'first', 'marcink', 'lukaszb', 'one_more22', 'MARCIN', 'maRCiN', 'john', | |
113 | 'marian.user', 'marco-polo', 'marco_polo' |
|
113 | 'marian.user', 'marco-polo', 'marco_polo' | |
114 | ], key=lambda k: k.lower()) |
|
114 | ], key=lambda k: k.lower()) | |
115 | self.assertEqual(s, extract_mentioned_users(sample)) |
|
115 | self.assertEqual(s, extract_mentioned_users(sample)) | |
116 |
|
116 | |||
117 | def test_age(self): |
|
117 | def test_age(self): | |
118 | from rhodecode.lib.utils2 import age |
|
118 | from rhodecode.lib.utils2 import age | |
119 | from dateutil import relativedelta |
|
119 | from dateutil import relativedelta | |
120 | n = datetime.datetime.now() |
|
120 | n = datetime.datetime.now() | |
121 | delt = lambda *args, **kwargs: relativedelta.relativedelta(*args, **kwargs) |
|
121 | delt = lambda *args, **kwargs: relativedelta.relativedelta(*args, **kwargs) | |
122 |
|
122 | |||
123 | self.assertEqual(age(n), u'just now') |
|
123 | self.assertEqual(age(n), u'just now') | |
124 | self.assertEqual(age(n + delt(seconds=-1)), u'1 second ago') |
|
124 | self.assertEqual(age(n + delt(seconds=-1)), u'1 second ago') | |
125 | self.assertEqual(age(n + delt(seconds=-60 * 2)), u'2 minutes ago') |
|
125 | self.assertEqual(age(n + delt(seconds=-60 * 2)), u'2 minutes ago') | |
126 | self.assertEqual(age(n + delt(hours=-1)), u'1 hour ago') |
|
126 | self.assertEqual(age(n + delt(hours=-1)), u'1 hour ago') | |
127 | self.assertEqual(age(n + delt(hours=-24)), u'1 day ago') |
|
127 | self.assertEqual(age(n + delt(hours=-24)), u'1 day ago') | |
128 | self.assertEqual(age(n + delt(hours=-24 * 5)), u'5 days ago') |
|
128 | self.assertEqual(age(n + delt(hours=-24 * 5)), u'5 days ago') | |
129 | self.assertEqual(age(n + delt(months=-1)), u'1 month ago') |
|
129 | self.assertEqual(age(n + delt(months=-1)), u'1 month ago') | |
130 | self.assertEqual(age(n + delt(months=-1, days=-2)), u'1 month and 2 days ago') |
|
130 | self.assertEqual(age(n + delt(months=-1, days=-2)), u'1 month and 2 days ago') | |
131 | self.assertEqual(age(n + delt(years=-1, months=-1)), u'1 year and 1 month ago') |
|
131 | self.assertEqual(age(n + delt(years=-1, months=-1)), u'1 year and 1 month ago') | |
132 |
|
132 | |||
133 | def test_age_in_future(self): |
|
133 | def test_age_in_future(self): | |
134 | from rhodecode.lib.utils2 import age |
|
134 | from rhodecode.lib.utils2 import age | |
135 | from dateutil import relativedelta |
|
135 | from dateutil import relativedelta | |
136 | n = datetime.datetime.now() |
|
136 | n = datetime.datetime.now() | |
137 | delt = lambda *args, **kwargs: relativedelta.relativedelta(*args, **kwargs) |
|
137 | delt = lambda *args, **kwargs: relativedelta.relativedelta(*args, **kwargs) | |
138 |
|
138 | |||
139 | self.assertEqual(age(n), u'just now') |
|
139 | self.assertEqual(age(n), u'just now') | |
140 | self.assertEqual(age(n + delt(seconds=1)), u'in 1 second') |
|
140 | self.assertEqual(age(n + delt(seconds=1)), u'in 1 second') | |
141 | self.assertEqual(age(n + delt(seconds=60 * 2)), u'in 2 minutes') |
|
141 | self.assertEqual(age(n + delt(seconds=60 * 2)), u'in 2 minutes') | |
142 | self.assertEqual(age(n + delt(hours=1)), u'in 1 hour') |
|
142 | self.assertEqual(age(n + delt(hours=1)), u'in 1 hour') | |
143 | self.assertEqual(age(n + delt(hours=24)), u'in 1 day') |
|
143 | self.assertEqual(age(n + delt(hours=24)), u'in 1 day') | |
144 | self.assertEqual(age(n + delt(hours=24 * 5)), u'in 5 days') |
|
144 | self.assertEqual(age(n + delt(hours=24 * 5)), u'in 5 days') | |
145 | self.assertEqual(age(n + delt(months=1)), u'in 1 month') |
|
145 | self.assertEqual(age(n + delt(months=1)), u'in 1 month') | |
146 | self.assertEqual(age(n + delt(months=1, days=1)), u'in 1 month and 1 day') |
|
146 | self.assertEqual(age(n + delt(months=1, days=1)), u'in 1 month and 1 day') | |
147 | self.assertEqual(age(n + delt(years=1, months=1)), u'in 1 year and 1 month') |
|
147 | self.assertEqual(age(n + delt(years=1, months=1)), u'in 1 year and 1 month') | |
148 |
|
148 | |||
149 | def test_tag_exctrator(self): |
|
149 | def test_tag_exctrator(self): | |
150 | sample = ( |
|
150 | sample = ( | |
151 | "hello pta[tag] gog [[]] [[] sda ero[or]d [me =>>< sa]" |
|
151 | "hello pta[tag] gog [[]] [[] sda ero[or]d [me =>>< sa]" | |
152 | "[requires] [stale] [see<>=>] [see => http://url.com]" |
|
152 | "[requires] [stale] [see<>=>] [see => http://url.com]" | |
153 | "[requires => url] [lang => python] [just a tag]" |
|
153 | "[requires => url] [lang => python] [just a tag]" | |
154 | "[,d] [ => ULR ] [obsolete] [desc]]" |
|
154 | "[,d] [ => ULR ] [obsolete] [desc]]" | |
155 | ) |
|
155 | ) | |
156 | from rhodecode.lib.helpers import desc_stylize |
|
156 | from rhodecode.lib.helpers import desc_stylize | |
157 | res = desc_stylize(sample) |
|
157 | res = desc_stylize(sample) | |
158 | self.assertTrue('<div class="metatag" tag="tag">tag</div>' in res) |
|
158 | self.assertTrue('<div class="metatag" tag="tag">tag</div>' in res) | |
159 | self.assertTrue('<div class="metatag" tag="obsolete">obsolete</div>' in res) |
|
159 | self.assertTrue('<div class="metatag" tag="obsolete">obsolete</div>' in res) | |
160 | self.assertTrue('<div class="metatag" tag="stale">stale</div>' in res) |
|
160 | self.assertTrue('<div class="metatag" tag="stale">stale</div>' in res) | |
161 | self.assertTrue('<div class="metatag" tag="lang">python</div>' in res) |
|
161 | self.assertTrue('<div class="metatag" tag="lang">python</div>' in res) | |
162 | self.assertTrue('<div class="metatag" tag="requires">requires => <a href="/url">url</a></div>' in res) |
|
162 | self.assertTrue('<div class="metatag" tag="requires">requires => <a href="/url">url</a></div>' in res) | |
163 | self.assertTrue('<div class="metatag" tag="tag">tag</div>' in res) |
|
163 | self.assertTrue('<div class="metatag" tag="tag">tag</div>' in res) | |
164 |
|
164 | |||
165 | def test_alternative_gravatar(self): |
|
165 | def test_alternative_gravatar(self): | |
166 | from rhodecode.lib.helpers import gravatar_url |
|
166 | from rhodecode.lib.helpers import gravatar_url | |
167 | _md5 = lambda s: hashlib.md5(s).hexdigest() |
|
167 | _md5 = lambda s: hashlib.md5(s).hexdigest() | |
168 |
|
168 | |||
169 | def fake_conf(**kwargs): |
|
169 | def fake_conf(**kwargs): | |
170 | from pylons import config |
|
170 | from pylons import config | |
171 | config['app_conf'] = {} |
|
171 | config['app_conf'] = {} | |
172 | config['app_conf']['use_gravatar'] = True |
|
172 | config['app_conf']['use_gravatar'] = True | |
173 | config['app_conf'].update(kwargs) |
|
173 | config['app_conf'].update(kwargs) | |
174 | return config |
|
174 | return config | |
175 |
|
175 | |||
176 | class fake_url(): |
|
176 | class fake_url(): | |
177 | @classmethod |
|
177 | @classmethod | |
178 | def current(cls, *args, **kwargs): |
|
178 | def current(cls, *args, **kwargs): | |
179 | return 'https://server.com' |
|
179 | return 'https://server.com' | |
180 |
|
180 | |||
181 | with mock.patch('pylons.url', fake_url): |
|
181 | with mock.patch('pylons.url', fake_url): | |
182 | fake = fake_conf(alternative_gravatar_url='http://test.com/{email}') |
|
182 | fake = fake_conf(alternative_gravatar_url='http://test.com/{email}') | |
183 | with mock.patch('pylons.config', fake): |
|
183 | with mock.patch('pylons.config', fake): | |
184 | from pylons import url |
|
184 | from pylons import url | |
185 | assert url.current() == 'https://server.com' |
|
185 | assert url.current() == 'https://server.com' | |
186 | grav = gravatar_url(email_address='test@foo.com', size=24) |
|
186 | grav = gravatar_url(email_address='test@foo.com', size=24) | |
187 | assert grav == 'http://test.com/test@foo.com' |
|
187 | assert grav == 'http://test.com/test@foo.com' | |
188 |
|
188 | |||
189 | fake = fake_conf(alternative_gravatar_url='http://test.com/{email}') |
|
189 | fake = fake_conf(alternative_gravatar_url='http://test.com/{email}') | |
190 | with mock.patch('pylons.config', fake): |
|
190 | with mock.patch('pylons.config', fake): | |
191 | grav = gravatar_url(email_address='test@foo.com', size=24) |
|
191 | grav = gravatar_url(email_address='test@foo.com', size=24) | |
192 | assert grav == 'http://test.com/test@foo.com' |
|
192 | assert grav == 'http://test.com/test@foo.com' | |
193 |
|
193 | |||
194 | fake = fake_conf(alternative_gravatar_url='http://test.com/{md5email}') |
|
194 | fake = fake_conf(alternative_gravatar_url='http://test.com/{md5email}') | |
195 | with mock.patch('pylons.config', fake): |
|
195 | with mock.patch('pylons.config', fake): | |
196 | em = 'test@foo.com' |
|
196 | em = 'test@foo.com' | |
197 | grav = gravatar_url(email_address=em, size=24) |
|
197 | grav = gravatar_url(email_address=em, size=24) | |
198 | assert grav == 'http://test.com/%s' % (_md5(em)) |
|
198 | assert grav == 'http://test.com/%s' % (_md5(em)) | |
199 |
|
199 | |||
200 | fake = fake_conf(alternative_gravatar_url='http://test.com/{md5email}/{size}') |
|
200 | fake = fake_conf(alternative_gravatar_url='http://test.com/{md5email}/{size}') | |
201 | with mock.patch('pylons.config', fake): |
|
201 | with mock.patch('pylons.config', fake): | |
202 | em = 'test@foo.com' |
|
202 | em = 'test@foo.com' | |
203 | grav = gravatar_url(email_address=em, size=24) |
|
203 | grav = gravatar_url(email_address=em, size=24) | |
204 | assert grav == 'http://test.com/%s/%s' % (_md5(em), 24) |
|
204 | assert grav == 'http://test.com/%s/%s' % (_md5(em), 24) | |
205 |
|
205 | |||
206 | fake = fake_conf(alternative_gravatar_url='{scheme}://{netloc}/{md5email}/{size}') |
|
206 | fake = fake_conf(alternative_gravatar_url='{scheme}://{netloc}/{md5email}/{size}') | |
207 | with mock.patch('pylons.config', fake): |
|
207 | with mock.patch('pylons.config', fake): | |
208 | em = 'test@foo.com' |
|
208 | em = 'test@foo.com' | |
209 | grav = gravatar_url(email_address=em, size=24) |
|
209 | grav = gravatar_url(email_address=em, size=24) | |
210 | assert grav == 'https://server.com/%s/%s' % (_md5(em), 24) |
|
210 | assert grav == 'https://server.com/%s/%s' % (_md5(em), 24) | |
211 |
|
211 | |||
212 | @parameterized.expand([ |
|
212 | @parameterized.expand([ | |
213 | ("", |
|
213 | ("", | |
214 | ""), |
|
214 | ""), | |
215 | ("git-svn-id: https://svn.apache.org/repos/asf/libcloud/trunk@1441655 13f79535-47bb-0310-9956-ffa450edef68", |
|
215 | ("git-svn-id: https://svn.apache.org/repos/asf/libcloud/trunk@1441655 13f79535-47bb-0310-9956-ffa450edef68", | |
216 | "git-svn-id: https://svn.apache.org/repos/asf/libcloud/trunk@1441655 13f79535-47bb-0310-9956-ffa450edef68"), |
|
216 | "git-svn-id: https://svn.apache.org/repos/asf/libcloud/trunk@1441655 13f79535-47bb-0310-9956-ffa450edef68"), | |
217 | ("from rev 000000000000", |
|
217 | ("from rev 000000000000", | |
218 | "from rev url[000000000000]"), |
|
218 | "from rev url[000000000000]"), | |
219 | ("from rev 000000000000123123 also rev 000000000000", |
|
219 | ("from rev 000000000000123123 also rev 000000000000", | |
220 | "from rev url[000000000000123123] also rev url[000000000000]"), |
|
220 | "from rev url[000000000000123123] also rev url[000000000000]"), | |
221 | ("this should-000 00", |
|
221 | ("this should-000 00", | |
222 | "this should-000 00"), |
|
222 | "this should-000 00"), | |
223 | ("longtextffffffffff rev 123123123123", |
|
223 | ("longtextffffffffff rev 123123123123", | |
224 | "longtextffffffffff rev url[123123123123]"), |
|
224 | "longtextffffffffff rev url[123123123123]"), | |
225 | ("rev ffffffffffffffffffffffffffffffffffffffffffffffffff", |
|
225 | ("rev ffffffffffffffffffffffffffffffffffffffffffffffffff", | |
226 | "rev ffffffffffffffffffffffffffffffffffffffffffffffffff"), |
|
226 | "rev ffffffffffffffffffffffffffffffffffffffffffffffffff"), | |
227 | ("ffffffffffff some text traalaa", |
|
227 | ("ffffffffffff some text traalaa", | |
228 | "url[ffffffffffff] some text traalaa"), |
|
228 | "url[ffffffffffff] some text traalaa"), | |
229 | ("""Multi line |
|
229 | ("""Multi line | |
230 |
123123123123 |
|
230 | 123123123123 | |
231 | some text 123123123123""", |
|
231 | some text 123123123123""", | |
232 | """Multi line |
|
232 | """Multi line | |
233 |
url[123123123123] |
|
233 | url[123123123123] | |
234 | some text url[123123123123]""") |
|
234 | some text url[123123123123]""") | |
235 | ]) |
|
235 | ]) | |
236 | def test_urlify_changesets(self, sample, expected): |
|
236 | def test_urlify_changesets(self, sample, expected): | |
237 | import re |
|
237 | import re | |
238 |
|
238 | |||
239 | def fake_url(self, *args, **kwargs): |
|
239 | def fake_url(self, *args, **kwargs): | |
240 | return '/some-url' |
|
240 | return '/some-url' | |
241 |
|
241 | |||
242 | #quickly change expected url[] into a link |
|
242 | #quickly change expected url[] into a link | |
243 | URL_PAT = re.compile(r'(?:url\[)(.+?)(?:\])') |
|
243 | URL_PAT = re.compile(r'(?:url\[)(.+?)(?:\])') | |
244 |
|
244 | |||
245 | def url_func(match_obj): |
|
245 | def url_func(match_obj): | |
246 | _url = match_obj.groups()[0] |
|
246 | _url = match_obj.groups()[0] | |
247 | tmpl = """<a class="revision-link" href="/some-url">%s</a>""" |
|
247 | tmpl = """<a class="revision-link" href="/some-url">%s</a>""" | |
248 | return tmpl % _url |
|
248 | return tmpl % _url | |
249 |
|
249 | |||
250 | expected = URL_PAT.sub(url_func, expected) |
|
250 | expected = URL_PAT.sub(url_func, expected) | |
251 |
|
251 | |||
252 | with mock.patch('pylons.url', fake_url): |
|
252 | with mock.patch('pylons.url', fake_url): | |
253 | from rhodecode.lib.helpers import urlify_changesets |
|
253 | from rhodecode.lib.helpers import urlify_changesets | |
254 | self.assertEqual(urlify_changesets(sample, 'repo_name'), expected) |
|
254 | self.assertEqual(urlify_changesets(sample, 'repo_name'), expected) |
General Comments 0
You need to be logged in to leave comments.
Login now