Show More
@@ -1,681 +1,674 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | # This program is free software: you can redistribute it and/or modify |
|
2 | # This program is free software: you can redistribute it and/or modify | |
3 | # it under the terms of the GNU General Public License as published by |
|
3 | # it under the terms of the GNU General Public License as published by | |
4 | # the Free Software Foundation, either version 3 of the License, or |
|
4 | # the Free Software Foundation, either version 3 of the License, or | |
5 | # (at your option) any later version. |
|
5 | # (at your option) any later version. | |
6 | # |
|
6 | # | |
7 | # This program is distributed in the hope that it will be useful, |
|
7 | # This program is distributed in the hope that it will be useful, | |
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | # GNU General Public License for more details. |
|
10 | # GNU General Public License for more details. | |
11 | # |
|
11 | # | |
12 | # You should have received a copy of the GNU General Public License |
|
12 | # You should have received a copy of the GNU General Public License | |
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
14 | """ |
|
14 | """ | |
15 | kallithea.controllers.admin.repos |
|
15 | kallithea.controllers.admin.repos | |
16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
17 |
|
17 | |||
18 | Repositories controller for Kallithea |
|
18 | Repositories controller for Kallithea | |
19 |
|
19 | |||
20 | This file was forked by the Kallithea project in July 2014. |
|
20 | This file was forked by the Kallithea project in July 2014. | |
21 | Original author and date, and relevant copyright and licensing information is below: |
|
21 | Original author and date, and relevant copyright and licensing information is below: | |
22 | :created_on: Apr 7, 2010 |
|
22 | :created_on: Apr 7, 2010 | |
23 | :author: marcink |
|
23 | :author: marcink | |
24 | :copyright: (c) 2013 RhodeCode GmbH, and others. |
|
24 | :copyright: (c) 2013 RhodeCode GmbH, and others. | |
25 | :license: GPLv3, see LICENSE.md for more details. |
|
25 | :license: GPLv3, see LICENSE.md for more details. | |
26 | """ |
|
26 | """ | |
27 |
|
27 | |||
28 | import logging |
|
28 | import logging | |
29 | import traceback |
|
29 | import traceback | |
30 | import formencode |
|
30 | import formencode | |
31 | from formencode import htmlfill |
|
31 | from formencode import htmlfill | |
32 | from webob.exc import HTTPInternalServerError, HTTPForbidden, HTTPNotFound |
|
32 | from webob.exc import HTTPInternalServerError, HTTPForbidden, HTTPNotFound | |
33 | from pylons import request, tmpl_context as c, url |
|
33 | from pylons import request, tmpl_context as c, url | |
34 | from pylons.controllers.util import redirect |
|
34 | from pylons.controllers.util import redirect | |
35 | from pylons.i18n.translation import _ |
|
35 | from pylons.i18n.translation import _ | |
36 | from sqlalchemy.sql.expression import func |
|
36 | from sqlalchemy.sql.expression import func | |
37 |
|
37 | |||
38 | from kallithea.lib import helpers as h |
|
38 | from kallithea.lib import helpers as h | |
39 | from kallithea.lib.auth import LoginRequired, HasPermissionAllDecorator, \ |
|
39 | from kallithea.lib.auth import LoginRequired, HasPermissionAllDecorator, \ | |
40 | HasRepoPermissionAllDecorator, NotAnonymous,HasPermissionAny, \ |
|
40 | HasRepoPermissionAllDecorator, NotAnonymous,HasPermissionAny, \ | |
41 | HasRepoGroupPermissionAny, HasRepoPermissionAnyDecorator |
|
41 | HasRepoGroupPermissionAny, HasRepoPermissionAnyDecorator | |
42 | from kallithea.lib.base import BaseRepoController, render |
|
42 | from kallithea.lib.base import BaseRepoController, render | |
43 | from kallithea.lib.utils import action_logger, repo_name_slug, jsonify |
|
43 | from kallithea.lib.utils import action_logger, repo_name_slug, jsonify | |
44 | from kallithea.lib.helpers import get_token |
|
|||
45 | from kallithea.lib.vcs import RepositoryError |
|
44 | from kallithea.lib.vcs import RepositoryError | |
46 | from kallithea.model.meta import Session |
|
45 | from kallithea.model.meta import Session | |
47 | from kallithea.model.db import User, Repository, UserFollowing, RepoGroup,\ |
|
46 | from kallithea.model.db import User, Repository, UserFollowing, RepoGroup,\ | |
48 | Setting, RepositoryField |
|
47 | Setting, RepositoryField | |
49 | from kallithea.model.forms import RepoForm, RepoFieldForm, RepoPermsForm |
|
48 | from kallithea.model.forms import RepoForm, RepoFieldForm, RepoPermsForm | |
50 | from kallithea.model.scm import ScmModel, RepoGroupList, RepoList |
|
49 | from kallithea.model.scm import ScmModel, RepoGroupList, RepoList | |
51 | from kallithea.model.repo import RepoModel |
|
50 | from kallithea.model.repo import RepoModel | |
52 | from kallithea.lib.compat import json |
|
51 | from kallithea.lib.compat import json | |
53 | from kallithea.lib.exceptions import AttachedForksError |
|
52 | from kallithea.lib.exceptions import AttachedForksError | |
54 | from kallithea.lib.utils2 import safe_int |
|
53 | from kallithea.lib.utils2 import safe_int | |
55 |
|
54 | |||
56 | log = logging.getLogger(__name__) |
|
55 | log = logging.getLogger(__name__) | |
57 |
|
56 | |||
58 |
|
57 | |||
59 | class ReposController(BaseRepoController): |
|
58 | class ReposController(BaseRepoController): | |
60 | """ |
|
59 | """ | |
61 | REST Controller styled on the Atom Publishing Protocol""" |
|
60 | REST Controller styled on the Atom Publishing Protocol""" | |
62 | # To properly map this controller, ensure your config/routing.py |
|
61 | # To properly map this controller, ensure your config/routing.py | |
63 | # file has a resource setup: |
|
62 | # file has a resource setup: | |
64 | # map.resource('repo', 'repos') |
|
63 | # map.resource('repo', 'repos') | |
65 |
|
64 | |||
66 | @LoginRequired() |
|
65 | @LoginRequired() | |
67 | def __before__(self): |
|
66 | def __before__(self): | |
68 | super(ReposController, self).__before__() |
|
67 | super(ReposController, self).__before__() | |
69 |
|
68 | |||
70 | def _load_repo(self, repo_name): |
|
69 | def _load_repo(self, repo_name): | |
71 | repo_obj = Repository.get_by_repo_name(repo_name) |
|
70 | repo_obj = Repository.get_by_repo_name(repo_name) | |
72 |
|
71 | |||
73 | if repo_obj is None: |
|
72 | if repo_obj is None: | |
74 | h.not_mapped_error(repo_name) |
|
73 | h.not_mapped_error(repo_name) | |
75 | return redirect(url('repos')) |
|
74 | return redirect(url('repos')) | |
76 |
|
75 | |||
77 | return repo_obj |
|
76 | return repo_obj | |
78 |
|
77 | |||
79 | def __load_defaults(self, repo=None): |
|
78 | def __load_defaults(self, repo=None): | |
80 | acl_groups = RepoGroupList(RepoGroup.query().all(), |
|
79 | acl_groups = RepoGroupList(RepoGroup.query().all(), | |
81 | perm_set=['group.write', 'group.admin']) |
|
80 | perm_set=['group.write', 'group.admin']) | |
82 | c.repo_groups = RepoGroup.groups_choices(groups=acl_groups) |
|
81 | c.repo_groups = RepoGroup.groups_choices(groups=acl_groups) | |
83 | c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups) |
|
82 | c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups) | |
84 |
|
83 | |||
85 | # in case someone no longer have a group.write access to a repository |
|
84 | # in case someone no longer have a group.write access to a repository | |
86 | # pre fill the list with this entry, we don't care if this is the same |
|
85 | # pre fill the list with this entry, we don't care if this is the same | |
87 | # but it will allow saving repo data properly. |
|
86 | # but it will allow saving repo data properly. | |
88 |
|
87 | |||
89 | repo_group = None |
|
88 | repo_group = None | |
90 | if repo: |
|
89 | if repo: | |
91 | repo_group = repo.group |
|
90 | repo_group = repo.group | |
92 | if repo_group and unicode(repo_group.group_id) not in c.repo_groups_choices: |
|
91 | if repo_group and unicode(repo_group.group_id) not in c.repo_groups_choices: | |
93 | c.repo_groups_choices.append(unicode(repo_group.group_id)) |
|
92 | c.repo_groups_choices.append(unicode(repo_group.group_id)) | |
94 | c.repo_groups.append(RepoGroup._generate_choice(repo_group)) |
|
93 | c.repo_groups.append(RepoGroup._generate_choice(repo_group)) | |
95 |
|
94 | |||
96 | choices, c.landing_revs = ScmModel().get_repo_landing_revs() |
|
95 | choices, c.landing_revs = ScmModel().get_repo_landing_revs() | |
97 | c.landing_revs_choices = choices |
|
96 | c.landing_revs_choices = choices | |
98 |
|
97 | |||
99 | def __load_data(self, repo_name=None): |
|
98 | def __load_data(self, repo_name=None): | |
100 | """ |
|
99 | """ | |
101 | Load defaults settings for edit, and update |
|
100 | Load defaults settings for edit, and update | |
102 |
|
101 | |||
103 | :param repo_name: |
|
102 | :param repo_name: | |
104 | """ |
|
103 | """ | |
105 | c.repo_info = self._load_repo(repo_name) |
|
104 | c.repo_info = self._load_repo(repo_name) | |
106 | self.__load_defaults(c.repo_info) |
|
105 | self.__load_defaults(c.repo_info) | |
107 |
|
106 | |||
108 | ##override defaults for exact repo info here git/hg etc |
|
107 | ##override defaults for exact repo info here git/hg etc | |
109 | choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info) |
|
108 | choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info) | |
110 | c.landing_revs_choices = choices |
|
109 | c.landing_revs_choices = choices | |
111 | defaults = RepoModel()._get_defaults(repo_name) |
|
110 | defaults = RepoModel()._get_defaults(repo_name) | |
112 |
|
111 | |||
113 | return defaults |
|
112 | return defaults | |
114 |
|
113 | |||
115 | def index(self, format='html'): |
|
114 | def index(self, format='html'): | |
116 | """GET /repos: All items in the collection""" |
|
115 | """GET /repos: All items in the collection""" | |
117 | # url('repos') |
|
116 | # url('repos') | |
118 | _list = Repository.query()\ |
|
117 | _list = Repository.query()\ | |
119 | .order_by(func.lower(Repository.repo_name))\ |
|
118 | .order_by(func.lower(Repository.repo_name))\ | |
120 | .all() |
|
119 | .all() | |
121 |
|
120 | |||
122 | c.repos_list = RepoList(_list, perm_set=['repository.admin']) |
|
121 | c.repos_list = RepoList(_list, perm_set=['repository.admin']) | |
123 | repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list, |
|
122 | repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list, | |
124 | admin=True, |
|
123 | admin=True, | |
125 | super_user_actions=True) |
|
124 | super_user_actions=True) | |
126 | #json used to render the grid |
|
125 | #json used to render the grid | |
127 | c.data = json.dumps(repos_data) |
|
126 | c.data = json.dumps(repos_data) | |
128 |
|
127 | |||
129 | return render('admin/repos/repos.html') |
|
128 | return render('admin/repos/repos.html') | |
130 |
|
129 | |||
131 | @NotAnonymous() |
|
130 | @NotAnonymous() | |
132 | def create(self): |
|
131 | def create(self): | |
133 | """ |
|
132 | """ | |
134 | POST /repos: Create a new item""" |
|
133 | POST /repos: Create a new item""" | |
135 | # url('repos') |
|
134 | # url('repos') | |
136 |
|
135 | |||
137 | self.__load_defaults() |
|
136 | self.__load_defaults() | |
138 | form_result = {} |
|
137 | form_result = {} | |
139 | task_id = None |
|
138 | task_id = None | |
140 | try: |
|
139 | try: | |
141 | # CanWriteToGroup validators checks permissions of this POST |
|
140 | # CanWriteToGroup validators checks permissions of this POST | |
142 | form_result = RepoForm(repo_groups=c.repo_groups_choices, |
|
141 | form_result = RepoForm(repo_groups=c.repo_groups_choices, | |
143 | landing_revs=c.landing_revs_choices)()\ |
|
142 | landing_revs=c.landing_revs_choices)()\ | |
144 | .to_python(dict(request.POST)) |
|
143 | .to_python(dict(request.POST)) | |
145 |
|
144 | |||
146 | # create is done sometimes async on celery, db transaction |
|
145 | # create is done sometimes async on celery, db transaction | |
147 | # management is handled there. |
|
146 | # management is handled there. | |
148 | task = RepoModel().create(form_result, self.authuser.user_id) |
|
147 | task = RepoModel().create(form_result, self.authuser.user_id) | |
149 | from celery.result import BaseAsyncResult |
|
148 | from celery.result import BaseAsyncResult | |
150 | if isinstance(task, BaseAsyncResult): |
|
149 | if isinstance(task, BaseAsyncResult): | |
151 | task_id = task.task_id |
|
150 | task_id = task.task_id | |
152 | except formencode.Invalid, errors: |
|
151 | except formencode.Invalid, errors: | |
153 | return htmlfill.render( |
|
152 | return htmlfill.render( | |
154 | render('admin/repos/repo_add.html'), |
|
153 | render('admin/repos/repo_add.html'), | |
155 | defaults=errors.value, |
|
154 | defaults=errors.value, | |
156 | errors=errors.error_dict or {}, |
|
155 | errors=errors.error_dict or {}, | |
157 | prefix_error=False, |
|
156 | prefix_error=False, | |
158 | force_defaults=False, |
|
157 | force_defaults=False, | |
159 | encoding="UTF-8") |
|
158 | encoding="UTF-8") | |
160 |
|
159 | |||
161 | except Exception: |
|
160 | except Exception: | |
162 | log.error(traceback.format_exc()) |
|
161 | log.error(traceback.format_exc()) | |
163 | msg = (_('Error creating repository %s') |
|
162 | msg = (_('Error creating repository %s') | |
164 | % form_result.get('repo_name')) |
|
163 | % form_result.get('repo_name')) | |
165 | h.flash(msg, category='error') |
|
164 | h.flash(msg, category='error') | |
166 | return redirect(url('home')) |
|
165 | return redirect(url('home')) | |
167 |
|
166 | |||
168 | return redirect(h.url('repo_creating_home', |
|
167 | return redirect(h.url('repo_creating_home', | |
169 | repo_name=form_result['repo_name_full'], |
|
168 | repo_name=form_result['repo_name_full'], | |
170 | task_id=task_id)) |
|
169 | task_id=task_id)) | |
171 |
|
170 | |||
172 | @NotAnonymous() |
|
171 | @NotAnonymous() | |
173 | def create_repository(self): |
|
172 | def create_repository(self): | |
174 | """GET /_admin/create_repository: Form to create a new item""" |
|
173 | """GET /_admin/create_repository: Form to create a new item""" | |
175 | new_repo = request.GET.get('repo', '') |
|
174 | new_repo = request.GET.get('repo', '') | |
176 | parent_group = request.GET.get('parent_group') |
|
175 | parent_group = request.GET.get('parent_group') | |
177 | if not HasPermissionAny('hg.admin', 'hg.create.repository')(): |
|
176 | if not HasPermissionAny('hg.admin', 'hg.create.repository')(): | |
178 | #you're not super admin nor have global create permissions, |
|
177 | #you're not super admin nor have global create permissions, | |
179 | #but maybe you have at least write permission to a parent group ? |
|
178 | #but maybe you have at least write permission to a parent group ? | |
180 | _gr = RepoGroup.get(parent_group) |
|
179 | _gr = RepoGroup.get(parent_group) | |
181 | gr_name = _gr.group_name if _gr else None |
|
180 | gr_name = _gr.group_name if _gr else None | |
182 | # create repositories with write permission on group is set to true |
|
181 | # create repositories with write permission on group is set to true | |
183 | create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')() |
|
182 | create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')() | |
184 | group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name) |
|
183 | group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name) | |
185 | group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name) |
|
184 | group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name) | |
186 | if not (group_admin or (group_write and create_on_write)): |
|
185 | if not (group_admin or (group_write and create_on_write)): | |
187 | raise HTTPForbidden |
|
186 | raise HTTPForbidden | |
188 |
|
187 | |||
189 | acl_groups = RepoGroupList(RepoGroup.query().all(), |
|
188 | acl_groups = RepoGroupList(RepoGroup.query().all(), | |
190 | perm_set=['group.write', 'group.admin']) |
|
189 | perm_set=['group.write', 'group.admin']) | |
191 | c.repo_groups = RepoGroup.groups_choices(groups=acl_groups) |
|
190 | c.repo_groups = RepoGroup.groups_choices(groups=acl_groups) | |
192 | c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups) |
|
191 | c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups) | |
193 | choices, c.landing_revs = ScmModel().get_repo_landing_revs() |
|
192 | choices, c.landing_revs = ScmModel().get_repo_landing_revs() | |
194 |
|
193 | |||
195 | c.new_repo = repo_name_slug(new_repo) |
|
194 | c.new_repo = repo_name_slug(new_repo) | |
196 |
|
195 | |||
197 | ## apply the defaults from defaults page |
|
196 | ## apply the defaults from defaults page | |
198 | defaults = Setting.get_default_repo_settings(strip_prefix=True) |
|
197 | defaults = Setting.get_default_repo_settings(strip_prefix=True) | |
199 | if parent_group: |
|
198 | if parent_group: | |
200 | defaults.update({'repo_group': parent_group}) |
|
199 | defaults.update({'repo_group': parent_group}) | |
201 |
|
200 | |||
202 | return htmlfill.render( |
|
201 | return htmlfill.render( | |
203 | render('admin/repos/repo_add.html'), |
|
202 | render('admin/repos/repo_add.html'), | |
204 | defaults=defaults, |
|
203 | defaults=defaults, | |
205 | errors={}, |
|
204 | errors={}, | |
206 | prefix_error=False, |
|
205 | prefix_error=False, | |
207 | encoding="UTF-8", |
|
206 | encoding="UTF-8", | |
208 | force_defaults=False) |
|
207 | force_defaults=False) | |
209 |
|
208 | |||
210 | @LoginRequired() |
|
209 | @LoginRequired() | |
211 | @NotAnonymous() |
|
210 | @NotAnonymous() | |
212 | def repo_creating(self, repo_name): |
|
211 | def repo_creating(self, repo_name): | |
213 | c.repo = repo_name |
|
212 | c.repo = repo_name | |
214 | c.task_id = request.GET.get('task_id') |
|
213 | c.task_id = request.GET.get('task_id') | |
215 | if not c.repo: |
|
214 | if not c.repo: | |
216 | raise HTTPNotFound() |
|
215 | raise HTTPNotFound() | |
217 | return render('admin/repos/repo_creating.html') |
|
216 | return render('admin/repos/repo_creating.html') | |
218 |
|
217 | |||
219 | @LoginRequired() |
|
218 | @LoginRequired() | |
220 | @NotAnonymous() |
|
219 | @NotAnonymous() | |
221 | @jsonify |
|
220 | @jsonify | |
222 | def repo_check(self, repo_name): |
|
221 | def repo_check(self, repo_name): | |
223 | c.repo = repo_name |
|
222 | c.repo = repo_name | |
224 | task_id = request.GET.get('task_id') |
|
223 | task_id = request.GET.get('task_id') | |
225 |
|
224 | |||
226 | if task_id and task_id not in ['None']: |
|
225 | if task_id and task_id not in ['None']: | |
227 | from kallithea import CELERY_ON |
|
226 | from kallithea import CELERY_ON | |
228 | from celery.result import AsyncResult |
|
227 | from celery.result import AsyncResult | |
229 | if CELERY_ON: |
|
228 | if CELERY_ON: | |
230 | task = AsyncResult(task_id) |
|
229 | task = AsyncResult(task_id) | |
231 | if task.failed(): |
|
230 | if task.failed(): | |
232 | raise HTTPInternalServerError(task.traceback) |
|
231 | raise HTTPInternalServerError(task.traceback) | |
233 |
|
232 | |||
234 | repo = Repository.get_by_repo_name(repo_name) |
|
233 | repo = Repository.get_by_repo_name(repo_name) | |
235 | if repo and repo.repo_state == Repository.STATE_CREATED: |
|
234 | if repo and repo.repo_state == Repository.STATE_CREATED: | |
236 | if repo.clone_uri: |
|
235 | if repo.clone_uri: | |
237 | clone_uri = repo.clone_uri_hidden |
|
236 | clone_uri = repo.clone_uri_hidden | |
238 | h.flash(_('Created repository %s from %s') |
|
237 | h.flash(_('Created repository %s from %s') | |
239 | % (repo.repo_name, clone_uri), category='success') |
|
238 | % (repo.repo_name, clone_uri), category='success') | |
240 | else: |
|
239 | else: | |
241 | repo_url = h.link_to(repo.repo_name, |
|
240 | repo_url = h.link_to(repo.repo_name, | |
242 | h.url('summary_home', |
|
241 | h.url('summary_home', | |
243 | repo_name=repo.repo_name)) |
|
242 | repo_name=repo.repo_name)) | |
244 | fork = repo.fork |
|
243 | fork = repo.fork | |
245 | if fork: |
|
244 | if fork: | |
246 | fork_name = fork.repo_name |
|
245 | fork_name = fork.repo_name | |
247 | h.flash(h.literal(_('Forked repository %s as %s') |
|
246 | h.flash(h.literal(_('Forked repository %s as %s') | |
248 | % (fork_name, repo_url)), category='success') |
|
247 | % (fork_name, repo_url)), category='success') | |
249 | else: |
|
248 | else: | |
250 | h.flash(h.literal(_('Created repository %s') % repo_url), |
|
249 | h.flash(h.literal(_('Created repository %s') % repo_url), | |
251 | category='success') |
|
250 | category='success') | |
252 | return {'result': True} |
|
251 | return {'result': True} | |
253 | return {'result': False} |
|
252 | return {'result': False} | |
254 |
|
253 | |||
255 | @HasRepoPermissionAllDecorator('repository.admin') |
|
254 | @HasRepoPermissionAllDecorator('repository.admin') | |
256 | def update(self, repo_name): |
|
255 | def update(self, repo_name): | |
257 | """ |
|
256 | """ | |
258 | PUT /repos/repo_name: Update an existing item""" |
|
257 | PUT /repos/repo_name: Update an existing item""" | |
259 | # Forms posted to this method should contain a hidden field: |
|
258 | # Forms posted to this method should contain a hidden field: | |
260 | # <input type="hidden" name="_method" value="PUT" /> |
|
259 | # <input type="hidden" name="_method" value="PUT" /> | |
261 | # Or using helpers: |
|
260 | # Or using helpers: | |
262 | # h.form(url('repo', repo_name=ID), |
|
261 | # h.form(url('repo', repo_name=ID), | |
263 | # method='put') |
|
262 | # method='put') | |
264 | # url('repo', repo_name=ID) |
|
263 | # url('repo', repo_name=ID) | |
265 | c.repo_info = self._load_repo(repo_name) |
|
264 | c.repo_info = self._load_repo(repo_name) | |
266 | c.active = 'settings' |
|
265 | c.active = 'settings' | |
267 | c.repo_fields = RepositoryField.query()\ |
|
266 | c.repo_fields = RepositoryField.query()\ | |
268 | .filter(RepositoryField.repository == c.repo_info).all() |
|
267 | .filter(RepositoryField.repository == c.repo_info).all() | |
269 | self.__load_defaults(c.repo_info) |
|
268 | self.__load_defaults(c.repo_info) | |
270 |
|
269 | |||
271 | repo_model = RepoModel() |
|
270 | repo_model = RepoModel() | |
272 | changed_name = repo_name |
|
271 | changed_name = repo_name | |
273 | #override the choices with extracted revisions ! |
|
272 | #override the choices with extracted revisions ! | |
274 | choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name) |
|
273 | choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name) | |
275 | c.landing_revs_choices = choices |
|
274 | c.landing_revs_choices = choices | |
276 | repo = Repository.get_by_repo_name(repo_name) |
|
275 | repo = Repository.get_by_repo_name(repo_name) | |
277 | old_data = { |
|
276 | old_data = { | |
278 | 'repo_name': repo_name, |
|
277 | 'repo_name': repo_name, | |
279 | 'repo_group': repo.group.get_dict() if repo.group else {}, |
|
278 | 'repo_group': repo.group.get_dict() if repo.group else {}, | |
280 | 'repo_type': repo.repo_type, |
|
279 | 'repo_type': repo.repo_type, | |
281 | } |
|
280 | } | |
282 | _form = RepoForm(edit=True, old_data=old_data, |
|
281 | _form = RepoForm(edit=True, old_data=old_data, | |
283 | repo_groups=c.repo_groups_choices, |
|
282 | repo_groups=c.repo_groups_choices, | |
284 | landing_revs=c.landing_revs_choices)() |
|
283 | landing_revs=c.landing_revs_choices)() | |
285 |
|
284 | |||
286 | try: |
|
285 | try: | |
287 | form_result = _form.to_python(dict(request.POST)) |
|
286 | form_result = _form.to_python(dict(request.POST)) | |
288 | repo = repo_model.update(repo_name, **form_result) |
|
287 | repo = repo_model.update(repo_name, **form_result) | |
289 | ScmModel().mark_for_invalidation(repo_name) |
|
288 | ScmModel().mark_for_invalidation(repo_name) | |
290 | h.flash(_('Repository %s updated successfully') % repo_name, |
|
289 | h.flash(_('Repository %s updated successfully') % repo_name, | |
291 | category='success') |
|
290 | category='success') | |
292 | changed_name = repo.repo_name |
|
291 | changed_name = repo.repo_name | |
293 | action_logger(self.authuser, 'admin_updated_repo', |
|
292 | action_logger(self.authuser, 'admin_updated_repo', | |
294 | changed_name, self.ip_addr, self.sa) |
|
293 | changed_name, self.ip_addr, self.sa) | |
295 | Session().commit() |
|
294 | Session().commit() | |
296 | except formencode.Invalid, errors: |
|
295 | except formencode.Invalid, errors: | |
297 | defaults = self.__load_data(repo_name) |
|
296 | defaults = self.__load_data(repo_name) | |
298 | defaults.update(errors.value) |
|
297 | defaults.update(errors.value) | |
299 | return htmlfill.render( |
|
298 | return htmlfill.render( | |
300 | render('admin/repos/repo_edit.html'), |
|
299 | render('admin/repos/repo_edit.html'), | |
301 | defaults=defaults, |
|
300 | defaults=defaults, | |
302 | errors=errors.error_dict or {}, |
|
301 | errors=errors.error_dict or {}, | |
303 | prefix_error=False, |
|
302 | prefix_error=False, | |
304 | encoding="UTF-8", |
|
303 | encoding="UTF-8", | |
305 | force_defaults=False) |
|
304 | force_defaults=False) | |
306 |
|
305 | |||
307 | except Exception: |
|
306 | except Exception: | |
308 | log.error(traceback.format_exc()) |
|
307 | log.error(traceback.format_exc()) | |
309 | h.flash(_('Error occurred during update of repository %s') \ |
|
308 | h.flash(_('Error occurred during update of repository %s') \ | |
310 | % repo_name, category='error') |
|
309 | % repo_name, category='error') | |
311 | return redirect(url('edit_repo', repo_name=changed_name)) |
|
310 | return redirect(url('edit_repo', repo_name=changed_name)) | |
312 |
|
311 | |||
313 | @HasRepoPermissionAllDecorator('repository.admin') |
|
312 | @HasRepoPermissionAllDecorator('repository.admin') | |
314 | def delete(self, repo_name): |
|
313 | def delete(self, repo_name): | |
315 | """ |
|
314 | """ | |
316 | DELETE /repos/repo_name: Delete an existing item""" |
|
315 | DELETE /repos/repo_name: Delete an existing item""" | |
317 | # Forms posted to this method should contain a hidden field: |
|
316 | # Forms posted to this method should contain a hidden field: | |
318 | # <input type="hidden" name="_method" value="DELETE" /> |
|
317 | # <input type="hidden" name="_method" value="DELETE" /> | |
319 | # Or using helpers: |
|
318 | # Or using helpers: | |
320 | # h.form(url('repo', repo_name=ID), |
|
319 | # h.form(url('repo', repo_name=ID), | |
321 | # method='delete') |
|
320 | # method='delete') | |
322 | # url('repo', repo_name=ID) |
|
321 | # url('repo', repo_name=ID) | |
323 |
|
322 | |||
324 | repo_model = RepoModel() |
|
323 | repo_model = RepoModel() | |
325 | repo = repo_model.get_by_repo_name(repo_name) |
|
324 | repo = repo_model.get_by_repo_name(repo_name) | |
326 | if not repo: |
|
325 | if not repo: | |
327 | h.not_mapped_error(repo_name) |
|
326 | h.not_mapped_error(repo_name) | |
328 | return redirect(url('repos')) |
|
327 | return redirect(url('repos')) | |
329 | try: |
|
328 | try: | |
330 | _forks = repo.forks.count() |
|
329 | _forks = repo.forks.count() | |
331 | handle_forks = None |
|
330 | handle_forks = None | |
332 | if _forks and request.POST.get('forks'): |
|
331 | if _forks and request.POST.get('forks'): | |
333 | do = request.POST['forks'] |
|
332 | do = request.POST['forks'] | |
334 | if do == 'detach_forks': |
|
333 | if do == 'detach_forks': | |
335 | handle_forks = 'detach' |
|
334 | handle_forks = 'detach' | |
336 | h.flash(_('Detached %s forks') % _forks, category='success') |
|
335 | h.flash(_('Detached %s forks') % _forks, category='success') | |
337 | elif do == 'delete_forks': |
|
336 | elif do == 'delete_forks': | |
338 | handle_forks = 'delete' |
|
337 | handle_forks = 'delete' | |
339 | h.flash(_('Deleted %s forks') % _forks, category='success') |
|
338 | h.flash(_('Deleted %s forks') % _forks, category='success') | |
340 | repo_model.delete(repo, forks=handle_forks) |
|
339 | repo_model.delete(repo, forks=handle_forks) | |
341 | action_logger(self.authuser, 'admin_deleted_repo', |
|
340 | action_logger(self.authuser, 'admin_deleted_repo', | |
342 | repo_name, self.ip_addr, self.sa) |
|
341 | repo_name, self.ip_addr, self.sa) | |
343 | ScmModel().mark_for_invalidation(repo_name) |
|
342 | ScmModel().mark_for_invalidation(repo_name) | |
344 | h.flash(_('Deleted repository %s') % repo_name, category='success') |
|
343 | h.flash(_('Deleted repository %s') % repo_name, category='success') | |
345 | Session().commit() |
|
344 | Session().commit() | |
346 | except AttachedForksError: |
|
345 | except AttachedForksError: | |
347 | h.flash(_('Cannot delete %s it still contains attached forks') |
|
346 | h.flash(_('Cannot delete %s it still contains attached forks') | |
348 | % repo_name, category='warning') |
|
347 | % repo_name, category='warning') | |
349 |
|
348 | |||
350 | except Exception: |
|
349 | except Exception: | |
351 | log.error(traceback.format_exc()) |
|
350 | log.error(traceback.format_exc()) | |
352 | h.flash(_('An error occurred during deletion of %s') % repo_name, |
|
351 | h.flash(_('An error occurred during deletion of %s') % repo_name, | |
353 | category='error') |
|
352 | category='error') | |
354 |
|
353 | |||
355 | if repo.group: |
|
354 | if repo.group: | |
356 | return redirect(url('repos_group_home', group_name=repo.group.group_name)) |
|
355 | return redirect(url('repos_group_home', group_name=repo.group.group_name)) | |
357 | return redirect(url('repos')) |
|
356 | return redirect(url('repos')) | |
358 |
|
357 | |||
359 | @HasPermissionAllDecorator('hg.admin') |
|
358 | @HasPermissionAllDecorator('hg.admin') | |
360 | def show(self, repo_name, format='html'): |
|
359 | def show(self, repo_name, format='html'): | |
361 | """GET /repos/repo_name: Show a specific item""" |
|
360 | """GET /repos/repo_name: Show a specific item""" | |
362 | # url('repo', repo_name=ID) |
|
361 | # url('repo', repo_name=ID) | |
363 |
|
362 | |||
364 | @HasRepoPermissionAllDecorator('repository.admin') |
|
363 | @HasRepoPermissionAllDecorator('repository.admin') | |
365 | def edit(self, repo_name): |
|
364 | def edit(self, repo_name): | |
366 | """GET /repo_name/settings: Form to edit an existing item""" |
|
365 | """GET /repo_name/settings: Form to edit an existing item""" | |
367 | # url('edit_repo', repo_name=ID) |
|
366 | # url('edit_repo', repo_name=ID) | |
368 | defaults = self.__load_data(repo_name) |
|
367 | defaults = self.__load_data(repo_name) | |
369 | if 'clone_uri' in defaults: |
|
368 | if 'clone_uri' in defaults: | |
370 | del defaults['clone_uri'] |
|
369 | del defaults['clone_uri'] | |
371 |
|
370 | |||
372 | c.repo_fields = RepositoryField.query()\ |
|
371 | c.repo_fields = RepositoryField.query()\ | |
373 | .filter(RepositoryField.repository == c.repo_info).all() |
|
372 | .filter(RepositoryField.repository == c.repo_info).all() | |
374 | c.active = 'settings' |
|
373 | c.active = 'settings' | |
375 | return htmlfill.render( |
|
374 | return htmlfill.render( | |
376 | render('admin/repos/repo_edit.html'), |
|
375 | render('admin/repos/repo_edit.html'), | |
377 | defaults=defaults, |
|
376 | defaults=defaults, | |
378 | encoding="UTF-8", |
|
377 | encoding="UTF-8", | |
379 | force_defaults=False) |
|
378 | force_defaults=False) | |
380 |
|
379 | |||
381 | @HasRepoPermissionAllDecorator('repository.admin') |
|
380 | @HasRepoPermissionAllDecorator('repository.admin') | |
382 | def edit_permissions(self, repo_name): |
|
381 | def edit_permissions(self, repo_name): | |
383 | """GET /repo_name/settings: Form to edit an existing item""" |
|
382 | """GET /repo_name/settings: Form to edit an existing item""" | |
384 | # url('edit_repo', repo_name=ID) |
|
383 | # url('edit_repo', repo_name=ID) | |
385 | c.repo_info = self._load_repo(repo_name) |
|
384 | c.repo_info = self._load_repo(repo_name) | |
386 | repo_model = RepoModel() |
|
385 | repo_model = RepoModel() | |
387 | c.users_array = repo_model.get_users_js() |
|
386 | c.users_array = repo_model.get_users_js() | |
388 | c.user_groups_array = repo_model.get_user_groups_js() |
|
387 | c.user_groups_array = repo_model.get_user_groups_js() | |
389 | c.active = 'permissions' |
|
388 | c.active = 'permissions' | |
390 | defaults = RepoModel()._get_defaults(repo_name) |
|
389 | defaults = RepoModel()._get_defaults(repo_name) | |
391 |
|
390 | |||
392 | return htmlfill.render( |
|
391 | return htmlfill.render( | |
393 | render('admin/repos/repo_edit.html'), |
|
392 | render('admin/repos/repo_edit.html'), | |
394 | defaults=defaults, |
|
393 | defaults=defaults, | |
395 | encoding="UTF-8", |
|
394 | encoding="UTF-8", | |
396 | force_defaults=False) |
|
395 | force_defaults=False) | |
397 |
|
396 | |||
398 | def edit_permissions_update(self, repo_name): |
|
397 | def edit_permissions_update(self, repo_name): | |
399 | form = RepoPermsForm()().to_python(request.POST) |
|
398 | form = RepoPermsForm()().to_python(request.POST) | |
400 | RepoModel()._update_permissions(repo_name, form['perms_new'], |
|
399 | RepoModel()._update_permissions(repo_name, form['perms_new'], | |
401 | form['perms_updates']) |
|
400 | form['perms_updates']) | |
402 | #TODO: implement this |
|
401 | #TODO: implement this | |
403 | #action_logger(self.authuser, 'admin_changed_repo_permissions', |
|
402 | #action_logger(self.authuser, 'admin_changed_repo_permissions', | |
404 | # repo_name, self.ip_addr, self.sa) |
|
403 | # repo_name, self.ip_addr, self.sa) | |
405 | Session().commit() |
|
404 | Session().commit() | |
406 | h.flash(_('Repository permissions updated'), category='success') |
|
405 | h.flash(_('Repository permissions updated'), category='success') | |
407 | return redirect(url('edit_repo_perms', repo_name=repo_name)) |
|
406 | return redirect(url('edit_repo_perms', repo_name=repo_name)) | |
408 |
|
407 | |||
409 | def edit_permissions_revoke(self, repo_name): |
|
408 | def edit_permissions_revoke(self, repo_name): | |
410 | try: |
|
409 | try: | |
411 | obj_type = request.POST.get('obj_type') |
|
410 | obj_type = request.POST.get('obj_type') | |
412 | obj_id = None |
|
411 | obj_id = None | |
413 | if obj_type == 'user': |
|
412 | if obj_type == 'user': | |
414 | obj_id = safe_int(request.POST.get('user_id')) |
|
413 | obj_id = safe_int(request.POST.get('user_id')) | |
415 | elif obj_type == 'user_group': |
|
414 | elif obj_type == 'user_group': | |
416 | obj_id = safe_int(request.POST.get('user_group_id')) |
|
415 | obj_id = safe_int(request.POST.get('user_group_id')) | |
417 |
|
416 | |||
418 | if obj_type == 'user': |
|
417 | if obj_type == 'user': | |
419 | RepoModel().revoke_user_permission(repo=repo_name, user=obj_id) |
|
418 | RepoModel().revoke_user_permission(repo=repo_name, user=obj_id) | |
420 | elif obj_type == 'user_group': |
|
419 | elif obj_type == 'user_group': | |
421 | RepoModel().revoke_user_group_permission( |
|
420 | RepoModel().revoke_user_group_permission( | |
422 | repo=repo_name, group_name=obj_id |
|
421 | repo=repo_name, group_name=obj_id | |
423 | ) |
|
422 | ) | |
424 | #TODO: implement this |
|
423 | #TODO: implement this | |
425 | #action_logger(self.authuser, 'admin_revoked_repo_permissions', |
|
424 | #action_logger(self.authuser, 'admin_revoked_repo_permissions', | |
426 | # repo_name, self.ip_addr, self.sa) |
|
425 | # repo_name, self.ip_addr, self.sa) | |
427 | Session().commit() |
|
426 | Session().commit() | |
428 | except Exception: |
|
427 | except Exception: | |
429 | log.error(traceback.format_exc()) |
|
428 | log.error(traceback.format_exc()) | |
430 | h.flash(_('An error occurred during revoking of permission'), |
|
429 | h.flash(_('An error occurred during revoking of permission'), | |
431 | category='error') |
|
430 | category='error') | |
432 | raise HTTPInternalServerError() |
|
431 | raise HTTPInternalServerError() | |
433 |
|
432 | |||
434 | @HasRepoPermissionAllDecorator('repository.admin') |
|
433 | @HasRepoPermissionAllDecorator('repository.admin') | |
435 | def edit_fields(self, repo_name): |
|
434 | def edit_fields(self, repo_name): | |
436 | """GET /repo_name/settings: Form to edit an existing item""" |
|
435 | """GET /repo_name/settings: Form to edit an existing item""" | |
437 | # url('edit_repo', repo_name=ID) |
|
436 | # url('edit_repo', repo_name=ID) | |
438 | c.repo_info = self._load_repo(repo_name) |
|
437 | c.repo_info = self._load_repo(repo_name) | |
439 | c.repo_fields = RepositoryField.query()\ |
|
438 | c.repo_fields = RepositoryField.query()\ | |
440 | .filter(RepositoryField.repository == c.repo_info).all() |
|
439 | .filter(RepositoryField.repository == c.repo_info).all() | |
441 | c.active = 'fields' |
|
440 | c.active = 'fields' | |
442 | if request.POST: |
|
441 | if request.POST: | |
443 |
|
442 | |||
444 | return redirect(url('repo_edit_fields')) |
|
443 | return redirect(url('repo_edit_fields')) | |
445 | return render('admin/repos/repo_edit.html') |
|
444 | return render('admin/repos/repo_edit.html') | |
446 |
|
445 | |||
447 | @HasRepoPermissionAllDecorator('repository.admin') |
|
446 | @HasRepoPermissionAllDecorator('repository.admin') | |
448 | def create_repo_field(self, repo_name): |
|
447 | def create_repo_field(self, repo_name): | |
449 | try: |
|
448 | try: | |
450 | form_result = RepoFieldForm()().to_python(dict(request.POST)) |
|
449 | form_result = RepoFieldForm()().to_python(dict(request.POST)) | |
451 | new_field = RepositoryField() |
|
450 | new_field = RepositoryField() | |
452 | new_field.repository = Repository.get_by_repo_name(repo_name) |
|
451 | new_field.repository = Repository.get_by_repo_name(repo_name) | |
453 | new_field.field_key = form_result['new_field_key'] |
|
452 | new_field.field_key = form_result['new_field_key'] | |
454 | new_field.field_type = form_result['new_field_type'] # python type |
|
453 | new_field.field_type = form_result['new_field_type'] # python type | |
455 | new_field.field_value = form_result['new_field_value'] # set initial blank value |
|
454 | new_field.field_value = form_result['new_field_value'] # set initial blank value | |
456 | new_field.field_desc = form_result['new_field_desc'] |
|
455 | new_field.field_desc = form_result['new_field_desc'] | |
457 | new_field.field_label = form_result['new_field_label'] |
|
456 | new_field.field_label = form_result['new_field_label'] | |
458 | Session().add(new_field) |
|
457 | Session().add(new_field) | |
459 | Session().commit() |
|
458 | Session().commit() | |
460 | except Exception, e: |
|
459 | except Exception, e: | |
461 | log.error(traceback.format_exc()) |
|
460 | log.error(traceback.format_exc()) | |
462 | msg = _('An error occurred during creation of field') |
|
461 | msg = _('An error occurred during creation of field') | |
463 | if isinstance(e, formencode.Invalid): |
|
462 | if isinstance(e, formencode.Invalid): | |
464 | msg += ". " + e.msg |
|
463 | msg += ". " + e.msg | |
465 | h.flash(msg, category='error') |
|
464 | h.flash(msg, category='error') | |
466 | return redirect(url('edit_repo_fields', repo_name=repo_name)) |
|
465 | return redirect(url('edit_repo_fields', repo_name=repo_name)) | |
467 |
|
466 | |||
468 | @HasRepoPermissionAllDecorator('repository.admin') |
|
467 | @HasRepoPermissionAllDecorator('repository.admin') | |
469 | def delete_repo_field(self, repo_name, field_id): |
|
468 | def delete_repo_field(self, repo_name, field_id): | |
470 | field = RepositoryField.get_or_404(field_id) |
|
469 | field = RepositoryField.get_or_404(field_id) | |
471 | try: |
|
470 | try: | |
472 | Session().delete(field) |
|
471 | Session().delete(field) | |
473 | Session().commit() |
|
472 | Session().commit() | |
474 | except Exception, e: |
|
473 | except Exception, e: | |
475 | log.error(traceback.format_exc()) |
|
474 | log.error(traceback.format_exc()) | |
476 | msg = _('An error occurred during removal of field') |
|
475 | msg = _('An error occurred during removal of field') | |
477 | h.flash(msg, category='error') |
|
476 | h.flash(msg, category='error') | |
478 | return redirect(url('edit_repo_fields', repo_name=repo_name)) |
|
477 | return redirect(url('edit_repo_fields', repo_name=repo_name)) | |
479 |
|
478 | |||
480 | @HasRepoPermissionAllDecorator('repository.admin') |
|
479 | @HasRepoPermissionAllDecorator('repository.admin') | |
481 | def edit_advanced(self, repo_name): |
|
480 | def edit_advanced(self, repo_name): | |
482 | """GET /repo_name/settings: Form to edit an existing item""" |
|
481 | """GET /repo_name/settings: Form to edit an existing item""" | |
483 | # url('edit_repo', repo_name=ID) |
|
482 | # url('edit_repo', repo_name=ID) | |
484 | c.repo_info = self._load_repo(repo_name) |
|
483 | c.repo_info = self._load_repo(repo_name) | |
485 | c.default_user_id = User.get_default_user().user_id |
|
484 | c.default_user_id = User.get_default_user().user_id | |
486 | c.in_public_journal = UserFollowing.query()\ |
|
485 | c.in_public_journal = UserFollowing.query()\ | |
487 | .filter(UserFollowing.user_id == c.default_user_id)\ |
|
486 | .filter(UserFollowing.user_id == c.default_user_id)\ | |
488 | .filter(UserFollowing.follows_repository == c.repo_info).scalar() |
|
487 | .filter(UserFollowing.follows_repository == c.repo_info).scalar() | |
489 |
|
488 | |||
490 | _repos = Repository.query().order_by(Repository.repo_name).all() |
|
489 | _repos = Repository.query().order_by(Repository.repo_name).all() | |
491 | read_access_repos = RepoList(_repos) |
|
490 | read_access_repos = RepoList(_repos) | |
492 | c.repos_list = [(None, _('-- Not a fork --'))] |
|
491 | c.repos_list = [(None, _('-- Not a fork --'))] | |
493 | c.repos_list += [(x.repo_id, x.repo_name) |
|
492 | c.repos_list += [(x.repo_id, x.repo_name) | |
494 | for x in read_access_repos |
|
493 | for x in read_access_repos | |
495 | if x.repo_id != c.repo_info.repo_id] |
|
494 | if x.repo_id != c.repo_info.repo_id] | |
496 |
|
495 | |||
497 | defaults = { |
|
496 | defaults = { | |
498 | 'id_fork_of': c.repo_info.fork.repo_id if c.repo_info.fork else '' |
|
497 | 'id_fork_of': c.repo_info.fork.repo_id if c.repo_info.fork else '' | |
499 | } |
|
498 | } | |
500 |
|
499 | |||
501 | c.active = 'advanced' |
|
500 | c.active = 'advanced' | |
502 | if request.POST: |
|
501 | if request.POST: | |
503 | return redirect(url('repo_edit_advanced')) |
|
502 | return redirect(url('repo_edit_advanced')) | |
504 | return htmlfill.render( |
|
503 | return htmlfill.render( | |
505 | render('admin/repos/repo_edit.html'), |
|
504 | render('admin/repos/repo_edit.html'), | |
506 | defaults=defaults, |
|
505 | defaults=defaults, | |
507 | encoding="UTF-8", |
|
506 | encoding="UTF-8", | |
508 | force_defaults=False) |
|
507 | force_defaults=False) | |
509 |
|
508 | |||
510 | @HasRepoPermissionAllDecorator('repository.admin') |
|
509 | @HasRepoPermissionAllDecorator('repository.admin') | |
511 | def edit_advanced_journal(self, repo_name): |
|
510 | def edit_advanced_journal(self, repo_name): | |
512 | """ |
|
511 | """ | |
513 | Sets this repository to be visible in public journal, |
|
512 | Sets this repository to be visible in public journal, | |
514 | in other words asking default user to follow this repo |
|
513 | in other words asking default user to follow this repo | |
515 |
|
514 | |||
516 | :param repo_name: |
|
515 | :param repo_name: | |
517 | """ |
|
516 | """ | |
518 |
|
517 | |||
519 | cur_token = request.POST.get('auth_token') |
|
518 | try: | |
520 | token = get_token() |
|
519 | repo_id = Repository.get_by_repo_name(repo_name).repo_id | |
521 | if cur_token == token: |
|
520 | user_id = User.get_default_user().user_id | |
522 | try: |
|
521 | self.scm_model.toggle_following_repo(repo_id, user_id) | |
523 | repo_id = Repository.get_by_repo_name(repo_name).repo_id |
|
522 | h.flash(_('Updated repository visibility in public journal'), | |
524 | user_id = User.get_default_user().user_id |
|
523 | category='success') | |
525 | self.scm_model.toggle_following_repo(repo_id, user_id) |
|
524 | Session().commit() | |
526 | h.flash(_('Updated repository visibility in public journal'), |
|
525 | except Exception: | |
527 | category='success') |
|
526 | h.flash(_('An error occurred during setting this' | |
528 | Session().commit() |
|
527 | ' repository in public journal'), | |
529 | except Exception: |
|
528 | category='error') | |
530 | h.flash(_('An error occurred during setting this' |
|
|||
531 | ' repository in public journal'), |
|
|||
532 | category='error') |
|
|||
533 |
|
||||
534 | else: |
|
|||
535 | h.flash(_('Token mismatch'), category='error') |
|
|||
536 | return redirect(url('edit_repo_advanced', repo_name=repo_name)) |
|
529 | return redirect(url('edit_repo_advanced', repo_name=repo_name)) | |
537 |
|
530 | |||
538 |
|
531 | |||
539 | @HasRepoPermissionAllDecorator('repository.admin') |
|
532 | @HasRepoPermissionAllDecorator('repository.admin') | |
540 | def edit_advanced_fork(self, repo_name): |
|
533 | def edit_advanced_fork(self, repo_name): | |
541 | """ |
|
534 | """ | |
542 | Mark given repository as a fork of another |
|
535 | Mark given repository as a fork of another | |
543 |
|
536 | |||
544 | :param repo_name: |
|
537 | :param repo_name: | |
545 | """ |
|
538 | """ | |
546 | try: |
|
539 | try: | |
547 | fork_id = request.POST.get('id_fork_of') |
|
540 | fork_id = request.POST.get('id_fork_of') | |
548 | repo = ScmModel().mark_as_fork(repo_name, fork_id, |
|
541 | repo = ScmModel().mark_as_fork(repo_name, fork_id, | |
549 | self.authuser.username) |
|
542 | self.authuser.username) | |
550 | fork = repo.fork.repo_name if repo.fork else _('Nothing') |
|
543 | fork = repo.fork.repo_name if repo.fork else _('Nothing') | |
551 | Session().commit() |
|
544 | Session().commit() | |
552 | h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork), |
|
545 | h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork), | |
553 | category='success') |
|
546 | category='success') | |
554 | except RepositoryError, e: |
|
547 | except RepositoryError, e: | |
555 | log.error(traceback.format_exc()) |
|
548 | log.error(traceback.format_exc()) | |
556 | h.flash(str(e), category='error') |
|
549 | h.flash(str(e), category='error') | |
557 | except Exception, e: |
|
550 | except Exception, e: | |
558 | log.error(traceback.format_exc()) |
|
551 | log.error(traceback.format_exc()) | |
559 | h.flash(_('An error occurred during this operation'), |
|
552 | h.flash(_('An error occurred during this operation'), | |
560 | category='error') |
|
553 | category='error') | |
561 |
|
554 | |||
562 | return redirect(url('edit_repo_advanced', repo_name=repo_name)) |
|
555 | return redirect(url('edit_repo_advanced', repo_name=repo_name)) | |
563 |
|
556 | |||
564 | @HasRepoPermissionAllDecorator('repository.admin') |
|
557 | @HasRepoPermissionAllDecorator('repository.admin') | |
565 | def edit_advanced_locking(self, repo_name): |
|
558 | def edit_advanced_locking(self, repo_name): | |
566 | """ |
|
559 | """ | |
567 | Unlock repository when it is locked ! |
|
560 | Unlock repository when it is locked ! | |
568 |
|
561 | |||
569 | :param repo_name: |
|
562 | :param repo_name: | |
570 | """ |
|
563 | """ | |
571 | try: |
|
564 | try: | |
572 | repo = Repository.get_by_repo_name(repo_name) |
|
565 | repo = Repository.get_by_repo_name(repo_name) | |
573 | if request.POST.get('set_lock'): |
|
566 | if request.POST.get('set_lock'): | |
574 | Repository.lock(repo, c.authuser.user_id) |
|
567 | Repository.lock(repo, c.authuser.user_id) | |
575 | h.flash(_('Locked repository'), category='success') |
|
568 | h.flash(_('Locked repository'), category='success') | |
576 | elif request.POST.get('set_unlock'): |
|
569 | elif request.POST.get('set_unlock'): | |
577 | Repository.unlock(repo) |
|
570 | Repository.unlock(repo) | |
578 | h.flash(_('Unlocked repository'), category='success') |
|
571 | h.flash(_('Unlocked repository'), category='success') | |
579 | except Exception, e: |
|
572 | except Exception, e: | |
580 | log.error(traceback.format_exc()) |
|
573 | log.error(traceback.format_exc()) | |
581 | h.flash(_('An error occurred during unlocking'), |
|
574 | h.flash(_('An error occurred during unlocking'), | |
582 | category='error') |
|
575 | category='error') | |
583 | return redirect(url('edit_repo_advanced', repo_name=repo_name)) |
|
576 | return redirect(url('edit_repo_advanced', repo_name=repo_name)) | |
584 |
|
577 | |||
585 | @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin') |
|
578 | @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin') | |
586 | def toggle_locking(self, repo_name): |
|
579 | def toggle_locking(self, repo_name): | |
587 | """ |
|
580 | """ | |
588 | Toggle locking of repository by simple GET call to url |
|
581 | Toggle locking of repository by simple GET call to url | |
589 |
|
582 | |||
590 | :param repo_name: |
|
583 | :param repo_name: | |
591 | """ |
|
584 | """ | |
592 |
|
585 | |||
593 | try: |
|
586 | try: | |
594 | repo = Repository.get_by_repo_name(repo_name) |
|
587 | repo = Repository.get_by_repo_name(repo_name) | |
595 |
|
588 | |||
596 | if repo.enable_locking: |
|
589 | if repo.enable_locking: | |
597 | if repo.locked[0]: |
|
590 | if repo.locked[0]: | |
598 | Repository.unlock(repo) |
|
591 | Repository.unlock(repo) | |
599 | action = _('Unlocked') |
|
592 | action = _('Unlocked') | |
600 | else: |
|
593 | else: | |
601 | Repository.lock(repo, c.authuser.user_id) |
|
594 | Repository.lock(repo, c.authuser.user_id) | |
602 | action = _('Locked') |
|
595 | action = _('Locked') | |
603 |
|
596 | |||
604 | h.flash(_('Repository has been %s') % action, |
|
597 | h.flash(_('Repository has been %s') % action, | |
605 | category='success') |
|
598 | category='success') | |
606 | except Exception, e: |
|
599 | except Exception, e: | |
607 | log.error(traceback.format_exc()) |
|
600 | log.error(traceback.format_exc()) | |
608 | h.flash(_('An error occurred during unlocking'), |
|
601 | h.flash(_('An error occurred during unlocking'), | |
609 | category='error') |
|
602 | category='error') | |
610 | return redirect(url('summary_home', repo_name=repo_name)) |
|
603 | return redirect(url('summary_home', repo_name=repo_name)) | |
611 |
|
604 | |||
612 | @HasRepoPermissionAllDecorator('repository.admin') |
|
605 | @HasRepoPermissionAllDecorator('repository.admin') | |
613 | def edit_caches(self, repo_name): |
|
606 | def edit_caches(self, repo_name): | |
614 | """GET /repo_name/settings: Form to edit an existing item""" |
|
607 | """GET /repo_name/settings: Form to edit an existing item""" | |
615 | # url('edit_repo', repo_name=ID) |
|
608 | # url('edit_repo', repo_name=ID) | |
616 | c.repo_info = self._load_repo(repo_name) |
|
609 | c.repo_info = self._load_repo(repo_name) | |
617 | c.active = 'caches' |
|
610 | c.active = 'caches' | |
618 | if request.POST: |
|
611 | if request.POST: | |
619 | try: |
|
612 | try: | |
620 | ScmModel().mark_for_invalidation(repo_name, delete=True) |
|
613 | ScmModel().mark_for_invalidation(repo_name, delete=True) | |
621 | Session().commit() |
|
614 | Session().commit() | |
622 | h.flash(_('Cache invalidation successful'), |
|
615 | h.flash(_('Cache invalidation successful'), | |
623 | category='success') |
|
616 | category='success') | |
624 | except Exception, e: |
|
617 | except Exception, e: | |
625 | log.error(traceback.format_exc()) |
|
618 | log.error(traceback.format_exc()) | |
626 | h.flash(_('An error occurred during cache invalidation'), |
|
619 | h.flash(_('An error occurred during cache invalidation'), | |
627 | category='error') |
|
620 | category='error') | |
628 |
|
621 | |||
629 | return redirect(url('edit_repo_caches', repo_name=c.repo_name)) |
|
622 | return redirect(url('edit_repo_caches', repo_name=c.repo_name)) | |
630 | return render('admin/repos/repo_edit.html') |
|
623 | return render('admin/repos/repo_edit.html') | |
631 |
|
624 | |||
632 | @HasRepoPermissionAllDecorator('repository.admin') |
|
625 | @HasRepoPermissionAllDecorator('repository.admin') | |
633 | def edit_remote(self, repo_name): |
|
626 | def edit_remote(self, repo_name): | |
634 | """GET /repo_name/settings: Form to edit an existing item""" |
|
627 | """GET /repo_name/settings: Form to edit an existing item""" | |
635 | # url('edit_repo', repo_name=ID) |
|
628 | # url('edit_repo', repo_name=ID) | |
636 | c.repo_info = self._load_repo(repo_name) |
|
629 | c.repo_info = self._load_repo(repo_name) | |
637 | c.active = 'remote' |
|
630 | c.active = 'remote' | |
638 | if request.POST: |
|
631 | if request.POST: | |
639 | try: |
|
632 | try: | |
640 | ScmModel().pull_changes(repo_name, self.authuser.username) |
|
633 | ScmModel().pull_changes(repo_name, self.authuser.username) | |
641 | h.flash(_('Pulled from remote location'), category='success') |
|
634 | h.flash(_('Pulled from remote location'), category='success') | |
642 | except Exception, e: |
|
635 | except Exception, e: | |
643 | log.error(traceback.format_exc()) |
|
636 | log.error(traceback.format_exc()) | |
644 | h.flash(_('An error occurred during pull from remote location'), |
|
637 | h.flash(_('An error occurred during pull from remote location'), | |
645 | category='error') |
|
638 | category='error') | |
646 | return redirect(url('edit_repo_remote', repo_name=c.repo_name)) |
|
639 | return redirect(url('edit_repo_remote', repo_name=c.repo_name)) | |
647 | return render('admin/repos/repo_edit.html') |
|
640 | return render('admin/repos/repo_edit.html') | |
648 |
|
641 | |||
649 | @HasRepoPermissionAllDecorator('repository.admin') |
|
642 | @HasRepoPermissionAllDecorator('repository.admin') | |
650 | def edit_statistics(self, repo_name): |
|
643 | def edit_statistics(self, repo_name): | |
651 | """GET /repo_name/settings: Form to edit an existing item""" |
|
644 | """GET /repo_name/settings: Form to edit an existing item""" | |
652 | # url('edit_repo', repo_name=ID) |
|
645 | # url('edit_repo', repo_name=ID) | |
653 | c.repo_info = self._load_repo(repo_name) |
|
646 | c.repo_info = self._load_repo(repo_name) | |
654 | repo = c.repo_info.scm_instance |
|
647 | repo = c.repo_info.scm_instance | |
655 |
|
648 | |||
656 | if c.repo_info.stats: |
|
649 | if c.repo_info.stats: | |
657 | # this is on what revision we ended up so we add +1 for count |
|
650 | # this is on what revision we ended up so we add +1 for count | |
658 | last_rev = c.repo_info.stats.stat_on_revision + 1 |
|
651 | last_rev = c.repo_info.stats.stat_on_revision + 1 | |
659 | else: |
|
652 | else: | |
660 | last_rev = 0 |
|
653 | last_rev = 0 | |
661 | c.stats_revision = last_rev |
|
654 | c.stats_revision = last_rev | |
662 |
|
655 | |||
663 | c.repo_last_rev = repo.count() if repo.revisions else 0 |
|
656 | c.repo_last_rev = repo.count() if repo.revisions else 0 | |
664 |
|
657 | |||
665 | if last_rev == 0 or c.repo_last_rev == 0: |
|
658 | if last_rev == 0 or c.repo_last_rev == 0: | |
666 | c.stats_percentage = 0 |
|
659 | c.stats_percentage = 0 | |
667 | else: |
|
660 | else: | |
668 | c.stats_percentage = '%.2f' % ((float((last_rev)) / c.repo_last_rev) * 100) |
|
661 | c.stats_percentage = '%.2f' % ((float((last_rev)) / c.repo_last_rev) * 100) | |
669 |
|
662 | |||
670 | c.active = 'statistics' |
|
663 | c.active = 'statistics' | |
671 | if request.POST: |
|
664 | if request.POST: | |
672 | try: |
|
665 | try: | |
673 | RepoModel().delete_stats(repo_name) |
|
666 | RepoModel().delete_stats(repo_name) | |
674 | Session().commit() |
|
667 | Session().commit() | |
675 | except Exception, e: |
|
668 | except Exception, e: | |
676 | log.error(traceback.format_exc()) |
|
669 | log.error(traceback.format_exc()) | |
677 | h.flash(_('An error occurred during deletion of repository stats'), |
|
670 | h.flash(_('An error occurred during deletion of repository stats'), | |
678 | category='error') |
|
671 | category='error') | |
679 | return redirect(url('edit_repo_statistics', repo_name=c.repo_name)) |
|
672 | return redirect(url('edit_repo_statistics', repo_name=c.repo_name)) | |
680 |
|
673 | |||
681 | return render('admin/repos/repo_edit.html') |
|
674 | return render('admin/repos/repo_edit.html') |
@@ -1,379 +1,374 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | # This program is free software: you can redistribute it and/or modify |
|
2 | # This program is free software: you can redistribute it and/or modify | |
3 | # it under the terms of the GNU General Public License as published by |
|
3 | # it under the terms of the GNU General Public License as published by | |
4 | # the Free Software Foundation, either version 3 of the License, or |
|
4 | # the Free Software Foundation, either version 3 of the License, or | |
5 | # (at your option) any later version. |
|
5 | # (at your option) any later version. | |
6 | # |
|
6 | # | |
7 | # This program is distributed in the hope that it will be useful, |
|
7 | # This program is distributed in the hope that it will be useful, | |
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | # GNU General Public License for more details. |
|
10 | # GNU General Public License for more details. | |
11 | # |
|
11 | # | |
12 | # You should have received a copy of the GNU General Public License |
|
12 | # You should have received a copy of the GNU General Public License | |
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
14 | """ |
|
14 | """ | |
15 | kallithea.controllers.journal |
|
15 | kallithea.controllers.journal | |
16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
17 |
|
17 | |||
18 | Journal controller for pylons |
|
18 | Journal controller for pylons | |
19 |
|
19 | |||
20 | This file was forked by the Kallithea project in July 2014. |
|
20 | This file was forked by the Kallithea project in July 2014. | |
21 | Original author and date, and relevant copyright and licensing information is below: |
|
21 | Original author and date, and relevant copyright and licensing information is below: | |
22 | :created_on: Nov 21, 2010 |
|
22 | :created_on: Nov 21, 2010 | |
23 | :author: marcink |
|
23 | :author: marcink | |
24 | :copyright: (c) 2013 RhodeCode GmbH, and others. |
|
24 | :copyright: (c) 2013 RhodeCode GmbH, and others. | |
25 | :license: GPLv3, see LICENSE.md for more details. |
|
25 | :license: GPLv3, see LICENSE.md for more details. | |
26 |
|
26 | |||
27 | """ |
|
27 | """ | |
28 |
|
28 | |||
29 | import logging |
|
29 | import logging | |
30 | import traceback |
|
30 | import traceback | |
31 | from itertools import groupby |
|
31 | from itertools import groupby | |
32 |
|
32 | |||
33 | from sqlalchemy import or_ |
|
33 | from sqlalchemy import or_ | |
34 | from sqlalchemy.orm import joinedload |
|
34 | from sqlalchemy.orm import joinedload | |
35 | from sqlalchemy.sql.expression import func |
|
35 | from sqlalchemy.sql.expression import func | |
36 |
|
36 | |||
37 | from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed |
|
37 | from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed | |
38 |
|
38 | |||
39 | from webob.exc import HTTPBadRequest |
|
39 | from webob.exc import HTTPBadRequest | |
40 | from pylons import request, tmpl_context as c, response, url |
|
40 | from pylons import request, tmpl_context as c, response, url | |
41 | from pylons.i18n.translation import _ |
|
41 | from pylons.i18n.translation import _ | |
42 |
|
42 | |||
43 | from kallithea.controllers.admin.admin import _journal_filter |
|
43 | from kallithea.controllers.admin.admin import _journal_filter | |
44 | from kallithea.model.db import UserLog, UserFollowing, Repository, User |
|
44 | from kallithea.model.db import UserLog, UserFollowing, Repository, User | |
45 | from kallithea.model.meta import Session |
|
45 | from kallithea.model.meta import Session | |
46 | from kallithea.model.repo import RepoModel |
|
46 | from kallithea.model.repo import RepoModel | |
47 | import kallithea.lib.helpers as h |
|
47 | import kallithea.lib.helpers as h | |
48 | from kallithea.lib.helpers import Page |
|
48 | from kallithea.lib.helpers import Page | |
49 | from kallithea.lib.auth import LoginRequired, NotAnonymous |
|
49 | from kallithea.lib.auth import LoginRequired, NotAnonymous | |
50 | from kallithea.lib.base import BaseController, render |
|
50 | from kallithea.lib.base import BaseController, render | |
51 | from kallithea.lib.utils2 import safe_int, AttributeDict |
|
51 | from kallithea.lib.utils2 import safe_int, AttributeDict | |
52 | from kallithea.lib.compat import json |
|
52 | from kallithea.lib.compat import json | |
53 |
|
53 | |||
54 | log = logging.getLogger(__name__) |
|
54 | log = logging.getLogger(__name__) | |
55 |
|
55 | |||
56 |
|
56 | |||
57 | class JournalController(BaseController): |
|
57 | class JournalController(BaseController): | |
58 |
|
58 | |||
59 | def __before__(self): |
|
59 | def __before__(self): | |
60 | super(JournalController, self).__before__() |
|
60 | super(JournalController, self).__before__() | |
61 | self.language = 'en-us' |
|
61 | self.language = 'en-us' | |
62 | self.ttl = "5" |
|
62 | self.ttl = "5" | |
63 | self.feed_nr = 20 |
|
63 | self.feed_nr = 20 | |
64 | c.search_term = request.GET.get('filter') |
|
64 | c.search_term = request.GET.get('filter') | |
65 |
|
65 | |||
66 | def _get_daily_aggregate(self, journal): |
|
66 | def _get_daily_aggregate(self, journal): | |
67 | groups = [] |
|
67 | groups = [] | |
68 | for k, g in groupby(journal, lambda x: x.action_as_day): |
|
68 | for k, g in groupby(journal, lambda x: x.action_as_day): | |
69 | user_group = [] |
|
69 | user_group = [] | |
70 | #groupby username if it's a present value, else fallback to journal username |
|
70 | #groupby username if it's a present value, else fallback to journal username | |
71 | for _unused, g2 in groupby(list(g), lambda x: x.user.username if x.user else x.username): |
|
71 | for _unused, g2 in groupby(list(g), lambda x: x.user.username if x.user else x.username): | |
72 | l = list(g2) |
|
72 | l = list(g2) | |
73 | user_group.append((l[0].user, l)) |
|
73 | user_group.append((l[0].user, l)) | |
74 |
|
74 | |||
75 | groups.append((k, user_group,)) |
|
75 | groups.append((k, user_group,)) | |
76 |
|
76 | |||
77 | return groups |
|
77 | return groups | |
78 |
|
78 | |||
79 | def _get_journal_data(self, following_repos): |
|
79 | def _get_journal_data(self, following_repos): | |
80 | repo_ids = [x.follows_repository.repo_id for x in following_repos |
|
80 | repo_ids = [x.follows_repository.repo_id for x in following_repos | |
81 | if x.follows_repository is not None] |
|
81 | if x.follows_repository is not None] | |
82 | user_ids = [x.follows_user.user_id for x in following_repos |
|
82 | user_ids = [x.follows_user.user_id for x in following_repos | |
83 | if x.follows_user is not None] |
|
83 | if x.follows_user is not None] | |
84 |
|
84 | |||
85 | filtering_criterion = None |
|
85 | filtering_criterion = None | |
86 |
|
86 | |||
87 | if repo_ids and user_ids: |
|
87 | if repo_ids and user_ids: | |
88 | filtering_criterion = or_(UserLog.repository_id.in_(repo_ids), |
|
88 | filtering_criterion = or_(UserLog.repository_id.in_(repo_ids), | |
89 | UserLog.user_id.in_(user_ids)) |
|
89 | UserLog.user_id.in_(user_ids)) | |
90 | if repo_ids and not user_ids: |
|
90 | if repo_ids and not user_ids: | |
91 | filtering_criterion = UserLog.repository_id.in_(repo_ids) |
|
91 | filtering_criterion = UserLog.repository_id.in_(repo_ids) | |
92 | if not repo_ids and user_ids: |
|
92 | if not repo_ids and user_ids: | |
93 | filtering_criterion = UserLog.user_id.in_(user_ids) |
|
93 | filtering_criterion = UserLog.user_id.in_(user_ids) | |
94 | if filtering_criterion is not None: |
|
94 | if filtering_criterion is not None: | |
95 | journal = self.sa.query(UserLog)\ |
|
95 | journal = self.sa.query(UserLog)\ | |
96 | .options(joinedload(UserLog.user))\ |
|
96 | .options(joinedload(UserLog.user))\ | |
97 | .options(joinedload(UserLog.repository)) |
|
97 | .options(joinedload(UserLog.repository)) | |
98 | #filter |
|
98 | #filter | |
99 | journal = _journal_filter(journal, c.search_term) |
|
99 | journal = _journal_filter(journal, c.search_term) | |
100 | journal = journal.filter(filtering_criterion)\ |
|
100 | journal = journal.filter(filtering_criterion)\ | |
101 | .order_by(UserLog.action_date.desc()) |
|
101 | .order_by(UserLog.action_date.desc()) | |
102 | else: |
|
102 | else: | |
103 | journal = [] |
|
103 | journal = [] | |
104 |
|
104 | |||
105 | return journal |
|
105 | return journal | |
106 |
|
106 | |||
107 | def _atom_feed(self, repos, public=True): |
|
107 | def _atom_feed(self, repos, public=True): | |
108 | journal = self._get_journal_data(repos) |
|
108 | journal = self._get_journal_data(repos) | |
109 | if public: |
|
109 | if public: | |
110 | _link = h.canonical_url('public_journal_atom') |
|
110 | _link = h.canonical_url('public_journal_atom') | |
111 | _desc = '%s %s %s' % (c.site_name, _('public journal'), |
|
111 | _desc = '%s %s %s' % (c.site_name, _('public journal'), | |
112 | 'atom feed') |
|
112 | 'atom feed') | |
113 | else: |
|
113 | else: | |
114 | _link = h.canonical_url('journal_atom') |
|
114 | _link = h.canonical_url('journal_atom') | |
115 | _desc = '%s %s %s' % (c.site_name, _('journal'), 'atom feed') |
|
115 | _desc = '%s %s %s' % (c.site_name, _('journal'), 'atom feed') | |
116 |
|
116 | |||
117 | feed = Atom1Feed(title=_desc, |
|
117 | feed = Atom1Feed(title=_desc, | |
118 | link=_link, |
|
118 | link=_link, | |
119 | description=_desc, |
|
119 | description=_desc, | |
120 | language=self.language, |
|
120 | language=self.language, | |
121 | ttl=self.ttl) |
|
121 | ttl=self.ttl) | |
122 |
|
122 | |||
123 | for entry in journal[:self.feed_nr]: |
|
123 | for entry in journal[:self.feed_nr]: | |
124 | user = entry.user |
|
124 | user = entry.user | |
125 | if user is None: |
|
125 | if user is None: | |
126 | #fix deleted users |
|
126 | #fix deleted users | |
127 | user = AttributeDict({'short_contact': entry.username, |
|
127 | user = AttributeDict({'short_contact': entry.username, | |
128 | 'email': '', |
|
128 | 'email': '', | |
129 | 'full_contact': ''}) |
|
129 | 'full_contact': ''}) | |
130 | action, action_extra, ico = h.action_parser(entry, feed=True) |
|
130 | action, action_extra, ico = h.action_parser(entry, feed=True) | |
131 | title = "%s - %s %s" % (user.short_contact, action(), |
|
131 | title = "%s - %s %s" % (user.short_contact, action(), | |
132 | entry.repository.repo_name) |
|
132 | entry.repository.repo_name) | |
133 | desc = action_extra() |
|
133 | desc = action_extra() | |
134 | _url = None |
|
134 | _url = None | |
135 | if entry.repository is not None: |
|
135 | if entry.repository is not None: | |
136 | _url = h.canonical_url('changelog_home', |
|
136 | _url = h.canonical_url('changelog_home', | |
137 | repo_name=entry.repository.repo_name) |
|
137 | repo_name=entry.repository.repo_name) | |
138 |
|
138 | |||
139 | feed.add_item(title=title, |
|
139 | feed.add_item(title=title, | |
140 | pubdate=entry.action_date, |
|
140 | pubdate=entry.action_date, | |
141 | link=_url or h.canonical_url(''), |
|
141 | link=_url or h.canonical_url(''), | |
142 | author_email=user.email, |
|
142 | author_email=user.email, | |
143 | author_name=user.full_contact, |
|
143 | author_name=user.full_contact, | |
144 | description=desc) |
|
144 | description=desc) | |
145 |
|
145 | |||
146 | response.content_type = feed.mime_type |
|
146 | response.content_type = feed.mime_type | |
147 | return feed.writeString('utf-8') |
|
147 | return feed.writeString('utf-8') | |
148 |
|
148 | |||
149 | def _rss_feed(self, repos, public=True): |
|
149 | def _rss_feed(self, repos, public=True): | |
150 | journal = self._get_journal_data(repos) |
|
150 | journal = self._get_journal_data(repos) | |
151 | if public: |
|
151 | if public: | |
152 | _link = h.canonical_url('public_journal_atom') |
|
152 | _link = h.canonical_url('public_journal_atom') | |
153 | _desc = '%s %s %s' % (c.site_name, _('public journal'), |
|
153 | _desc = '%s %s %s' % (c.site_name, _('public journal'), | |
154 | 'rss feed') |
|
154 | 'rss feed') | |
155 | else: |
|
155 | else: | |
156 | _link = h.canonical_url('journal_atom') |
|
156 | _link = h.canonical_url('journal_atom') | |
157 | _desc = '%s %s %s' % (c.site_name, _('journal'), 'rss feed') |
|
157 | _desc = '%s %s %s' % (c.site_name, _('journal'), 'rss feed') | |
158 |
|
158 | |||
159 | feed = Rss201rev2Feed(title=_desc, |
|
159 | feed = Rss201rev2Feed(title=_desc, | |
160 | link=_link, |
|
160 | link=_link, | |
161 | description=_desc, |
|
161 | description=_desc, | |
162 | language=self.language, |
|
162 | language=self.language, | |
163 | ttl=self.ttl) |
|
163 | ttl=self.ttl) | |
164 |
|
164 | |||
165 | for entry in journal[:self.feed_nr]: |
|
165 | for entry in journal[:self.feed_nr]: | |
166 | user = entry.user |
|
166 | user = entry.user | |
167 | if user is None: |
|
167 | if user is None: | |
168 | #fix deleted users |
|
168 | #fix deleted users | |
169 | user = AttributeDict({'short_contact': entry.username, |
|
169 | user = AttributeDict({'short_contact': entry.username, | |
170 | 'email': '', |
|
170 | 'email': '', | |
171 | 'full_contact': ''}) |
|
171 | 'full_contact': ''}) | |
172 | action, action_extra, ico = h.action_parser(entry, feed=True) |
|
172 | action, action_extra, ico = h.action_parser(entry, feed=True) | |
173 | title = "%s - %s %s" % (user.short_contact, action(), |
|
173 | title = "%s - %s %s" % (user.short_contact, action(), | |
174 | entry.repository.repo_name) |
|
174 | entry.repository.repo_name) | |
175 | desc = action_extra() |
|
175 | desc = action_extra() | |
176 | _url = None |
|
176 | _url = None | |
177 | if entry.repository is not None: |
|
177 | if entry.repository is not None: | |
178 | _url = h.canonical_url('changelog_home', |
|
178 | _url = h.canonical_url('changelog_home', | |
179 | repo_name=entry.repository.repo_name) |
|
179 | repo_name=entry.repository.repo_name) | |
180 |
|
180 | |||
181 | feed.add_item(title=title, |
|
181 | feed.add_item(title=title, | |
182 | pubdate=entry.action_date, |
|
182 | pubdate=entry.action_date, | |
183 | link=_url or h.canonical_url(''), |
|
183 | link=_url or h.canonical_url(''), | |
184 | author_email=user.email, |
|
184 | author_email=user.email, | |
185 | author_name=user.full_contact, |
|
185 | author_name=user.full_contact, | |
186 | description=desc) |
|
186 | description=desc) | |
187 |
|
187 | |||
188 | response.content_type = feed.mime_type |
|
188 | response.content_type = feed.mime_type | |
189 | return feed.writeString('utf-8') |
|
189 | return feed.writeString('utf-8') | |
190 |
|
190 | |||
191 | @LoginRequired() |
|
191 | @LoginRequired() | |
192 | @NotAnonymous() |
|
192 | @NotAnonymous() | |
193 | def index(self): |
|
193 | def index(self): | |
194 | # Return a rendered template |
|
194 | # Return a rendered template | |
195 | p = safe_int(request.GET.get('page', 1), 1) |
|
195 | p = safe_int(request.GET.get('page', 1), 1) | |
196 | c.user = User.get(self.authuser.user_id) |
|
196 | c.user = User.get(self.authuser.user_id) | |
197 | c.following = self.sa.query(UserFollowing)\ |
|
197 | c.following = self.sa.query(UserFollowing)\ | |
198 | .filter(UserFollowing.user_id == self.authuser.user_id)\ |
|
198 | .filter(UserFollowing.user_id == self.authuser.user_id)\ | |
199 | .options(joinedload(UserFollowing.follows_repository))\ |
|
199 | .options(joinedload(UserFollowing.follows_repository))\ | |
200 | .all() |
|
200 | .all() | |
201 |
|
201 | |||
202 | journal = self._get_journal_data(c.following) |
|
202 | journal = self._get_journal_data(c.following) | |
203 |
|
203 | |||
204 | def url_generator(**kw): |
|
204 | def url_generator(**kw): | |
205 | return url.current(filter=c.search_term, **kw) |
|
205 | return url.current(filter=c.search_term, **kw) | |
206 |
|
206 | |||
207 | c.journal_pager = Page(journal, page=p, items_per_page=20, url=url_generator) |
|
207 | c.journal_pager = Page(journal, page=p, items_per_page=20, url=url_generator) | |
208 | c.journal_day_aggreagate = self._get_daily_aggregate(c.journal_pager) |
|
208 | c.journal_day_aggreagate = self._get_daily_aggregate(c.journal_pager) | |
209 |
|
209 | |||
210 | if request.environ.get('HTTP_X_PARTIAL_XHR'): |
|
210 | if request.environ.get('HTTP_X_PARTIAL_XHR'): | |
211 | return render('journal/journal_data.html') |
|
211 | return render('journal/journal_data.html') | |
212 |
|
212 | |||
213 | repos_list = Session().query(Repository)\ |
|
213 | repos_list = Session().query(Repository)\ | |
214 | .filter(Repository.user_id == |
|
214 | .filter(Repository.user_id == | |
215 | self.authuser.user_id)\ |
|
215 | self.authuser.user_id)\ | |
216 | .order_by(func.lower(Repository.repo_name)).all() |
|
216 | .order_by(func.lower(Repository.repo_name)).all() | |
217 |
|
217 | |||
218 | repos_data = RepoModel().get_repos_as_dict(repos_list=repos_list, |
|
218 | repos_data = RepoModel().get_repos_as_dict(repos_list=repos_list, | |
219 | admin=True) |
|
219 | admin=True) | |
220 | #json used to render the grid |
|
220 | #json used to render the grid | |
221 | c.data = json.dumps(repos_data) |
|
221 | c.data = json.dumps(repos_data) | |
222 |
|
222 | |||
223 | watched_repos_data = [] |
|
223 | watched_repos_data = [] | |
224 |
|
224 | |||
225 | ## watched repos |
|
225 | ## watched repos | |
226 | _render = RepoModel._render_datatable |
|
226 | _render = RepoModel._render_datatable | |
227 |
|
227 | |||
228 | def quick_menu(repo_name): |
|
228 | def quick_menu(repo_name): | |
229 | return _render('quick_menu', repo_name) |
|
229 | return _render('quick_menu', repo_name) | |
230 |
|
230 | |||
231 | def repo_lnk(name, rtype, rstate, private, fork_of): |
|
231 | def repo_lnk(name, rtype, rstate, private, fork_of): | |
232 | return _render('repo_name', name, rtype, rstate, private, fork_of, |
|
232 | return _render('repo_name', name, rtype, rstate, private, fork_of, | |
233 | short_name=False, admin=False) |
|
233 | short_name=False, admin=False) | |
234 |
|
234 | |||
235 | def last_rev(repo_name, cs_cache): |
|
235 | def last_rev(repo_name, cs_cache): | |
236 | return _render('revision', repo_name, cs_cache.get('revision'), |
|
236 | return _render('revision', repo_name, cs_cache.get('revision'), | |
237 | cs_cache.get('raw_id'), cs_cache.get('author'), |
|
237 | cs_cache.get('raw_id'), cs_cache.get('author'), | |
238 | cs_cache.get('message')) |
|
238 | cs_cache.get('message')) | |
239 |
|
239 | |||
240 | def desc(desc): |
|
240 | def desc(desc): | |
241 | from pylons import tmpl_context as c |
|
241 | from pylons import tmpl_context as c | |
242 | if c.visual.stylify_metatags: |
|
242 | if c.visual.stylify_metatags: | |
243 | return h.urlify_text(h.desc_stylize(h.truncate(desc, 60))) |
|
243 | return h.urlify_text(h.desc_stylize(h.truncate(desc, 60))) | |
244 | else: |
|
244 | else: | |
245 | return h.urlify_text(h.truncate(desc, 60)) |
|
245 | return h.urlify_text(h.truncate(desc, 60)) | |
246 |
|
246 | |||
247 | def repo_actions(repo_name): |
|
247 | def repo_actions(repo_name): | |
248 | return _render('repo_actions', repo_name) |
|
248 | return _render('repo_actions', repo_name) | |
249 |
|
249 | |||
250 | def owner_actions(user_id, username): |
|
250 | def owner_actions(user_id, username): | |
251 | return _render('user_name', user_id, username) |
|
251 | return _render('user_name', user_id, username) | |
252 |
|
252 | |||
253 | def toogle_follow(repo_id): |
|
253 | def toogle_follow(repo_id): | |
254 | return _render('toggle_follow', repo_id) |
|
254 | return _render('toggle_follow', repo_id) | |
255 |
|
255 | |||
256 | for entry in c.following: |
|
256 | for entry in c.following: | |
257 | repo = entry.follows_repository |
|
257 | repo = entry.follows_repository | |
258 | cs_cache = repo.changeset_cache |
|
258 | cs_cache = repo.changeset_cache | |
259 | row = { |
|
259 | row = { | |
260 | "menu": quick_menu(repo.repo_name), |
|
260 | "menu": quick_menu(repo.repo_name), | |
261 | "raw_name": repo.repo_name.lower(), |
|
261 | "raw_name": repo.repo_name.lower(), | |
262 | "name": repo_lnk(repo.repo_name, repo.repo_type, |
|
262 | "name": repo_lnk(repo.repo_name, repo.repo_type, | |
263 | repo.repo_state, repo.private, repo.fork), |
|
263 | repo.repo_state, repo.private, repo.fork), | |
264 | "last_changeset": last_rev(repo.repo_name, cs_cache), |
|
264 | "last_changeset": last_rev(repo.repo_name, cs_cache), | |
265 | "last_rev_raw": cs_cache.get('revision'), |
|
265 | "last_rev_raw": cs_cache.get('revision'), | |
266 | "action": toogle_follow(repo.repo_id) |
|
266 | "action": toogle_follow(repo.repo_id) | |
267 | } |
|
267 | } | |
268 |
|
268 | |||
269 | watched_repos_data.append(row) |
|
269 | watched_repos_data.append(row) | |
270 |
|
270 | |||
271 | c.watched_data = json.dumps({ |
|
271 | c.watched_data = json.dumps({ | |
272 | "totalRecords": len(c.following), |
|
272 | "totalRecords": len(c.following), | |
273 | "startIndex": 0, |
|
273 | "startIndex": 0, | |
274 | "sort": "name", |
|
274 | "sort": "name", | |
275 | "dir": "asc", |
|
275 | "dir": "asc", | |
276 | "records": watched_repos_data |
|
276 | "records": watched_repos_data | |
277 | }) |
|
277 | }) | |
278 | return render('journal/journal.html') |
|
278 | return render('journal/journal.html') | |
279 |
|
279 | |||
280 | @LoginRequired(api_access=True) |
|
280 | @LoginRequired(api_access=True) | |
281 | @NotAnonymous() |
|
281 | @NotAnonymous() | |
282 | def journal_atom(self): |
|
282 | def journal_atom(self): | |
283 | """ |
|
283 | """ | |
284 | Produce an atom-1.0 feed via feedgenerator module |
|
284 | Produce an atom-1.0 feed via feedgenerator module | |
285 | """ |
|
285 | """ | |
286 | following = self.sa.query(UserFollowing)\ |
|
286 | following = self.sa.query(UserFollowing)\ | |
287 | .filter(UserFollowing.user_id == self.authuser.user_id)\ |
|
287 | .filter(UserFollowing.user_id == self.authuser.user_id)\ | |
288 | .options(joinedload(UserFollowing.follows_repository))\ |
|
288 | .options(joinedload(UserFollowing.follows_repository))\ | |
289 | .all() |
|
289 | .all() | |
290 | return self._atom_feed(following, public=False) |
|
290 | return self._atom_feed(following, public=False) | |
291 |
|
291 | |||
292 | @LoginRequired(api_access=True) |
|
292 | @LoginRequired(api_access=True) | |
293 | @NotAnonymous() |
|
293 | @NotAnonymous() | |
294 | def journal_rss(self): |
|
294 | def journal_rss(self): | |
295 | """ |
|
295 | """ | |
296 | Produce an rss feed via feedgenerator module |
|
296 | Produce an rss feed via feedgenerator module | |
297 | """ |
|
297 | """ | |
298 | following = self.sa.query(UserFollowing)\ |
|
298 | following = self.sa.query(UserFollowing)\ | |
299 | .filter(UserFollowing.user_id == self.authuser.user_id)\ |
|
299 | .filter(UserFollowing.user_id == self.authuser.user_id)\ | |
300 | .options(joinedload(UserFollowing.follows_repository))\ |
|
300 | .options(joinedload(UserFollowing.follows_repository))\ | |
301 | .all() |
|
301 | .all() | |
302 | return self._rss_feed(following, public=False) |
|
302 | return self._rss_feed(following, public=False) | |
303 |
|
303 | |||
304 | @LoginRequired() |
|
304 | @LoginRequired() | |
305 | @NotAnonymous() |
|
305 | @NotAnonymous() | |
306 | def toggle_following(self): |
|
306 | def toggle_following(self): | |
307 |
|
|
307 | user_id = request.POST.get('follows_user_id') | |
308 | token = h.get_token() |
|
308 | if user_id: | |
309 | if cur_token == token: |
|
309 | try: | |
|
310 | self.scm_model.toggle_following_user(user_id, | |||
|
311 | self.authuser.user_id) | |||
|
312 | Session.commit() | |||
|
313 | return 'ok' | |||
|
314 | except Exception: | |||
|
315 | log.error(traceback.format_exc()) | |||
|
316 | raise HTTPBadRequest() | |||
310 |
|
317 | |||
311 |
|
|
318 | repo_id = request.POST.get('follows_repo_id') | |
312 |
|
|
319 | if repo_id: | |
313 |
|
|
320 | try: | |
314 |
|
|
321 | self.scm_model.toggle_following_repo(repo_id, | |
315 |
|
|
322 | self.authuser.user_id) | |
316 |
|
|
323 | Session.commit() | |
317 |
|
|
324 | return 'ok' | |
318 |
|
|
325 | except Exception: | |
319 |
|
|
326 | log.error(traceback.format_exc()) | |
320 |
|
|
327 | raise HTTPBadRequest() | |
321 |
|
328 | |||
322 | repo_id = request.POST.get('follows_repo_id') |
|
|||
323 | if repo_id: |
|
|||
324 | try: |
|
|||
325 | self.scm_model.toggle_following_repo(repo_id, |
|
|||
326 | self.authuser.user_id) |
|
|||
327 | Session.commit() |
|
|||
328 | return 'ok' |
|
|||
329 | except Exception: |
|
|||
330 | log.error(traceback.format_exc()) |
|
|||
331 | raise HTTPBadRequest() |
|
|||
332 |
|
||||
333 | log.debug('token mismatch %s vs %s' % (cur_token, token)) |
|
|||
334 | raise HTTPBadRequest() |
|
329 | raise HTTPBadRequest() | |
335 |
|
330 | |||
336 | @LoginRequired() |
|
331 | @LoginRequired() | |
337 | def public_journal(self): |
|
332 | def public_journal(self): | |
338 | # Return a rendered template |
|
333 | # Return a rendered template | |
339 | p = safe_int(request.GET.get('page', 1), 1) |
|
334 | p = safe_int(request.GET.get('page', 1), 1) | |
340 |
|
335 | |||
341 | c.following = self.sa.query(UserFollowing)\ |
|
336 | c.following = self.sa.query(UserFollowing)\ | |
342 | .filter(UserFollowing.user_id == self.authuser.user_id)\ |
|
337 | .filter(UserFollowing.user_id == self.authuser.user_id)\ | |
343 | .options(joinedload(UserFollowing.follows_repository))\ |
|
338 | .options(joinedload(UserFollowing.follows_repository))\ | |
344 | .all() |
|
339 | .all() | |
345 |
|
340 | |||
346 | journal = self._get_journal_data(c.following) |
|
341 | journal = self._get_journal_data(c.following) | |
347 |
|
342 | |||
348 | c.journal_pager = Page(journal, page=p, items_per_page=20) |
|
343 | c.journal_pager = Page(journal, page=p, items_per_page=20) | |
349 |
|
344 | |||
350 | c.journal_day_aggreagate = self._get_daily_aggregate(c.journal_pager) |
|
345 | c.journal_day_aggreagate = self._get_daily_aggregate(c.journal_pager) | |
351 |
|
346 | |||
352 | if request.environ.get('HTTP_X_PARTIAL_XHR'): |
|
347 | if request.environ.get('HTTP_X_PARTIAL_XHR'): | |
353 | return render('journal/journal_data.html') |
|
348 | return render('journal/journal_data.html') | |
354 |
|
349 | |||
355 | return render('journal/public_journal.html') |
|
350 | return render('journal/public_journal.html') | |
356 |
|
351 | |||
357 | @LoginRequired(api_access=True) |
|
352 | @LoginRequired(api_access=True) | |
358 | def public_journal_atom(self): |
|
353 | def public_journal_atom(self): | |
359 | """ |
|
354 | """ | |
360 | Produce an atom-1.0 feed via feedgenerator module |
|
355 | Produce an atom-1.0 feed via feedgenerator module | |
361 | """ |
|
356 | """ | |
362 | c.following = self.sa.query(UserFollowing)\ |
|
357 | c.following = self.sa.query(UserFollowing)\ | |
363 | .filter(UserFollowing.user_id == self.authuser.user_id)\ |
|
358 | .filter(UserFollowing.user_id == self.authuser.user_id)\ | |
364 | .options(joinedload(UserFollowing.follows_repository))\ |
|
359 | .options(joinedload(UserFollowing.follows_repository))\ | |
365 | .all() |
|
360 | .all() | |
366 |
|
361 | |||
367 | return self._atom_feed(c.following) |
|
362 | return self._atom_feed(c.following) | |
368 |
|
363 | |||
369 | @LoginRequired(api_access=True) |
|
364 | @LoginRequired(api_access=True) | |
370 | def public_journal_rss(self): |
|
365 | def public_journal_rss(self): | |
371 | """ |
|
366 | """ | |
372 | Produce an rss2 feed via feedgenerator module |
|
367 | Produce an rss2 feed via feedgenerator module | |
373 | """ |
|
368 | """ | |
374 | c.following = self.sa.query(UserFollowing)\ |
|
369 | c.following = self.sa.query(UserFollowing)\ | |
375 | .filter(UserFollowing.user_id == self.authuser.user_id)\ |
|
370 | .filter(UserFollowing.user_id == self.authuser.user_id)\ | |
376 | .options(joinedload(UserFollowing.follows_repository))\ |
|
371 | .options(joinedload(UserFollowing.follows_repository))\ | |
377 | .all() |
|
372 | .all() | |
378 |
|
373 | |||
379 | return self._rss_feed(c.following) |
|
374 | return self._rss_feed(c.following) |
@@ -1,1469 +1,1452 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | # This program is free software: you can redistribute it and/or modify |
|
2 | # This program is free software: you can redistribute it and/or modify | |
3 | # it under the terms of the GNU General Public License as published by |
|
3 | # it under the terms of the GNU General Public License as published by | |
4 | # the Free Software Foundation, either version 3 of the License, or |
|
4 | # the Free Software Foundation, either version 3 of the License, or | |
5 | # (at your option) any later version. |
|
5 | # (at your option) any later version. | |
6 | # |
|
6 | # | |
7 | # This program is distributed in the hope that it will be useful, |
|
7 | # This program is distributed in the hope that it will be useful, | |
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | # GNU General Public License for more details. |
|
10 | # GNU General Public License for more details. | |
11 | # |
|
11 | # | |
12 | # You should have received a copy of the GNU General Public License |
|
12 | # You should have received a copy of the GNU General Public License | |
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
14 | """ |
|
14 | """ | |
15 | Helper functions |
|
15 | Helper functions | |
16 |
|
16 | |||
17 | Consists of functions to typically be used within templates, but also |
|
17 | Consists of functions to typically be used within templates, but also | |
18 | available to Controllers. This module is available to both as 'h'. |
|
18 | available to Controllers. This module is available to both as 'h'. | |
19 | """ |
|
19 | """ | |
20 | import random |
|
20 | import random | |
21 | import hashlib |
|
21 | import hashlib | |
22 | import StringIO |
|
22 | import StringIO | |
23 | import math |
|
23 | import math | |
24 | import logging |
|
24 | import logging | |
25 | import re |
|
25 | import re | |
26 | import urlparse |
|
26 | import urlparse | |
27 | import textwrap |
|
27 | import textwrap | |
28 |
|
28 | |||
29 | from pygments.formatters.html import HtmlFormatter |
|
29 | from pygments.formatters.html import HtmlFormatter | |
30 | from pygments import highlight as code_highlight |
|
30 | from pygments import highlight as code_highlight | |
31 | from pylons import url |
|
31 | from pylons import url | |
32 | from pylons.i18n.translation import _, ungettext |
|
32 | from pylons.i18n.translation import _, ungettext | |
33 | from hashlib import md5 |
|
33 | from hashlib import md5 | |
34 |
|
34 | |||
35 | from webhelpers.html import literal, HTML, escape |
|
35 | from webhelpers.html import literal, HTML, escape | |
36 | from webhelpers.html.tools import * |
|
36 | from webhelpers.html.tools import * | |
37 | from webhelpers.html.builder import make_tag |
|
37 | from webhelpers.html.builder import make_tag | |
38 | from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \ |
|
38 | from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \ | |
39 | end_form, file, form, hidden, image, javascript_link, link_to, \ |
|
39 | end_form, file, form, hidden, image, javascript_link, link_to, \ | |
40 | link_to_if, link_to_unless, ol, required_legend, select, stylesheet_link, \ |
|
40 | link_to_if, link_to_unless, ol, required_legend, select, stylesheet_link, \ | |
41 | submit, text, password, textarea, title, ul, xml_declaration, radio |
|
41 | submit, text, password, textarea, title, ul, xml_declaration, radio | |
42 | from webhelpers.html.tools import auto_link, button_to, highlight, \ |
|
42 | from webhelpers.html.tools import auto_link, button_to, highlight, \ | |
43 | js_obfuscate, mail_to, strip_links, strip_tags, tag_re |
|
43 | js_obfuscate, mail_to, strip_links, strip_tags, tag_re | |
44 | from webhelpers.number import format_byte_size, format_bit_size |
|
44 | from webhelpers.number import format_byte_size, format_bit_size | |
45 | from webhelpers.pylonslib import Flash as _Flash |
|
45 | from webhelpers.pylonslib import Flash as _Flash | |
46 | from webhelpers.pylonslib.secure_form import secure_form |
|
46 | from webhelpers.pylonslib.secure_form import secure_form | |
47 | from webhelpers.text import chop_at, collapse, convert_accented_entities, \ |
|
47 | from webhelpers.text import chop_at, collapse, convert_accented_entities, \ | |
48 | convert_misc_entities, lchop, plural, rchop, remove_formatting, \ |
|
48 | convert_misc_entities, lchop, plural, rchop, remove_formatting, \ | |
49 | replace_whitespace, urlify, truncate, wrap_paragraphs |
|
49 | replace_whitespace, urlify, truncate, wrap_paragraphs | |
50 | from webhelpers.date import time_ago_in_words |
|
50 | from webhelpers.date import time_ago_in_words | |
51 | from webhelpers.paginate import Page as _Page |
|
51 | from webhelpers.paginate import Page as _Page | |
52 | from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \ |
|
52 | from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \ | |
53 | convert_boolean_attrs, NotGiven, _make_safe_id_component |
|
53 | convert_boolean_attrs, NotGiven, _make_safe_id_component | |
54 |
|
54 | |||
55 | from kallithea.lib.annotate import annotate_highlight |
|
55 | from kallithea.lib.annotate import annotate_highlight | |
56 | from kallithea.lib.utils import repo_name_slug, get_custom_lexer |
|
56 | from kallithea.lib.utils import repo_name_slug, get_custom_lexer | |
57 | from kallithea.lib.utils2 import str2bool, safe_unicode, safe_str, \ |
|
57 | from kallithea.lib.utils2 import str2bool, safe_unicode, safe_str, \ | |
58 | get_changeset_safe, datetime_to_time, time_to_datetime, AttributeDict,\ |
|
58 | get_changeset_safe, datetime_to_time, time_to_datetime, AttributeDict,\ | |
59 | safe_int |
|
59 | safe_int | |
60 | from kallithea.lib.markup_renderer import MarkupRenderer, url_re |
|
60 | from kallithea.lib.markup_renderer import MarkupRenderer, url_re | |
61 | from kallithea.lib.vcs.exceptions import ChangesetDoesNotExistError |
|
61 | from kallithea.lib.vcs.exceptions import ChangesetDoesNotExistError | |
62 | from kallithea.lib.vcs.backends.base import BaseChangeset, EmptyChangeset |
|
62 | from kallithea.lib.vcs.backends.base import BaseChangeset, EmptyChangeset | |
63 | from kallithea.config.conf import DATE_FORMAT, DATETIME_FORMAT |
|
63 | from kallithea.config.conf import DATE_FORMAT, DATETIME_FORMAT | |
64 | from kallithea.model.changeset_status import ChangesetStatusModel |
|
64 | from kallithea.model.changeset_status import ChangesetStatusModel | |
65 | from kallithea.model.db import URL_SEP, Permission |
|
65 | from kallithea.model.db import URL_SEP, Permission | |
66 |
|
66 | |||
67 | log = logging.getLogger(__name__) |
|
67 | log = logging.getLogger(__name__) | |
68 |
|
68 | |||
69 |
|
69 | |||
70 | def canonical_url(*args, **kargs): |
|
70 | def canonical_url(*args, **kargs): | |
71 | '''Like url(x, qualified=True), but returns url that not only is qualified |
|
71 | '''Like url(x, qualified=True), but returns url that not only is qualified | |
72 | but also canonical, as configured in canonical_url''' |
|
72 | but also canonical, as configured in canonical_url''' | |
73 | from kallithea import CONFIG |
|
73 | from kallithea import CONFIG | |
74 | try: |
|
74 | try: | |
75 | parts = CONFIG.get('canonical_url', '').split('://', 1) |
|
75 | parts = CONFIG.get('canonical_url', '').split('://', 1) | |
76 | kargs['host'] = parts[1].split('/', 1)[0] |
|
76 | kargs['host'] = parts[1].split('/', 1)[0] | |
77 | kargs['protocol'] = parts[0] |
|
77 | kargs['protocol'] = parts[0] | |
78 | except IndexError: |
|
78 | except IndexError: | |
79 | kargs['qualified'] = True |
|
79 | kargs['qualified'] = True | |
80 | return url(*args, **kargs) |
|
80 | return url(*args, **kargs) | |
81 |
|
81 | |||
82 | def canonical_hostname(): |
|
82 | def canonical_hostname(): | |
83 | '''Return canonical hostname of system''' |
|
83 | '''Return canonical hostname of system''' | |
84 | from kallithea import CONFIG |
|
84 | from kallithea import CONFIG | |
85 | try: |
|
85 | try: | |
86 | parts = CONFIG.get('canonical_url', '').split('://', 1) |
|
86 | parts = CONFIG.get('canonical_url', '').split('://', 1) | |
87 | return parts[1].split('/', 1)[0] |
|
87 | return parts[1].split('/', 1)[0] | |
88 | except IndexError: |
|
88 | except IndexError: | |
89 | parts = url('home', qualified=True).split('://', 1) |
|
89 | parts = url('home', qualified=True).split('://', 1) | |
90 | return parts[1].split('/', 1)[0] |
|
90 | return parts[1].split('/', 1)[0] | |
91 |
|
91 | |||
92 | def html_escape(text, html_escape_table=None): |
|
92 | def html_escape(text, html_escape_table=None): | |
93 | """Produce entities within text.""" |
|
93 | """Produce entities within text.""" | |
94 | if not html_escape_table: |
|
94 | if not html_escape_table: | |
95 | html_escape_table = { |
|
95 | html_escape_table = { | |
96 | "&": "&", |
|
96 | "&": "&", | |
97 | '"': """, |
|
97 | '"': """, | |
98 | "'": "'", |
|
98 | "'": "'", | |
99 | ">": ">", |
|
99 | ">": ">", | |
100 | "<": "<", |
|
100 | "<": "<", | |
101 | } |
|
101 | } | |
102 | return "".join(html_escape_table.get(c, c) for c in text) |
|
102 | return "".join(html_escape_table.get(c, c) for c in text) | |
103 |
|
103 | |||
104 |
|
104 | |||
105 | def shorter(text, size=20): |
|
105 | def shorter(text, size=20): | |
106 | postfix = '...' |
|
106 | postfix = '...' | |
107 | if len(text) > size: |
|
107 | if len(text) > size: | |
108 | return text[:size - len(postfix)] + postfix |
|
108 | return text[:size - len(postfix)] + postfix | |
109 | return text |
|
109 | return text | |
110 |
|
110 | |||
111 |
|
111 | |||
112 | def _reset(name, value=None, id=NotGiven, type="reset", **attrs): |
|
112 | def _reset(name, value=None, id=NotGiven, type="reset", **attrs): | |
113 | """ |
|
113 | """ | |
114 | Reset button |
|
114 | Reset button | |
115 | """ |
|
115 | """ | |
116 | _set_input_attrs(attrs, type, name, value) |
|
116 | _set_input_attrs(attrs, type, name, value) | |
117 | _set_id_attr(attrs, id, name) |
|
117 | _set_id_attr(attrs, id, name) | |
118 | convert_boolean_attrs(attrs, ["disabled"]) |
|
118 | convert_boolean_attrs(attrs, ["disabled"]) | |
119 | return HTML.input(**attrs) |
|
119 | return HTML.input(**attrs) | |
120 |
|
120 | |||
121 | reset = _reset |
|
121 | reset = _reset | |
122 | safeid = _make_safe_id_component |
|
122 | safeid = _make_safe_id_component | |
123 |
|
123 | |||
124 |
|
124 | |||
125 | def FID(raw_id, path): |
|
125 | def FID(raw_id, path): | |
126 | """ |
|
126 | """ | |
127 | Creates a unique ID for filenode based on it's hash of path and revision |
|
127 | Creates a unique ID for filenode based on it's hash of path and revision | |
128 | it's safe to use in urls |
|
128 | it's safe to use in urls | |
129 |
|
129 | |||
130 | :param raw_id: |
|
130 | :param raw_id: | |
131 | :param path: |
|
131 | :param path: | |
132 | """ |
|
132 | """ | |
133 |
|
133 | |||
134 | return 'C-%s-%s' % (short_id(raw_id), md5(safe_str(path)).hexdigest()[:12]) |
|
134 | return 'C-%s-%s' % (short_id(raw_id), md5(safe_str(path)).hexdigest()[:12]) | |
135 |
|
135 | |||
136 |
|
136 | |||
137 | def get_token(): |
|
|||
138 | """Return the current authentication token, creating one if one doesn't |
|
|||
139 | already exist. |
|
|||
140 | """ |
|
|||
141 | token_key = "_authentication_token" |
|
|||
142 | from pylons import session |
|
|||
143 | if not token_key in session: |
|
|||
144 | try: |
|
|||
145 | token = hashlib.sha1(str(random.getrandbits(128))).hexdigest() |
|
|||
146 | except AttributeError: # Python < 2.4 |
|
|||
147 | token = hashlib.sha1(str(random.randrange(2 ** 128))).hexdigest() |
|
|||
148 | session[token_key] = token |
|
|||
149 | if hasattr(session, 'save'): |
|
|||
150 | session.save() |
|
|||
151 | return session[token_key] |
|
|||
152 |
|
||||
153 |
|
||||
154 | class _GetError(object): |
|
137 | class _GetError(object): | |
155 | """Get error from form_errors, and represent it as span wrapped error |
|
138 | """Get error from form_errors, and represent it as span wrapped error | |
156 | message |
|
139 | message | |
157 |
|
140 | |||
158 | :param field_name: field to fetch errors for |
|
141 | :param field_name: field to fetch errors for | |
159 | :param form_errors: form errors dict |
|
142 | :param form_errors: form errors dict | |
160 | """ |
|
143 | """ | |
161 |
|
144 | |||
162 | def __call__(self, field_name, form_errors): |
|
145 | def __call__(self, field_name, form_errors): | |
163 | tmpl = """<span class="error_msg">%s</span>""" |
|
146 | tmpl = """<span class="error_msg">%s</span>""" | |
164 | if form_errors and field_name in form_errors: |
|
147 | if form_errors and field_name in form_errors: | |
165 | return literal(tmpl % form_errors.get(field_name)) |
|
148 | return literal(tmpl % form_errors.get(field_name)) | |
166 |
|
149 | |||
167 | get_error = _GetError() |
|
150 | get_error = _GetError() | |
168 |
|
151 | |||
169 |
|
152 | |||
170 | class _ToolTip(object): |
|
153 | class _ToolTip(object): | |
171 |
|
154 | |||
172 | def __call__(self, tooltip_title, trim_at=50): |
|
155 | def __call__(self, tooltip_title, trim_at=50): | |
173 | """ |
|
156 | """ | |
174 | Special function just to wrap our text into nice formatted |
|
157 | Special function just to wrap our text into nice formatted | |
175 | autowrapped text |
|
158 | autowrapped text | |
176 |
|
159 | |||
177 | :param tooltip_title: |
|
160 | :param tooltip_title: | |
178 | """ |
|
161 | """ | |
179 | tooltip_title = escape(tooltip_title) |
|
162 | tooltip_title = escape(tooltip_title) | |
180 | tooltip_title = tooltip_title.replace('<', '<').replace('>', '>') |
|
163 | tooltip_title = tooltip_title.replace('<', '<').replace('>', '>') | |
181 | return tooltip_title |
|
164 | return tooltip_title | |
182 | tooltip = _ToolTip() |
|
165 | tooltip = _ToolTip() | |
183 |
|
166 | |||
184 |
|
167 | |||
185 | class _FilesBreadCrumbs(object): |
|
168 | class _FilesBreadCrumbs(object): | |
186 |
|
169 | |||
187 | def __call__(self, repo_name, rev, paths): |
|
170 | def __call__(self, repo_name, rev, paths): | |
188 | if isinstance(paths, str): |
|
171 | if isinstance(paths, str): | |
189 | paths = safe_unicode(paths) |
|
172 | paths = safe_unicode(paths) | |
190 | url_l = [link_to(repo_name, url('files_home', |
|
173 | url_l = [link_to(repo_name, url('files_home', | |
191 | repo_name=repo_name, |
|
174 | repo_name=repo_name, | |
192 | revision=rev, f_path=''), |
|
175 | revision=rev, f_path=''), | |
193 | class_='ypjax-link')] |
|
176 | class_='ypjax-link')] | |
194 | paths_l = paths.split('/') |
|
177 | paths_l = paths.split('/') | |
195 | for cnt, p in enumerate(paths_l): |
|
178 | for cnt, p in enumerate(paths_l): | |
196 | if p != '': |
|
179 | if p != '': | |
197 | url_l.append(link_to(p, |
|
180 | url_l.append(link_to(p, | |
198 | url('files_home', |
|
181 | url('files_home', | |
199 | repo_name=repo_name, |
|
182 | repo_name=repo_name, | |
200 | revision=rev, |
|
183 | revision=rev, | |
201 | f_path='/'.join(paths_l[:cnt + 1]) |
|
184 | f_path='/'.join(paths_l[:cnt + 1]) | |
202 | ), |
|
185 | ), | |
203 | class_='ypjax-link' |
|
186 | class_='ypjax-link' | |
204 | ) |
|
187 | ) | |
205 | ) |
|
188 | ) | |
206 |
|
189 | |||
207 | return literal('/'.join(url_l)) |
|
190 | return literal('/'.join(url_l)) | |
208 |
|
191 | |||
209 | files_breadcrumbs = _FilesBreadCrumbs() |
|
192 | files_breadcrumbs = _FilesBreadCrumbs() | |
210 |
|
193 | |||
211 |
|
194 | |||
212 | class CodeHtmlFormatter(HtmlFormatter): |
|
195 | class CodeHtmlFormatter(HtmlFormatter): | |
213 | """ |
|
196 | """ | |
214 | My code Html Formatter for source codes |
|
197 | My code Html Formatter for source codes | |
215 | """ |
|
198 | """ | |
216 |
|
199 | |||
217 | def wrap(self, source, outfile): |
|
200 | def wrap(self, source, outfile): | |
218 | return self._wrap_div(self._wrap_pre(self._wrap_code(source))) |
|
201 | return self._wrap_div(self._wrap_pre(self._wrap_code(source))) | |
219 |
|
202 | |||
220 | def _wrap_code(self, source): |
|
203 | def _wrap_code(self, source): | |
221 | for cnt, it in enumerate(source): |
|
204 | for cnt, it in enumerate(source): | |
222 | i, t = it |
|
205 | i, t = it | |
223 | t = '<div id="L%s">%s</div>' % (cnt + 1, t) |
|
206 | t = '<div id="L%s">%s</div>' % (cnt + 1, t) | |
224 | yield i, t |
|
207 | yield i, t | |
225 |
|
208 | |||
226 | def _wrap_tablelinenos(self, inner): |
|
209 | def _wrap_tablelinenos(self, inner): | |
227 | dummyoutfile = StringIO.StringIO() |
|
210 | dummyoutfile = StringIO.StringIO() | |
228 | lncount = 0 |
|
211 | lncount = 0 | |
229 | for t, line in inner: |
|
212 | for t, line in inner: | |
230 | if t: |
|
213 | if t: | |
231 | lncount += 1 |
|
214 | lncount += 1 | |
232 | dummyoutfile.write(line) |
|
215 | dummyoutfile.write(line) | |
233 |
|
216 | |||
234 | fl = self.linenostart |
|
217 | fl = self.linenostart | |
235 | mw = len(str(lncount + fl - 1)) |
|
218 | mw = len(str(lncount + fl - 1)) | |
236 | sp = self.linenospecial |
|
219 | sp = self.linenospecial | |
237 | st = self.linenostep |
|
220 | st = self.linenostep | |
238 | la = self.lineanchors |
|
221 | la = self.lineanchors | |
239 | aln = self.anchorlinenos |
|
222 | aln = self.anchorlinenos | |
240 | nocls = self.noclasses |
|
223 | nocls = self.noclasses | |
241 | if sp: |
|
224 | if sp: | |
242 | lines = [] |
|
225 | lines = [] | |
243 |
|
226 | |||
244 | for i in range(fl, fl + lncount): |
|
227 | for i in range(fl, fl + lncount): | |
245 | if i % st == 0: |
|
228 | if i % st == 0: | |
246 | if i % sp == 0: |
|
229 | if i % sp == 0: | |
247 | if aln: |
|
230 | if aln: | |
248 | lines.append('<a href="#%s%d" class="special">%*d</a>' % |
|
231 | lines.append('<a href="#%s%d" class="special">%*d</a>' % | |
249 | (la, i, mw, i)) |
|
232 | (la, i, mw, i)) | |
250 | else: |
|
233 | else: | |
251 | lines.append('<span class="special">%*d</span>' % (mw, i)) |
|
234 | lines.append('<span class="special">%*d</span>' % (mw, i)) | |
252 | else: |
|
235 | else: | |
253 | if aln: |
|
236 | if aln: | |
254 | lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i)) |
|
237 | lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i)) | |
255 | else: |
|
238 | else: | |
256 | lines.append('%*d' % (mw, i)) |
|
239 | lines.append('%*d' % (mw, i)) | |
257 | else: |
|
240 | else: | |
258 | lines.append('') |
|
241 | lines.append('') | |
259 | ls = '\n'.join(lines) |
|
242 | ls = '\n'.join(lines) | |
260 | else: |
|
243 | else: | |
261 | lines = [] |
|
244 | lines = [] | |
262 | for i in range(fl, fl + lncount): |
|
245 | for i in range(fl, fl + lncount): | |
263 | if i % st == 0: |
|
246 | if i % st == 0: | |
264 | if aln: |
|
247 | if aln: | |
265 | lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i)) |
|
248 | lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i)) | |
266 | else: |
|
249 | else: | |
267 | lines.append('%*d' % (mw, i)) |
|
250 | lines.append('%*d' % (mw, i)) | |
268 | else: |
|
251 | else: | |
269 | lines.append('') |
|
252 | lines.append('') | |
270 | ls = '\n'.join(lines) |
|
253 | ls = '\n'.join(lines) | |
271 |
|
254 | |||
272 | # in case you wonder about the seemingly redundant <div> here: since the |
|
255 | # in case you wonder about the seemingly redundant <div> here: since the | |
273 | # content in the other cell also is wrapped in a div, some browsers in |
|
256 | # content in the other cell also is wrapped in a div, some browsers in | |
274 | # some configurations seem to mess up the formatting... |
|
257 | # some configurations seem to mess up the formatting... | |
275 | if nocls: |
|
258 | if nocls: | |
276 | yield 0, ('<table class="%stable">' % self.cssclass + |
|
259 | yield 0, ('<table class="%stable">' % self.cssclass + | |
277 | '<tr><td><div class="linenodiv" ' |
|
260 | '<tr><td><div class="linenodiv" ' | |
278 | 'style="background-color: #f0f0f0; padding-right: 10px">' |
|
261 | 'style="background-color: #f0f0f0; padding-right: 10px">' | |
279 | '<pre style="line-height: 125%">' + |
|
262 | '<pre style="line-height: 125%">' + | |
280 | ls + '</pre></div></td><td id="hlcode" class="code">') |
|
263 | ls + '</pre></div></td><td id="hlcode" class="code">') | |
281 | else: |
|
264 | else: | |
282 | yield 0, ('<table class="%stable">' % self.cssclass + |
|
265 | yield 0, ('<table class="%stable">' % self.cssclass + | |
283 | '<tr><td class="linenos"><div class="linenodiv"><pre>' + |
|
266 | '<tr><td class="linenos"><div class="linenodiv"><pre>' + | |
284 | ls + '</pre></div></td><td id="hlcode" class="code">') |
|
267 | ls + '</pre></div></td><td id="hlcode" class="code">') | |
285 | yield 0, dummyoutfile.getvalue() |
|
268 | yield 0, dummyoutfile.getvalue() | |
286 | yield 0, '</td></tr></table>' |
|
269 | yield 0, '</td></tr></table>' | |
287 |
|
270 | |||
288 |
|
271 | |||
289 | _whitespace_re = re.compile(r'(\t)|( )(?=\n|</div>)') |
|
272 | _whitespace_re = re.compile(r'(\t)|( )(?=\n|</div>)') | |
290 |
|
273 | |||
291 | def _markup_whitespace(m): |
|
274 | def _markup_whitespace(m): | |
292 | groups = m.groups() |
|
275 | groups = m.groups() | |
293 | if groups[0]: |
|
276 | if groups[0]: | |
294 | return '<u>\t</u>' |
|
277 | return '<u>\t</u>' | |
295 | if groups[1]: |
|
278 | if groups[1]: | |
296 | return ' <i></i>' |
|
279 | return ' <i></i>' | |
297 |
|
280 | |||
298 | def markup_whitespace(s): |
|
281 | def markup_whitespace(s): | |
299 | return _whitespace_re.sub(_markup_whitespace, s) |
|
282 | return _whitespace_re.sub(_markup_whitespace, s) | |
300 |
|
283 | |||
301 | def pygmentize(filenode, **kwargs): |
|
284 | def pygmentize(filenode, **kwargs): | |
302 | """ |
|
285 | """ | |
303 | pygmentize function using pygments |
|
286 | pygmentize function using pygments | |
304 |
|
287 | |||
305 | :param filenode: |
|
288 | :param filenode: | |
306 | """ |
|
289 | """ | |
307 | lexer = get_custom_lexer(filenode.extension) or filenode.lexer |
|
290 | lexer = get_custom_lexer(filenode.extension) or filenode.lexer | |
308 | return literal(markup_whitespace( |
|
291 | return literal(markup_whitespace( | |
309 | code_highlight(filenode.content, lexer, CodeHtmlFormatter(**kwargs)))) |
|
292 | code_highlight(filenode.content, lexer, CodeHtmlFormatter(**kwargs)))) | |
310 |
|
293 | |||
311 |
|
294 | |||
312 | def pygmentize_annotation(repo_name, filenode, **kwargs): |
|
295 | def pygmentize_annotation(repo_name, filenode, **kwargs): | |
313 | """ |
|
296 | """ | |
314 | pygmentize function for annotation |
|
297 | pygmentize function for annotation | |
315 |
|
298 | |||
316 | :param filenode: |
|
299 | :param filenode: | |
317 | """ |
|
300 | """ | |
318 |
|
301 | |||
319 | color_dict = {} |
|
302 | color_dict = {} | |
320 |
|
303 | |||
321 | def gen_color(n=10000): |
|
304 | def gen_color(n=10000): | |
322 | """generator for getting n of evenly distributed colors using |
|
305 | """generator for getting n of evenly distributed colors using | |
323 | hsv color and golden ratio. It always return same order of colors |
|
306 | hsv color and golden ratio. It always return same order of colors | |
324 |
|
307 | |||
325 | :returns: RGB tuple |
|
308 | :returns: RGB tuple | |
326 | """ |
|
309 | """ | |
327 |
|
310 | |||
328 | def hsv_to_rgb(h, s, v): |
|
311 | def hsv_to_rgb(h, s, v): | |
329 | if s == 0.0: |
|
312 | if s == 0.0: | |
330 | return v, v, v |
|
313 | return v, v, v | |
331 | i = int(h * 6.0) # XXX assume int() truncates! |
|
314 | i = int(h * 6.0) # XXX assume int() truncates! | |
332 | f = (h * 6.0) - i |
|
315 | f = (h * 6.0) - i | |
333 | p = v * (1.0 - s) |
|
316 | p = v * (1.0 - s) | |
334 | q = v * (1.0 - s * f) |
|
317 | q = v * (1.0 - s * f) | |
335 | t = v * (1.0 - s * (1.0 - f)) |
|
318 | t = v * (1.0 - s * (1.0 - f)) | |
336 | i = i % 6 |
|
319 | i = i % 6 | |
337 | if i == 0: |
|
320 | if i == 0: | |
338 | return v, t, p |
|
321 | return v, t, p | |
339 | if i == 1: |
|
322 | if i == 1: | |
340 | return q, v, p |
|
323 | return q, v, p | |
341 | if i == 2: |
|
324 | if i == 2: | |
342 | return p, v, t |
|
325 | return p, v, t | |
343 | if i == 3: |
|
326 | if i == 3: | |
344 | return p, q, v |
|
327 | return p, q, v | |
345 | if i == 4: |
|
328 | if i == 4: | |
346 | return t, p, v |
|
329 | return t, p, v | |
347 | if i == 5: |
|
330 | if i == 5: | |
348 | return v, p, q |
|
331 | return v, p, q | |
349 |
|
332 | |||
350 | golden_ratio = 0.618033988749895 |
|
333 | golden_ratio = 0.618033988749895 | |
351 | h = 0.22717784590367374 |
|
334 | h = 0.22717784590367374 | |
352 |
|
335 | |||
353 | for _unused in xrange(n): |
|
336 | for _unused in xrange(n): | |
354 | h += golden_ratio |
|
337 | h += golden_ratio | |
355 | h %= 1 |
|
338 | h %= 1 | |
356 | HSV_tuple = [h, 0.95, 0.95] |
|
339 | HSV_tuple = [h, 0.95, 0.95] | |
357 | RGB_tuple = hsv_to_rgb(*HSV_tuple) |
|
340 | RGB_tuple = hsv_to_rgb(*HSV_tuple) | |
358 | yield map(lambda x: str(int(x * 256)), RGB_tuple) |
|
341 | yield map(lambda x: str(int(x * 256)), RGB_tuple) | |
359 |
|
342 | |||
360 | cgenerator = gen_color() |
|
343 | cgenerator = gen_color() | |
361 |
|
344 | |||
362 | def get_color_string(cs): |
|
345 | def get_color_string(cs): | |
363 | if cs in color_dict: |
|
346 | if cs in color_dict: | |
364 | col = color_dict[cs] |
|
347 | col = color_dict[cs] | |
365 | else: |
|
348 | else: | |
366 | col = color_dict[cs] = cgenerator.next() |
|
349 | col = color_dict[cs] = cgenerator.next() | |
367 | return "color: rgb(%s)! important;" % (', '.join(col)) |
|
350 | return "color: rgb(%s)! important;" % (', '.join(col)) | |
368 |
|
351 | |||
369 | def url_func(repo_name): |
|
352 | def url_func(repo_name): | |
370 |
|
353 | |||
371 | def _url_func(changeset): |
|
354 | def _url_func(changeset): | |
372 | author = changeset.author |
|
355 | author = changeset.author | |
373 | date = changeset.date |
|
356 | date = changeset.date | |
374 | message = tooltip(changeset.message) |
|
357 | message = tooltip(changeset.message) | |
375 |
|
358 | |||
376 | tooltip_html = ("<div style='font-size:0.8em'><b>Author:</b>" |
|
359 | tooltip_html = ("<div style='font-size:0.8em'><b>Author:</b>" | |
377 | " %s<br/><b>Date:</b> %s</b><br/><b>Message:" |
|
360 | " %s<br/><b>Date:</b> %s</b><br/><b>Message:" | |
378 | "</b> %s<br/></div>") |
|
361 | "</b> %s<br/></div>") | |
379 |
|
362 | |||
380 | tooltip_html = tooltip_html % (author, date, message) |
|
363 | tooltip_html = tooltip_html % (author, date, message) | |
381 | lnk_format = show_id(changeset) |
|
364 | lnk_format = show_id(changeset) | |
382 | uri = link_to( |
|
365 | uri = link_to( | |
383 | lnk_format, |
|
366 | lnk_format, | |
384 | url('changeset_home', repo_name=repo_name, |
|
367 | url('changeset_home', repo_name=repo_name, | |
385 | revision=changeset.raw_id), |
|
368 | revision=changeset.raw_id), | |
386 | style=get_color_string(changeset.raw_id), |
|
369 | style=get_color_string(changeset.raw_id), | |
387 | class_='tooltip', |
|
370 | class_='tooltip', | |
388 | title=tooltip_html |
|
371 | title=tooltip_html | |
389 | ) |
|
372 | ) | |
390 |
|
373 | |||
391 | uri += '\n' |
|
374 | uri += '\n' | |
392 | return uri |
|
375 | return uri | |
393 | return _url_func |
|
376 | return _url_func | |
394 |
|
377 | |||
395 | return literal(markup_whitespace(annotate_highlight(filenode, url_func(repo_name), **kwargs))) |
|
378 | return literal(markup_whitespace(annotate_highlight(filenode, url_func(repo_name), **kwargs))) | |
396 |
|
379 | |||
397 |
|
380 | |||
398 | def is_following_repo(repo_name, user_id): |
|
381 | def is_following_repo(repo_name, user_id): | |
399 | from kallithea.model.scm import ScmModel |
|
382 | from kallithea.model.scm import ScmModel | |
400 | return ScmModel().is_following_repo(repo_name, user_id) |
|
383 | return ScmModel().is_following_repo(repo_name, user_id) | |
401 |
|
384 | |||
402 | class _Message(object): |
|
385 | class _Message(object): | |
403 | """A message returned by ``Flash.pop_messages()``. |
|
386 | """A message returned by ``Flash.pop_messages()``. | |
404 |
|
387 | |||
405 | Converting the message to a string returns the message text. Instances |
|
388 | Converting the message to a string returns the message text. Instances | |
406 | also have the following attributes: |
|
389 | also have the following attributes: | |
407 |
|
390 | |||
408 | * ``message``: the message text. |
|
391 | * ``message``: the message text. | |
409 | * ``category``: the category specified when the message was created. |
|
392 | * ``category``: the category specified when the message was created. | |
410 | """ |
|
393 | """ | |
411 |
|
394 | |||
412 | def __init__(self, category, message): |
|
395 | def __init__(self, category, message): | |
413 | self.category = category |
|
396 | self.category = category | |
414 | self.message = message |
|
397 | self.message = message | |
415 |
|
398 | |||
416 | def __str__(self): |
|
399 | def __str__(self): | |
417 | return self.message |
|
400 | return self.message | |
418 |
|
401 | |||
419 | __unicode__ = __str__ |
|
402 | __unicode__ = __str__ | |
420 |
|
403 | |||
421 | def __html__(self): |
|
404 | def __html__(self): | |
422 | return escape(safe_unicode(self.message)) |
|
405 | return escape(safe_unicode(self.message)) | |
423 |
|
406 | |||
424 | class Flash(_Flash): |
|
407 | class Flash(_Flash): | |
425 |
|
408 | |||
426 | def pop_messages(self): |
|
409 | def pop_messages(self): | |
427 | """Return all accumulated messages and delete them from the session. |
|
410 | """Return all accumulated messages and delete them from the session. | |
428 |
|
411 | |||
429 | The return value is a list of ``Message`` objects. |
|
412 | The return value is a list of ``Message`` objects. | |
430 | """ |
|
413 | """ | |
431 | from pylons import session |
|
414 | from pylons import session | |
432 | messages = session.pop(self.session_key, []) |
|
415 | messages = session.pop(self.session_key, []) | |
433 | session.save() |
|
416 | session.save() | |
434 | return [_Message(*m) for m in messages] |
|
417 | return [_Message(*m) for m in messages] | |
435 |
|
418 | |||
436 | flash = Flash() |
|
419 | flash = Flash() | |
437 |
|
420 | |||
438 | #============================================================================== |
|
421 | #============================================================================== | |
439 | # SCM FILTERS available via h. |
|
422 | # SCM FILTERS available via h. | |
440 | #============================================================================== |
|
423 | #============================================================================== | |
441 | from kallithea.lib.vcs.utils import author_name, author_email |
|
424 | from kallithea.lib.vcs.utils import author_name, author_email | |
442 | from kallithea.lib.utils2 import credentials_filter, age as _age |
|
425 | from kallithea.lib.utils2 import credentials_filter, age as _age | |
443 | from kallithea.model.db import User, ChangesetStatus |
|
426 | from kallithea.model.db import User, ChangesetStatus | |
444 |
|
427 | |||
445 | age = lambda x, y=False: _age(x, y) |
|
428 | age = lambda x, y=False: _age(x, y) | |
446 | capitalize = lambda x: x.capitalize() |
|
429 | capitalize = lambda x: x.capitalize() | |
447 | email = author_email |
|
430 | email = author_email | |
448 | short_id = lambda x: x[:12] |
|
431 | short_id = lambda x: x[:12] | |
449 | hide_credentials = lambda x: ''.join(credentials_filter(x)) |
|
432 | hide_credentials = lambda x: ''.join(credentials_filter(x)) | |
450 |
|
433 | |||
451 |
|
434 | |||
452 | def show_id(cs): |
|
435 | def show_id(cs): | |
453 | """ |
|
436 | """ | |
454 | Configurable function that shows ID |
|
437 | Configurable function that shows ID | |
455 | by default it's r123:fffeeefffeee |
|
438 | by default it's r123:fffeeefffeee | |
456 |
|
439 | |||
457 | :param cs: changeset instance |
|
440 | :param cs: changeset instance | |
458 | """ |
|
441 | """ | |
459 | from kallithea import CONFIG |
|
442 | from kallithea import CONFIG | |
460 | def_len = safe_int(CONFIG.get('show_sha_length', 12)) |
|
443 | def_len = safe_int(CONFIG.get('show_sha_length', 12)) | |
461 | show_rev = str2bool(CONFIG.get('show_revision_number', False)) |
|
444 | show_rev = str2bool(CONFIG.get('show_revision_number', False)) | |
462 |
|
445 | |||
463 | raw_id = cs.raw_id[:def_len] |
|
446 | raw_id = cs.raw_id[:def_len] | |
464 | if show_rev: |
|
447 | if show_rev: | |
465 | return 'r%s:%s' % (cs.revision, raw_id) |
|
448 | return 'r%s:%s' % (cs.revision, raw_id) | |
466 | else: |
|
449 | else: | |
467 | return '%s' % (raw_id) |
|
450 | return '%s' % (raw_id) | |
468 |
|
451 | |||
469 |
|
452 | |||
470 | def fmt_date(date): |
|
453 | def fmt_date(date): | |
471 | if date: |
|
454 | if date: | |
472 | return date.strftime("%Y-%m-%d %H:%M:%S").decode('utf8') |
|
455 | return date.strftime("%Y-%m-%d %H:%M:%S").decode('utf8') | |
473 |
|
456 | |||
474 | return "" |
|
457 | return "" | |
475 |
|
458 | |||
476 |
|
459 | |||
477 | def is_git(repository): |
|
460 | def is_git(repository): | |
478 | if hasattr(repository, 'alias'): |
|
461 | if hasattr(repository, 'alias'): | |
479 | _type = repository.alias |
|
462 | _type = repository.alias | |
480 | elif hasattr(repository, 'repo_type'): |
|
463 | elif hasattr(repository, 'repo_type'): | |
481 | _type = repository.repo_type |
|
464 | _type = repository.repo_type | |
482 | else: |
|
465 | else: | |
483 | _type = repository |
|
466 | _type = repository | |
484 | return _type == 'git' |
|
467 | return _type == 'git' | |
485 |
|
468 | |||
486 |
|
469 | |||
487 | def is_hg(repository): |
|
470 | def is_hg(repository): | |
488 | if hasattr(repository, 'alias'): |
|
471 | if hasattr(repository, 'alias'): | |
489 | _type = repository.alias |
|
472 | _type = repository.alias | |
490 | elif hasattr(repository, 'repo_type'): |
|
473 | elif hasattr(repository, 'repo_type'): | |
491 | _type = repository.repo_type |
|
474 | _type = repository.repo_type | |
492 | else: |
|
475 | else: | |
493 | _type = repository |
|
476 | _type = repository | |
494 | return _type == 'hg' |
|
477 | return _type == 'hg' | |
495 |
|
478 | |||
496 |
|
479 | |||
497 | def user_or_none(author): |
|
480 | def user_or_none(author): | |
498 | email = author_email(author) |
|
481 | email = author_email(author) | |
499 | if email is not None: |
|
482 | if email is not None: | |
500 | user = User.get_by_email(email, case_insensitive=True, cache=True) |
|
483 | user = User.get_by_email(email, case_insensitive=True, cache=True) | |
501 | if user is not None: |
|
484 | if user is not None: | |
502 | return user |
|
485 | return user | |
503 |
|
486 | |||
504 | user = User.get_by_username(author_name(author), case_insensitive=True, cache=True) |
|
487 | user = User.get_by_username(author_name(author), case_insensitive=True, cache=True) | |
505 | if user is not None: |
|
488 | if user is not None: | |
506 | return user |
|
489 | return user | |
507 |
|
490 | |||
508 | return None |
|
491 | return None | |
509 |
|
492 | |||
510 | def email_or_none(author): |
|
493 | def email_or_none(author): | |
511 | if not author: |
|
494 | if not author: | |
512 | return None |
|
495 | return None | |
513 | user = user_or_none(author) |
|
496 | user = user_or_none(author) | |
514 | if user is not None: |
|
497 | if user is not None: | |
515 | return user.email # always use main email address - not necessarily the one used to find user |
|
498 | return user.email # always use main email address - not necessarily the one used to find user | |
516 |
|
499 | |||
517 | # extract email from the commit string |
|
500 | # extract email from the commit string | |
518 | email = author_email(author) |
|
501 | email = author_email(author) | |
519 | if email: |
|
502 | if email: | |
520 | return email |
|
503 | return email | |
521 |
|
504 | |||
522 | # No valid email, not a valid user in the system, none! |
|
505 | # No valid email, not a valid user in the system, none! | |
523 | return None |
|
506 | return None | |
524 |
|
507 | |||
525 | def person(author, show_attr="username"): |
|
508 | def person(author, show_attr="username"): | |
526 | """Find the user identified by 'author', return one of the users attributes, |
|
509 | """Find the user identified by 'author', return one of the users attributes, | |
527 | default to the username attribute, None if there is no user""" |
|
510 | default to the username attribute, None if there is no user""" | |
528 | # attr to return from fetched user |
|
511 | # attr to return from fetched user | |
529 | person_getter = lambda usr: getattr(usr, show_attr) |
|
512 | person_getter = lambda usr: getattr(usr, show_attr) | |
530 |
|
513 | |||
531 | # if author is already an instance use it for extraction |
|
514 | # if author is already an instance use it for extraction | |
532 | if isinstance(author, User): |
|
515 | if isinstance(author, User): | |
533 | return person_getter(author) |
|
516 | return person_getter(author) | |
534 |
|
517 | |||
535 | user = user_or_none(author) |
|
518 | user = user_or_none(author) | |
536 | if user is not None: |
|
519 | if user is not None: | |
537 | return person_getter(user) |
|
520 | return person_getter(user) | |
538 |
|
521 | |||
539 | # Still nothing? Just pass back the author name if any, else the email |
|
522 | # Still nothing? Just pass back the author name if any, else the email | |
540 | return author_name(author) or email(author) |
|
523 | return author_name(author) or email(author) | |
541 |
|
524 | |||
542 |
|
525 | |||
543 | def person_by_id(id_, show_attr="username"): |
|
526 | def person_by_id(id_, show_attr="username"): | |
544 | # attr to return from fetched user |
|
527 | # attr to return from fetched user | |
545 | person_getter = lambda usr: getattr(usr, show_attr) |
|
528 | person_getter = lambda usr: getattr(usr, show_attr) | |
546 |
|
529 | |||
547 | #maybe it's an ID ? |
|
530 | #maybe it's an ID ? | |
548 | if str(id_).isdigit() or isinstance(id_, int): |
|
531 | if str(id_).isdigit() or isinstance(id_, int): | |
549 | id_ = int(id_) |
|
532 | id_ = int(id_) | |
550 | user = User.get(id_) |
|
533 | user = User.get(id_) | |
551 | if user is not None: |
|
534 | if user is not None: | |
552 | return person_getter(user) |
|
535 | return person_getter(user) | |
553 | return id_ |
|
536 | return id_ | |
554 |
|
537 | |||
555 |
|
538 | |||
556 | def desc_stylize(value): |
|
539 | def desc_stylize(value): | |
557 | """ |
|
540 | """ | |
558 | converts tags from value into html equivalent |
|
541 | converts tags from value into html equivalent | |
559 |
|
542 | |||
560 | :param value: |
|
543 | :param value: | |
561 | """ |
|
544 | """ | |
562 | if not value: |
|
545 | if not value: | |
563 | return '' |
|
546 | return '' | |
564 |
|
547 | |||
565 | value = re.sub(r'\[see\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]', |
|
548 | value = re.sub(r'\[see\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]', | |
566 | '<div class="metatag" tag="see">see => \\1 </div>', value) |
|
549 | '<div class="metatag" tag="see">see => \\1 </div>', value) | |
567 | value = re.sub(r'\[license\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]', |
|
550 | value = re.sub(r'\[license\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]', | |
568 | '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/\\1">\\1</a></div>', value) |
|
551 | '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/\\1">\\1</a></div>', value) | |
569 | value = re.sub(r'\[(requires|recommends|conflicts|base)\ \=\>\ *([a-zA-Z0-9\-\/]*)\]', |
|
552 | value = re.sub(r'\[(requires|recommends|conflicts|base)\ \=\>\ *([a-zA-Z0-9\-\/]*)\]', | |
570 | '<div class="metatag" tag="\\1">\\1 => <a href="/\\2">\\2</a></div>', value) |
|
553 | '<div class="metatag" tag="\\1">\\1 => <a href="/\\2">\\2</a></div>', value) | |
571 | value = re.sub(r'\[(lang|language)\ \=\>\ *([a-zA-Z\-\/\#\+]*)\]', |
|
554 | value = re.sub(r'\[(lang|language)\ \=\>\ *([a-zA-Z\-\/\#\+]*)\]', | |
572 | '<div class="metatag" tag="lang">\\2</div>', value) |
|
555 | '<div class="metatag" tag="lang">\\2</div>', value) | |
573 | value = re.sub(r'\[([a-z]+)\]', |
|
556 | value = re.sub(r'\[([a-z]+)\]', | |
574 | '<div class="metatag" tag="\\1">\\1</div>', value) |
|
557 | '<div class="metatag" tag="\\1">\\1</div>', value) | |
575 |
|
558 | |||
576 | return value |
|
559 | return value | |
577 |
|
560 | |||
578 |
|
561 | |||
579 | def boolicon(value): |
|
562 | def boolicon(value): | |
580 | """Returns boolean value of a value, represented as small html image of true/false |
|
563 | """Returns boolean value of a value, represented as small html image of true/false | |
581 | icons |
|
564 | icons | |
582 |
|
565 | |||
583 | :param value: value |
|
566 | :param value: value | |
584 | """ |
|
567 | """ | |
585 |
|
568 | |||
586 | if value: |
|
569 | if value: | |
587 | return HTML.tag('i', class_="icon-ok") |
|
570 | return HTML.tag('i', class_="icon-ok") | |
588 | else: |
|
571 | else: | |
589 | return HTML.tag('i', class_="icon-minus-circled") |
|
572 | return HTML.tag('i', class_="icon-minus-circled") | |
590 |
|
573 | |||
591 |
|
574 | |||
592 | def action_parser(user_log, feed=False, parse_cs=False): |
|
575 | def action_parser(user_log, feed=False, parse_cs=False): | |
593 | """ |
|
576 | """ | |
594 | This helper will action_map the specified string action into translated |
|
577 | This helper will action_map the specified string action into translated | |
595 | fancy names with icons and links |
|
578 | fancy names with icons and links | |
596 |
|
579 | |||
597 | :param user_log: user log instance |
|
580 | :param user_log: user log instance | |
598 | :param feed: use output for feeds (no html and fancy icons) |
|
581 | :param feed: use output for feeds (no html and fancy icons) | |
599 | :param parse_cs: parse Changesets into VCS instances |
|
582 | :param parse_cs: parse Changesets into VCS instances | |
600 | """ |
|
583 | """ | |
601 |
|
584 | |||
602 | action = user_log.action |
|
585 | action = user_log.action | |
603 | action_params = ' ' |
|
586 | action_params = ' ' | |
604 |
|
587 | |||
605 | x = action.split(':') |
|
588 | x = action.split(':') | |
606 |
|
589 | |||
607 | if len(x) > 1: |
|
590 | if len(x) > 1: | |
608 | action, action_params = x |
|
591 | action, action_params = x | |
609 |
|
592 | |||
610 | def get_cs_links(): |
|
593 | def get_cs_links(): | |
611 | revs_limit = 3 # display this amount always |
|
594 | revs_limit = 3 # display this amount always | |
612 | revs_top_limit = 50 # show upto this amount of changesets hidden |
|
595 | revs_top_limit = 50 # show upto this amount of changesets hidden | |
613 | revs_ids = action_params.split(',') |
|
596 | revs_ids = action_params.split(',') | |
614 | deleted = user_log.repository is None |
|
597 | deleted = user_log.repository is None | |
615 | if deleted: |
|
598 | if deleted: | |
616 | return ','.join(revs_ids) |
|
599 | return ','.join(revs_ids) | |
617 |
|
600 | |||
618 | repo_name = user_log.repository.repo_name |
|
601 | repo_name = user_log.repository.repo_name | |
619 |
|
602 | |||
620 | def lnk(rev, repo_name): |
|
603 | def lnk(rev, repo_name): | |
621 | if isinstance(rev, BaseChangeset) or isinstance(rev, AttributeDict): |
|
604 | if isinstance(rev, BaseChangeset) or isinstance(rev, AttributeDict): | |
622 | lazy_cs = True |
|
605 | lazy_cs = True | |
623 | if getattr(rev, 'op', None) and getattr(rev, 'ref_name', None): |
|
606 | if getattr(rev, 'op', None) and getattr(rev, 'ref_name', None): | |
624 | lazy_cs = False |
|
607 | lazy_cs = False | |
625 | lbl = '?' |
|
608 | lbl = '?' | |
626 | if rev.op == 'delete_branch': |
|
609 | if rev.op == 'delete_branch': | |
627 | lbl = '%s' % _('Deleted branch: %s') % rev.ref_name |
|
610 | lbl = '%s' % _('Deleted branch: %s') % rev.ref_name | |
628 | title = '' |
|
611 | title = '' | |
629 | elif rev.op == 'tag': |
|
612 | elif rev.op == 'tag': | |
630 | lbl = '%s' % _('Created tag: %s') % rev.ref_name |
|
613 | lbl = '%s' % _('Created tag: %s') % rev.ref_name | |
631 | title = '' |
|
614 | title = '' | |
632 | _url = '#' |
|
615 | _url = '#' | |
633 |
|
616 | |||
634 | else: |
|
617 | else: | |
635 | lbl = '%s' % (rev.short_id[:8]) |
|
618 | lbl = '%s' % (rev.short_id[:8]) | |
636 | _url = url('changeset_home', repo_name=repo_name, |
|
619 | _url = url('changeset_home', repo_name=repo_name, | |
637 | revision=rev.raw_id) |
|
620 | revision=rev.raw_id) | |
638 | title = tooltip(rev.message) |
|
621 | title = tooltip(rev.message) | |
639 | else: |
|
622 | else: | |
640 | ## changeset cannot be found/striped/removed etc. |
|
623 | ## changeset cannot be found/striped/removed etc. | |
641 | lbl = ('%s' % rev)[:12] |
|
624 | lbl = ('%s' % rev)[:12] | |
642 | _url = '#' |
|
625 | _url = '#' | |
643 | title = _('Changeset not found') |
|
626 | title = _('Changeset not found') | |
644 | if parse_cs: |
|
627 | if parse_cs: | |
645 | return link_to(lbl, _url, title=title, class_='tooltip') |
|
628 | return link_to(lbl, _url, title=title, class_='tooltip') | |
646 | return link_to(lbl, _url, raw_id=rev.raw_id, repo_name=repo_name, |
|
629 | return link_to(lbl, _url, raw_id=rev.raw_id, repo_name=repo_name, | |
647 | class_='lazy-cs' if lazy_cs else '') |
|
630 | class_='lazy-cs' if lazy_cs else '') | |
648 |
|
631 | |||
649 | def _get_op(rev_txt): |
|
632 | def _get_op(rev_txt): | |
650 | _op = None |
|
633 | _op = None | |
651 | _name = rev_txt |
|
634 | _name = rev_txt | |
652 | if len(rev_txt.split('=>')) == 2: |
|
635 | if len(rev_txt.split('=>')) == 2: | |
653 | _op, _name = rev_txt.split('=>') |
|
636 | _op, _name = rev_txt.split('=>') | |
654 | return _op, _name |
|
637 | return _op, _name | |
655 |
|
638 | |||
656 | revs = [] |
|
639 | revs = [] | |
657 | if len(filter(lambda v: v != '', revs_ids)) > 0: |
|
640 | if len(filter(lambda v: v != '', revs_ids)) > 0: | |
658 | repo = None |
|
641 | repo = None | |
659 | for rev in revs_ids[:revs_top_limit]: |
|
642 | for rev in revs_ids[:revs_top_limit]: | |
660 | _op, _name = _get_op(rev) |
|
643 | _op, _name = _get_op(rev) | |
661 |
|
644 | |||
662 | # we want parsed changesets, or new log store format is bad |
|
645 | # we want parsed changesets, or new log store format is bad | |
663 | if parse_cs: |
|
646 | if parse_cs: | |
664 | try: |
|
647 | try: | |
665 | if repo is None: |
|
648 | if repo is None: | |
666 | repo = user_log.repository.scm_instance |
|
649 | repo = user_log.repository.scm_instance | |
667 | _rev = repo.get_changeset(rev) |
|
650 | _rev = repo.get_changeset(rev) | |
668 | revs.append(_rev) |
|
651 | revs.append(_rev) | |
669 | except ChangesetDoesNotExistError: |
|
652 | except ChangesetDoesNotExistError: | |
670 | log.error('cannot find revision %s in this repo' % rev) |
|
653 | log.error('cannot find revision %s in this repo' % rev) | |
671 | revs.append(rev) |
|
654 | revs.append(rev) | |
672 | continue |
|
655 | continue | |
673 | else: |
|
656 | else: | |
674 | _rev = AttributeDict({ |
|
657 | _rev = AttributeDict({ | |
675 | 'short_id': rev[:12], |
|
658 | 'short_id': rev[:12], | |
676 | 'raw_id': rev, |
|
659 | 'raw_id': rev, | |
677 | 'message': '', |
|
660 | 'message': '', | |
678 | 'op': _op, |
|
661 | 'op': _op, | |
679 | 'ref_name': _name |
|
662 | 'ref_name': _name | |
680 | }) |
|
663 | }) | |
681 | revs.append(_rev) |
|
664 | revs.append(_rev) | |
682 | cs_links = [" " + ', '.join( |
|
665 | cs_links = [" " + ', '.join( | |
683 | [lnk(rev, repo_name) for rev in revs[:revs_limit]] |
|
666 | [lnk(rev, repo_name) for rev in revs[:revs_limit]] | |
684 | )] |
|
667 | )] | |
685 | _op1, _name1 = _get_op(revs_ids[0]) |
|
668 | _op1, _name1 = _get_op(revs_ids[0]) | |
686 | _op2, _name2 = _get_op(revs_ids[-1]) |
|
669 | _op2, _name2 = _get_op(revs_ids[-1]) | |
687 |
|
670 | |||
688 | _rev = '%s...%s' % (_name1, _name2) |
|
671 | _rev = '%s...%s' % (_name1, _name2) | |
689 |
|
672 | |||
690 | compare_view = ( |
|
673 | compare_view = ( | |
691 | ' <div class="compare_view tooltip" title="%s">' |
|
674 | ' <div class="compare_view tooltip" title="%s">' | |
692 | '<a href="%s">%s</a> </div>' % ( |
|
675 | '<a href="%s">%s</a> </div>' % ( | |
693 | _('Show all combined changesets %s->%s') % ( |
|
676 | _('Show all combined changesets %s->%s') % ( | |
694 | revs_ids[0][:12], revs_ids[-1][:12] |
|
677 | revs_ids[0][:12], revs_ids[-1][:12] | |
695 | ), |
|
678 | ), | |
696 | url('changeset_home', repo_name=repo_name, |
|
679 | url('changeset_home', repo_name=repo_name, | |
697 | revision=_rev |
|
680 | revision=_rev | |
698 | ), |
|
681 | ), | |
699 | _('compare view') |
|
682 | _('compare view') | |
700 | ) |
|
683 | ) | |
701 | ) |
|
684 | ) | |
702 |
|
685 | |||
703 | # if we have exactly one more than normally displayed |
|
686 | # if we have exactly one more than normally displayed | |
704 | # just display it, takes less space than displaying |
|
687 | # just display it, takes less space than displaying | |
705 | # "and 1 more revisions" |
|
688 | # "and 1 more revisions" | |
706 | if len(revs_ids) == revs_limit + 1: |
|
689 | if len(revs_ids) == revs_limit + 1: | |
707 | cs_links.append(", " + lnk(revs[revs_limit], repo_name)) |
|
690 | cs_links.append(", " + lnk(revs[revs_limit], repo_name)) | |
708 |
|
691 | |||
709 | # hidden-by-default ones |
|
692 | # hidden-by-default ones | |
710 | if len(revs_ids) > revs_limit + 1: |
|
693 | if len(revs_ids) > revs_limit + 1: | |
711 | uniq_id = revs_ids[0] |
|
694 | uniq_id = revs_ids[0] | |
712 | html_tmpl = ( |
|
695 | html_tmpl = ( | |
713 | '<span> %s <a class="show_more" id="_%s" ' |
|
696 | '<span> %s <a class="show_more" id="_%s" ' | |
714 | 'href="#more">%s</a> %s</span>' |
|
697 | 'href="#more">%s</a> %s</span>' | |
715 | ) |
|
698 | ) | |
716 | if not feed: |
|
699 | if not feed: | |
717 | cs_links.append(html_tmpl % ( |
|
700 | cs_links.append(html_tmpl % ( | |
718 | _('and'), |
|
701 | _('and'), | |
719 | uniq_id, _('%s more') % (len(revs_ids) - revs_limit), |
|
702 | uniq_id, _('%s more') % (len(revs_ids) - revs_limit), | |
720 | _('revisions') |
|
703 | _('revisions') | |
721 | ) |
|
704 | ) | |
722 | ) |
|
705 | ) | |
723 |
|
706 | |||
724 | if not feed: |
|
707 | if not feed: | |
725 | html_tmpl = '<span id="%s" style="display:none">, %s </span>' |
|
708 | html_tmpl = '<span id="%s" style="display:none">, %s </span>' | |
726 | else: |
|
709 | else: | |
727 | html_tmpl = '<span id="%s"> %s </span>' |
|
710 | html_tmpl = '<span id="%s"> %s </span>' | |
728 |
|
711 | |||
729 | morelinks = ', '.join( |
|
712 | morelinks = ', '.join( | |
730 | [lnk(rev, repo_name) for rev in revs[revs_limit:]] |
|
713 | [lnk(rev, repo_name) for rev in revs[revs_limit:]] | |
731 | ) |
|
714 | ) | |
732 |
|
715 | |||
733 | if len(revs_ids) > revs_top_limit: |
|
716 | if len(revs_ids) > revs_top_limit: | |
734 | morelinks += ', ...' |
|
717 | morelinks += ', ...' | |
735 |
|
718 | |||
736 | cs_links.append(html_tmpl % (uniq_id, morelinks)) |
|
719 | cs_links.append(html_tmpl % (uniq_id, morelinks)) | |
737 | if len(revs) > 1: |
|
720 | if len(revs) > 1: | |
738 | cs_links.append(compare_view) |
|
721 | cs_links.append(compare_view) | |
739 | return ''.join(cs_links) |
|
722 | return ''.join(cs_links) | |
740 |
|
723 | |||
741 | def get_fork_name(): |
|
724 | def get_fork_name(): | |
742 | repo_name = action_params |
|
725 | repo_name = action_params | |
743 | _url = url('summary_home', repo_name=repo_name) |
|
726 | _url = url('summary_home', repo_name=repo_name) | |
744 | return _('fork name %s') % link_to(action_params, _url) |
|
727 | return _('fork name %s') % link_to(action_params, _url) | |
745 |
|
728 | |||
746 | def get_user_name(): |
|
729 | def get_user_name(): | |
747 | user_name = action_params |
|
730 | user_name = action_params | |
748 | return user_name |
|
731 | return user_name | |
749 |
|
732 | |||
750 | def get_users_group(): |
|
733 | def get_users_group(): | |
751 | group_name = action_params |
|
734 | group_name = action_params | |
752 | return group_name |
|
735 | return group_name | |
753 |
|
736 | |||
754 | def get_pull_request(): |
|
737 | def get_pull_request(): | |
755 | pull_request_id = action_params |
|
738 | pull_request_id = action_params | |
756 | deleted = user_log.repository is None |
|
739 | deleted = user_log.repository is None | |
757 | if deleted: |
|
740 | if deleted: | |
758 | repo_name = user_log.repository_name |
|
741 | repo_name = user_log.repository_name | |
759 | else: |
|
742 | else: | |
760 | repo_name = user_log.repository.repo_name |
|
743 | repo_name = user_log.repository.repo_name | |
761 | return link_to(_('Pull request #%s') % pull_request_id, |
|
744 | return link_to(_('Pull request #%s') % pull_request_id, | |
762 | url('pullrequest_show', repo_name=repo_name, |
|
745 | url('pullrequest_show', repo_name=repo_name, | |
763 | pull_request_id=pull_request_id)) |
|
746 | pull_request_id=pull_request_id)) | |
764 |
|
747 | |||
765 | def get_archive_name(): |
|
748 | def get_archive_name(): | |
766 | archive_name = action_params |
|
749 | archive_name = action_params | |
767 | return archive_name |
|
750 | return archive_name | |
768 |
|
751 | |||
769 | # action : translated str, callback(extractor), icon |
|
752 | # action : translated str, callback(extractor), icon | |
770 | action_map = { |
|
753 | action_map = { | |
771 | 'user_deleted_repo': (_('[deleted] repository'), |
|
754 | 'user_deleted_repo': (_('[deleted] repository'), | |
772 | None, 'icon-trashcan'), |
|
755 | None, 'icon-trashcan'), | |
773 | 'user_created_repo': (_('[created] repository'), |
|
756 | 'user_created_repo': (_('[created] repository'), | |
774 | None, 'icon-plus'), |
|
757 | None, 'icon-plus'), | |
775 | 'user_created_fork': (_('[created] repository as fork'), |
|
758 | 'user_created_fork': (_('[created] repository as fork'), | |
776 | None, 'icon-fork'), |
|
759 | None, 'icon-fork'), | |
777 | 'user_forked_repo': (_('[forked] repository'), |
|
760 | 'user_forked_repo': (_('[forked] repository'), | |
778 | get_fork_name, 'icon-fork'), |
|
761 | get_fork_name, 'icon-fork'), | |
779 | 'user_updated_repo': (_('[updated] repository'), |
|
762 | 'user_updated_repo': (_('[updated] repository'), | |
780 | None, 'icon-pencil'), |
|
763 | None, 'icon-pencil'), | |
781 | 'user_downloaded_archive': (_('[downloaded] archive from repository'), |
|
764 | 'user_downloaded_archive': (_('[downloaded] archive from repository'), | |
782 | get_archive_name, 'icon-download-cloud'), |
|
765 | get_archive_name, 'icon-download-cloud'), | |
783 | 'admin_deleted_repo': (_('[delete] repository'), |
|
766 | 'admin_deleted_repo': (_('[delete] repository'), | |
784 | None, 'icon-trashcan'), |
|
767 | None, 'icon-trashcan'), | |
785 | 'admin_created_repo': (_('[created] repository'), |
|
768 | 'admin_created_repo': (_('[created] repository'), | |
786 | None, 'icon-plus'), |
|
769 | None, 'icon-plus'), | |
787 | 'admin_forked_repo': (_('[forked] repository'), |
|
770 | 'admin_forked_repo': (_('[forked] repository'), | |
788 | None, 'icon-fork'), |
|
771 | None, 'icon-fork'), | |
789 | 'admin_updated_repo': (_('[updated] repository'), |
|
772 | 'admin_updated_repo': (_('[updated] repository'), | |
790 | None, 'icon-pencil'), |
|
773 | None, 'icon-pencil'), | |
791 | 'admin_created_user': (_('[created] user'), |
|
774 | 'admin_created_user': (_('[created] user'), | |
792 | get_user_name, 'icon-user'), |
|
775 | get_user_name, 'icon-user'), | |
793 | 'admin_updated_user': (_('[updated] user'), |
|
776 | 'admin_updated_user': (_('[updated] user'), | |
794 | get_user_name, 'icon-user'), |
|
777 | get_user_name, 'icon-user'), | |
795 | 'admin_created_users_group': (_('[created] user group'), |
|
778 | 'admin_created_users_group': (_('[created] user group'), | |
796 | get_users_group, 'icon-pencil'), |
|
779 | get_users_group, 'icon-pencil'), | |
797 | 'admin_updated_users_group': (_('[updated] user group'), |
|
780 | 'admin_updated_users_group': (_('[updated] user group'), | |
798 | get_users_group, 'icon-pencil'), |
|
781 | get_users_group, 'icon-pencil'), | |
799 | 'user_commented_revision': (_('[commented] on revision in repository'), |
|
782 | 'user_commented_revision': (_('[commented] on revision in repository'), | |
800 | get_cs_links, 'icon-comment'), |
|
783 | get_cs_links, 'icon-comment'), | |
801 | 'user_commented_pull_request': (_('[commented] on pull request for'), |
|
784 | 'user_commented_pull_request': (_('[commented] on pull request for'), | |
802 | get_pull_request, 'icon-comment'), |
|
785 | get_pull_request, 'icon-comment'), | |
803 | 'user_closed_pull_request': (_('[closed] pull request for'), |
|
786 | 'user_closed_pull_request': (_('[closed] pull request for'), | |
804 | get_pull_request, 'icon-ok'), |
|
787 | get_pull_request, 'icon-ok'), | |
805 | 'push': (_('[pushed] into'), |
|
788 | 'push': (_('[pushed] into'), | |
806 | get_cs_links, 'icon-move-up'), |
|
789 | get_cs_links, 'icon-move-up'), | |
807 | 'push_local': (_('[committed via Kallithea] into repository'), |
|
790 | 'push_local': (_('[committed via Kallithea] into repository'), | |
808 | get_cs_links, 'icon-pencil'), |
|
791 | get_cs_links, 'icon-pencil'), | |
809 | 'push_remote': (_('[pulled from remote] into repository'), |
|
792 | 'push_remote': (_('[pulled from remote] into repository'), | |
810 | get_cs_links, 'icon-move-up'), |
|
793 | get_cs_links, 'icon-move-up'), | |
811 | 'pull': (_('[pulled] from'), |
|
794 | 'pull': (_('[pulled] from'), | |
812 | None, 'icon-move-down'), |
|
795 | None, 'icon-move-down'), | |
813 | 'started_following_repo': (_('[started following] repository'), |
|
796 | 'started_following_repo': (_('[started following] repository'), | |
814 | None, 'icon-heart'), |
|
797 | None, 'icon-heart'), | |
815 | 'stopped_following_repo': (_('[stopped following] repository'), |
|
798 | 'stopped_following_repo': (_('[stopped following] repository'), | |
816 | None, 'icon-heart-empty'), |
|
799 | None, 'icon-heart-empty'), | |
817 | } |
|
800 | } | |
818 |
|
801 | |||
819 | action_str = action_map.get(action, action) |
|
802 | action_str = action_map.get(action, action) | |
820 | if feed: |
|
803 | if feed: | |
821 | action = action_str[0].replace('[', '').replace(']', '') |
|
804 | action = action_str[0].replace('[', '').replace(']', '') | |
822 | else: |
|
805 | else: | |
823 | action = action_str[0]\ |
|
806 | action = action_str[0]\ | |
824 | .replace('[', '<span class="journal_highlight">')\ |
|
807 | .replace('[', '<span class="journal_highlight">')\ | |
825 | .replace(']', '</span>') |
|
808 | .replace(']', '</span>') | |
826 |
|
809 | |||
827 | action_params_func = lambda: "" |
|
810 | action_params_func = lambda: "" | |
828 |
|
811 | |||
829 | if callable(action_str[1]): |
|
812 | if callable(action_str[1]): | |
830 | action_params_func = action_str[1] |
|
813 | action_params_func = action_str[1] | |
831 |
|
814 | |||
832 | def action_parser_icon(): |
|
815 | def action_parser_icon(): | |
833 | action = user_log.action |
|
816 | action = user_log.action | |
834 | action_params = None |
|
817 | action_params = None | |
835 | x = action.split(':') |
|
818 | x = action.split(':') | |
836 |
|
819 | |||
837 | if len(x) > 1: |
|
820 | if len(x) > 1: | |
838 | action, action_params = x |
|
821 | action, action_params = x | |
839 |
|
822 | |||
840 | tmpl = """<i class="%s" alt="%s"></i>""" |
|
823 | tmpl = """<i class="%s" alt="%s"></i>""" | |
841 | ico = action_map.get(action, ['', '', ''])[2] |
|
824 | ico = action_map.get(action, ['', '', ''])[2] | |
842 | return literal(tmpl % (ico, action)) |
|
825 | return literal(tmpl % (ico, action)) | |
843 |
|
826 | |||
844 | # returned callbacks we need to call to get |
|
827 | # returned callbacks we need to call to get | |
845 | return [lambda: literal(action), action_params_func, action_parser_icon] |
|
828 | return [lambda: literal(action), action_params_func, action_parser_icon] | |
846 |
|
829 | |||
847 |
|
830 | |||
848 |
|
831 | |||
849 | #============================================================================== |
|
832 | #============================================================================== | |
850 | # PERMS |
|
833 | # PERMS | |
851 | #============================================================================== |
|
834 | #============================================================================== | |
852 | from kallithea.lib.auth import HasPermissionAny, HasPermissionAll, \ |
|
835 | from kallithea.lib.auth import HasPermissionAny, HasPermissionAll, \ | |
853 | HasRepoPermissionAny, HasRepoPermissionAll, HasRepoGroupPermissionAll, \ |
|
836 | HasRepoPermissionAny, HasRepoPermissionAll, HasRepoGroupPermissionAll, \ | |
854 | HasRepoGroupPermissionAny |
|
837 | HasRepoGroupPermissionAny | |
855 |
|
838 | |||
856 |
|
839 | |||
857 | #============================================================================== |
|
840 | #============================================================================== | |
858 | # GRAVATAR URL |
|
841 | # GRAVATAR URL | |
859 | #============================================================================== |
|
842 | #============================================================================== | |
860 | def gravatar(email_address, cls='', size=30, ssl_enabled=True): |
|
843 | def gravatar(email_address, cls='', size=30, ssl_enabled=True): | |
861 | """return html element of the gravatar |
|
844 | """return html element of the gravatar | |
862 |
|
845 | |||
863 | This method will return an <img> with the resolution double the size (for |
|
846 | This method will return an <img> with the resolution double the size (for | |
864 | retina screens) of the image. If the url returned from gravatar_url is |
|
847 | retina screens) of the image. If the url returned from gravatar_url is | |
865 | empty then we fallback to using an icon. |
|
848 | empty then we fallback to using an icon. | |
866 |
|
849 | |||
867 | """ |
|
850 | """ | |
868 | src = gravatar_url(email_address, size*2, ssl_enabled) |
|
851 | src = gravatar_url(email_address, size*2, ssl_enabled) | |
869 |
|
852 | |||
870 | # here it makes sense to use style="width: ..." (instead of, say, a |
|
853 | # here it makes sense to use style="width: ..." (instead of, say, a | |
871 | # stylesheet) because we using this to generate a high-res (retina) size |
|
854 | # stylesheet) because we using this to generate a high-res (retina) size | |
872 | tmpl = """<img alt="gravatar" class="{cls}" style="width: {size}px; height: {size}px" src="{src}"/>""" |
|
855 | tmpl = """<img alt="gravatar" class="{cls}" style="width: {size}px; height: {size}px" src="{src}"/>""" | |
873 |
|
856 | |||
874 | # if src is empty then there was no gravatar, so we use a font icon |
|
857 | # if src is empty then there was no gravatar, so we use a font icon | |
875 | if not src: |
|
858 | if not src: | |
876 | tmpl = """<i class="icon-user {cls}" style="font-size: {size}px;"></i>""" |
|
859 | tmpl = """<i class="icon-user {cls}" style="font-size: {size}px;"></i>""" | |
877 |
|
860 | |||
878 | tmpl = tmpl.format(cls=cls, size=size, src=src) |
|
861 | tmpl = tmpl.format(cls=cls, size=size, src=src) | |
879 | return literal(tmpl) |
|
862 | return literal(tmpl) | |
880 |
|
863 | |||
881 | def gravatar_url(email_address, size=30, ssl_enabled=True): |
|
864 | def gravatar_url(email_address, size=30, ssl_enabled=True): | |
882 | # doh, we need to re-import those to mock it later |
|
865 | # doh, we need to re-import those to mock it later | |
883 | from pylons import url |
|
866 | from pylons import url | |
884 | from pylons import tmpl_context as c |
|
867 | from pylons import tmpl_context as c | |
885 |
|
868 | |||
886 | _def = 'anonymous@kallithea-scm.org' # default gravatar |
|
869 | _def = 'anonymous@kallithea-scm.org' # default gravatar | |
887 | _use_gravatar = c.visual.use_gravatar |
|
870 | _use_gravatar = c.visual.use_gravatar | |
888 | _gravatar_url = c.visual.gravatar_url or User.DEFAULT_GRAVATAR_URL |
|
871 | _gravatar_url = c.visual.gravatar_url or User.DEFAULT_GRAVATAR_URL | |
889 |
|
872 | |||
890 | email_address = email_address or _def |
|
873 | email_address = email_address or _def | |
891 |
|
874 | |||
892 | if not _use_gravatar or not email_address or email_address == _def: |
|
875 | if not _use_gravatar or not email_address or email_address == _def: | |
893 | return "" |
|
876 | return "" | |
894 |
|
877 | |||
895 | if _use_gravatar: |
|
878 | if _use_gravatar: | |
896 | _md5 = lambda s: hashlib.md5(s).hexdigest() |
|
879 | _md5 = lambda s: hashlib.md5(s).hexdigest() | |
897 |
|
880 | |||
898 | tmpl = _gravatar_url |
|
881 | tmpl = _gravatar_url | |
899 | parsed_url = urlparse.urlparse(url.current(qualified=True)) |
|
882 | parsed_url = urlparse.urlparse(url.current(qualified=True)) | |
900 | tmpl = tmpl.replace('{email}', email_address)\ |
|
883 | tmpl = tmpl.replace('{email}', email_address)\ | |
901 | .replace('{md5email}', _md5(safe_str(email_address).lower())) \ |
|
884 | .replace('{md5email}', _md5(safe_str(email_address).lower())) \ | |
902 | .replace('{netloc}', parsed_url.netloc)\ |
|
885 | .replace('{netloc}', parsed_url.netloc)\ | |
903 | .replace('{scheme}', parsed_url.scheme)\ |
|
886 | .replace('{scheme}', parsed_url.scheme)\ | |
904 | .replace('{size}', safe_str(size)) |
|
887 | .replace('{size}', safe_str(size)) | |
905 | return tmpl |
|
888 | return tmpl | |
906 |
|
889 | |||
907 | class Page(_Page): |
|
890 | class Page(_Page): | |
908 | """ |
|
891 | """ | |
909 | Custom pager to match rendering style with YUI paginator |
|
892 | Custom pager to match rendering style with YUI paginator | |
910 | """ |
|
893 | """ | |
911 |
|
894 | |||
912 | def _get_pos(self, cur_page, max_page, items): |
|
895 | def _get_pos(self, cur_page, max_page, items): | |
913 | edge = (items / 2) + 1 |
|
896 | edge = (items / 2) + 1 | |
914 | if (cur_page <= edge): |
|
897 | if (cur_page <= edge): | |
915 | radius = max(items / 2, items - cur_page) |
|
898 | radius = max(items / 2, items - cur_page) | |
916 | elif (max_page - cur_page) < edge: |
|
899 | elif (max_page - cur_page) < edge: | |
917 | radius = (items - 1) - (max_page - cur_page) |
|
900 | radius = (items - 1) - (max_page - cur_page) | |
918 | else: |
|
901 | else: | |
919 | radius = items / 2 |
|
902 | radius = items / 2 | |
920 |
|
903 | |||
921 | left = max(1, (cur_page - (radius))) |
|
904 | left = max(1, (cur_page - (radius))) | |
922 | right = min(max_page, cur_page + (radius)) |
|
905 | right = min(max_page, cur_page + (radius)) | |
923 | return left, cur_page, right |
|
906 | return left, cur_page, right | |
924 |
|
907 | |||
925 | def _range(self, regexp_match): |
|
908 | def _range(self, regexp_match): | |
926 | """ |
|
909 | """ | |
927 | Return range of linked pages (e.g. '1 2 [3] 4 5 6 7 8'). |
|
910 | Return range of linked pages (e.g. '1 2 [3] 4 5 6 7 8'). | |
928 |
|
911 | |||
929 | Arguments: |
|
912 | Arguments: | |
930 |
|
913 | |||
931 | regexp_match |
|
914 | regexp_match | |
932 | A "re" (regular expressions) match object containing the |
|
915 | A "re" (regular expressions) match object containing the | |
933 | radius of linked pages around the current page in |
|
916 | radius of linked pages around the current page in | |
934 | regexp_match.group(1) as a string |
|
917 | regexp_match.group(1) as a string | |
935 |
|
918 | |||
936 | This function is supposed to be called as a callable in |
|
919 | This function is supposed to be called as a callable in | |
937 | re.sub. |
|
920 | re.sub. | |
938 |
|
921 | |||
939 | """ |
|
922 | """ | |
940 | radius = int(regexp_match.group(1)) |
|
923 | radius = int(regexp_match.group(1)) | |
941 |
|
924 | |||
942 | # Compute the first and last page number within the radius |
|
925 | # Compute the first and last page number within the radius | |
943 | # e.g. '1 .. 5 6 [7] 8 9 .. 12' |
|
926 | # e.g. '1 .. 5 6 [7] 8 9 .. 12' | |
944 | # -> leftmost_page = 5 |
|
927 | # -> leftmost_page = 5 | |
945 | # -> rightmost_page = 9 |
|
928 | # -> rightmost_page = 9 | |
946 | leftmost_page, _cur, rightmost_page = self._get_pos(self.page, |
|
929 | leftmost_page, _cur, rightmost_page = self._get_pos(self.page, | |
947 | self.last_page, |
|
930 | self.last_page, | |
948 | (radius * 2) + 1) |
|
931 | (radius * 2) + 1) | |
949 | nav_items = [] |
|
932 | nav_items = [] | |
950 |
|
933 | |||
951 | # Create a link to the first page (unless we are on the first page |
|
934 | # Create a link to the first page (unless we are on the first page | |
952 | # or there would be no need to insert '..' spacers) |
|
935 | # or there would be no need to insert '..' spacers) | |
953 | if self.page != self.first_page and self.first_page < leftmost_page: |
|
936 | if self.page != self.first_page and self.first_page < leftmost_page: | |
954 | nav_items.append(self._pagerlink(self.first_page, self.first_page)) |
|
937 | nav_items.append(self._pagerlink(self.first_page, self.first_page)) | |
955 |
|
938 | |||
956 | # Insert dots if there are pages between the first page |
|
939 | # Insert dots if there are pages between the first page | |
957 | # and the currently displayed page range |
|
940 | # and the currently displayed page range | |
958 | if leftmost_page - self.first_page > 1: |
|
941 | if leftmost_page - self.first_page > 1: | |
959 | # Wrap in a SPAN tag if nolink_attr is set |
|
942 | # Wrap in a SPAN tag if nolink_attr is set | |
960 | text = '..' |
|
943 | text = '..' | |
961 | if self.dotdot_attr: |
|
944 | if self.dotdot_attr: | |
962 | text = HTML.span(c=text, **self.dotdot_attr) |
|
945 | text = HTML.span(c=text, **self.dotdot_attr) | |
963 | nav_items.append(text) |
|
946 | nav_items.append(text) | |
964 |
|
947 | |||
965 | for thispage in xrange(leftmost_page, rightmost_page + 1): |
|
948 | for thispage in xrange(leftmost_page, rightmost_page + 1): | |
966 | # Highlight the current page number and do not use a link |
|
949 | # Highlight the current page number and do not use a link | |
967 | if thispage == self.page: |
|
950 | if thispage == self.page: | |
968 | text = '%s' % (thispage,) |
|
951 | text = '%s' % (thispage,) | |
969 | # Wrap in a SPAN tag if nolink_attr is set |
|
952 | # Wrap in a SPAN tag if nolink_attr is set | |
970 | if self.curpage_attr: |
|
953 | if self.curpage_attr: | |
971 | text = HTML.span(c=text, **self.curpage_attr) |
|
954 | text = HTML.span(c=text, **self.curpage_attr) | |
972 | nav_items.append(text) |
|
955 | nav_items.append(text) | |
973 | # Otherwise create just a link to that page |
|
956 | # Otherwise create just a link to that page | |
974 | else: |
|
957 | else: | |
975 | text = '%s' % (thispage,) |
|
958 | text = '%s' % (thispage,) | |
976 | nav_items.append(self._pagerlink(thispage, text)) |
|
959 | nav_items.append(self._pagerlink(thispage, text)) | |
977 |
|
960 | |||
978 | # Insert dots if there are pages between the displayed |
|
961 | # Insert dots if there are pages between the displayed | |
979 | # page numbers and the end of the page range |
|
962 | # page numbers and the end of the page range | |
980 | if self.last_page - rightmost_page > 1: |
|
963 | if self.last_page - rightmost_page > 1: | |
981 | text = '..' |
|
964 | text = '..' | |
982 | # Wrap in a SPAN tag if nolink_attr is set |
|
965 | # Wrap in a SPAN tag if nolink_attr is set | |
983 | if self.dotdot_attr: |
|
966 | if self.dotdot_attr: | |
984 | text = HTML.span(c=text, **self.dotdot_attr) |
|
967 | text = HTML.span(c=text, **self.dotdot_attr) | |
985 | nav_items.append(text) |
|
968 | nav_items.append(text) | |
986 |
|
969 | |||
987 | # Create a link to the very last page (unless we are on the last |
|
970 | # Create a link to the very last page (unless we are on the last | |
988 | # page or there would be no need to insert '..' spacers) |
|
971 | # page or there would be no need to insert '..' spacers) | |
989 | if self.page != self.last_page and rightmost_page < self.last_page: |
|
972 | if self.page != self.last_page and rightmost_page < self.last_page: | |
990 | nav_items.append(self._pagerlink(self.last_page, self.last_page)) |
|
973 | nav_items.append(self._pagerlink(self.last_page, self.last_page)) | |
991 |
|
974 | |||
992 | #_page_link = url.current() |
|
975 | #_page_link = url.current() | |
993 | #nav_items.append(literal('<link rel="prerender" href="%s?page=%s">' % (_page_link, str(int(self.page)+1)))) |
|
976 | #nav_items.append(literal('<link rel="prerender" href="%s?page=%s">' % (_page_link, str(int(self.page)+1)))) | |
994 | #nav_items.append(literal('<link rel="prefetch" href="%s?page=%s">' % (_page_link, str(int(self.page)+1)))) |
|
977 | #nav_items.append(literal('<link rel="prefetch" href="%s?page=%s">' % (_page_link, str(int(self.page)+1)))) | |
995 | return self.separator.join(nav_items) |
|
978 | return self.separator.join(nav_items) | |
996 |
|
979 | |||
997 | def pager(self, format='~2~', page_param='page', partial_param='partial', |
|
980 | def pager(self, format='~2~', page_param='page', partial_param='partial', | |
998 | show_if_single_page=False, separator=' ', onclick=None, |
|
981 | show_if_single_page=False, separator=' ', onclick=None, | |
999 | symbol_first='<<', symbol_last='>>', |
|
982 | symbol_first='<<', symbol_last='>>', | |
1000 | symbol_previous='<', symbol_next='>', |
|
983 | symbol_previous='<', symbol_next='>', | |
1001 | link_attr={'class': 'pager_link', 'rel': 'prerender'}, |
|
984 | link_attr={'class': 'pager_link', 'rel': 'prerender'}, | |
1002 | curpage_attr={'class': 'pager_curpage'}, |
|
985 | curpage_attr={'class': 'pager_curpage'}, | |
1003 | dotdot_attr={'class': 'pager_dotdot'}, **kwargs): |
|
986 | dotdot_attr={'class': 'pager_dotdot'}, **kwargs): | |
1004 |
|
987 | |||
1005 | self.curpage_attr = curpage_attr |
|
988 | self.curpage_attr = curpage_attr | |
1006 | self.separator = separator |
|
989 | self.separator = separator | |
1007 | self.pager_kwargs = kwargs |
|
990 | self.pager_kwargs = kwargs | |
1008 | self.page_param = page_param |
|
991 | self.page_param = page_param | |
1009 | self.partial_param = partial_param |
|
992 | self.partial_param = partial_param | |
1010 | self.onclick = onclick |
|
993 | self.onclick = onclick | |
1011 | self.link_attr = link_attr |
|
994 | self.link_attr = link_attr | |
1012 | self.dotdot_attr = dotdot_attr |
|
995 | self.dotdot_attr = dotdot_attr | |
1013 |
|
996 | |||
1014 | # Don't show navigator if there is no more than one page |
|
997 | # Don't show navigator if there is no more than one page | |
1015 | if self.page_count == 0 or (self.page_count == 1 and not show_if_single_page): |
|
998 | if self.page_count == 0 or (self.page_count == 1 and not show_if_single_page): | |
1016 | return '' |
|
999 | return '' | |
1017 |
|
1000 | |||
1018 | from string import Template |
|
1001 | from string import Template | |
1019 | # Replace ~...~ in token format by range of pages |
|
1002 | # Replace ~...~ in token format by range of pages | |
1020 | result = re.sub(r'~(\d+)~', self._range, format) |
|
1003 | result = re.sub(r'~(\d+)~', self._range, format) | |
1021 |
|
1004 | |||
1022 | # Interpolate '%' variables |
|
1005 | # Interpolate '%' variables | |
1023 | result = Template(result).safe_substitute({ |
|
1006 | result = Template(result).safe_substitute({ | |
1024 | 'first_page': self.first_page, |
|
1007 | 'first_page': self.first_page, | |
1025 | 'last_page': self.last_page, |
|
1008 | 'last_page': self.last_page, | |
1026 | 'page': self.page, |
|
1009 | 'page': self.page, | |
1027 | 'page_count': self.page_count, |
|
1010 | 'page_count': self.page_count, | |
1028 | 'items_per_page': self.items_per_page, |
|
1011 | 'items_per_page': self.items_per_page, | |
1029 | 'first_item': self.first_item, |
|
1012 | 'first_item': self.first_item, | |
1030 | 'last_item': self.last_item, |
|
1013 | 'last_item': self.last_item, | |
1031 | 'item_count': self.item_count, |
|
1014 | 'item_count': self.item_count, | |
1032 | 'link_first': self.page > self.first_page and \ |
|
1015 | 'link_first': self.page > self.first_page and \ | |
1033 | self._pagerlink(self.first_page, symbol_first) or '', |
|
1016 | self._pagerlink(self.first_page, symbol_first) or '', | |
1034 | 'link_last': self.page < self.last_page and \ |
|
1017 | 'link_last': self.page < self.last_page and \ | |
1035 | self._pagerlink(self.last_page, symbol_last) or '', |
|
1018 | self._pagerlink(self.last_page, symbol_last) or '', | |
1036 | 'link_previous': self.previous_page and \ |
|
1019 | 'link_previous': self.previous_page and \ | |
1037 | self._pagerlink(self.previous_page, symbol_previous) \ |
|
1020 | self._pagerlink(self.previous_page, symbol_previous) \ | |
1038 | or HTML.span(symbol_previous, class_="yui-pg-previous"), |
|
1021 | or HTML.span(symbol_previous, class_="yui-pg-previous"), | |
1039 | 'link_next': self.next_page and \ |
|
1022 | 'link_next': self.next_page and \ | |
1040 | self._pagerlink(self.next_page, symbol_next) \ |
|
1023 | self._pagerlink(self.next_page, symbol_next) \ | |
1041 | or HTML.span(symbol_next, class_="yui-pg-next") |
|
1024 | or HTML.span(symbol_next, class_="yui-pg-next") | |
1042 | }) |
|
1025 | }) | |
1043 |
|
1026 | |||
1044 | return literal(result) |
|
1027 | return literal(result) | |
1045 |
|
1028 | |||
1046 |
|
1029 | |||
1047 | #============================================================================== |
|
1030 | #============================================================================== | |
1048 | # REPO PAGER, PAGER FOR REPOSITORY |
|
1031 | # REPO PAGER, PAGER FOR REPOSITORY | |
1049 | #============================================================================== |
|
1032 | #============================================================================== | |
1050 | class RepoPage(Page): |
|
1033 | class RepoPage(Page): | |
1051 |
|
1034 | |||
1052 | def __init__(self, collection, page=1, items_per_page=20, |
|
1035 | def __init__(self, collection, page=1, items_per_page=20, | |
1053 | item_count=None, url=None, **kwargs): |
|
1036 | item_count=None, url=None, **kwargs): | |
1054 |
|
1037 | |||
1055 | """Create a "RepoPage" instance. special pager for paging |
|
1038 | """Create a "RepoPage" instance. special pager for paging | |
1056 | repository |
|
1039 | repository | |
1057 | """ |
|
1040 | """ | |
1058 | self._url_generator = url |
|
1041 | self._url_generator = url | |
1059 |
|
1042 | |||
1060 | # Safe the kwargs class-wide so they can be used in the pager() method |
|
1043 | # Safe the kwargs class-wide so they can be used in the pager() method | |
1061 | self.kwargs = kwargs |
|
1044 | self.kwargs = kwargs | |
1062 |
|
1045 | |||
1063 | # Save a reference to the collection |
|
1046 | # Save a reference to the collection | |
1064 | self.original_collection = collection |
|
1047 | self.original_collection = collection | |
1065 |
|
1048 | |||
1066 | self.collection = collection |
|
1049 | self.collection = collection | |
1067 |
|
1050 | |||
1068 | # The self.page is the number of the current page. |
|
1051 | # The self.page is the number of the current page. | |
1069 | # The first page has the number 1! |
|
1052 | # The first page has the number 1! | |
1070 | try: |
|
1053 | try: | |
1071 | self.page = int(page) # make it int() if we get it as a string |
|
1054 | self.page = int(page) # make it int() if we get it as a string | |
1072 | except (ValueError, TypeError): |
|
1055 | except (ValueError, TypeError): | |
1073 | self.page = 1 |
|
1056 | self.page = 1 | |
1074 |
|
1057 | |||
1075 | self.items_per_page = items_per_page |
|
1058 | self.items_per_page = items_per_page | |
1076 |
|
1059 | |||
1077 | # Unless the user tells us how many items the collections has |
|
1060 | # Unless the user tells us how many items the collections has | |
1078 | # we calculate that ourselves. |
|
1061 | # we calculate that ourselves. | |
1079 | if item_count is not None: |
|
1062 | if item_count is not None: | |
1080 | self.item_count = item_count |
|
1063 | self.item_count = item_count | |
1081 | else: |
|
1064 | else: | |
1082 | self.item_count = len(self.collection) |
|
1065 | self.item_count = len(self.collection) | |
1083 |
|
1066 | |||
1084 | # Compute the number of the first and last available page |
|
1067 | # Compute the number of the first and last available page | |
1085 | if self.item_count > 0: |
|
1068 | if self.item_count > 0: | |
1086 | self.first_page = 1 |
|
1069 | self.first_page = 1 | |
1087 | self.page_count = int(math.ceil(float(self.item_count) / |
|
1070 | self.page_count = int(math.ceil(float(self.item_count) / | |
1088 | self.items_per_page)) |
|
1071 | self.items_per_page)) | |
1089 | self.last_page = self.first_page + self.page_count - 1 |
|
1072 | self.last_page = self.first_page + self.page_count - 1 | |
1090 |
|
1073 | |||
1091 | # Make sure that the requested page number is the range of |
|
1074 | # Make sure that the requested page number is the range of | |
1092 | # valid pages |
|
1075 | # valid pages | |
1093 | if self.page > self.last_page: |
|
1076 | if self.page > self.last_page: | |
1094 | self.page = self.last_page |
|
1077 | self.page = self.last_page | |
1095 | elif self.page < self.first_page: |
|
1078 | elif self.page < self.first_page: | |
1096 | self.page = self.first_page |
|
1079 | self.page = self.first_page | |
1097 |
|
1080 | |||
1098 | # Note: the number of items on this page can be less than |
|
1081 | # Note: the number of items on this page can be less than | |
1099 | # items_per_page if the last page is not full |
|
1082 | # items_per_page if the last page is not full | |
1100 | self.first_item = max(0, (self.item_count) - (self.page * |
|
1083 | self.first_item = max(0, (self.item_count) - (self.page * | |
1101 | items_per_page)) |
|
1084 | items_per_page)) | |
1102 | self.last_item = ((self.item_count - 1) - items_per_page * |
|
1085 | self.last_item = ((self.item_count - 1) - items_per_page * | |
1103 | (self.page - 1)) |
|
1086 | (self.page - 1)) | |
1104 |
|
1087 | |||
1105 | self.items = list(self.collection[self.first_item:self.last_item + 1]) |
|
1088 | self.items = list(self.collection[self.first_item:self.last_item + 1]) | |
1106 |
|
1089 | |||
1107 | # Links to previous and next page |
|
1090 | # Links to previous and next page | |
1108 | if self.page > self.first_page: |
|
1091 | if self.page > self.first_page: | |
1109 | self.previous_page = self.page - 1 |
|
1092 | self.previous_page = self.page - 1 | |
1110 | else: |
|
1093 | else: | |
1111 | self.previous_page = None |
|
1094 | self.previous_page = None | |
1112 |
|
1095 | |||
1113 | if self.page < self.last_page: |
|
1096 | if self.page < self.last_page: | |
1114 | self.next_page = self.page + 1 |
|
1097 | self.next_page = self.page + 1 | |
1115 | else: |
|
1098 | else: | |
1116 | self.next_page = None |
|
1099 | self.next_page = None | |
1117 |
|
1100 | |||
1118 | # No items available |
|
1101 | # No items available | |
1119 | else: |
|
1102 | else: | |
1120 | self.first_page = None |
|
1103 | self.first_page = None | |
1121 | self.page_count = 0 |
|
1104 | self.page_count = 0 | |
1122 | self.last_page = None |
|
1105 | self.last_page = None | |
1123 | self.first_item = None |
|
1106 | self.first_item = None | |
1124 | self.last_item = None |
|
1107 | self.last_item = None | |
1125 | self.previous_page = None |
|
1108 | self.previous_page = None | |
1126 | self.next_page = None |
|
1109 | self.next_page = None | |
1127 | self.items = [] |
|
1110 | self.items = [] | |
1128 |
|
1111 | |||
1129 | # This is a subclass of the 'list' type. Initialise the list now. |
|
1112 | # This is a subclass of the 'list' type. Initialise the list now. | |
1130 | list.__init__(self, reversed(self.items)) |
|
1113 | list.__init__(self, reversed(self.items)) | |
1131 |
|
1114 | |||
1132 |
|
1115 | |||
1133 | def changed_tooltip(nodes): |
|
1116 | def changed_tooltip(nodes): | |
1134 | """ |
|
1117 | """ | |
1135 | Generates a html string for changed nodes in changeset page. |
|
1118 | Generates a html string for changed nodes in changeset page. | |
1136 | It limits the output to 30 entries |
|
1119 | It limits the output to 30 entries | |
1137 |
|
1120 | |||
1138 | :param nodes: LazyNodesGenerator |
|
1121 | :param nodes: LazyNodesGenerator | |
1139 | """ |
|
1122 | """ | |
1140 | if nodes: |
|
1123 | if nodes: | |
1141 | pref = ': <br/> ' |
|
1124 | pref = ': <br/> ' | |
1142 | suf = '' |
|
1125 | suf = '' | |
1143 | if len(nodes) > 30: |
|
1126 | if len(nodes) > 30: | |
1144 | suf = '<br/>' + _(' and %s more') % (len(nodes) - 30) |
|
1127 | suf = '<br/>' + _(' and %s more') % (len(nodes) - 30) | |
1145 | return literal(pref + '<br/> '.join([safe_unicode(x.path) |
|
1128 | return literal(pref + '<br/> '.join([safe_unicode(x.path) | |
1146 | for x in nodes[:30]]) + suf) |
|
1129 | for x in nodes[:30]]) + suf) | |
1147 | else: |
|
1130 | else: | |
1148 | return ': ' + _('No Files') |
|
1131 | return ': ' + _('No Files') | |
1149 |
|
1132 | |||
1150 |
|
1133 | |||
1151 | def repo_link(groups_and_repos): |
|
1134 | def repo_link(groups_and_repos): | |
1152 | """ |
|
1135 | """ | |
1153 | Makes a breadcrumbs link to repo within a group |
|
1136 | Makes a breadcrumbs link to repo within a group | |
1154 | joins » on each group to create a fancy link |
|
1137 | joins » on each group to create a fancy link | |
1155 |
|
1138 | |||
1156 | ex:: |
|
1139 | ex:: | |
1157 | group >> subgroup >> repo |
|
1140 | group >> subgroup >> repo | |
1158 |
|
1141 | |||
1159 | :param groups_and_repos: |
|
1142 | :param groups_and_repos: | |
1160 | :param last_url: |
|
1143 | :param last_url: | |
1161 | """ |
|
1144 | """ | |
1162 | groups, just_name, repo_name = groups_and_repos |
|
1145 | groups, just_name, repo_name = groups_and_repos | |
1163 | last_url = url('summary_home', repo_name=repo_name) |
|
1146 | last_url = url('summary_home', repo_name=repo_name) | |
1164 | last_link = link_to(just_name, last_url) |
|
1147 | last_link = link_to(just_name, last_url) | |
1165 |
|
1148 | |||
1166 | def make_link(group): |
|
1149 | def make_link(group): | |
1167 | return link_to(group.name, |
|
1150 | return link_to(group.name, | |
1168 | url('repos_group_home', group_name=group.group_name)) |
|
1151 | url('repos_group_home', group_name=group.group_name)) | |
1169 | return literal(' » '.join(map(make_link, groups) + ['<span>%s</span>' % last_link])) |
|
1152 | return literal(' » '.join(map(make_link, groups) + ['<span>%s</span>' % last_link])) | |
1170 |
|
1153 | |||
1171 |
|
1154 | |||
1172 | def fancy_file_stats(stats): |
|
1155 | def fancy_file_stats(stats): | |
1173 | """ |
|
1156 | """ | |
1174 | Displays a fancy two colored bar for number of added/deleted |
|
1157 | Displays a fancy two colored bar for number of added/deleted | |
1175 | lines of code on file |
|
1158 | lines of code on file | |
1176 |
|
1159 | |||
1177 | :param stats: two element list of added/deleted lines of code |
|
1160 | :param stats: two element list of added/deleted lines of code | |
1178 | """ |
|
1161 | """ | |
1179 | from kallithea.lib.diffs import NEW_FILENODE, DEL_FILENODE, \ |
|
1162 | from kallithea.lib.diffs import NEW_FILENODE, DEL_FILENODE, \ | |
1180 | MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE |
|
1163 | MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE | |
1181 |
|
1164 | |||
1182 | def cgen(l_type, a_v, d_v): |
|
1165 | def cgen(l_type, a_v, d_v): | |
1183 | mapping = {'tr': 'top-right-rounded-corner-mid', |
|
1166 | mapping = {'tr': 'top-right-rounded-corner-mid', | |
1184 | 'tl': 'top-left-rounded-corner-mid', |
|
1167 | 'tl': 'top-left-rounded-corner-mid', | |
1185 | 'br': 'bottom-right-rounded-corner-mid', |
|
1168 | 'br': 'bottom-right-rounded-corner-mid', | |
1186 | 'bl': 'bottom-left-rounded-corner-mid'} |
|
1169 | 'bl': 'bottom-left-rounded-corner-mid'} | |
1187 | map_getter = lambda x: mapping[x] |
|
1170 | map_getter = lambda x: mapping[x] | |
1188 |
|
1171 | |||
1189 | if l_type == 'a' and d_v: |
|
1172 | if l_type == 'a' and d_v: | |
1190 | #case when added and deleted are present |
|
1173 | #case when added and deleted are present | |
1191 | return ' '.join(map(map_getter, ['tl', 'bl'])) |
|
1174 | return ' '.join(map(map_getter, ['tl', 'bl'])) | |
1192 |
|
1175 | |||
1193 | if l_type == 'a' and not d_v: |
|
1176 | if l_type == 'a' and not d_v: | |
1194 | return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl'])) |
|
1177 | return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl'])) | |
1195 |
|
1178 | |||
1196 | if l_type == 'd' and a_v: |
|
1179 | if l_type == 'd' and a_v: | |
1197 | return ' '.join(map(map_getter, ['tr', 'br'])) |
|
1180 | return ' '.join(map(map_getter, ['tr', 'br'])) | |
1198 |
|
1181 | |||
1199 | if l_type == 'd' and not a_v: |
|
1182 | if l_type == 'd' and not a_v: | |
1200 | return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl'])) |
|
1183 | return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl'])) | |
1201 |
|
1184 | |||
1202 | a, d = stats['added'], stats['deleted'] |
|
1185 | a, d = stats['added'], stats['deleted'] | |
1203 | width = 100 |
|
1186 | width = 100 | |
1204 |
|
1187 | |||
1205 | if stats['binary']: |
|
1188 | if stats['binary']: | |
1206 | #binary mode |
|
1189 | #binary mode | |
1207 | lbl = '' |
|
1190 | lbl = '' | |
1208 | bin_op = 1 |
|
1191 | bin_op = 1 | |
1209 |
|
1192 | |||
1210 | if BIN_FILENODE in stats['ops']: |
|
1193 | if BIN_FILENODE in stats['ops']: | |
1211 | lbl = 'bin+' |
|
1194 | lbl = 'bin+' | |
1212 |
|
1195 | |||
1213 | if NEW_FILENODE in stats['ops']: |
|
1196 | if NEW_FILENODE in stats['ops']: | |
1214 | lbl += _('new file') |
|
1197 | lbl += _('new file') | |
1215 | bin_op = NEW_FILENODE |
|
1198 | bin_op = NEW_FILENODE | |
1216 | elif MOD_FILENODE in stats['ops']: |
|
1199 | elif MOD_FILENODE in stats['ops']: | |
1217 | lbl += _('mod') |
|
1200 | lbl += _('mod') | |
1218 | bin_op = MOD_FILENODE |
|
1201 | bin_op = MOD_FILENODE | |
1219 | elif DEL_FILENODE in stats['ops']: |
|
1202 | elif DEL_FILENODE in stats['ops']: | |
1220 | lbl += _('del') |
|
1203 | lbl += _('del') | |
1221 | bin_op = DEL_FILENODE |
|
1204 | bin_op = DEL_FILENODE | |
1222 | elif RENAMED_FILENODE in stats['ops']: |
|
1205 | elif RENAMED_FILENODE in stats['ops']: | |
1223 | lbl += _('rename') |
|
1206 | lbl += _('rename') | |
1224 | bin_op = RENAMED_FILENODE |
|
1207 | bin_op = RENAMED_FILENODE | |
1225 |
|
1208 | |||
1226 | #chmod can go with other operations |
|
1209 | #chmod can go with other operations | |
1227 | if CHMOD_FILENODE in stats['ops']: |
|
1210 | if CHMOD_FILENODE in stats['ops']: | |
1228 | _org_lbl = _('chmod') |
|
1211 | _org_lbl = _('chmod') | |
1229 | lbl += _org_lbl if lbl.endswith('+') else '+%s' % _org_lbl |
|
1212 | lbl += _org_lbl if lbl.endswith('+') else '+%s' % _org_lbl | |
1230 |
|
1213 | |||
1231 | #import ipdb;ipdb.set_trace() |
|
1214 | #import ipdb;ipdb.set_trace() | |
1232 | b_d = '<div class="bin bin%s %s" style="width:100%%">%s</div>' % (bin_op, cgen('a', a_v='', d_v=0), lbl) |
|
1215 | b_d = '<div class="bin bin%s %s" style="width:100%%">%s</div>' % (bin_op, cgen('a', a_v='', d_v=0), lbl) | |
1233 | b_a = '<div class="bin bin1" style="width:0%%"></div>' |
|
1216 | b_a = '<div class="bin bin1" style="width:0%%"></div>' | |
1234 | return literal('<div style="width:%spx">%s%s</div>' % (width, b_a, b_d)) |
|
1217 | return literal('<div style="width:%spx">%s%s</div>' % (width, b_a, b_d)) | |
1235 |
|
1218 | |||
1236 | t = stats['added'] + stats['deleted'] |
|
1219 | t = stats['added'] + stats['deleted'] | |
1237 | unit = float(width) / (t or 1) |
|
1220 | unit = float(width) / (t or 1) | |
1238 |
|
1221 | |||
1239 | # needs > 9% of width to be visible or 0 to be hidden |
|
1222 | # needs > 9% of width to be visible or 0 to be hidden | |
1240 | a_p = max(9, unit * a) if a > 0 else 0 |
|
1223 | a_p = max(9, unit * a) if a > 0 else 0 | |
1241 | d_p = max(9, unit * d) if d > 0 else 0 |
|
1224 | d_p = max(9, unit * d) if d > 0 else 0 | |
1242 | p_sum = a_p + d_p |
|
1225 | p_sum = a_p + d_p | |
1243 |
|
1226 | |||
1244 | if p_sum > width: |
|
1227 | if p_sum > width: | |
1245 | #adjust the percentage to be == 100% since we adjusted to 9 |
|
1228 | #adjust the percentage to be == 100% since we adjusted to 9 | |
1246 | if a_p > d_p: |
|
1229 | if a_p > d_p: | |
1247 | a_p = a_p - (p_sum - width) |
|
1230 | a_p = a_p - (p_sum - width) | |
1248 | else: |
|
1231 | else: | |
1249 | d_p = d_p - (p_sum - width) |
|
1232 | d_p = d_p - (p_sum - width) | |
1250 |
|
1233 | |||
1251 | a_v = a if a > 0 else '' |
|
1234 | a_v = a if a > 0 else '' | |
1252 | d_v = d if d > 0 else '' |
|
1235 | d_v = d if d > 0 else '' | |
1253 |
|
1236 | |||
1254 | d_a = '<div class="added %s" style="width:%s%%">%s</div>' % ( |
|
1237 | d_a = '<div class="added %s" style="width:%s%%">%s</div>' % ( | |
1255 | cgen('a', a_v, d_v), a_p, a_v |
|
1238 | cgen('a', a_v, d_v), a_p, a_v | |
1256 | ) |
|
1239 | ) | |
1257 | d_d = '<div class="deleted %s" style="width:%s%%">%s</div>' % ( |
|
1240 | d_d = '<div class="deleted %s" style="width:%s%%">%s</div>' % ( | |
1258 | cgen('d', a_v, d_v), d_p, d_v |
|
1241 | cgen('d', a_v, d_v), d_p, d_v | |
1259 | ) |
|
1242 | ) | |
1260 | return literal('<div style="width:%spx">%s%s</div>' % (width, d_a, d_d)) |
|
1243 | return literal('<div style="width:%spx">%s%s</div>' % (width, d_a, d_d)) | |
1261 |
|
1244 | |||
1262 |
|
1245 | |||
1263 | def urlify_text(text_, safe=True): |
|
1246 | def urlify_text(text_, safe=True): | |
1264 | """ |
|
1247 | """ | |
1265 | Extract urls from text and make html links out of them |
|
1248 | Extract urls from text and make html links out of them | |
1266 |
|
1249 | |||
1267 | :param text_: |
|
1250 | :param text_: | |
1268 | """ |
|
1251 | """ | |
1269 |
|
1252 | |||
1270 | def url_func(match_obj): |
|
1253 | def url_func(match_obj): | |
1271 | url_full = match_obj.groups()[0] |
|
1254 | url_full = match_obj.groups()[0] | |
1272 | return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full}) |
|
1255 | return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full}) | |
1273 | _newtext = url_re.sub(url_func, text_) |
|
1256 | _newtext = url_re.sub(url_func, text_) | |
1274 | if safe: |
|
1257 | if safe: | |
1275 | return literal(_newtext) |
|
1258 | return literal(_newtext) | |
1276 | return _newtext |
|
1259 | return _newtext | |
1277 |
|
1260 | |||
1278 |
|
1261 | |||
1279 | def urlify_changesets(text_, repository): |
|
1262 | def urlify_changesets(text_, repository): | |
1280 | """ |
|
1263 | """ | |
1281 | Extract revision ids from changeset and make link from them |
|
1264 | Extract revision ids from changeset and make link from them | |
1282 |
|
1265 | |||
1283 | :param text_: |
|
1266 | :param text_: | |
1284 | :param repository: repo name to build the URL with |
|
1267 | :param repository: repo name to build the URL with | |
1285 | """ |
|
1268 | """ | |
1286 | from pylons import url # doh, we need to re-import url to mock it later |
|
1269 | from pylons import url # doh, we need to re-import url to mock it later | |
1287 |
|
1270 | |||
1288 | def url_func(match_obj): |
|
1271 | def url_func(match_obj): | |
1289 | rev = match_obj.group(0) |
|
1272 | rev = match_obj.group(0) | |
1290 | return '<a class="revision-link" href="%(url)s">%(rev)s</a>' % { |
|
1273 | return '<a class="revision-link" href="%(url)s">%(rev)s</a>' % { | |
1291 | 'url': url('changeset_home', repo_name=repository, revision=rev), |
|
1274 | 'url': url('changeset_home', repo_name=repository, revision=rev), | |
1292 | 'rev': rev, |
|
1275 | 'rev': rev, | |
1293 | } |
|
1276 | } | |
1294 |
|
1277 | |||
1295 | return re.sub(r'(?:^|(?<=[\s(),]))([0-9a-fA-F]{12,40})(?=$|\s|[.,:()])', url_func, text_) |
|
1278 | return re.sub(r'(?:^|(?<=[\s(),]))([0-9a-fA-F]{12,40})(?=$|\s|[.,:()])', url_func, text_) | |
1296 |
|
1279 | |||
1297 | def linkify_others(t, l): |
|
1280 | def linkify_others(t, l): | |
1298 | urls = re.compile(r'(\<a.*?\<\/a\>)',) |
|
1281 | urls = re.compile(r'(\<a.*?\<\/a\>)',) | |
1299 | links = [] |
|
1282 | links = [] | |
1300 | for e in urls.split(t): |
|
1283 | for e in urls.split(t): | |
1301 | if not urls.match(e): |
|
1284 | if not urls.match(e): | |
1302 | links.append('<a class="message-link" href="%s">%s</a>' % (l, e)) |
|
1285 | links.append('<a class="message-link" href="%s">%s</a>' % (l, e)) | |
1303 | else: |
|
1286 | else: | |
1304 | links.append(e) |
|
1287 | links.append(e) | |
1305 |
|
1288 | |||
1306 | return ''.join(links) |
|
1289 | return ''.join(links) | |
1307 |
|
1290 | |||
1308 | def urlify_commit(text_, repository, link_=None): |
|
1291 | def urlify_commit(text_, repository, link_=None): | |
1309 | """ |
|
1292 | """ | |
1310 | Parses given text message and makes proper links. |
|
1293 | Parses given text message and makes proper links. | |
1311 | issues are linked to given issue-server, and rest is a changeset link |
|
1294 | issues are linked to given issue-server, and rest is a changeset link | |
1312 | if link_ is given, in other case it's a plain text |
|
1295 | if link_ is given, in other case it's a plain text | |
1313 |
|
1296 | |||
1314 | :param text_: |
|
1297 | :param text_: | |
1315 | :param repository: |
|
1298 | :param repository: | |
1316 | :param link_: changeset link |
|
1299 | :param link_: changeset link | |
1317 | """ |
|
1300 | """ | |
1318 | def escaper(string): |
|
1301 | def escaper(string): | |
1319 | return string.replace('<', '<').replace('>', '>') |
|
1302 | return string.replace('<', '<').replace('>', '>') | |
1320 |
|
1303 | |||
1321 | # urlify changesets - extract revisions and make link out of them |
|
1304 | # urlify changesets - extract revisions and make link out of them | |
1322 | newtext = urlify_changesets(escaper(text_), repository) |
|
1305 | newtext = urlify_changesets(escaper(text_), repository) | |
1323 |
|
1306 | |||
1324 | # extract http/https links and make them real urls |
|
1307 | # extract http/https links and make them real urls | |
1325 | newtext = urlify_text(newtext, safe=False) |
|
1308 | newtext = urlify_text(newtext, safe=False) | |
1326 |
|
1309 | |||
1327 | newtext = urlify_issues(newtext, repository, link_) |
|
1310 | newtext = urlify_issues(newtext, repository, link_) | |
1328 |
|
1311 | |||
1329 | return literal(newtext) |
|
1312 | return literal(newtext) | |
1330 |
|
1313 | |||
1331 | def urlify_issues(newtext, repository, link_=None): |
|
1314 | def urlify_issues(newtext, repository, link_=None): | |
1332 | from kallithea import CONFIG as conf |
|
1315 | from kallithea import CONFIG as conf | |
1333 |
|
1316 | |||
1334 | # allow multiple issue servers to be used |
|
1317 | # allow multiple issue servers to be used | |
1335 | valid_indices = [ |
|
1318 | valid_indices = [ | |
1336 | x.group(1) |
|
1319 | x.group(1) | |
1337 | for x in map(lambda x: re.match(r'issue_pat(.*)', x), conf.keys()) |
|
1320 | for x in map(lambda x: re.match(r'issue_pat(.*)', x), conf.keys()) | |
1338 | if x and 'issue_server_link%s' % x.group(1) in conf |
|
1321 | if x and 'issue_server_link%s' % x.group(1) in conf | |
1339 | and 'issue_prefix%s' % x.group(1) in conf |
|
1322 | and 'issue_prefix%s' % x.group(1) in conf | |
1340 | ] |
|
1323 | ] | |
1341 |
|
1324 | |||
1342 | if valid_indices: |
|
1325 | if valid_indices: | |
1343 | log.debug('found issue server suffixes `%s` during valuation of: %s' |
|
1326 | log.debug('found issue server suffixes `%s` during valuation of: %s' | |
1344 | % (','.join(valid_indices), newtext)) |
|
1327 | % (','.join(valid_indices), newtext)) | |
1345 |
|
1328 | |||
1346 | for pattern_index in valid_indices: |
|
1329 | for pattern_index in valid_indices: | |
1347 | ISSUE_PATTERN = conf.get('issue_pat%s' % pattern_index) |
|
1330 | ISSUE_PATTERN = conf.get('issue_pat%s' % pattern_index) | |
1348 | ISSUE_SERVER_LNK = conf.get('issue_server_link%s' % pattern_index) |
|
1331 | ISSUE_SERVER_LNK = conf.get('issue_server_link%s' % pattern_index) | |
1349 | ISSUE_PREFIX = conf.get('issue_prefix%s' % pattern_index) |
|
1332 | ISSUE_PREFIX = conf.get('issue_prefix%s' % pattern_index) | |
1350 |
|
1333 | |||
1351 | log.debug('pattern suffix `%s` PAT:%s SERVER_LINK:%s PREFIX:%s' |
|
1334 | log.debug('pattern suffix `%s` PAT:%s SERVER_LINK:%s PREFIX:%s' | |
1352 | % (pattern_index, ISSUE_PATTERN, ISSUE_SERVER_LNK, |
|
1335 | % (pattern_index, ISSUE_PATTERN, ISSUE_SERVER_LNK, | |
1353 | ISSUE_PREFIX)) |
|
1336 | ISSUE_PREFIX)) | |
1354 |
|
1337 | |||
1355 | URL_PAT = re.compile(r'%s' % ISSUE_PATTERN) |
|
1338 | URL_PAT = re.compile(r'%s' % ISSUE_PATTERN) | |
1356 |
|
1339 | |||
1357 | def url_func(match_obj): |
|
1340 | def url_func(match_obj): | |
1358 | pref = '' |
|
1341 | pref = '' | |
1359 | if match_obj.group().startswith(' '): |
|
1342 | if match_obj.group().startswith(' '): | |
1360 | pref = ' ' |
|
1343 | pref = ' ' | |
1361 |
|
1344 | |||
1362 | issue_id = ''.join(match_obj.groups()) |
|
1345 | issue_id = ''.join(match_obj.groups()) | |
1363 | issue_url = ISSUE_SERVER_LNK.replace('{id}', issue_id) |
|
1346 | issue_url = ISSUE_SERVER_LNK.replace('{id}', issue_id) | |
1364 | if repository: |
|
1347 | if repository: | |
1365 | issue_url = issue_url.replace('{repo}', repository) |
|
1348 | issue_url = issue_url.replace('{repo}', repository) | |
1366 | repo_name = repository.split(URL_SEP)[-1] |
|
1349 | repo_name = repository.split(URL_SEP)[-1] | |
1367 | issue_url = issue_url.replace('{repo_name}', repo_name) |
|
1350 | issue_url = issue_url.replace('{repo_name}', repo_name) | |
1368 |
|
1351 | |||
1369 | return ( |
|
1352 | return ( | |
1370 | '%(pref)s<a class="%(cls)s" href="%(url)s">' |
|
1353 | '%(pref)s<a class="%(cls)s" href="%(url)s">' | |
1371 | '%(issue-prefix)s%(id-repr)s' |
|
1354 | '%(issue-prefix)s%(id-repr)s' | |
1372 | '</a>' |
|
1355 | '</a>' | |
1373 | ) % { |
|
1356 | ) % { | |
1374 | 'pref': pref, |
|
1357 | 'pref': pref, | |
1375 | 'cls': 'issue-tracker-link', |
|
1358 | 'cls': 'issue-tracker-link', | |
1376 | 'url': issue_url, |
|
1359 | 'url': issue_url, | |
1377 | 'id-repr': issue_id, |
|
1360 | 'id-repr': issue_id, | |
1378 | 'issue-prefix': ISSUE_PREFIX, |
|
1361 | 'issue-prefix': ISSUE_PREFIX, | |
1379 | 'serv': ISSUE_SERVER_LNK, |
|
1362 | 'serv': ISSUE_SERVER_LNK, | |
1380 | } |
|
1363 | } | |
1381 | newtext = URL_PAT.sub(url_func, newtext) |
|
1364 | newtext = URL_PAT.sub(url_func, newtext) | |
1382 | log.debug('processed prefix:`%s` => %s' % (pattern_index, newtext)) |
|
1365 | log.debug('processed prefix:`%s` => %s' % (pattern_index, newtext)) | |
1383 |
|
1366 | |||
1384 | # if we actually did something above |
|
1367 | # if we actually did something above | |
1385 | if link_: |
|
1368 | if link_: | |
1386 | # wrap not links into final link => link_ |
|
1369 | # wrap not links into final link => link_ | |
1387 | newtext = linkify_others(newtext, link_) |
|
1370 | newtext = linkify_others(newtext, link_) | |
1388 | return newtext |
|
1371 | return newtext | |
1389 |
|
1372 | |||
1390 |
|
1373 | |||
1391 | def rst(source): |
|
1374 | def rst(source): | |
1392 | return literal('<div class="rst-block">%s</div>' % |
|
1375 | return literal('<div class="rst-block">%s</div>' % | |
1393 | MarkupRenderer.rst(source)) |
|
1376 | MarkupRenderer.rst(source)) | |
1394 |
|
1377 | |||
1395 |
|
1378 | |||
1396 | def rst_w_mentions(source): |
|
1379 | def rst_w_mentions(source): | |
1397 | """ |
|
1380 | """ | |
1398 | Wrapped rst renderer with @mention highlighting |
|
1381 | Wrapped rst renderer with @mention highlighting | |
1399 |
|
1382 | |||
1400 | :param source: |
|
1383 | :param source: | |
1401 | """ |
|
1384 | """ | |
1402 | return literal('<div class="rst-block">%s</div>' % |
|
1385 | return literal('<div class="rst-block">%s</div>' % | |
1403 | MarkupRenderer.rst_with_mentions(source)) |
|
1386 | MarkupRenderer.rst_with_mentions(source)) | |
1404 |
|
1387 | |||
1405 | def short_ref(ref_type, ref_name): |
|
1388 | def short_ref(ref_type, ref_name): | |
1406 | if ref_type == 'rev': |
|
1389 | if ref_type == 'rev': | |
1407 | return short_id(ref_name) |
|
1390 | return short_id(ref_name) | |
1408 | return ref_name |
|
1391 | return ref_name | |
1409 |
|
1392 | |||
1410 | def link_to_ref(repo_name, ref_type, ref_name, rev=None): |
|
1393 | def link_to_ref(repo_name, ref_type, ref_name, rev=None): | |
1411 | """ |
|
1394 | """ | |
1412 | Return full markup for a href to changeset_home for a changeset. |
|
1395 | Return full markup for a href to changeset_home for a changeset. | |
1413 | If ref_type is branch it will link to changelog. |
|
1396 | If ref_type is branch it will link to changelog. | |
1414 | ref_name is shortened if ref_type is 'rev'. |
|
1397 | ref_name is shortened if ref_type is 'rev'. | |
1415 | if rev is specified show it too, explicitly linking to that revision. |
|
1398 | if rev is specified show it too, explicitly linking to that revision. | |
1416 | """ |
|
1399 | """ | |
1417 | txt = short_ref(ref_type, ref_name) |
|
1400 | txt = short_ref(ref_type, ref_name) | |
1418 | if ref_type == 'branch': |
|
1401 | if ref_type == 'branch': | |
1419 | u = url('changelog_home', repo_name=repo_name, branch=ref_name) |
|
1402 | u = url('changelog_home', repo_name=repo_name, branch=ref_name) | |
1420 | else: |
|
1403 | else: | |
1421 | u = url('changeset_home', repo_name=repo_name, revision=ref_name) |
|
1404 | u = url('changeset_home', repo_name=repo_name, revision=ref_name) | |
1422 | l = link_to(repo_name + '#' + txt, u) |
|
1405 | l = link_to(repo_name + '#' + txt, u) | |
1423 | if rev and ref_type != 'rev': |
|
1406 | if rev and ref_type != 'rev': | |
1424 | l = literal('%s (%s)' % (l, link_to(short_id(rev), url('changeset_home', repo_name=repo_name, revision=rev)))) |
|
1407 | l = literal('%s (%s)' % (l, link_to(short_id(rev), url('changeset_home', repo_name=repo_name, revision=rev)))) | |
1425 | return l |
|
1408 | return l | |
1426 |
|
1409 | |||
1427 | def changeset_status(repo, revision): |
|
1410 | def changeset_status(repo, revision): | |
1428 | return ChangesetStatusModel().get_status(repo, revision) |
|
1411 | return ChangesetStatusModel().get_status(repo, revision) | |
1429 |
|
1412 | |||
1430 |
|
1413 | |||
1431 | def changeset_status_lbl(changeset_status): |
|
1414 | def changeset_status_lbl(changeset_status): | |
1432 | return dict(ChangesetStatus.STATUSES).get(changeset_status) |
|
1415 | return dict(ChangesetStatus.STATUSES).get(changeset_status) | |
1433 |
|
1416 | |||
1434 |
|
1417 | |||
1435 | def get_permission_name(key): |
|
1418 | def get_permission_name(key): | |
1436 | return dict(Permission.PERMS).get(key) |
|
1419 | return dict(Permission.PERMS).get(key) | |
1437 |
|
1420 | |||
1438 |
|
1421 | |||
1439 | def journal_filter_help(): |
|
1422 | def journal_filter_help(): | |
1440 | return _(textwrap.dedent(''' |
|
1423 | return _(textwrap.dedent(''' | |
1441 | Example filter terms: |
|
1424 | Example filter terms: | |
1442 | repository:vcs |
|
1425 | repository:vcs | |
1443 | username:developer |
|
1426 | username:developer | |
1444 | action:*push* |
|
1427 | action:*push* | |
1445 | ip:127.0.0.1 |
|
1428 | ip:127.0.0.1 | |
1446 | date:20120101 |
|
1429 | date:20120101 | |
1447 | date:[20120101100000 TO 20120102] |
|
1430 | date:[20120101100000 TO 20120102] | |
1448 |
|
1431 | |||
1449 | Generate wildcards using '*' character: |
|
1432 | Generate wildcards using '*' character: | |
1450 | "repository:vcs*" - search everything starting with 'vcs' |
|
1433 | "repository:vcs*" - search everything starting with 'vcs' | |
1451 | "repository:*vcs*" - search for repository containing 'vcs' |
|
1434 | "repository:*vcs*" - search for repository containing 'vcs' | |
1452 |
|
1435 | |||
1453 | Optional AND / OR operators in queries |
|
1436 | Optional AND / OR operators in queries | |
1454 | "repository:vcs OR repository:test" |
|
1437 | "repository:vcs OR repository:test" | |
1455 | "username:test AND repository:test*" |
|
1438 | "username:test AND repository:test*" | |
1456 | ''')) |
|
1439 | ''')) | |
1457 |
|
1440 | |||
1458 |
|
1441 | |||
1459 | def not_mapped_error(repo_name): |
|
1442 | def not_mapped_error(repo_name): | |
1460 | flash(_('%s repository is not mapped to db perhaps' |
|
1443 | flash(_('%s repository is not mapped to db perhaps' | |
1461 | ' it was created or renamed from the filesystem' |
|
1444 | ' it was created or renamed from the filesystem' | |
1462 | ' please run the application again' |
|
1445 | ' please run the application again' | |
1463 | ' in order to rescan repositories') % repo_name, category='error') |
|
1446 | ' in order to rescan repositories') % repo_name, category='error') | |
1464 |
|
1447 | |||
1465 |
|
1448 | |||
1466 | def ip_range(ip_addr): |
|
1449 | def ip_range(ip_addr): | |
1467 | from kallithea.model.db import UserIpMap |
|
1450 | from kallithea.model.db import UserIpMap | |
1468 | s, e = UserIpMap._get_ip_range(ip_addr) |
|
1451 | s, e = UserIpMap._get_ip_range(ip_addr) | |
1469 | return '%s - %s' % (s, e) |
|
1452 | return '%s - %s' % (s, e) |
@@ -1,2158 +1,2154 b'' | |||||
1 | /** |
|
1 | /** | |
2 | Kallithea JS Files |
|
2 | Kallithea JS Files | |
3 | **/ |
|
3 | **/ | |
4 | 'use strict'; |
|
4 | 'use strict'; | |
5 |
|
5 | |||
6 | if (typeof console == "undefined" || typeof console.log == "undefined"){ |
|
6 | if (typeof console == "undefined" || typeof console.log == "undefined"){ | |
7 | console = { log: function() {} } |
|
7 | console = { log: function() {} } | |
8 | } |
|
8 | } | |
9 |
|
9 | |||
10 | /** |
|
10 | /** | |
11 | * INJECT .format function into String |
|
11 | * INJECT .format function into String | |
12 | * Usage: "My name is {0} {1}".format("Johny","Bravo") |
|
12 | * Usage: "My name is {0} {1}".format("Johny","Bravo") | |
13 | * Return "My name is Johny Bravo" |
|
13 | * Return "My name is Johny Bravo" | |
14 | * Inspired by https://gist.github.com/1049426 |
|
14 | * Inspired by https://gist.github.com/1049426 | |
15 | */ |
|
15 | */ | |
16 | String.prototype.format = function() { |
|
16 | String.prototype.format = function() { | |
17 | function format() { |
|
17 | function format() { | |
18 | var str = this; |
|
18 | var str = this; | |
19 | var len = arguments.length+1; |
|
19 | var len = arguments.length+1; | |
20 | var safe = undefined; |
|
20 | var safe = undefined; | |
21 | var arg = undefined; |
|
21 | var arg = undefined; | |
22 |
|
22 | |||
23 | // For each {0} {1} {n...} replace with the argument in that position. If |
|
23 | // For each {0} {1} {n...} replace with the argument in that position. If | |
24 | // the argument is an object or an array it will be stringified to JSON. |
|
24 | // the argument is an object or an array it will be stringified to JSON. | |
25 | for (var i=0; i < len; arg = arguments[i++]) { |
|
25 | for (var i=0; i < len; arg = arguments[i++]) { | |
26 | safe = typeof arg === 'object' ? JSON.stringify(arg) : arg; |
|
26 | safe = typeof arg === 'object' ? JSON.stringify(arg) : arg; | |
27 | str = str.replace(RegExp('\\{'+(i-1)+'\\}', 'g'), safe); |
|
27 | str = str.replace(RegExp('\\{'+(i-1)+'\\}', 'g'), safe); | |
28 | } |
|
28 | } | |
29 | return str; |
|
29 | return str; | |
30 | } |
|
30 | } | |
31 |
|
31 | |||
32 | // Save a reference of what may already exist under the property native. |
|
32 | // Save a reference of what may already exist under the property native. | |
33 | // Allows for doing something like: if("".format.native) { /* use native */ } |
|
33 | // Allows for doing something like: if("".format.native) { /* use native */ } | |
34 | format.native = String.prototype.format; |
|
34 | format.native = String.prototype.format; | |
35 |
|
35 | |||
36 | // Replace the prototype property |
|
36 | // Replace the prototype property | |
37 | return format; |
|
37 | return format; | |
38 |
|
38 | |||
39 | }(); |
|
39 | }(); | |
40 |
|
40 | |||
41 | String.prototype.strip = function(char) { |
|
41 | String.prototype.strip = function(char) { | |
42 | if(char === undefined){ |
|
42 | if(char === undefined){ | |
43 | char = '\\s'; |
|
43 | char = '\\s'; | |
44 | } |
|
44 | } | |
45 | return this.replace(new RegExp('^'+char+'+|'+char+'+$','g'), ''); |
|
45 | return this.replace(new RegExp('^'+char+'+|'+char+'+$','g'), ''); | |
46 | } |
|
46 | } | |
47 |
|
47 | |||
48 | String.prototype.lstrip = function(char) { |
|
48 | String.prototype.lstrip = function(char) { | |
49 | if(char === undefined){ |
|
49 | if(char === undefined){ | |
50 | char = '\\s'; |
|
50 | char = '\\s'; | |
51 | } |
|
51 | } | |
52 | return this.replace(new RegExp('^'+char+'+'),''); |
|
52 | return this.replace(new RegExp('^'+char+'+'),''); | |
53 | } |
|
53 | } | |
54 |
|
54 | |||
55 | String.prototype.rstrip = function(char) { |
|
55 | String.prototype.rstrip = function(char) { | |
56 | if(char === undefined){ |
|
56 | if(char === undefined){ | |
57 | char = '\\s'; |
|
57 | char = '\\s'; | |
58 | } |
|
58 | } | |
59 | return this.replace(new RegExp(''+char+'+$'),''); |
|
59 | return this.replace(new RegExp(''+char+'+$'),''); | |
60 | } |
|
60 | } | |
61 |
|
61 | |||
62 | /* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Polyfill |
|
62 | /* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Polyfill | |
63 | under MIT license / public domain, see |
|
63 | under MIT license / public domain, see | |
64 | https://developer.mozilla.org/en-US/docs/MDN/About#Copyrights_and_licenses */ |
|
64 | https://developer.mozilla.org/en-US/docs/MDN/About#Copyrights_and_licenses */ | |
65 | if(!Array.prototype.indexOf) { |
|
65 | if(!Array.prototype.indexOf) { | |
66 | Array.prototype.indexOf = function (searchElement, fromIndex) { |
|
66 | Array.prototype.indexOf = function (searchElement, fromIndex) { | |
67 | if ( this === undefined || this === null ) { |
|
67 | if ( this === undefined || this === null ) { | |
68 | throw new TypeError( '"this" is null or not defined' ); |
|
68 | throw new TypeError( '"this" is null or not defined' ); | |
69 | } |
|
69 | } | |
70 |
|
70 | |||
71 | var length = this.length >>> 0; // Hack to convert object.length to a UInt32 |
|
71 | var length = this.length >>> 0; // Hack to convert object.length to a UInt32 | |
72 |
|
72 | |||
73 | fromIndex = +fromIndex || 0; |
|
73 | fromIndex = +fromIndex || 0; | |
74 |
|
74 | |||
75 | if (Math.abs(fromIndex) === Infinity) { |
|
75 | if (Math.abs(fromIndex) === Infinity) { | |
76 | fromIndex = 0; |
|
76 | fromIndex = 0; | |
77 | } |
|
77 | } | |
78 |
|
78 | |||
79 | if (fromIndex < 0) { |
|
79 | if (fromIndex < 0) { | |
80 | fromIndex += length; |
|
80 | fromIndex += length; | |
81 | if (fromIndex < 0) { |
|
81 | if (fromIndex < 0) { | |
82 | fromIndex = 0; |
|
82 | fromIndex = 0; | |
83 | } |
|
83 | } | |
84 | } |
|
84 | } | |
85 |
|
85 | |||
86 | for (;fromIndex < length; fromIndex++) { |
|
86 | for (;fromIndex < length; fromIndex++) { | |
87 | if (this[fromIndex] === searchElement) { |
|
87 | if (this[fromIndex] === searchElement) { | |
88 | return fromIndex; |
|
88 | return fromIndex; | |
89 | } |
|
89 | } | |
90 | } |
|
90 | } | |
91 |
|
91 | |||
92 | return -1; |
|
92 | return -1; | |
93 | }; |
|
93 | }; | |
94 | } |
|
94 | } | |
95 |
|
95 | |||
96 | /* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter#Compatibility |
|
96 | /* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter#Compatibility | |
97 | under MIT license / public domain, see |
|
97 | under MIT license / public domain, see | |
98 | https://developer.mozilla.org/en-US/docs/MDN/About#Copyrights_and_licenses */ |
|
98 | https://developer.mozilla.org/en-US/docs/MDN/About#Copyrights_and_licenses */ | |
99 | if (!Array.prototype.filter) |
|
99 | if (!Array.prototype.filter) | |
100 | { |
|
100 | { | |
101 | Array.prototype.filter = function(fun /*, thisArg */) |
|
101 | Array.prototype.filter = function(fun /*, thisArg */) | |
102 | { |
|
102 | { | |
103 | if (this === void 0 || this === null) |
|
103 | if (this === void 0 || this === null) | |
104 | throw new TypeError(); |
|
104 | throw new TypeError(); | |
105 |
|
105 | |||
106 | var t = Object(this); |
|
106 | var t = Object(this); | |
107 | var len = t.length >>> 0; |
|
107 | var len = t.length >>> 0; | |
108 | if (typeof fun !== "function") |
|
108 | if (typeof fun !== "function") | |
109 | throw new TypeError(); |
|
109 | throw new TypeError(); | |
110 |
|
110 | |||
111 | var res = []; |
|
111 | var res = []; | |
112 | var thisArg = arguments.length >= 2 ? arguments[1] : void 0; |
|
112 | var thisArg = arguments.length >= 2 ? arguments[1] : void 0; | |
113 | for (var i = 0; i < len; i++) |
|
113 | for (var i = 0; i < len; i++) | |
114 | { |
|
114 | { | |
115 | if (i in t) |
|
115 | if (i in t) | |
116 | { |
|
116 | { | |
117 | var val = t[i]; |
|
117 | var val = t[i]; | |
118 |
|
118 | |||
119 | // NOTE: Technically this should Object.defineProperty at |
|
119 | // NOTE: Technically this should Object.defineProperty at | |
120 | // the next index, as push can be affected by |
|
120 | // the next index, as push can be affected by | |
121 | // properties on Object.prototype and Array.prototype. |
|
121 | // properties on Object.prototype and Array.prototype. | |
122 | // But that method's new, and collisions should be |
|
122 | // But that method's new, and collisions should be | |
123 | // rare, so use the more-compatible alternative. |
|
123 | // rare, so use the more-compatible alternative. | |
124 | if (fun.call(thisArg, val, i, t)) |
|
124 | if (fun.call(thisArg, val, i, t)) | |
125 | res.push(val); |
|
125 | res.push(val); | |
126 | } |
|
126 | } | |
127 | } |
|
127 | } | |
128 |
|
128 | |||
129 | return res; |
|
129 | return res; | |
130 | }; |
|
130 | }; | |
131 | } |
|
131 | } | |
132 |
|
132 | |||
133 | /** |
|
133 | /** | |
134 | * A customized version of PyRoutes.JS from https://pypi.python.org/pypi/pyroutes.js/ |
|
134 | * A customized version of PyRoutes.JS from https://pypi.python.org/pypi/pyroutes.js/ | |
135 | * which is copyright Stephane Klein and was made available under the BSD License. |
|
135 | * which is copyright Stephane Klein and was made available under the BSD License. | |
136 | * |
|
136 | * | |
137 | * Usage pyroutes.url('mark_error_fixed',{"error_id":error_id}) // /mark_error_fixed/<error_id> |
|
137 | * Usage pyroutes.url('mark_error_fixed',{"error_id":error_id}) // /mark_error_fixed/<error_id> | |
138 | */ |
|
138 | */ | |
139 | var pyroutes = (function() { |
|
139 | var pyroutes = (function() { | |
140 | // access global map defined in special file pyroutes |
|
140 | // access global map defined in special file pyroutes | |
141 | var matchlist = PROUTES_MAP; |
|
141 | var matchlist = PROUTES_MAP; | |
142 | var sprintf = (function() { |
|
142 | var sprintf = (function() { | |
143 | function get_type(variable) { |
|
143 | function get_type(variable) { | |
144 | return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase(); |
|
144 | return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase(); | |
145 | } |
|
145 | } | |
146 | function str_repeat(input, multiplier) { |
|
146 | function str_repeat(input, multiplier) { | |
147 | for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */} |
|
147 | for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */} | |
148 | return output.join(''); |
|
148 | return output.join(''); | |
149 | } |
|
149 | } | |
150 |
|
150 | |||
151 | var str_format = function() { |
|
151 | var str_format = function() { | |
152 | if (!str_format.cache.hasOwnProperty(arguments[0])) { |
|
152 | if (!str_format.cache.hasOwnProperty(arguments[0])) { | |
153 | str_format.cache[arguments[0]] = str_format.parse(arguments[0]); |
|
153 | str_format.cache[arguments[0]] = str_format.parse(arguments[0]); | |
154 | } |
|
154 | } | |
155 | return str_format.format.call(null, str_format.cache[arguments[0]], arguments); |
|
155 | return str_format.format.call(null, str_format.cache[arguments[0]], arguments); | |
156 | }; |
|
156 | }; | |
157 |
|
157 | |||
158 | str_format.format = function(parse_tree, argv) { |
|
158 | str_format.format = function(parse_tree, argv) { | |
159 | var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length; |
|
159 | var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length; | |
160 | for (i = 0; i < tree_length; i++) { |
|
160 | for (i = 0; i < tree_length; i++) { | |
161 | node_type = get_type(parse_tree[i]); |
|
161 | node_type = get_type(parse_tree[i]); | |
162 | if (node_type === 'string') { |
|
162 | if (node_type === 'string') { | |
163 | output.push(parse_tree[i]); |
|
163 | output.push(parse_tree[i]); | |
164 | } |
|
164 | } | |
165 | else if (node_type === 'array') { |
|
165 | else if (node_type === 'array') { | |
166 | match = parse_tree[i]; // convenience purposes only |
|
166 | match = parse_tree[i]; // convenience purposes only | |
167 | if (match[2]) { // keyword argument |
|
167 | if (match[2]) { // keyword argument | |
168 | arg = argv[cursor]; |
|
168 | arg = argv[cursor]; | |
169 | for (k = 0; k < match[2].length; k++) { |
|
169 | for (k = 0; k < match[2].length; k++) { | |
170 | if (!arg.hasOwnProperty(match[2][k])) { |
|
170 | if (!arg.hasOwnProperty(match[2][k])) { | |
171 | throw(sprintf('[sprintf] property "%s" does not exist', match[2][k])); |
|
171 | throw(sprintf('[sprintf] property "%s" does not exist', match[2][k])); | |
172 | } |
|
172 | } | |
173 | arg = arg[match[2][k]]; |
|
173 | arg = arg[match[2][k]]; | |
174 | } |
|
174 | } | |
175 | } |
|
175 | } | |
176 | else if (match[1]) { // positional argument (explicit) |
|
176 | else if (match[1]) { // positional argument (explicit) | |
177 | arg = argv[match[1]]; |
|
177 | arg = argv[match[1]]; | |
178 | } |
|
178 | } | |
179 | else { // positional argument (implicit) |
|
179 | else { // positional argument (implicit) | |
180 | arg = argv[cursor++]; |
|
180 | arg = argv[cursor++]; | |
181 | } |
|
181 | } | |
182 |
|
182 | |||
183 | if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) { |
|
183 | if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) { | |
184 | throw(sprintf('[sprintf] expecting number but found %s', get_type(arg))); |
|
184 | throw(sprintf('[sprintf] expecting number but found %s', get_type(arg))); | |
185 | } |
|
185 | } | |
186 | switch (match[8]) { |
|
186 | switch (match[8]) { | |
187 | case 'b': arg = arg.toString(2); break; |
|
187 | case 'b': arg = arg.toString(2); break; | |
188 | case 'c': arg = String.fromCharCode(arg); break; |
|
188 | case 'c': arg = String.fromCharCode(arg); break; | |
189 | case 'd': arg = parseInt(arg, 10); break; |
|
189 | case 'd': arg = parseInt(arg, 10); break; | |
190 | case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break; |
|
190 | case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break; | |
191 | case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break; |
|
191 | case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break; | |
192 | case 'o': arg = arg.toString(8); break; |
|
192 | case 'o': arg = arg.toString(8); break; | |
193 | case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break; |
|
193 | case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break; | |
194 | case 'u': arg = Math.abs(arg); break; |
|
194 | case 'u': arg = Math.abs(arg); break; | |
195 | case 'x': arg = arg.toString(16); break; |
|
195 | case 'x': arg = arg.toString(16); break; | |
196 | case 'X': arg = arg.toString(16).toUpperCase(); break; |
|
196 | case 'X': arg = arg.toString(16).toUpperCase(); break; | |
197 | } |
|
197 | } | |
198 | arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg); |
|
198 | arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg); | |
199 | pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' '; |
|
199 | pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' '; | |
200 | pad_length = match[6] - String(arg).length; |
|
200 | pad_length = match[6] - String(arg).length; | |
201 | pad = match[6] ? str_repeat(pad_character, pad_length) : ''; |
|
201 | pad = match[6] ? str_repeat(pad_character, pad_length) : ''; | |
202 | output.push(match[5] ? arg + pad : pad + arg); |
|
202 | output.push(match[5] ? arg + pad : pad + arg); | |
203 | } |
|
203 | } | |
204 | } |
|
204 | } | |
205 | return output.join(''); |
|
205 | return output.join(''); | |
206 | }; |
|
206 | }; | |
207 |
|
207 | |||
208 | str_format.cache = {}; |
|
208 | str_format.cache = {}; | |
209 |
|
209 | |||
210 | str_format.parse = function(fmt) { |
|
210 | str_format.parse = function(fmt) { | |
211 | var _fmt = fmt, match = [], parse_tree = [], arg_names = 0; |
|
211 | var _fmt = fmt, match = [], parse_tree = [], arg_names = 0; | |
212 | while (_fmt) { |
|
212 | while (_fmt) { | |
213 | if ((match = /^[^\x25]+/.exec(_fmt)) !== null) { |
|
213 | if ((match = /^[^\x25]+/.exec(_fmt)) !== null) { | |
214 | parse_tree.push(match[0]); |
|
214 | parse_tree.push(match[0]); | |
215 | } |
|
215 | } | |
216 | else if ((match = /^\x25{2}/.exec(_fmt)) !== null) { |
|
216 | else if ((match = /^\x25{2}/.exec(_fmt)) !== null) { | |
217 | parse_tree.push('%'); |
|
217 | parse_tree.push('%'); | |
218 | } |
|
218 | } | |
219 | else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) { |
|
219 | else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) { | |
220 | if (match[2]) { |
|
220 | if (match[2]) { | |
221 | arg_names |= 1; |
|
221 | arg_names |= 1; | |
222 | var field_list = [], replacement_field = match[2], field_match = []; |
|
222 | var field_list = [], replacement_field = match[2], field_match = []; | |
223 | if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) { |
|
223 | if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) { | |
224 | field_list.push(field_match[1]); |
|
224 | field_list.push(field_match[1]); | |
225 | while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') { |
|
225 | while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') { | |
226 | if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) { |
|
226 | if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) { | |
227 | field_list.push(field_match[1]); |
|
227 | field_list.push(field_match[1]); | |
228 | } |
|
228 | } | |
229 | else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) { |
|
229 | else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) { | |
230 | field_list.push(field_match[1]); |
|
230 | field_list.push(field_match[1]); | |
231 | } |
|
231 | } | |
232 | else { |
|
232 | else { | |
233 | throw('[sprintf] huh?'); |
|
233 | throw('[sprintf] huh?'); | |
234 | } |
|
234 | } | |
235 | } |
|
235 | } | |
236 | } |
|
236 | } | |
237 | else { |
|
237 | else { | |
238 | throw('[sprintf] huh?'); |
|
238 | throw('[sprintf] huh?'); | |
239 | } |
|
239 | } | |
240 | match[2] = field_list; |
|
240 | match[2] = field_list; | |
241 | } |
|
241 | } | |
242 | else { |
|
242 | else { | |
243 | arg_names |= 2; |
|
243 | arg_names |= 2; | |
244 | } |
|
244 | } | |
245 | if (arg_names === 3) { |
|
245 | if (arg_names === 3) { | |
246 | throw('[sprintf] mixing positional and named placeholders is not (yet) supported'); |
|
246 | throw('[sprintf] mixing positional and named placeholders is not (yet) supported'); | |
247 | } |
|
247 | } | |
248 | parse_tree.push(match); |
|
248 | parse_tree.push(match); | |
249 | } |
|
249 | } | |
250 | else { |
|
250 | else { | |
251 | throw('[sprintf] huh?'); |
|
251 | throw('[sprintf] huh?'); | |
252 | } |
|
252 | } | |
253 | _fmt = _fmt.substring(match[0].length); |
|
253 | _fmt = _fmt.substring(match[0].length); | |
254 | } |
|
254 | } | |
255 | return parse_tree; |
|
255 | return parse_tree; | |
256 | }; |
|
256 | }; | |
257 |
|
257 | |||
258 | return str_format; |
|
258 | return str_format; | |
259 | })(); |
|
259 | })(); | |
260 |
|
260 | |||
261 | var vsprintf = function(fmt, argv) { |
|
261 | var vsprintf = function(fmt, argv) { | |
262 | argv.unshift(fmt); |
|
262 | argv.unshift(fmt); | |
263 | return sprintf.apply(null, argv); |
|
263 | return sprintf.apply(null, argv); | |
264 | }; |
|
264 | }; | |
265 | return { |
|
265 | return { | |
266 | 'url': function(route_name, params) { |
|
266 | 'url': function(route_name, params) { | |
267 | var result = route_name; |
|
267 | var result = route_name; | |
268 | if (typeof(params) != 'object'){ |
|
268 | if (typeof(params) != 'object'){ | |
269 | params = {}; |
|
269 | params = {}; | |
270 | } |
|
270 | } | |
271 | if (matchlist.hasOwnProperty(route_name)) { |
|
271 | if (matchlist.hasOwnProperty(route_name)) { | |
272 | var route = matchlist[route_name]; |
|
272 | var route = matchlist[route_name]; | |
273 | // param substitution |
|
273 | // param substitution | |
274 | for(var i=0; i < route[1].length; i++) { |
|
274 | for(var i=0; i < route[1].length; i++) { | |
275 | if (!params.hasOwnProperty(route[1][i])) |
|
275 | if (!params.hasOwnProperty(route[1][i])) | |
276 | throw new Error(route[1][i] + ' missing in "' + route_name + '" route generation'); |
|
276 | throw new Error(route[1][i] + ' missing in "' + route_name + '" route generation'); | |
277 | } |
|
277 | } | |
278 | result = sprintf(route[0], params); |
|
278 | result = sprintf(route[0], params); | |
279 |
|
279 | |||
280 | var ret = []; |
|
280 | var ret = []; | |
281 | //extra params => GET |
|
281 | //extra params => GET | |
282 | for(var param in params){ |
|
282 | for(var param in params){ | |
283 | if (route[1].indexOf(param) == -1){ |
|
283 | if (route[1].indexOf(param) == -1){ | |
284 | ret.push(encodeURIComponent(param) + "=" + encodeURIComponent(params[param])); |
|
284 | ret.push(encodeURIComponent(param) + "=" + encodeURIComponent(params[param])); | |
285 | } |
|
285 | } | |
286 | } |
|
286 | } | |
287 | var _parts = ret.join("&"); |
|
287 | var _parts = ret.join("&"); | |
288 | if(_parts){ |
|
288 | if(_parts){ | |
289 | result = result +'?'+ _parts |
|
289 | result = result +'?'+ _parts | |
290 | } |
|
290 | } | |
291 | } |
|
291 | } | |
292 |
|
292 | |||
293 | return result; |
|
293 | return result; | |
294 | }, |
|
294 | }, | |
295 | 'register': function(route_name, route_tmpl, req_params) { |
|
295 | 'register': function(route_name, route_tmpl, req_params) { | |
296 | if (typeof(req_params) != 'object') { |
|
296 | if (typeof(req_params) != 'object') { | |
297 | req_params = []; |
|
297 | req_params = []; | |
298 | } |
|
298 | } | |
299 | var keys = []; |
|
299 | var keys = []; | |
300 | for (var i=0; i < req_params.length; i++) { |
|
300 | for (var i=0; i < req_params.length; i++) { | |
301 | keys.push(req_params[i]) |
|
301 | keys.push(req_params[i]) | |
302 | } |
|
302 | } | |
303 | matchlist[route_name] = [ |
|
303 | matchlist[route_name] = [ | |
304 | unescape(route_tmpl), |
|
304 | unescape(route_tmpl), | |
305 | keys |
|
305 | keys | |
306 | ] |
|
306 | ] | |
307 | }, |
|
307 | }, | |
308 | '_routes': function(){ |
|
308 | '_routes': function(){ | |
309 | return matchlist; |
|
309 | return matchlist; | |
310 | } |
|
310 | } | |
311 | } |
|
311 | } | |
312 | })(); |
|
312 | })(); | |
313 |
|
313 | |||
314 |
|
314 | |||
315 | /** |
|
315 | /** | |
316 | * GLOBAL YUI Shortcuts |
|
316 | * GLOBAL YUI Shortcuts | |
317 | */ |
|
317 | */ | |
318 | var YUD = YAHOO.util.Dom; |
|
318 | var YUD = YAHOO.util.Dom; | |
319 | var YUE = YAHOO.util.Event; |
|
319 | var YUE = YAHOO.util.Event; | |
320 |
|
320 | |||
321 | /* Invoke all functions in callbacks */ |
|
321 | /* Invoke all functions in callbacks */ | |
322 | var _run_callbacks = function(callbacks){ |
|
322 | var _run_callbacks = function(callbacks){ | |
323 | if (callbacks !== undefined){ |
|
323 | if (callbacks !== undefined){ | |
324 | var _l = callbacks.length; |
|
324 | var _l = callbacks.length; | |
325 | for (var i=0;i<_l;i++){ |
|
325 | for (var i=0;i<_l;i++){ | |
326 | var func = callbacks[i]; |
|
326 | var func = callbacks[i]; | |
327 | if(typeof(func)=='function'){ |
|
327 | if(typeof(func)=='function'){ | |
328 | try{ |
|
328 | try{ | |
329 | func(); |
|
329 | func(); | |
330 | }catch (err){}; |
|
330 | }catch (err){}; | |
331 | } |
|
331 | } | |
332 | } |
|
332 | } | |
333 | } |
|
333 | } | |
334 | } |
|
334 | } | |
335 |
|
335 | |||
336 | /** |
|
336 | /** | |
337 | * turns objects into GET query string |
|
337 | * turns objects into GET query string | |
338 | */ |
|
338 | */ | |
339 | var _toQueryString = function(o) { |
|
339 | var _toQueryString = function(o) { | |
340 | if(typeof o !== 'object') { |
|
340 | if(typeof o !== 'object') { | |
341 | return false; |
|
341 | return false; | |
342 | } |
|
342 | } | |
343 | var _p, _qs = []; |
|
343 | var _p, _qs = []; | |
344 | for(_p in o) { |
|
344 | for(_p in o) { | |
345 | _qs.push(encodeURIComponent(_p) + '=' + encodeURIComponent(o[_p])); |
|
345 | _qs.push(encodeURIComponent(_p) + '=' + encodeURIComponent(o[_p])); | |
346 | } |
|
346 | } | |
347 | return _qs.join('&'); |
|
347 | return _qs.join('&'); | |
348 | }; |
|
348 | }; | |
349 |
|
349 | |||
350 | /** |
|
350 | /** | |
351 | * Load HTML into DOM using Ajax |
|
351 | * Load HTML into DOM using Ajax | |
352 | * |
|
352 | * | |
353 | * @param $target: load html async and place it (or an error message) here |
|
353 | * @param $target: load html async and place it (or an error message) here | |
354 | * @param success: success callback function |
|
354 | * @param success: success callback function | |
355 | * @param args: query parameters to pass to url |
|
355 | * @param args: query parameters to pass to url | |
356 | */ |
|
356 | */ | |
357 | function asynchtml(url, $target, success, args){ |
|
357 | function asynchtml(url, $target, success, args){ | |
358 | if(args===undefined){ |
|
358 | if(args===undefined){ | |
359 | args=null; |
|
359 | args=null; | |
360 | } |
|
360 | } | |
361 | $target.html(_TM['Loading ...']).css('opacity','0.3'); |
|
361 | $target.html(_TM['Loading ...']).css('opacity','0.3'); | |
362 |
|
362 | |||
363 | return $.ajax({url: url, data: args, headers: {'X-PARTIAL-XHR': '1'}, cache: false, dataType: 'html'}) |
|
363 | return $.ajax({url: url, data: args, headers: {'X-PARTIAL-XHR': '1'}, cache: false, dataType: 'html'}) | |
364 | .done(function(html) { |
|
364 | .done(function(html) { | |
365 | $target.html(html); |
|
365 | $target.html(html); | |
366 | $target.css('opacity','1.0'); |
|
366 | $target.css('opacity','1.0'); | |
367 | //execute the given original callback |
|
367 | //execute the given original callback | |
368 | if (success !== undefined && success) { |
|
368 | if (success !== undefined && success) { | |
369 | success(); |
|
369 | success(); | |
370 | } |
|
370 | } | |
371 | }) |
|
371 | }) | |
372 | .fail(function(jqXHR, textStatus, errorThrown) { |
|
372 | .fail(function(jqXHR, textStatus, errorThrown) { | |
373 | if (textStatus == "abort") |
|
373 | if (textStatus == "abort") | |
374 | return; |
|
374 | return; | |
375 | console.log('Ajax failure: ' + textStatus); |
|
375 | console.log('Ajax failure: ' + textStatus); | |
376 | $target.html('<span class="error_red">ERROR: {0}</span>'.format(textStatus)); |
|
376 | $target.html('<span class="error_red">ERROR: {0}</span>'.format(textStatus)); | |
377 | $target.css('opacity','1.0'); |
|
377 | $target.css('opacity','1.0'); | |
378 | }) |
|
378 | }) | |
379 | ; |
|
379 | ; | |
380 | }; |
|
380 | }; | |
381 |
|
381 | |||
382 | var ajaxGET = function(url,success) { |
|
382 | var ajaxGET = function(url,success) { | |
383 | return $.ajax({url: url, headers: {'X-PARTIAL-XHR': '1'}, cache: false}) |
|
383 | return $.ajax({url: url, headers: {'X-PARTIAL-XHR': '1'}, cache: false}) | |
384 | .done(success) |
|
384 | .done(success) | |
385 | .fail(function(jqXHR, textStatus, errorThrown) { |
|
385 | .fail(function(jqXHR, textStatus, errorThrown) { | |
386 | if (textStatus == "abort") |
|
386 | if (textStatus == "abort") | |
387 | return; |
|
387 | return; | |
388 | alert("Ajax GET error: " + textStatus); |
|
388 | alert("Ajax GET error: " + textStatus); | |
389 | }) |
|
389 | }) | |
390 | ; |
|
390 | ; | |
391 | }; |
|
391 | }; | |
392 |
|
392 | |||
393 | var ajaxPOST = function(url, postData, success, failure) { |
|
393 | var ajaxPOST = function(url, postData, success, failure) { | |
394 | var postData = _toQueryString(postData); |
|
394 | var postData = _toQueryString(postData); | |
395 | if(failure === undefined) { |
|
395 | if(failure === undefined) { | |
396 | failure = function(jqXHR, textStatus, errorThrown) { |
|
396 | failure = function(jqXHR, textStatus, errorThrown) { | |
397 | if (textStatus != "abort") |
|
397 | if (textStatus != "abort") | |
398 | alert("Error posting to server: " + textStatus); |
|
398 | alert("Error posting to server: " + textStatus); | |
399 | }; |
|
399 | }; | |
400 | } |
|
400 | } | |
401 | return $.ajax({url: url, data: postData, type: 'POST', headers: {'X-PARTIAL-XHR': '1'}, cache: false}) |
|
401 | return $.ajax({url: url, data: postData, type: 'POST', headers: {'X-PARTIAL-XHR': '1'}, cache: false}) | |
402 | .done(success) |
|
402 | .done(success) | |
403 | .fail(failure); |
|
403 | .fail(failure); | |
404 | }; |
|
404 | }; | |
405 |
|
405 | |||
406 |
|
406 | |||
407 | /** |
|
407 | /** | |
408 | * activate .show_more links |
|
408 | * activate .show_more links | |
409 | * the .show_more must have an id that is the the id of an element to hide prefixed with _ |
|
409 | * the .show_more must have an id that is the the id of an element to hide prefixed with _ | |
410 | * the parentnode will be displayed |
|
410 | * the parentnode will be displayed | |
411 | */ |
|
411 | */ | |
412 | var show_more_event = function(){ |
|
412 | var show_more_event = function(){ | |
413 | $('.show_more').click(function(e){ |
|
413 | $('.show_more').click(function(e){ | |
414 | var el = e.currentTarget; |
|
414 | var el = e.currentTarget; | |
415 | $('#' + el.id.substring(1)).hide(); |
|
415 | $('#' + el.id.substring(1)).hide(); | |
416 | $(el.parentNode).show(); |
|
416 | $(el.parentNode).show(); | |
417 | }); |
|
417 | }); | |
418 | }; |
|
418 | }; | |
419 |
|
419 | |||
420 | /** |
|
420 | /** | |
421 | * activate .lazy-cs mouseover for showing changeset tooltip |
|
421 | * activate .lazy-cs mouseover for showing changeset tooltip | |
422 | */ |
|
422 | */ | |
423 | var show_changeset_tooltip = function(){ |
|
423 | var show_changeset_tooltip = function(){ | |
424 | $('.lazy-cs').mouseover(function(e){ |
|
424 | $('.lazy-cs').mouseover(function(e){ | |
425 | var $target = $(e.currentTarget); |
|
425 | var $target = $(e.currentTarget); | |
426 | var rid = $target.attr('raw_id'); |
|
426 | var rid = $target.attr('raw_id'); | |
427 | var repo_name = $target.attr('repo_name'); |
|
427 | var repo_name = $target.attr('repo_name'); | |
428 | if(rid && !$target.hasClass('tooltip')){ |
|
428 | if(rid && !$target.hasClass('tooltip')){ | |
429 | _show_tooltip(e, _TM['loading ...']); |
|
429 | _show_tooltip(e, _TM['loading ...']); | |
430 | var url = pyroutes.url('changeset_info', {"repo_name": repo_name, "revision": rid}); |
|
430 | var url = pyroutes.url('changeset_info', {"repo_name": repo_name, "revision": rid}); | |
431 | ajaxGET(url, function(json){ |
|
431 | ajaxGET(url, function(json){ | |
432 | $target.addClass('tooltip') |
|
432 | $target.addClass('tooltip') | |
433 | _show_tooltip(e, json['message']); |
|
433 | _show_tooltip(e, json['message']); | |
434 | _activate_tooltip($target); |
|
434 | _activate_tooltip($target); | |
435 | }); |
|
435 | }); | |
436 | } |
|
436 | } | |
437 | }); |
|
437 | }); | |
438 | }; |
|
438 | }; | |
439 |
|
439 | |||
440 | var _onSuccessFollow = function(target){ |
|
440 | var _onSuccessFollow = function(target){ | |
441 | var $target = $(target); |
|
441 | var $target = $(target); | |
442 | var $f_cnt = $('#current_followers_count'); |
|
442 | var $f_cnt = $('#current_followers_count'); | |
443 | if($target.hasClass('follow')){ |
|
443 | if($target.hasClass('follow')){ | |
444 | $target.attr('class', 'following'); |
|
444 | $target.attr('class', 'following'); | |
445 | $target.attr('title', _TM['Stop following this repository']); |
|
445 | $target.attr('title', _TM['Stop following this repository']); | |
446 | if($f_cnt.html()){ |
|
446 | if($f_cnt.html()){ | |
447 | var cnt = Number($f_cnt.html())+1; |
|
447 | var cnt = Number($f_cnt.html())+1; | |
448 | $f_cnt.html(cnt); |
|
448 | $f_cnt.html(cnt); | |
449 | } |
|
449 | } | |
450 | } |
|
450 | } | |
451 | else{ |
|
451 | else{ | |
452 | $target.attr('class', 'follow'); |
|
452 | $target.attr('class', 'follow'); | |
453 | $target.attr('title', _TM['Start following this repository']); |
|
453 | $target.attr('title', _TM['Start following this repository']); | |
454 | if($f_cnt.html()){ |
|
454 | if($f_cnt.html()){ | |
455 | var cnt = Number($f_cnt.html())-1; |
|
455 | var cnt = Number($f_cnt.html())-1; | |
456 | $f_cnt.html(cnt); |
|
456 | $f_cnt.html(cnt); | |
457 | } |
|
457 | } | |
458 | } |
|
458 | } | |
459 | } |
|
459 | } | |
460 |
|
460 | |||
461 |
var toggleFollowingRepo = function(target, follows_repo_id |
|
461 | var toggleFollowingRepo = function(target, follows_repo_id){ | |
462 | var args = 'follows_repo_id=' + follows_repo_id; |
|
462 | var args = 'follows_repo_id=' + follows_repo_id; | |
463 | args += '&auth_token=' + token; |
|
|||
464 | if(user_id != undefined){ |
|
|||
465 | args +="&user_id=" + user_id; |
|
|||
466 | } |
|
|||
467 | $.post(TOGGLE_FOLLOW_URL, args, function(data){ |
|
463 | $.post(TOGGLE_FOLLOW_URL, args, function(data){ | |
468 | _onSuccessFollow(target); |
|
464 | _onSuccessFollow(target); | |
469 | }); |
|
465 | }); | |
470 | return false; |
|
466 | return false; | |
471 | }; |
|
467 | }; | |
472 |
|
468 | |||
473 |
var showRepoSize = function(target, repo_name |
|
469 | var showRepoSize = function(target, repo_name){ | |
474 |
var args = ' |
|
470 | var args = ''; | |
475 |
|
471 | |||
476 | if(!$("#" + target).hasClass('loaded')){ |
|
472 | if(!$("#" + target).hasClass('loaded')){ | |
477 | $("#" + target).html(_TM['Loading ...']); |
|
473 | $("#" + target).html(_TM['Loading ...']); | |
478 | var url = pyroutes.url('repo_size', {"repo_name":repo_name}); |
|
474 | var url = pyroutes.url('repo_size', {"repo_name":repo_name}); | |
479 | $.post(url, args, function(data) { |
|
475 | $.post(url, args, function(data) { | |
480 | $("#" + target).html(data); |
|
476 | $("#" + target).html(data); | |
481 | $("#" + target).addClass('loaded'); |
|
477 | $("#" + target).addClass('loaded'); | |
482 | }); |
|
478 | }); | |
483 | } |
|
479 | } | |
484 | return false; |
|
480 | return false; | |
485 | }; |
|
481 | }; | |
486 |
|
482 | |||
487 | /** |
|
483 | /** | |
488 | * tooltips |
|
484 | * tooltips | |
489 | */ |
|
485 | */ | |
490 |
|
486 | |||
491 | var tooltip_activate = function(){ |
|
487 | var tooltip_activate = function(){ | |
492 | $(document).ready(_init_tooltip); |
|
488 | $(document).ready(_init_tooltip); | |
493 | }; |
|
489 | }; | |
494 |
|
490 | |||
495 | var _activate_tooltip = function($tt){ |
|
491 | var _activate_tooltip = function($tt){ | |
496 | $tt.mouseover(_show_tooltip); |
|
492 | $tt.mouseover(_show_tooltip); | |
497 | $tt.mousemove(_move_tooltip); |
|
493 | $tt.mousemove(_move_tooltip); | |
498 | $tt.mouseout(_close_tooltip); |
|
494 | $tt.mouseout(_close_tooltip); | |
499 | }; |
|
495 | }; | |
500 |
|
496 | |||
501 | var _init_tooltip = function(){ |
|
497 | var _init_tooltip = function(){ | |
502 | var $tipBox = $('#tip-box'); |
|
498 | var $tipBox = $('#tip-box'); | |
503 | if(!$tipBox.length){ |
|
499 | if(!$tipBox.length){ | |
504 | $tipBox = $('<div id="tip-box"></div>') |
|
500 | $tipBox = $('<div id="tip-box"></div>') | |
505 | $(document.body).append($tipBox); |
|
501 | $(document.body).append($tipBox); | |
506 | } |
|
502 | } | |
507 |
|
503 | |||
508 | $tipBox.hide(); |
|
504 | $tipBox.hide(); | |
509 | $tipBox.css('position', 'absolute'); |
|
505 | $tipBox.css('position', 'absolute'); | |
510 | $tipBox.css('max-width', '600px'); |
|
506 | $tipBox.css('max-width', '600px'); | |
511 |
|
507 | |||
512 | _activate_tooltip($('.tooltip')); |
|
508 | _activate_tooltip($('.tooltip')); | |
513 | }; |
|
509 | }; | |
514 |
|
510 | |||
515 | var _show_tooltip = function(e, tipText){ |
|
511 | var _show_tooltip = function(e, tipText){ | |
516 | e.stopImmediatePropagation(); |
|
512 | e.stopImmediatePropagation(); | |
517 | var el = e.currentTarget; |
|
513 | var el = e.currentTarget; | |
518 | if(tipText){ |
|
514 | if(tipText){ | |
519 | // just use it |
|
515 | // just use it | |
520 | } else if(el.tagName.toLowerCase() === 'img'){ |
|
516 | } else if(el.tagName.toLowerCase() === 'img'){ | |
521 | tipText = el.alt ? el.alt : ''; |
|
517 | tipText = el.alt ? el.alt : ''; | |
522 | } else { |
|
518 | } else { | |
523 | tipText = el.title ? el.title : ''; |
|
519 | tipText = el.title ? el.title : ''; | |
524 | } |
|
520 | } | |
525 |
|
521 | |||
526 | if(tipText !== ''){ |
|
522 | if(tipText !== ''){ | |
527 | // save org title |
|
523 | // save org title | |
528 | $(el).attr('tt_title', tipText); |
|
524 | $(el).attr('tt_title', tipText); | |
529 | // reset title to not show org tooltips |
|
525 | // reset title to not show org tooltips | |
530 | $(el).attr('title', ''); |
|
526 | $(el).attr('title', ''); | |
531 |
|
527 | |||
532 | var $tipBox = $('#tip-box'); |
|
528 | var $tipBox = $('#tip-box'); | |
533 | $tipBox.html(tipText); |
|
529 | $tipBox.html(tipText); | |
534 | $tipBox.css('display', 'block'); |
|
530 | $tipBox.css('display', 'block'); | |
535 | } |
|
531 | } | |
536 | }; |
|
532 | }; | |
537 |
|
533 | |||
538 | var _move_tooltip = function(e){ |
|
534 | var _move_tooltip = function(e){ | |
539 | e.stopImmediatePropagation(); |
|
535 | e.stopImmediatePropagation(); | |
540 | var $tipBox = $('#tip-box'); |
|
536 | var $tipBox = $('#tip-box'); | |
541 | $tipBox.css('top', (e.pageY + 15) + 'px'); |
|
537 | $tipBox.css('top', (e.pageY + 15) + 'px'); | |
542 | $tipBox.css('left', (e.pageX + 15) + 'px'); |
|
538 | $tipBox.css('left', (e.pageX + 15) + 'px'); | |
543 | }; |
|
539 | }; | |
544 |
|
540 | |||
545 | var _close_tooltip = function(e){ |
|
541 | var _close_tooltip = function(e){ | |
546 | e.stopImmediatePropagation(); |
|
542 | e.stopImmediatePropagation(); | |
547 | var $tipBox = $('#tip-box'); |
|
543 | var $tipBox = $('#tip-box'); | |
548 | $tipBox.hide(); |
|
544 | $tipBox.hide(); | |
549 | var el = e.currentTarget; |
|
545 | var el = e.currentTarget; | |
550 | $(el).attr('title', $(el).attr('tt_title')); |
|
546 | $(el).attr('title', $(el).attr('tt_title')); | |
551 | }; |
|
547 | }; | |
552 |
|
548 | |||
553 | /** |
|
549 | /** | |
554 | * Quick filter widget |
|
550 | * Quick filter widget | |
555 | * |
|
551 | * | |
556 | * @param target: filter input target |
|
552 | * @param target: filter input target | |
557 | * @param nodes: list of nodes in html we want to filter. |
|
553 | * @param nodes: list of nodes in html we want to filter. | |
558 | * @param display_element function that takes current node from nodes and |
|
554 | * @param display_element function that takes current node from nodes and | |
559 | * does hide or show based on the node |
|
555 | * does hide or show based on the node | |
560 | */ |
|
556 | */ | |
561 | var q_filter = (function() { |
|
557 | var q_filter = (function() { | |
562 | var _namespace = {}; |
|
558 | var _namespace = {}; | |
563 | var namespace = function (target) { |
|
559 | var namespace = function (target) { | |
564 | if (!(target in _namespace)) { |
|
560 | if (!(target in _namespace)) { | |
565 | _namespace[target] = {}; |
|
561 | _namespace[target] = {}; | |
566 | } |
|
562 | } | |
567 | return _namespace[target]; |
|
563 | return _namespace[target]; | |
568 | }; |
|
564 | }; | |
569 | return function (target, $nodes, display_element) { |
|
565 | return function (target, $nodes, display_element) { | |
570 | var $nodes = $nodes; |
|
566 | var $nodes = $nodes; | |
571 | var $q_filter_field = $('#' + target); |
|
567 | var $q_filter_field = $('#' + target); | |
572 | var F = namespace(target); |
|
568 | var F = namespace(target); | |
573 |
|
569 | |||
574 | $q_filter_field.keyup(function (e) { |
|
570 | $q_filter_field.keyup(function (e) { | |
575 | clearTimeout(F.filterTimeout); |
|
571 | clearTimeout(F.filterTimeout); | |
576 | F.filterTimeout = setTimeout(F.updateFilter, 600); |
|
572 | F.filterTimeout = setTimeout(F.updateFilter, 600); | |
577 | }); |
|
573 | }); | |
578 |
|
574 | |||
579 | F.filterTimeout = null; |
|
575 | F.filterTimeout = null; | |
580 |
|
576 | |||
581 | F.updateFilter = function () { |
|
577 | F.updateFilter = function () { | |
582 | // Reset timeout |
|
578 | // Reset timeout | |
583 | F.filterTimeout = null; |
|
579 | F.filterTimeout = null; | |
584 |
|
580 | |||
585 | var obsolete = []; |
|
581 | var obsolete = []; | |
586 |
|
582 | |||
587 | var req = $q_filter_field.val().toLowerCase(); |
|
583 | var req = $q_filter_field.val().toLowerCase(); | |
588 |
|
584 | |||
589 | var showing = 0; |
|
585 | var showing = 0; | |
590 | $nodes.each(function () { |
|
586 | $nodes.each(function () { | |
591 | var n = this; |
|
587 | var n = this; | |
592 | var target_element = display_element(n); |
|
588 | var target_element = display_element(n); | |
593 | if (req && n.innerHTML.toLowerCase().indexOf(req) == -1) { |
|
589 | if (req && n.innerHTML.toLowerCase().indexOf(req) == -1) { | |
594 | $(target_element).hide(); |
|
590 | $(target_element).hide(); | |
595 | } |
|
591 | } | |
596 | else { |
|
592 | else { | |
597 | $(target_element).show(); |
|
593 | $(target_element).show(); | |
598 | showing += 1; |
|
594 | showing += 1; | |
599 | } |
|
595 | } | |
600 | }); |
|
596 | }); | |
601 |
|
597 | |||
602 | $('#repo_count').html(showing); |
|
598 | $('#repo_count').html(showing); | |
603 | /* FIXME: don't hardcode */ |
|
599 | /* FIXME: don't hardcode */ | |
604 | } |
|
600 | } | |
605 | } |
|
601 | } | |
606 | })(); |
|
602 | })(); | |
607 |
|
603 | |||
608 | /* return jQuery expression with a tr with body in 3rd column and class cls and id named after the body */ |
|
604 | /* return jQuery expression with a tr with body in 3rd column and class cls and id named after the body */ | |
609 | var _table_tr = function(cls, body){ |
|
605 | var _table_tr = function(cls, body){ | |
610 | // like: <div class="comment" id="comment-8" line="o92"><div class="comment-wrapp">... |
|
606 | // like: <div class="comment" id="comment-8" line="o92"><div class="comment-wrapp">... | |
611 | // except new inlines which are different ... |
|
607 | // except new inlines which are different ... | |
612 | var comment_id = ($(body).attr('id') || 'comment-new').split('comment-')[1]; |
|
608 | var comment_id = ($(body).attr('id') || 'comment-new').split('comment-')[1]; | |
613 | var tr_id = 'comment-tr-{0}'.format(comment_id); |
|
609 | var tr_id = 'comment-tr-{0}'.format(comment_id); | |
614 | return $(('<tr id="{0}" class="{1}">'+ |
|
610 | return $(('<tr id="{0}" class="{1}">'+ | |
615 | '<td class="lineno-inline new-inline"></td>'+ |
|
611 | '<td class="lineno-inline new-inline"></td>'+ | |
616 | '<td class="lineno-inline old-inline"></td>'+ |
|
612 | '<td class="lineno-inline old-inline"></td>'+ | |
617 | '<td>{2}</td>'+ |
|
613 | '<td>{2}</td>'+ | |
618 | '</tr>').format(tr_id, cls, body)); |
|
614 | '</tr>').format(tr_id, cls, body)); | |
619 | }; |
|
615 | }; | |
620 |
|
616 | |||
621 | /** return jQuery expression with new inline form based on template **/ |
|
617 | /** return jQuery expression with new inline form based on template **/ | |
622 | var _createInlineForm = function(parent_tr, f_path, line) { |
|
618 | var _createInlineForm = function(parent_tr, f_path, line) { | |
623 | var $tmpl = $('#comment-inline-form-template').html().format(f_path, line); |
|
619 | var $tmpl = $('#comment-inline-form-template').html().format(f_path, line); | |
624 | var $form = _table_tr('comment-form-inline', $tmpl) |
|
620 | var $form = _table_tr('comment-form-inline', $tmpl) | |
625 |
|
621 | |||
626 | // create event for hide button |
|
622 | // create event for hide button | |
627 | $form.find('.hide-inline-form').click(function(e) { |
|
623 | $form.find('.hide-inline-form').click(function(e) { | |
628 | var newtr = e.currentTarget.parentNode.parentNode.parentNode.parentNode.parentNode; |
|
624 | var newtr = e.currentTarget.parentNode.parentNode.parentNode.parentNode.parentNode; | |
629 | if($(newtr).next().hasClass('inline-comments-button')){ |
|
625 | if($(newtr).next().hasClass('inline-comments-button')){ | |
630 | $(newtr).next().show(); |
|
626 | $(newtr).next().show(); | |
631 | } |
|
627 | } | |
632 | $(newtr).remove(); |
|
628 | $(newtr).remove(); | |
633 | $(parent_tr).removeClass('form-open'); |
|
629 | $(parent_tr).removeClass('form-open'); | |
634 | $(parent_tr).removeClass('hl-comment'); |
|
630 | $(parent_tr).removeClass('hl-comment'); | |
635 | }); |
|
631 | }); | |
636 |
|
632 | |||
637 | return $form |
|
633 | return $form | |
638 | }; |
|
634 | }; | |
639 |
|
635 | |||
640 | /** |
|
636 | /** | |
641 | * Inject inline comment for an given TR. This tr should always be a .line . |
|
637 | * Inject inline comment for an given TR. This tr should always be a .line . | |
642 | * The form will be inject after any comments. |
|
638 | * The form will be inject after any comments. | |
643 | */ |
|
639 | */ | |
644 | var injectInlineForm = function(tr){ |
|
640 | var injectInlineForm = function(tr){ | |
645 | var $tr = $(tr); |
|
641 | var $tr = $(tr); | |
646 | if(!$tr.hasClass('line')){ |
|
642 | if(!$tr.hasClass('line')){ | |
647 | return |
|
643 | return | |
648 | } |
|
644 | } | |
649 | var submit_url = AJAX_COMMENT_URL; |
|
645 | var submit_url = AJAX_COMMENT_URL; | |
650 | var $td = $tr.find('.code'); |
|
646 | var $td = $tr.find('.code'); | |
651 | if($tr.hasClass('form-open') || $tr.hasClass('context') || $td.hasClass('no-comment')){ |
|
647 | if($tr.hasClass('form-open') || $tr.hasClass('context') || $td.hasClass('no-comment')){ | |
652 | return |
|
648 | return | |
653 | } |
|
649 | } | |
654 | $tr.addClass('form-open hl-comment'); |
|
650 | $tr.addClass('form-open hl-comment'); | |
655 | var $node = $tr.parent().parent().parent().find('.full_f_path'); |
|
651 | var $node = $tr.parent().parent().parent().find('.full_f_path'); | |
656 | var f_path = $node.attr('path'); |
|
652 | var f_path = $node.attr('path'); | |
657 | var lineno = _getLineNo(tr); |
|
653 | var lineno = _getLineNo(tr); | |
658 | var $form = _createInlineForm(tr, f_path, lineno, submit_url); |
|
654 | var $form = _createInlineForm(tr, f_path, lineno, submit_url); | |
659 |
|
655 | |||
660 | var $parent = $tr; |
|
656 | var $parent = $tr; | |
661 | while ($parent.next().hasClass('inline-comments')){ |
|
657 | while ($parent.next().hasClass('inline-comments')){ | |
662 | var $parent = $parent.next(); |
|
658 | var $parent = $parent.next(); | |
663 | } |
|
659 | } | |
664 | $form.insertAfter($parent); |
|
660 | $form.insertAfter($parent); | |
665 | var $overlay = $form.find('.submitting-overlay'); |
|
661 | var $overlay = $form.find('.submitting-overlay'); | |
666 | var $inlineform = $form.find('.inline-form'); |
|
662 | var $inlineform = $form.find('.inline-form'); | |
667 |
|
663 | |||
668 | $form.submit(function(e){ |
|
664 | $form.submit(function(e){ | |
669 | e.preventDefault(); |
|
665 | e.preventDefault(); | |
670 |
|
666 | |||
671 | if(lineno === undefined){ |
|
667 | if(lineno === undefined){ | |
672 | alert('Error submitting, line ' + lineno + ' not found.'); |
|
668 | alert('Error submitting, line ' + lineno + ' not found.'); | |
673 | return; |
|
669 | return; | |
674 | } |
|
670 | } | |
675 | if(f_path === undefined){ |
|
671 | if(f_path === undefined){ | |
676 | alert('Error submitting, file path ' + f_path + ' not found.'); |
|
672 | alert('Error submitting, file path ' + f_path + ' not found.'); | |
677 | return; |
|
673 | return; | |
678 | } |
|
674 | } | |
679 |
|
675 | |||
680 | var text = $('#text_'+lineno).val(); |
|
676 | var text = $('#text_'+lineno).val(); | |
681 | if(text == ""){ |
|
677 | if(text == ""){ | |
682 | return; |
|
678 | return; | |
683 | } |
|
679 | } | |
684 |
|
680 | |||
685 | $overlay.show(); |
|
681 | $overlay.show(); | |
686 |
|
682 | |||
687 | var success = function(json_data){ |
|
683 | var success = function(json_data){ | |
688 | $tr.removeClass('form-open'); |
|
684 | $tr.removeClass('form-open'); | |
689 | $form.remove(); |
|
685 | $form.remove(); | |
690 | _renderInlineComment(json_data); |
|
686 | _renderInlineComment(json_data); | |
691 | }; |
|
687 | }; | |
692 | var postData = { |
|
688 | var postData = { | |
693 | 'text': text, |
|
689 | 'text': text, | |
694 | 'f_path': f_path, |
|
690 | 'f_path': f_path, | |
695 | 'line': lineno |
|
691 | 'line': lineno | |
696 | }; |
|
692 | }; | |
697 | ajaxPOST(submit_url, postData, success); |
|
693 | ajaxPOST(submit_url, postData, success); | |
698 | }); |
|
694 | }); | |
699 |
|
695 | |||
700 | $('#preview-btn_'+lineno).click(function(e){ |
|
696 | $('#preview-btn_'+lineno).click(function(e){ | |
701 | var text = $('#text_'+lineno).val(); |
|
697 | var text = $('#text_'+lineno).val(); | |
702 | if(!text){ |
|
698 | if(!text){ | |
703 | return |
|
699 | return | |
704 | } |
|
700 | } | |
705 | $('#preview-box_'+lineno).addClass('unloaded'); |
|
701 | $('#preview-box_'+lineno).addClass('unloaded'); | |
706 | $('#preview-box_'+lineno).html(_TM['Loading ...']); |
|
702 | $('#preview-box_'+lineno).html(_TM['Loading ...']); | |
707 | $('#edit-container_'+lineno).hide(); |
|
703 | $('#edit-container_'+lineno).hide(); | |
708 | $('#edit-btn_'+lineno).show(); |
|
704 | $('#edit-btn_'+lineno).show(); | |
709 | $('#preview-container_'+lineno).show(); |
|
705 | $('#preview-container_'+lineno).show(); | |
710 | $('#preview-btn_'+lineno).hide(); |
|
706 | $('#preview-btn_'+lineno).hide(); | |
711 |
|
707 | |||
712 | var url = pyroutes.url('changeset_comment_preview', {'repo_name': REPO_NAME}); |
|
708 | var url = pyroutes.url('changeset_comment_preview', {'repo_name': REPO_NAME}); | |
713 | var post_data = {'text': text}; |
|
709 | var post_data = {'text': text}; | |
714 | ajaxPOST(url, post_data, function(html){ |
|
710 | ajaxPOST(url, post_data, function(html){ | |
715 | $('#preview-box_'+lineno).html(html); |
|
711 | $('#preview-box_'+lineno).html(html); | |
716 | $('#preview-box_'+lineno).removeClass('unloaded'); |
|
712 | $('#preview-box_'+lineno).removeClass('unloaded'); | |
717 | }) |
|
713 | }) | |
718 | }) |
|
714 | }) | |
719 | $('#edit-btn_'+lineno).click(function(e){ |
|
715 | $('#edit-btn_'+lineno).click(function(e){ | |
720 | $('#edit-container_'+lineno).show(); |
|
716 | $('#edit-container_'+lineno).show(); | |
721 | $('#edit-btn_'+lineno).hide(); |
|
717 | $('#edit-btn_'+lineno).hide(); | |
722 | $('#preview-container_'+lineno).hide(); |
|
718 | $('#preview-container_'+lineno).hide(); | |
723 | $('#preview-btn_'+lineno).show(); |
|
719 | $('#preview-btn_'+lineno).show(); | |
724 | }) |
|
720 | }) | |
725 |
|
721 | |||
726 | setTimeout(function(){ |
|
722 | setTimeout(function(){ | |
727 | // callbacks |
|
723 | // callbacks | |
728 | tooltip_activate(); |
|
724 | tooltip_activate(); | |
729 | MentionsAutoComplete('text_'+lineno, 'mentions_container_'+lineno, |
|
725 | MentionsAutoComplete('text_'+lineno, 'mentions_container_'+lineno, | |
730 | _USERS_AC_DATA, _GROUPS_AC_DATA); |
|
726 | _USERS_AC_DATA, _GROUPS_AC_DATA); | |
731 | $('#text_'+lineno).focus(); |
|
727 | $('#text_'+lineno).focus(); | |
732 | },10) |
|
728 | },10) | |
733 | }; |
|
729 | }; | |
734 |
|
730 | |||
735 | var deleteComment = function(comment_id){ |
|
731 | var deleteComment = function(comment_id){ | |
736 | var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__',comment_id); |
|
732 | var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__',comment_id); | |
737 | var postData = {'_method':'delete'}; |
|
733 | var postData = {'_method':'delete'}; | |
738 | var success = function(o){ |
|
734 | var success = function(o){ | |
739 | var $deleted = $('#comment-tr-'+comment_id); |
|
735 | var $deleted = $('#comment-tr-'+comment_id); | |
740 | var $prev = $deleted.prev('tr'); |
|
736 | var $prev = $deleted.prev('tr'); | |
741 | $deleted.remove(); |
|
737 | $deleted.remove(); | |
742 | _placeAddButton($prev); |
|
738 | _placeAddButton($prev); | |
743 | } |
|
739 | } | |
744 | ajaxPOST(url,postData,success); |
|
740 | ajaxPOST(url,postData,success); | |
745 | } |
|
741 | } | |
746 |
|
742 | |||
747 | var _getLineNo = function(tr) { |
|
743 | var _getLineNo = function(tr) { | |
748 | var line; |
|
744 | var line; | |
749 | var o = $(tr).children()[0].id.split('_'); |
|
745 | var o = $(tr).children()[0].id.split('_'); | |
750 | var n = $(tr).children()[1].id.split('_'); |
|
746 | var n = $(tr).children()[1].id.split('_'); | |
751 |
|
747 | |||
752 | if (n.length >= 2) { |
|
748 | if (n.length >= 2) { | |
753 | line = n[n.length-1]; |
|
749 | line = n[n.length-1]; | |
754 | } else if (o.length >= 2) { |
|
750 | } else if (o.length >= 2) { | |
755 | line = o[o.length-1]; |
|
751 | line = o[o.length-1]; | |
756 | } |
|
752 | } | |
757 |
|
753 | |||
758 | return line |
|
754 | return line | |
759 | }; |
|
755 | }; | |
760 |
|
756 | |||
761 | var _placeAddButton = function($line_tr){ |
|
757 | var _placeAddButton = function($line_tr){ | |
762 | var $tr = $line_tr; |
|
758 | var $tr = $line_tr; | |
763 | while ($tr.next().hasClass('inline-comments')){ |
|
759 | while ($tr.next().hasClass('inline-comments')){ | |
764 | $tr.find('.add-comment').remove(); |
|
760 | $tr.find('.add-comment').remove(); | |
765 | $tr = $tr.next(); |
|
761 | $tr = $tr.next(); | |
766 | } |
|
762 | } | |
767 | $tr.find('.add-comment').remove(); |
|
763 | $tr.find('.add-comment').remove(); | |
768 | var label = TRANSLATION_MAP['Add Another Comment']; |
|
764 | var label = TRANSLATION_MAP['Add Another Comment']; | |
769 | var $html_el = $('<div class="add-comment"><span class="btn btn-mini">{0}</span></div>'.format(label)); |
|
765 | var $html_el = $('<div class="add-comment"><span class="btn btn-mini">{0}</span></div>'.format(label)); | |
770 | $html_el.click(function(e) { |
|
766 | $html_el.click(function(e) { | |
771 | injectInlineForm($line_tr); |
|
767 | injectInlineForm($line_tr); | |
772 | }); |
|
768 | }); | |
773 | $tr.find('.comment').after($html_el); |
|
769 | $tr.find('.comment').after($html_el); | |
774 | }; |
|
770 | }; | |
775 |
|
771 | |||
776 | /** |
|
772 | /** | |
777 | * Places the inline comment into the changeset block in proper line position |
|
773 | * Places the inline comment into the changeset block in proper line position | |
778 | */ |
|
774 | */ | |
779 | var _placeInline = function(target_id, lineno, html){ |
|
775 | var _placeInline = function(target_id, lineno, html){ | |
780 | var $td = $("#{0}_{1}".format(target_id, lineno)); |
|
776 | var $td = $("#{0}_{1}".format(target_id, lineno)); | |
781 | if (!$td.length){ |
|
777 | if (!$td.length){ | |
782 | return false; |
|
778 | return false; | |
783 | } |
|
779 | } | |
784 |
|
780 | |||
785 | // check if there are comments already ! |
|
781 | // check if there are comments already ! | |
786 | var $line_tr = $td.parent(); // the tr |
|
782 | var $line_tr = $td.parent(); // the tr | |
787 | var $after_tr = $line_tr; |
|
783 | var $after_tr = $line_tr; | |
788 | while ($after_tr.next().hasClass('inline-comments')){ |
|
784 | while ($after_tr.next().hasClass('inline-comments')){ | |
789 | $after_tr = $after_tr.next(); |
|
785 | $after_tr = $after_tr.next(); | |
790 | } |
|
786 | } | |
791 | // put in the comment at the bottom |
|
787 | // put in the comment at the bottom | |
792 | var $tr = _table_tr('inline-comments', html) |
|
788 | var $tr = _table_tr('inline-comments', html) | |
793 | $tr.find('div.comment').addClass('inline-comment'); |
|
789 | $tr.find('div.comment').addClass('inline-comment'); | |
794 | $after_tr.after($tr); |
|
790 | $after_tr.after($tr); | |
795 |
|
791 | |||
796 | // scan nodes, and attach add button to last one |
|
792 | // scan nodes, and attach add button to last one | |
797 | _placeAddButton($line_tr); |
|
793 | _placeAddButton($line_tr); | |
798 | return true; |
|
794 | return true; | |
799 | } |
|
795 | } | |
800 |
|
796 | |||
801 | /** |
|
797 | /** | |
802 | * make a single inline comment and place it inside |
|
798 | * make a single inline comment and place it inside | |
803 | */ |
|
799 | */ | |
804 | var _renderInlineComment = function(json_data){ |
|
800 | var _renderInlineComment = function(json_data){ | |
805 | var html = json_data['rendered_text']; |
|
801 | var html = json_data['rendered_text']; | |
806 | var lineno = json_data['line_no']; |
|
802 | var lineno = json_data['line_no']; | |
807 | var target_id = json_data['target_id']; |
|
803 | var target_id = json_data['target_id']; | |
808 | return _placeInline(target_id, lineno, html); |
|
804 | return _placeInline(target_id, lineno, html); | |
809 | } |
|
805 | } | |
810 |
|
806 | |||
811 | /** |
|
807 | /** | |
812 | * Iterates over all the inlines, and places them inside proper blocks of data |
|
808 | * Iterates over all the inlines, and places them inside proper blocks of data | |
813 | */ |
|
809 | */ | |
814 | var renderInlineComments = function(file_comments){ |
|
810 | var renderInlineComments = function(file_comments){ | |
815 | for (var f in file_comments){ |
|
811 | for (var f in file_comments){ | |
816 | // holding all comments for a FILE |
|
812 | // holding all comments for a FILE | |
817 | var box = file_comments[f]; |
|
813 | var box = file_comments[f]; | |
818 |
|
814 | |||
819 | var target_id = $(box).attr('target_id'); |
|
815 | var target_id = $(box).attr('target_id'); | |
820 | // actual comments with line numbers |
|
816 | // actual comments with line numbers | |
821 | var comments = box.children; |
|
817 | var comments = box.children; | |
822 | var obsolete_comments = []; |
|
818 | var obsolete_comments = []; | |
823 | for(var i=0; i<comments.length; i++){ |
|
819 | for(var i=0; i<comments.length; i++){ | |
824 | var data = { |
|
820 | var data = { | |
825 | 'rendered_text': comments[i].outerHTML, |
|
821 | 'rendered_text': comments[i].outerHTML, | |
826 | 'line_no': $(comments[i]).attr('line'), |
|
822 | 'line_no': $(comments[i]).attr('line'), | |
827 | 'target_id': target_id |
|
823 | 'target_id': target_id | |
828 | } |
|
824 | } | |
829 | if (_renderInlineComment(data)) { |
|
825 | if (_renderInlineComment(data)) { | |
830 | obsolete_comments.push(comments[i]); |
|
826 | obsolete_comments.push(comments[i]); | |
831 | $(comments[i]).hide(); |
|
827 | $(comments[i]).hide(); | |
832 | }else{ |
|
828 | }else{ | |
833 | var txt = document.createTextNode( |
|
829 | var txt = document.createTextNode( | |
834 | "Comment to " + YUD.getAttribute(comments[i].parentNode,'path') + |
|
830 | "Comment to " + YUD.getAttribute(comments[i].parentNode,'path') + | |
835 | " line " + data.line_no + |
|
831 | " line " + data.line_no + | |
836 | " which is outside the diff context:"); |
|
832 | " which is outside the diff context:"); | |
837 | comments[i].insertBefore(txt, comments[i].firstChild); |
|
833 | comments[i].insertBefore(txt, comments[i].firstChild); | |
838 | } |
|
834 | } | |
839 | } |
|
835 | } | |
840 | // now remove all the obsolete comments that have been copied to their |
|
836 | // now remove all the obsolete comments that have been copied to their | |
841 | // respective locations. |
|
837 | // respective locations. | |
842 | for (var i=0; i < obsolete_comments.length; i++) { |
|
838 | for (var i=0; i < obsolete_comments.length; i++) { | |
843 | obsolete_comments[i].parentNode.removeChild(obsolete_comments[i]); |
|
839 | obsolete_comments[i].parentNode.removeChild(obsolete_comments[i]); | |
844 | } |
|
840 | } | |
845 |
|
841 | |||
846 | $(box).show(); |
|
842 | $(box).show(); | |
847 | } |
|
843 | } | |
848 | } |
|
844 | } | |
849 |
|
845 | |||
850 | /** |
|
846 | /** | |
851 | * Double link comments |
|
847 | * Double link comments | |
852 | */ |
|
848 | */ | |
853 | var linkInlineComments = function(firstlinks, comments){ |
|
849 | var linkInlineComments = function(firstlinks, comments){ | |
854 | var $comments = $(comments); |
|
850 | var $comments = $(comments); | |
855 | if ($comments.length > 0) { |
|
851 | if ($comments.length > 0) { | |
856 | $(firstlinks).html('<a href="#{0}">First comment</a>'.format($comments.attr('id'))); |
|
852 | $(firstlinks).html('<a href="#{0}">First comment</a>'.format($comments.attr('id'))); | |
857 | } |
|
853 | } | |
858 | if ($comments.length <= 1) { |
|
854 | if ($comments.length <= 1) { | |
859 | return; |
|
855 | return; | |
860 | } |
|
856 | } | |
861 |
|
857 | |||
862 | $comments.each(function(i, e){ |
|
858 | $comments.each(function(i, e){ | |
863 | var prev = ''; |
|
859 | var prev = ''; | |
864 | if (i > 0){ |
|
860 | if (i > 0){ | |
865 | var prev_anchor = YUD.getAttribute(comments.item(i-1),'id'); |
|
861 | var prev_anchor = YUD.getAttribute(comments.item(i-1),'id'); | |
866 | prev = '<a href="#{0}">Previous comment</a>'.format(prev_anchor); |
|
862 | prev = '<a href="#{0}">Previous comment</a>'.format(prev_anchor); | |
867 | } |
|
863 | } | |
868 | var next = ''; |
|
864 | var next = ''; | |
869 | if (i+1 < comments.length){ |
|
865 | if (i+1 < comments.length){ | |
870 | var next_anchor = YUD.getAttribute(comments.item(i+1),'id'); |
|
866 | var next_anchor = YUD.getAttribute(comments.item(i+1),'id'); | |
871 | next = '<a href="#{0}">Next comment</a>'.format(next_anchor); |
|
867 | next = '<a href="#{0}">Next comment</a>'.format(next_anchor); | |
872 | } |
|
868 | } | |
873 | var $div = $(('<div class="prev-next-comment">'+ |
|
869 | var $div = $(('<div class="prev-next-comment">'+ | |
874 | '<div class="prev-comment">{0}</div>'+ |
|
870 | '<div class="prev-comment">{0}</div>'+ | |
875 | '<div class="next-comment">{1}</div>').format(prev, next)); |
|
871 | '<div class="next-comment">{1}</div>').format(prev, next)); | |
876 | $div.prependTo(this); |
|
872 | $div.prependTo(this); | |
877 | }); |
|
873 | }); | |
878 | } |
|
874 | } | |
879 |
|
875 | |||
880 | /* activate files.html stuff */ |
|
876 | /* activate files.html stuff */ | |
881 | var fileBrowserListeners = function(current_url, node_list_url, url_base){ |
|
877 | var fileBrowserListeners = function(current_url, node_list_url, url_base){ | |
882 | var current_url_branch = "?branch=__BRANCH__"; |
|
878 | var current_url_branch = "?branch=__BRANCH__"; | |
883 |
|
879 | |||
884 | $('#stay_at_branch').on('click',function(e){ |
|
880 | $('#stay_at_branch').on('click',function(e){ | |
885 | if(e.currentTarget.checked){ |
|
881 | if(e.currentTarget.checked){ | |
886 | var uri = current_url_branch; |
|
882 | var uri = current_url_branch; | |
887 | uri = uri.replace('__BRANCH__',e.currentTarget.value); |
|
883 | uri = uri.replace('__BRANCH__',e.currentTarget.value); | |
888 | window.location = uri; |
|
884 | window.location = uri; | |
889 | } |
|
885 | } | |
890 | else{ |
|
886 | else{ | |
891 | window.location = current_url; |
|
887 | window.location = current_url; | |
892 | } |
|
888 | } | |
893 | }) |
|
889 | }) | |
894 |
|
890 | |||
895 | var $node_filter = $('#node_filter'); |
|
891 | var $node_filter = $('#node_filter'); | |
896 |
|
892 | |||
897 | var filterTimeout = null; |
|
893 | var filterTimeout = null; | |
898 | var nodes = null; |
|
894 | var nodes = null; | |
899 |
|
895 | |||
900 | var initFilter = function(){ |
|
896 | var initFilter = function(){ | |
901 | $('#node_filter_box_loading').show(); |
|
897 | $('#node_filter_box_loading').show(); | |
902 | $('#search_activate_id').hide(); |
|
898 | $('#search_activate_id').hide(); | |
903 | $('#add_node_id').hide(); |
|
899 | $('#add_node_id').hide(); | |
904 | $.ajax({url: node_list_url, headers: {'X-PARTIAL-XHR': '1'}, cache: false}) |
|
900 | $.ajax({url: node_list_url, headers: {'X-PARTIAL-XHR': '1'}, cache: false}) | |
905 | .done(function(json) { |
|
901 | .done(function(json) { | |
906 | nodes = json.nodes; |
|
902 | nodes = json.nodes; | |
907 | $('#node_filter_box_loading').hide(); |
|
903 | $('#node_filter_box_loading').hide(); | |
908 | $('#node_filter_box').show(); |
|
904 | $('#node_filter_box').show(); | |
909 | $node_filter.focus(); |
|
905 | $node_filter.focus(); | |
910 | if($node_filter.hasClass('init')){ |
|
906 | if($node_filter.hasClass('init')){ | |
911 | $node_filter.val(''); |
|
907 | $node_filter.val(''); | |
912 | $node_filter.removeClass('init'); |
|
908 | $node_filter.removeClass('init'); | |
913 | } |
|
909 | } | |
914 | }) |
|
910 | }) | |
915 | .fail(function() { |
|
911 | .fail(function() { | |
916 | console.log('failed to load'); |
|
912 | console.log('failed to load'); | |
917 | }) |
|
913 | }) | |
918 | ; |
|
914 | ; | |
919 | } |
|
915 | } | |
920 |
|
916 | |||
921 | var updateFilter = function(e) { |
|
917 | var updateFilter = function(e) { | |
922 | return function(){ |
|
918 | return function(){ | |
923 | // Reset timeout |
|
919 | // Reset timeout | |
924 | filterTimeout = null; |
|
920 | filterTimeout = null; | |
925 | var query = e.currentTarget.value.toLowerCase(); |
|
921 | var query = e.currentTarget.value.toLowerCase(); | |
926 | var match = []; |
|
922 | var match = []; | |
927 | var matches = 0; |
|
923 | var matches = 0; | |
928 | var matches_max = 20; |
|
924 | var matches_max = 20; | |
929 | if (query != ""){ |
|
925 | if (query != ""){ | |
930 | for(var i=0;i<nodes.length;i++){ |
|
926 | for(var i=0;i<nodes.length;i++){ | |
931 | var pos = nodes[i].name.toLowerCase().indexOf(query) |
|
927 | var pos = nodes[i].name.toLowerCase().indexOf(query) | |
932 | if(query && pos != -1){ |
|
928 | if(query && pos != -1){ | |
933 | matches++ |
|
929 | matches++ | |
934 | //show only certain amount to not kill browser |
|
930 | //show only certain amount to not kill browser | |
935 | if (matches > matches_max){ |
|
931 | if (matches > matches_max){ | |
936 | break; |
|
932 | break; | |
937 | } |
|
933 | } | |
938 |
|
934 | |||
939 | var n = nodes[i].name; |
|
935 | var n = nodes[i].name; | |
940 | var t = nodes[i].type; |
|
936 | var t = nodes[i].type; | |
941 | var n_hl = n.substring(0,pos) |
|
937 | var n_hl = n.substring(0,pos) | |
942 | +"<b>{0}</b>".format(n.substring(pos,pos+query.length)) |
|
938 | +"<b>{0}</b>".format(n.substring(pos,pos+query.length)) | |
943 | +n.substring(pos+query.length) |
|
939 | +n.substring(pos+query.length) | |
944 | var new_url = url_base.replace('__FPATH__',n); |
|
940 | var new_url = url_base.replace('__FPATH__',n); | |
945 | match.push('<tr><td><a class="browser-{0}" href="{1}">{2}</a></td><td colspan="5"></td></tr>'.format(t,new_url,n_hl)); |
|
941 | match.push('<tr><td><a class="browser-{0}" href="{1}">{2}</a></td><td colspan="5"></td></tr>'.format(t,new_url,n_hl)); | |
946 | } |
|
942 | } | |
947 | if(match.length >= matches_max){ |
|
943 | if(match.length >= matches_max){ | |
948 | match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['Search truncated'])); |
|
944 | match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['Search truncated'])); | |
949 | break; |
|
945 | break; | |
950 | } |
|
946 | } | |
951 | } |
|
947 | } | |
952 | } |
|
948 | } | |
953 | if(query != ""){ |
|
949 | if(query != ""){ | |
954 | $('#tbody').hide(); |
|
950 | $('#tbody').hide(); | |
955 | $('#tbody_filtered').show(); |
|
951 | $('#tbody_filtered').show(); | |
956 |
|
952 | |||
957 | if (match.length==0){ |
|
953 | if (match.length==0){ | |
958 | match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['No matching files'])); |
|
954 | match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['No matching files'])); | |
959 | } |
|
955 | } | |
960 |
|
956 | |||
961 | $('#tbody_filtered').html(match.join("")); |
|
957 | $('#tbody_filtered').html(match.join("")); | |
962 | } |
|
958 | } | |
963 | else{ |
|
959 | else{ | |
964 | $('#tbody').show(); |
|
960 | $('#tbody').show(); | |
965 | $('#tbody_filtered').hide(); |
|
961 | $('#tbody_filtered').hide(); | |
966 | } |
|
962 | } | |
967 | } |
|
963 | } | |
968 | }; |
|
964 | }; | |
969 |
|
965 | |||
970 | $('#filter_activate').click(function(){ |
|
966 | $('#filter_activate').click(function(){ | |
971 | initFilter(); |
|
967 | initFilter(); | |
972 | }); |
|
968 | }); | |
973 | $node_filter.click(function(){ |
|
969 | $node_filter.click(function(){ | |
974 | if($node_filter.hasClass('init')){ |
|
970 | if($node_filter.hasClass('init')){ | |
975 | $node_filter.val(''); |
|
971 | $node_filter.val(''); | |
976 | $node_filter.removeClass('init'); |
|
972 | $node_filter.removeClass('init'); | |
977 | } |
|
973 | } | |
978 | }); |
|
974 | }); | |
979 | $node_filter.keyup(function(e){ |
|
975 | $node_filter.keyup(function(e){ | |
980 | clearTimeout(filterTimeout); |
|
976 | clearTimeout(filterTimeout); | |
981 | filterTimeout = setTimeout(updateFilter(e),600); |
|
977 | filterTimeout = setTimeout(updateFilter(e),600); | |
982 | }); |
|
978 | }); | |
983 | }; |
|
979 | }; | |
984 |
|
980 | |||
985 |
|
981 | |||
986 | var initCodeMirror = function(textarea_id, resetUrl){ |
|
982 | var initCodeMirror = function(textarea_id, resetUrl){ | |
987 | var myCodeMirror = CodeMirror.fromTextArea($('#' + textarea_id)[0], { |
|
983 | var myCodeMirror = CodeMirror.fromTextArea($('#' + textarea_id)[0], { | |
988 | mode: "null", |
|
984 | mode: "null", | |
989 | lineNumbers: true, |
|
985 | lineNumbers: true, | |
990 | indentUnit: 4, |
|
986 | indentUnit: 4, | |
991 | autofocus: true |
|
987 | autofocus: true | |
992 | }); |
|
988 | }); | |
993 | CodeMirror.modeURL = "/codemirror/mode/%N/%N.js"; |
|
989 | CodeMirror.modeURL = "/codemirror/mode/%N/%N.js"; | |
994 |
|
990 | |||
995 | $('#reset').click(function(e){ |
|
991 | $('#reset').click(function(e){ | |
996 | window.location=resetUrl; |
|
992 | window.location=resetUrl; | |
997 | }); |
|
993 | }); | |
998 |
|
994 | |||
999 | $('#file_enable').click(function(){ |
|
995 | $('#file_enable').click(function(){ | |
1000 | $('#editor_container').show(); |
|
996 | $('#editor_container').show(); | |
1001 | $('#upload_file_container').hide(); |
|
997 | $('#upload_file_container').hide(); | |
1002 | $('#filename_container').show(); |
|
998 | $('#filename_container').show(); | |
1003 | $('#set_mode_header').show(); |
|
999 | $('#set_mode_header').show(); | |
1004 | }); |
|
1000 | }); | |
1005 |
|
1001 | |||
1006 | $('#upload_file_enable').click(function(){ |
|
1002 | $('#upload_file_enable').click(function(){ | |
1007 | $('#editor_container').hide(); |
|
1003 | $('#editor_container').hide(); | |
1008 | $('#upload_file_container').show(); |
|
1004 | $('#upload_file_container').show(); | |
1009 | $('#filename_container').hide(); |
|
1005 | $('#filename_container').hide(); | |
1010 | $('#set_mode_header').hide(); |
|
1006 | $('#set_mode_header').hide(); | |
1011 | }); |
|
1007 | }); | |
1012 |
|
1008 | |||
1013 | return myCodeMirror |
|
1009 | return myCodeMirror | |
1014 | }; |
|
1010 | }; | |
1015 |
|
1011 | |||
1016 | var setCodeMirrorMode = function(codeMirrorInstance, mode) { |
|
1012 | var setCodeMirrorMode = function(codeMirrorInstance, mode) { | |
1017 | CodeMirror.autoLoadMode(codeMirrorInstance, mode); |
|
1013 | CodeMirror.autoLoadMode(codeMirrorInstance, mode); | |
1018 | } |
|
1014 | } | |
1019 |
|
1015 | |||
1020 |
|
1016 | |||
1021 | var _getIdentNode = function(n){ |
|
1017 | var _getIdentNode = function(n){ | |
1022 | //iterate thrugh nodes until matching interesting node |
|
1018 | //iterate thrugh nodes until matching interesting node | |
1023 |
|
1019 | |||
1024 | if (typeof n == 'undefined'){ |
|
1020 | if (typeof n == 'undefined'){ | |
1025 | return -1 |
|
1021 | return -1 | |
1026 | } |
|
1022 | } | |
1027 |
|
1023 | |||
1028 | if(typeof n.id != "undefined" && n.id.match('L[0-9]+')){ |
|
1024 | if(typeof n.id != "undefined" && n.id.match('L[0-9]+')){ | |
1029 | return n |
|
1025 | return n | |
1030 | } |
|
1026 | } | |
1031 | else{ |
|
1027 | else{ | |
1032 | return _getIdentNode(n.parentNode); |
|
1028 | return _getIdentNode(n.parentNode); | |
1033 | } |
|
1029 | } | |
1034 | }; |
|
1030 | }; | |
1035 |
|
1031 | |||
1036 | /* generate links for multi line selects that can be shown by files.html page_highlights. |
|
1032 | /* generate links for multi line selects that can be shown by files.html page_highlights. | |
1037 | * This is a mouseup handler for hlcode from CodeHtmlFormatter and pygmentize */ |
|
1033 | * This is a mouseup handler for hlcode from CodeHtmlFormatter and pygmentize */ | |
1038 | var getSelectionLink = function(e) { |
|
1034 | var getSelectionLink = function(e) { | |
1039 | //get selection from start/to nodes |
|
1035 | //get selection from start/to nodes | |
1040 | if (typeof window.getSelection != "undefined") { |
|
1036 | if (typeof window.getSelection != "undefined") { | |
1041 | s = window.getSelection(); |
|
1037 | s = window.getSelection(); | |
1042 |
|
1038 | |||
1043 | var from = _getIdentNode(s.anchorNode); |
|
1039 | var from = _getIdentNode(s.anchorNode); | |
1044 | var till = _getIdentNode(s.focusNode); |
|
1040 | var till = _getIdentNode(s.focusNode); | |
1045 |
|
1041 | |||
1046 | var f_int = parseInt(from.id.replace('L','')); |
|
1042 | var f_int = parseInt(from.id.replace('L','')); | |
1047 | var t_int = parseInt(till.id.replace('L','')); |
|
1043 | var t_int = parseInt(till.id.replace('L','')); | |
1048 |
|
1044 | |||
1049 | var yoffset = 35; |
|
1045 | var yoffset = 35; | |
1050 | var ranges = [parseInt(from.id.replace('L','')), parseInt(till.id.replace('L',''))]; |
|
1046 | var ranges = [parseInt(from.id.replace('L','')), parseInt(till.id.replace('L',''))]; | |
1051 | if (ranges[0] > ranges[1]){ |
|
1047 | if (ranges[0] > ranges[1]){ | |
1052 | //highlight from bottom |
|
1048 | //highlight from bottom | |
1053 | yoffset = -yoffset; |
|
1049 | yoffset = -yoffset; | |
1054 | ranges = [ranges[1], ranges[0]]; |
|
1050 | ranges = [ranges[1], ranges[0]]; | |
1055 | } |
|
1051 | } | |
1056 | var $hl_div = $('div#linktt'); |
|
1052 | var $hl_div = $('div#linktt'); | |
1057 | // if we select more than 2 lines |
|
1053 | // if we select more than 2 lines | |
1058 | if (ranges[0] != ranges[1]){ |
|
1054 | if (ranges[0] != ranges[1]){ | |
1059 | if ($hl_div.length) { |
|
1055 | if ($hl_div.length) { | |
1060 | $hl_div.html(''); |
|
1056 | $hl_div.html(''); | |
1061 | } else { |
|
1057 | } else { | |
1062 | $hl_div = $('<div id="linktt" class="hl-tip-box">'); |
|
1058 | $hl_div = $('<div id="linktt" class="hl-tip-box">'); | |
1063 | $('body').prepend($hl_div); |
|
1059 | $('body').prepend($hl_div); | |
1064 | } |
|
1060 | } | |
1065 |
|
1061 | |||
1066 | $hl_div.append($('<a>').html(_TM['Selection link']).attr('href', location.href.substring(0, location.href.indexOf('#')) + '#L' + ranges[0] + '-'+ranges[1])); |
|
1062 | $hl_div.append($('<a>').html(_TM['Selection link']).attr('href', location.href.substring(0, location.href.indexOf('#')) + '#L' + ranges[0] + '-'+ranges[1])); | |
1067 | var xy = $(till).offset(); |
|
1063 | var xy = $(till).offset(); | |
1068 | $hl_div.css('top', (xy.top + yoffset) + 'px').css('left', xy.left + 'px'); |
|
1064 | $hl_div.css('top', (xy.top + yoffset) + 'px').css('left', xy.left + 'px'); | |
1069 | $hl_div.show(); |
|
1065 | $hl_div.show(); | |
1070 | } |
|
1066 | } | |
1071 | else{ |
|
1067 | else{ | |
1072 | $hl_div.hide(); |
|
1068 | $hl_div.hide(); | |
1073 | } |
|
1069 | } | |
1074 | } |
|
1070 | } | |
1075 | }; |
|
1071 | }; | |
1076 |
|
1072 | |||
1077 | var deleteNotification = function(url, notification_id, callbacks){ |
|
1073 | var deleteNotification = function(url, notification_id, callbacks){ | |
1078 | var success = function(o){ |
|
1074 | var success = function(o){ | |
1079 | $("#notification_"+notification_id).remove(); |
|
1075 | $("#notification_"+notification_id).remove(); | |
1080 | _run_callbacks(callbacks); |
|
1076 | _run_callbacks(callbacks); | |
1081 | }; |
|
1077 | }; | |
1082 | var failure = function(o){ |
|
1078 | var failure = function(o){ | |
1083 | alert("deleteNotification failure"); |
|
1079 | alert("deleteNotification failure"); | |
1084 | }; |
|
1080 | }; | |
1085 | var postData = {'_method': 'delete'}; |
|
1081 | var postData = {'_method': 'delete'}; | |
1086 | var sUrl = url.replace('__NOTIFICATION_ID__',notification_id); |
|
1082 | var sUrl = url.replace('__NOTIFICATION_ID__',notification_id); | |
1087 | ajaxPOST(sUrl, postData, success, failure); |
|
1083 | ajaxPOST(sUrl, postData, success, failure); | |
1088 | }; |
|
1084 | }; | |
1089 |
|
1085 | |||
1090 | var readNotification = function(url, notification_id, callbacks){ |
|
1086 | var readNotification = function(url, notification_id, callbacks){ | |
1091 | var success = function(o){ |
|
1087 | var success = function(o){ | |
1092 | var $obj = $("#notification_"+notification_id); |
|
1088 | var $obj = $("#notification_"+notification_id); | |
1093 | $obj.removeClass('unread'); |
|
1089 | $obj.removeClass('unread'); | |
1094 | $obj.find('.read-notification').remove(); |
|
1090 | $obj.find('.read-notification').remove(); | |
1095 | _run_callbacks(callbacks); |
|
1091 | _run_callbacks(callbacks); | |
1096 | }; |
|
1092 | }; | |
1097 | var failure = function(o){ |
|
1093 | var failure = function(o){ | |
1098 | alert("readNotification failure"); |
|
1094 | alert("readNotification failure"); | |
1099 | }; |
|
1095 | }; | |
1100 | var postData = {'_method': 'put'}; |
|
1096 | var postData = {'_method': 'put'}; | |
1101 | var sUrl = url.replace('__NOTIFICATION_ID__',notification_id); |
|
1097 | var sUrl = url.replace('__NOTIFICATION_ID__',notification_id); | |
1102 | ajaxPOST(sUrl, postData, success, failure); |
|
1098 | ajaxPOST(sUrl, postData, success, failure); | |
1103 | }; |
|
1099 | }; | |
1104 |
|
1100 | |||
1105 | /** MEMBERS AUTOCOMPLETE WIDGET **/ |
|
1101 | /** MEMBERS AUTOCOMPLETE WIDGET **/ | |
1106 |
|
1102 | |||
1107 | var _MembersAutoComplete = function (divid, cont, users_list, groups_list) { |
|
1103 | var _MembersAutoComplete = function (divid, cont, users_list, groups_list) { | |
1108 | var myUsers = users_list; |
|
1104 | var myUsers = users_list; | |
1109 | var myGroups = groups_list; |
|
1105 | var myGroups = groups_list; | |
1110 |
|
1106 | |||
1111 | // Define a custom search function for the DataSource of users |
|
1107 | // Define a custom search function for the DataSource of users | |
1112 | var matchUsers = function (sQuery) { |
|
1108 | var matchUsers = function (sQuery) { | |
1113 | // Case insensitive matching |
|
1109 | // Case insensitive matching | |
1114 | var query = sQuery.toLowerCase(); |
|
1110 | var query = sQuery.toLowerCase(); | |
1115 | var i = 0; |
|
1111 | var i = 0; | |
1116 | var l = myUsers.length; |
|
1112 | var l = myUsers.length; | |
1117 | var matches = []; |
|
1113 | var matches = []; | |
1118 |
|
1114 | |||
1119 | // Match against each name of each contact |
|
1115 | // Match against each name of each contact | |
1120 | for (; i < l; i++) { |
|
1116 | for (; i < l; i++) { | |
1121 | var contact = myUsers[i]; |
|
1117 | var contact = myUsers[i]; | |
1122 | if (((contact.fname+"").toLowerCase().indexOf(query) > -1) || |
|
1118 | if (((contact.fname+"").toLowerCase().indexOf(query) > -1) || | |
1123 | ((contact.lname+"").toLowerCase().indexOf(query) > -1) || |
|
1119 | ((contact.lname+"").toLowerCase().indexOf(query) > -1) || | |
1124 | ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) { |
|
1120 | ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) { | |
1125 | matches[matches.length] = contact; |
|
1121 | matches[matches.length] = contact; | |
1126 | } |
|
1122 | } | |
1127 | } |
|
1123 | } | |
1128 | return matches; |
|
1124 | return matches; | |
1129 | }; |
|
1125 | }; | |
1130 |
|
1126 | |||
1131 | // Define a custom search function for the DataSource of userGroups |
|
1127 | // Define a custom search function for the DataSource of userGroups | |
1132 | var matchGroups = function (sQuery) { |
|
1128 | var matchGroups = function (sQuery) { | |
1133 | // Case insensitive matching |
|
1129 | // Case insensitive matching | |
1134 | var query = sQuery.toLowerCase(); |
|
1130 | var query = sQuery.toLowerCase(); | |
1135 | var i = 0; |
|
1131 | var i = 0; | |
1136 | var l = myGroups.length; |
|
1132 | var l = myGroups.length; | |
1137 | var matches = []; |
|
1133 | var matches = []; | |
1138 |
|
1134 | |||
1139 | // Match against each name of each contact |
|
1135 | // Match against each name of each contact | |
1140 | for (; i < l; i++) { |
|
1136 | for (; i < l; i++) { | |
1141 | var matched_group = myGroups[i]; |
|
1137 | var matched_group = myGroups[i]; | |
1142 | if (matched_group.grname.toLowerCase().indexOf(query) > -1) { |
|
1138 | if (matched_group.grname.toLowerCase().indexOf(query) > -1) { | |
1143 | matches[matches.length] = matched_group; |
|
1139 | matches[matches.length] = matched_group; | |
1144 | } |
|
1140 | } | |
1145 | } |
|
1141 | } | |
1146 | return matches; |
|
1142 | return matches; | |
1147 | }; |
|
1143 | }; | |
1148 |
|
1144 | |||
1149 | //match all |
|
1145 | //match all | |
1150 | var matchAll = function (sQuery) { |
|
1146 | var matchAll = function (sQuery) { | |
1151 | var u = matchUsers(sQuery); |
|
1147 | var u = matchUsers(sQuery); | |
1152 | var g = matchGroups(sQuery); |
|
1148 | var g = matchGroups(sQuery); | |
1153 | return u.concat(g); |
|
1149 | return u.concat(g); | |
1154 | }; |
|
1150 | }; | |
1155 |
|
1151 | |||
1156 | // DataScheme for members |
|
1152 | // DataScheme for members | |
1157 | var memberDS = new YAHOO.util.FunctionDataSource(matchAll); |
|
1153 | var memberDS = new YAHOO.util.FunctionDataSource(matchAll); | |
1158 | memberDS.responseSchema = { |
|
1154 | memberDS.responseSchema = { | |
1159 | fields: ["id", "fname", "lname", "nname", "grname", "grmembers", "gravatar_lnk", "gravatar_size"] |
|
1155 | fields: ["id", "fname", "lname", "nname", "grname", "grmembers", "gravatar_lnk", "gravatar_size"] | |
1160 | }; |
|
1156 | }; | |
1161 |
|
1157 | |||
1162 | // DataScheme for owner |
|
1158 | // DataScheme for owner | |
1163 | var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers); |
|
1159 | var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers); | |
1164 | ownerDS.responseSchema = { |
|
1160 | ownerDS.responseSchema = { | |
1165 | fields: ["id", "fname", "lname", "nname", "gravatar_lnk", "gravatar_size"] |
|
1161 | fields: ["id", "fname", "lname", "nname", "gravatar_lnk", "gravatar_size"] | |
1166 | }; |
|
1162 | }; | |
1167 |
|
1163 | |||
1168 | // Instantiate AutoComplete for perms |
|
1164 | // Instantiate AutoComplete for perms | |
1169 | var membersAC = new YAHOO.widget.AutoComplete(divid, cont, memberDS); |
|
1165 | var membersAC = new YAHOO.widget.AutoComplete(divid, cont, memberDS); | |
1170 | membersAC.useShadow = false; |
|
1166 | membersAC.useShadow = false; | |
1171 | membersAC.resultTypeList = false; |
|
1167 | membersAC.resultTypeList = false; | |
1172 | membersAC.animVert = false; |
|
1168 | membersAC.animVert = false; | |
1173 | membersAC.animHoriz = false; |
|
1169 | membersAC.animHoriz = false; | |
1174 | membersAC.animSpeed = 0.1; |
|
1170 | membersAC.animSpeed = 0.1; | |
1175 |
|
1171 | |||
1176 | // Instantiate AutoComplete for owner |
|
1172 | // Instantiate AutoComplete for owner | |
1177 | var ownerAC = new YAHOO.widget.AutoComplete("user", "owner_container", ownerDS); |
|
1173 | var ownerAC = new YAHOO.widget.AutoComplete("user", "owner_container", ownerDS); | |
1178 | ownerAC.useShadow = false; |
|
1174 | ownerAC.useShadow = false; | |
1179 | ownerAC.resultTypeList = false; |
|
1175 | ownerAC.resultTypeList = false; | |
1180 | ownerAC.animVert = false; |
|
1176 | ownerAC.animVert = false; | |
1181 | ownerAC.animHoriz = false; |
|
1177 | ownerAC.animHoriz = false; | |
1182 | ownerAC.animSpeed = 0.1; |
|
1178 | ownerAC.animSpeed = 0.1; | |
1183 |
|
1179 | |||
1184 | // Helper highlight function for the formatter |
|
1180 | // Helper highlight function for the formatter | |
1185 | var highlightMatch = function (full, snippet, matchindex) { |
|
1181 | var highlightMatch = function (full, snippet, matchindex) { | |
1186 | return full.substring(0, matchindex) |
|
1182 | return full.substring(0, matchindex) | |
1187 | + "<span class='match'>" |
|
1183 | + "<span class='match'>" | |
1188 | + full.substr(matchindex, snippet.length) |
|
1184 | + full.substr(matchindex, snippet.length) | |
1189 | + "</span>" + full.substring(matchindex + snippet.length); |
|
1185 | + "</span>" + full.substring(matchindex + snippet.length); | |
1190 | }; |
|
1186 | }; | |
1191 |
|
1187 | |||
1192 | // Custom formatter to highlight the matching letters |
|
1188 | // Custom formatter to highlight the matching letters | |
1193 | var custom_formatter = function (oResultData, sQuery, sResultMatch) { |
|
1189 | var custom_formatter = function (oResultData, sQuery, sResultMatch) { | |
1194 | var query = sQuery.toLowerCase(); |
|
1190 | var query = sQuery.toLowerCase(); | |
1195 | var _gravatar = function(res, em, size, group){ |
|
1191 | var _gravatar = function(res, em, size, group){ | |
1196 | var elem = '<img alt="gravatar" class="perm-gravatar-ac" style="width: {0}px; height: {0}px" src="{1}"/>'.format(size, em); |
|
1192 | var elem = '<img alt="gravatar" class="perm-gravatar-ac" style="width: {0}px; height: {0}px" src="{1}"/>'.format(size, em); | |
1197 | if (!em) { |
|
1193 | if (!em) { | |
1198 | elem = '<i class="icon-user perm-gravatar-ac" style="font-size: {0}px;"></i>'.format(size); |
|
1194 | elem = '<i class="icon-user perm-gravatar-ac" style="font-size: {0}px;"></i>'.format(size); | |
1199 | } |
|
1195 | } | |
1200 | if (group !== undefined){ |
|
1196 | if (group !== undefined){ | |
1201 | elem = '<i class="perm-gravatar-ac icon-users"></i>' |
|
1197 | elem = '<i class="perm-gravatar-ac icon-users"></i>' | |
1202 | } |
|
1198 | } | |
1203 | var tmpl = '<div class="ac-container-wrap">{0}{1}</div>' |
|
1199 | var tmpl = '<div class="ac-container-wrap">{0}{1}</div>' | |
1204 | return tmpl.format(elem,res) |
|
1200 | return tmpl.format(elem,res) | |
1205 | } |
|
1201 | } | |
1206 | // group |
|
1202 | // group | |
1207 | if (oResultData.grname != undefined) { |
|
1203 | if (oResultData.grname != undefined) { | |
1208 | var grname = oResultData.grname; |
|
1204 | var grname = oResultData.grname; | |
1209 | var grmembers = oResultData.grmembers; |
|
1205 | var grmembers = oResultData.grmembers; | |
1210 | var grnameMatchIndex = grname.toLowerCase().indexOf(query); |
|
1206 | var grnameMatchIndex = grname.toLowerCase().indexOf(query); | |
1211 | var grprefix = "{0}: ".format(_TM['Group']); |
|
1207 | var grprefix = "{0}: ".format(_TM['Group']); | |
1212 | var grsuffix = " (" + grmembers + " )"; |
|
1208 | var grsuffix = " (" + grmembers + " )"; | |
1213 | var grsuffix = " ({0} {1})".format(grmembers, _TM['members']); |
|
1209 | var grsuffix = " ({0} {1})".format(grmembers, _TM['members']); | |
1214 |
|
1210 | |||
1215 | if (grnameMatchIndex > -1) { |
|
1211 | if (grnameMatchIndex > -1) { | |
1216 | return _gravatar(grprefix + highlightMatch(grname, query, grnameMatchIndex) + grsuffix,null,null,true); |
|
1212 | return _gravatar(grprefix + highlightMatch(grname, query, grnameMatchIndex) + grsuffix,null,null,true); | |
1217 | } |
|
1213 | } | |
1218 | return _gravatar(grprefix + oResultData.grname + grsuffix, null, null, true); |
|
1214 | return _gravatar(grprefix + oResultData.grname + grsuffix, null, null, true); | |
1219 | // Users |
|
1215 | // Users | |
1220 | } else if (oResultData.nname != undefined) { |
|
1216 | } else if (oResultData.nname != undefined) { | |
1221 | var fname = oResultData.fname || ""; |
|
1217 | var fname = oResultData.fname || ""; | |
1222 | var lname = oResultData.lname || ""; |
|
1218 | var lname = oResultData.lname || ""; | |
1223 | var nname = oResultData.nname; |
|
1219 | var nname = oResultData.nname; | |
1224 |
|
1220 | |||
1225 | // Guard against null value |
|
1221 | // Guard against null value | |
1226 | var fnameMatchIndex = fname.toLowerCase().indexOf(query), |
|
1222 | var fnameMatchIndex = fname.toLowerCase().indexOf(query), | |
1227 | lnameMatchIndex = lname.toLowerCase().indexOf(query), |
|
1223 | lnameMatchIndex = lname.toLowerCase().indexOf(query), | |
1228 | nnameMatchIndex = nname.toLowerCase().indexOf(query), |
|
1224 | nnameMatchIndex = nname.toLowerCase().indexOf(query), | |
1229 | displayfname, displaylname, displaynname; |
|
1225 | displayfname, displaylname, displaynname; | |
1230 |
|
1226 | |||
1231 | if (fnameMatchIndex > -1) { |
|
1227 | if (fnameMatchIndex > -1) { | |
1232 | displayfname = highlightMatch(fname, query, fnameMatchIndex); |
|
1228 | displayfname = highlightMatch(fname, query, fnameMatchIndex); | |
1233 | } else { |
|
1229 | } else { | |
1234 | displayfname = fname; |
|
1230 | displayfname = fname; | |
1235 | } |
|
1231 | } | |
1236 |
|
1232 | |||
1237 | if (lnameMatchIndex > -1) { |
|
1233 | if (lnameMatchIndex > -1) { | |
1238 | displaylname = highlightMatch(lname, query, lnameMatchIndex); |
|
1234 | displaylname = highlightMatch(lname, query, lnameMatchIndex); | |
1239 | } else { |
|
1235 | } else { | |
1240 | displaylname = lname; |
|
1236 | displaylname = lname; | |
1241 | } |
|
1237 | } | |
1242 |
|
1238 | |||
1243 | if (nnameMatchIndex > -1) { |
|
1239 | if (nnameMatchIndex > -1) { | |
1244 | displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")"; |
|
1240 | displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")"; | |
1245 | } else { |
|
1241 | } else { | |
1246 | displaynname = nname ? "(" + nname + ")" : ""; |
|
1242 | displaynname = nname ? "(" + nname + ")" : ""; | |
1247 | } |
|
1243 | } | |
1248 |
|
1244 | |||
1249 | return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk, oResultData.gravatar_size); |
|
1245 | return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk, oResultData.gravatar_size); | |
1250 | } else { |
|
1246 | } else { | |
1251 | return ''; |
|
1247 | return ''; | |
1252 | } |
|
1248 | } | |
1253 | }; |
|
1249 | }; | |
1254 | membersAC.formatResult = custom_formatter; |
|
1250 | membersAC.formatResult = custom_formatter; | |
1255 | ownerAC.formatResult = custom_formatter; |
|
1251 | ownerAC.formatResult = custom_formatter; | |
1256 |
|
1252 | |||
1257 | var myHandler = function (sType, aArgs) { |
|
1253 | var myHandler = function (sType, aArgs) { | |
1258 | var nextId = divid.split('perm_new_member_name_')[1]; |
|
1254 | var nextId = divid.split('perm_new_member_name_')[1]; | |
1259 | var myAC = aArgs[0]; // reference back to the AC instance |
|
1255 | var myAC = aArgs[0]; // reference back to the AC instance | |
1260 | var elLI = aArgs[1]; // reference to the selected LI element |
|
1256 | var elLI = aArgs[1]; // reference to the selected LI element | |
1261 | var oData = aArgs[2]; // object literal of selected item's result data |
|
1257 | var oData = aArgs[2]; // object literal of selected item's result data | |
1262 | //fill the autocomplete with value |
|
1258 | //fill the autocomplete with value | |
1263 | if (oData.nname != undefined) { |
|
1259 | if (oData.nname != undefined) { | |
1264 | //users |
|
1260 | //users | |
1265 | myAC.getInputEl().value = oData.nname; |
|
1261 | myAC.getInputEl().value = oData.nname; | |
1266 | $('#perm_new_member_type_'+nextId).val('user'); |
|
1262 | $('#perm_new_member_type_'+nextId).val('user'); | |
1267 | } else { |
|
1263 | } else { | |
1268 | //groups |
|
1264 | //groups | |
1269 | myAC.getInputEl().value = oData.grname; |
|
1265 | myAC.getInputEl().value = oData.grname; | |
1270 | $('#perm_new_member_type_'+nextId).val('users_group'); |
|
1266 | $('#perm_new_member_type_'+nextId).val('users_group'); | |
1271 | } |
|
1267 | } | |
1272 | }; |
|
1268 | }; | |
1273 |
|
1269 | |||
1274 | membersAC.itemSelectEvent.subscribe(myHandler); |
|
1270 | membersAC.itemSelectEvent.subscribe(myHandler); | |
1275 | if(ownerAC.itemSelectEvent){ |
|
1271 | if(ownerAC.itemSelectEvent){ | |
1276 | ownerAC.itemSelectEvent.subscribe(myHandler); |
|
1272 | ownerAC.itemSelectEvent.subscribe(myHandler); | |
1277 | } |
|
1273 | } | |
1278 |
|
1274 | |||
1279 | return { |
|
1275 | return { | |
1280 | memberDS: memberDS, |
|
1276 | memberDS: memberDS, | |
1281 | ownerDS: ownerDS, |
|
1277 | ownerDS: ownerDS, | |
1282 | membersAC: membersAC, |
|
1278 | membersAC: membersAC, | |
1283 | ownerAC: ownerAC |
|
1279 | ownerAC: ownerAC | |
1284 | }; |
|
1280 | }; | |
1285 | } |
|
1281 | } | |
1286 |
|
1282 | |||
1287 | var MentionsAutoComplete = function (divid, cont, users_list, groups_list) { |
|
1283 | var MentionsAutoComplete = function (divid, cont, users_list, groups_list) { | |
1288 | var myUsers = users_list; |
|
1284 | var myUsers = users_list; | |
1289 | var myGroups = groups_list; |
|
1285 | var myGroups = groups_list; | |
1290 |
|
1286 | |||
1291 | // Define a custom search function for the DataSource of users |
|
1287 | // Define a custom search function for the DataSource of users | |
1292 | var matchUsers = function (sQuery) { |
|
1288 | var matchUsers = function (sQuery) { | |
1293 | var org_sQuery = sQuery; |
|
1289 | var org_sQuery = sQuery; | |
1294 | if(this.mentionQuery == null){ |
|
1290 | if(this.mentionQuery == null){ | |
1295 | return [] |
|
1291 | return [] | |
1296 | } |
|
1292 | } | |
1297 | sQuery = this.mentionQuery; |
|
1293 | sQuery = this.mentionQuery; | |
1298 | // Case insensitive matching |
|
1294 | // Case insensitive matching | |
1299 | var query = sQuery.toLowerCase(); |
|
1295 | var query = sQuery.toLowerCase(); | |
1300 | var i = 0; |
|
1296 | var i = 0; | |
1301 | var l = myUsers.length; |
|
1297 | var l = myUsers.length; | |
1302 | var matches = []; |
|
1298 | var matches = []; | |
1303 |
|
1299 | |||
1304 | // Match against each name of each contact |
|
1300 | // Match against each name of each contact | |
1305 | for (; i < l; i++) { |
|
1301 | for (; i < l; i++) { | |
1306 | var contact = myUsers[i]; |
|
1302 | var contact = myUsers[i]; | |
1307 | if (((contact.fname+"").toLowerCase().indexOf(query) > -1) || |
|
1303 | if (((contact.fname+"").toLowerCase().indexOf(query) > -1) || | |
1308 | ((contact.lname+"").toLowerCase().indexOf(query) > -1) || |
|
1304 | ((contact.lname+"").toLowerCase().indexOf(query) > -1) || | |
1309 | ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) { |
|
1305 | ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) { | |
1310 | matches[matches.length] = contact; |
|
1306 | matches[matches.length] = contact; | |
1311 | } |
|
1307 | } | |
1312 | } |
|
1308 | } | |
1313 | return matches |
|
1309 | return matches | |
1314 | }; |
|
1310 | }; | |
1315 |
|
1311 | |||
1316 | //match all |
|
1312 | //match all | |
1317 | var matchAll = function (sQuery) { |
|
1313 | var matchAll = function (sQuery) { | |
1318 | return matchUsers(sQuery); |
|
1314 | return matchUsers(sQuery); | |
1319 | }; |
|
1315 | }; | |
1320 |
|
1316 | |||
1321 | // DataScheme for owner |
|
1317 | // DataScheme for owner | |
1322 | var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers); |
|
1318 | var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers); | |
1323 |
|
1319 | |||
1324 | ownerDS.responseSchema = { |
|
1320 | ownerDS.responseSchema = { | |
1325 | fields: ["id", "fname", "lname", "nname", "gravatar_lnk", "gravatar_size"] |
|
1321 | fields: ["id", "fname", "lname", "nname", "gravatar_lnk", "gravatar_size"] | |
1326 | }; |
|
1322 | }; | |
1327 |
|
1323 | |||
1328 | // Instantiate AutoComplete for mentions |
|
1324 | // Instantiate AutoComplete for mentions | |
1329 | var ownerAC = new YAHOO.widget.AutoComplete(divid, cont, ownerDS); |
|
1325 | var ownerAC = new YAHOO.widget.AutoComplete(divid, cont, ownerDS); | |
1330 | ownerAC.useShadow = false; |
|
1326 | ownerAC.useShadow = false; | |
1331 | ownerAC.resultTypeList = false; |
|
1327 | ownerAC.resultTypeList = false; | |
1332 | ownerAC.suppressInputUpdate = true; |
|
1328 | ownerAC.suppressInputUpdate = true; | |
1333 | ownerAC.animVert = false; |
|
1329 | ownerAC.animVert = false; | |
1334 | ownerAC.animHoriz = false; |
|
1330 | ownerAC.animHoriz = false; | |
1335 | ownerAC.animSpeed = 0.1; |
|
1331 | ownerAC.animSpeed = 0.1; | |
1336 |
|
1332 | |||
1337 | // Helper highlight function for the formatter |
|
1333 | // Helper highlight function for the formatter | |
1338 | var highlightMatch = function (full, snippet, matchindex) { |
|
1334 | var highlightMatch = function (full, snippet, matchindex) { | |
1339 | return full.substring(0, matchindex) |
|
1335 | return full.substring(0, matchindex) | |
1340 | + "<span class='match'>" |
|
1336 | + "<span class='match'>" | |
1341 | + full.substr(matchindex, snippet.length) |
|
1337 | + full.substr(matchindex, snippet.length) | |
1342 | + "</span>" + full.substring(matchindex + snippet.length); |
|
1338 | + "</span>" + full.substring(matchindex + snippet.length); | |
1343 | }; |
|
1339 | }; | |
1344 |
|
1340 | |||
1345 | // Custom formatter to highlight the matching letters |
|
1341 | // Custom formatter to highlight the matching letters | |
1346 | ownerAC.formatResult = function (oResultData, sQuery, sResultMatch) { |
|
1342 | ownerAC.formatResult = function (oResultData, sQuery, sResultMatch) { | |
1347 | var org_sQuery = sQuery; |
|
1343 | var org_sQuery = sQuery; | |
1348 | if(this.dataSource.mentionQuery != null){ |
|
1344 | if(this.dataSource.mentionQuery != null){ | |
1349 | sQuery = this.dataSource.mentionQuery; |
|
1345 | sQuery = this.dataSource.mentionQuery; | |
1350 | } |
|
1346 | } | |
1351 |
|
1347 | |||
1352 | var query = sQuery.toLowerCase(); |
|
1348 | var query = sQuery.toLowerCase(); | |
1353 | var _gravatar = function(res, em, size, group){ |
|
1349 | var _gravatar = function(res, em, size, group){ | |
1354 | var elem = '<img alt="gravatar" class="perm-gravatar-ac" style="width: {0}px; height: {0}px" src="{1}"/>'.format(size, em); |
|
1350 | var elem = '<img alt="gravatar" class="perm-gravatar-ac" style="width: {0}px; height: {0}px" src="{1}"/>'.format(size, em); | |
1355 | if (!em) { |
|
1351 | if (!em) { | |
1356 | elem = '<i class="icon-user perm-gravatar-ac" style="font-size: {0}px;"></i>'.format(size); |
|
1352 | elem = '<i class="icon-user perm-gravatar-ac" style="font-size: {0}px;"></i>'.format(size); | |
1357 | } |
|
1353 | } | |
1358 | if (group !== undefined){ |
|
1354 | if (group !== undefined){ | |
1359 | elem = '<i class="perm-gravatar-ac icon-users"></i>' |
|
1355 | elem = '<i class="perm-gravatar-ac icon-users"></i>' | |
1360 | } |
|
1356 | } | |
1361 | var tmpl = '<div class="ac-container-wrap">{0}{1}</div>' |
|
1357 | var tmpl = '<div class="ac-container-wrap">{0}{1}</div>' | |
1362 | return tmpl.format(elem,res) |
|
1358 | return tmpl.format(elem,res) | |
1363 | } |
|
1359 | } | |
1364 | if (oResultData.nname != undefined) { |
|
1360 | if (oResultData.nname != undefined) { | |
1365 | var fname = oResultData.fname || ""; |
|
1361 | var fname = oResultData.fname || ""; | |
1366 | var lname = oResultData.lname || ""; |
|
1362 | var lname = oResultData.lname || ""; | |
1367 | var nname = oResultData.nname; |
|
1363 | var nname = oResultData.nname; | |
1368 |
|
1364 | |||
1369 | // Guard against null value |
|
1365 | // Guard against null value | |
1370 | var fnameMatchIndex = fname.toLowerCase().indexOf(query), |
|
1366 | var fnameMatchIndex = fname.toLowerCase().indexOf(query), | |
1371 | lnameMatchIndex = lname.toLowerCase().indexOf(query), |
|
1367 | lnameMatchIndex = lname.toLowerCase().indexOf(query), | |
1372 | nnameMatchIndex = nname.toLowerCase().indexOf(query), |
|
1368 | nnameMatchIndex = nname.toLowerCase().indexOf(query), | |
1373 | displayfname, displaylname, displaynname; |
|
1369 | displayfname, displaylname, displaynname; | |
1374 |
|
1370 | |||
1375 | if (fnameMatchIndex > -1) { |
|
1371 | if (fnameMatchIndex > -1) { | |
1376 | displayfname = highlightMatch(fname, query, fnameMatchIndex); |
|
1372 | displayfname = highlightMatch(fname, query, fnameMatchIndex); | |
1377 | } else { |
|
1373 | } else { | |
1378 | displayfname = fname; |
|
1374 | displayfname = fname; | |
1379 | } |
|
1375 | } | |
1380 |
|
1376 | |||
1381 | if (lnameMatchIndex > -1) { |
|
1377 | if (lnameMatchIndex > -1) { | |
1382 | displaylname = highlightMatch(lname, query, lnameMatchIndex); |
|
1378 | displaylname = highlightMatch(lname, query, lnameMatchIndex); | |
1383 | } else { |
|
1379 | } else { | |
1384 | displaylname = lname; |
|
1380 | displaylname = lname; | |
1385 | } |
|
1381 | } | |
1386 |
|
1382 | |||
1387 | if (nnameMatchIndex > -1) { |
|
1383 | if (nnameMatchIndex > -1) { | |
1388 | displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")"; |
|
1384 | displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")"; | |
1389 | } else { |
|
1385 | } else { | |
1390 | displaynname = nname ? "(" + nname + ")" : ""; |
|
1386 | displaynname = nname ? "(" + nname + ")" : ""; | |
1391 | } |
|
1387 | } | |
1392 |
|
1388 | |||
1393 | return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk, oResultData.gravatar_size); |
|
1389 | return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk, oResultData.gravatar_size); | |
1394 | } else { |
|
1390 | } else { | |
1395 | return ''; |
|
1391 | return ''; | |
1396 | } |
|
1392 | } | |
1397 | }; |
|
1393 | }; | |
1398 |
|
1394 | |||
1399 | if(ownerAC.itemSelectEvent){ |
|
1395 | if(ownerAC.itemSelectEvent){ | |
1400 | ownerAC.itemSelectEvent.subscribe(function (sType, aArgs) { |
|
1396 | ownerAC.itemSelectEvent.subscribe(function (sType, aArgs) { | |
1401 | var myAC = aArgs[0]; // reference back to the AC instance |
|
1397 | var myAC = aArgs[0]; // reference back to the AC instance | |
1402 | var elLI = aArgs[1]; // reference to the selected LI element |
|
1398 | var elLI = aArgs[1]; // reference to the selected LI element | |
1403 | var oData = aArgs[2]; // object literal of selected item's result data |
|
1399 | var oData = aArgs[2]; // object literal of selected item's result data | |
1404 | //fill the autocomplete with value |
|
1400 | //fill the autocomplete with value | |
1405 | if (oData.nname != undefined) { |
|
1401 | if (oData.nname != undefined) { | |
1406 | //users |
|
1402 | //users | |
1407 | //Replace the mention name with replaced |
|
1403 | //Replace the mention name with replaced | |
1408 | var re = new RegExp(); |
|
1404 | var re = new RegExp(); | |
1409 | var org = myAC.getInputEl().value; |
|
1405 | var org = myAC.getInputEl().value; | |
1410 | var chunks = myAC.dataSource.chunks |
|
1406 | var chunks = myAC.dataSource.chunks | |
1411 | // replace middle chunk(the search term) with actuall match |
|
1407 | // replace middle chunk(the search term) with actuall match | |
1412 | chunks[1] = chunks[1].replace('@'+myAC.dataSource.mentionQuery, |
|
1408 | chunks[1] = chunks[1].replace('@'+myAC.dataSource.mentionQuery, | |
1413 | '@'+oData.nname+' '); |
|
1409 | '@'+oData.nname+' '); | |
1414 | myAC.getInputEl().value = chunks.join('') |
|
1410 | myAC.getInputEl().value = chunks.join('') | |
1415 | myAC.getInputEl().focus(); // Y U NO WORK !? |
|
1411 | myAC.getInputEl().focus(); // Y U NO WORK !? | |
1416 | } else { |
|
1412 | } else { | |
1417 | //groups |
|
1413 | //groups | |
1418 | myAC.getInputEl().value = oData.grname; |
|
1414 | myAC.getInputEl().value = oData.grname; | |
1419 | $('#perm_new_member_type').val('users_group'); |
|
1415 | $('#perm_new_member_type').val('users_group'); | |
1420 | } |
|
1416 | } | |
1421 | }); |
|
1417 | }); | |
1422 | } |
|
1418 | } | |
1423 |
|
1419 | |||
1424 | // in this keybuffer we will gather current value of search ! |
|
1420 | // in this keybuffer we will gather current value of search ! | |
1425 | // since we need to get this just when someone does `@` then we do the |
|
1421 | // since we need to get this just when someone does `@` then we do the | |
1426 | // search |
|
1422 | // search | |
1427 | ownerAC.dataSource.chunks = []; |
|
1423 | ownerAC.dataSource.chunks = []; | |
1428 | ownerAC.dataSource.mentionQuery = null; |
|
1424 | ownerAC.dataSource.mentionQuery = null; | |
1429 |
|
1425 | |||
1430 | ownerAC.get_mention = function(msg, max_pos) { |
|
1426 | ownerAC.get_mention = function(msg, max_pos) { | |
1431 | var org = msg; |
|
1427 | var org = msg; | |
1432 | // Must match utils2.py MENTIONS_REGEX. |
|
1428 | // Must match utils2.py MENTIONS_REGEX. | |
1433 | // Only matching on string up to cursor, so it must end with $ |
|
1429 | // Only matching on string up to cursor, so it must end with $ | |
1434 | var re = new RegExp('(?:^|[^a-zA-Z0-9])@([a-zA-Z0-9][-_.a-zA-Z0-9]*[a-zA-Z0-9])$') |
|
1430 | var re = new RegExp('(?:^|[^a-zA-Z0-9])@([a-zA-Z0-9][-_.a-zA-Z0-9]*[a-zA-Z0-9])$') | |
1435 | var chunks = []; |
|
1431 | var chunks = []; | |
1436 |
|
1432 | |||
1437 | // cut first chunk until current pos |
|
1433 | // cut first chunk until current pos | |
1438 | var to_max = msg.substr(0, max_pos); |
|
1434 | var to_max = msg.substr(0, max_pos); | |
1439 | var at_pos = Math.max(0,to_max.lastIndexOf('@')-1); |
|
1435 | var at_pos = Math.max(0,to_max.lastIndexOf('@')-1); | |
1440 | var msg2 = to_max.substr(at_pos); |
|
1436 | var msg2 = to_max.substr(at_pos); | |
1441 |
|
1437 | |||
1442 | chunks.push(org.substr(0,at_pos))// prefix chunk |
|
1438 | chunks.push(org.substr(0,at_pos))// prefix chunk | |
1443 | chunks.push(msg2) // search chunk |
|
1439 | chunks.push(msg2) // search chunk | |
1444 | chunks.push(org.substr(max_pos)) // postfix chunk |
|
1440 | chunks.push(org.substr(max_pos)) // postfix chunk | |
1445 |
|
1441 | |||
1446 | // clean up msg2 for filtering and regex match |
|
1442 | // clean up msg2 for filtering and regex match | |
1447 | var msg2 = msg2.lstrip(' ').lstrip('\n'); |
|
1443 | var msg2 = msg2.lstrip(' ').lstrip('\n'); | |
1448 |
|
1444 | |||
1449 | if(re.test(msg2)){ |
|
1445 | if(re.test(msg2)){ | |
1450 | var unam = re.exec(msg2)[1]; |
|
1446 | var unam = re.exec(msg2)[1]; | |
1451 | return [unam, chunks]; |
|
1447 | return [unam, chunks]; | |
1452 | } |
|
1448 | } | |
1453 | return [null, null]; |
|
1449 | return [null, null]; | |
1454 | }; |
|
1450 | }; | |
1455 |
|
1451 | |||
1456 | var $divid = $('#'+divid); |
|
1452 | var $divid = $('#'+divid); | |
1457 | $divid.keyup(function(e){ |
|
1453 | $divid.keyup(function(e){ | |
1458 | var currentMessage = $divid.val(); |
|
1454 | var currentMessage = $divid.val(); | |
1459 | var currentCaretPosition = $divid[0].selectionStart; |
|
1455 | var currentCaretPosition = $divid[0].selectionStart; | |
1460 |
|
1456 | |||
1461 | var unam = ownerAC.get_mention(currentMessage, currentCaretPosition); |
|
1457 | var unam = ownerAC.get_mention(currentMessage, currentCaretPosition); | |
1462 | var curr_search = null; |
|
1458 | var curr_search = null; | |
1463 | if(unam[0]){ |
|
1459 | if(unam[0]){ | |
1464 | curr_search = unam[0]; |
|
1460 | curr_search = unam[0]; | |
1465 | } |
|
1461 | } | |
1466 |
|
1462 | |||
1467 | ownerAC.dataSource.chunks = unam[1]; |
|
1463 | ownerAC.dataSource.chunks = unam[1]; | |
1468 | ownerAC.dataSource.mentionQuery = curr_search; |
|
1464 | ownerAC.dataSource.mentionQuery = curr_search; | |
1469 | }); |
|
1465 | }); | |
1470 | } |
|
1466 | } | |
1471 |
|
1467 | |||
1472 | var addReviewMember = function(id,fname,lname,nname,gravatar_link,gravatar_size){ |
|
1468 | var addReviewMember = function(id,fname,lname,nname,gravatar_link,gravatar_size){ | |
1473 | var displayname = "{0} {1} ({2})".format(fname, lname, nname); |
|
1469 | var displayname = "{0} {1} ({2})".format(fname, lname, nname); | |
1474 | var gravatarelm = '<img alt="gravatar" style="width: {0}px; height: {0}px" src="{1}"/>'.format(gravatar_size, gravatar_link); |
|
1470 | var gravatarelm = '<img alt="gravatar" style="width: {0}px; height: {0}px" src="{1}"/>'.format(gravatar_size, gravatar_link); | |
1475 | if (!gravatar_link) |
|
1471 | if (!gravatar_link) | |
1476 | gravatarelm = '<i class="icon-user" style="font-size: {0}px;"></i>'.format(gravatar_size); |
|
1472 | gravatarelm = '<i class="icon-user" style="font-size: {0}px;"></i>'.format(gravatar_size); | |
1477 | var element = ( |
|
1473 | var element = ( | |
1478 | ' <li id="reviewer_{2}">\n'+ |
|
1474 | ' <li id="reviewer_{2}">\n'+ | |
1479 | ' <div class="reviewers_member">\n'+ |
|
1475 | ' <div class="reviewers_member">\n'+ | |
1480 | ' <div class="reviewer_status tooltip" title="not_reviewed">\n'+ |
|
1476 | ' <div class="reviewer_status tooltip" title="not_reviewed">\n'+ | |
1481 | ' <i class="icon-circle changeset-status-not_reviewed"></i>\n'+ |
|
1477 | ' <i class="icon-circle changeset-status-not_reviewed"></i>\n'+ | |
1482 | ' </div>\n'+ |
|
1478 | ' </div>\n'+ | |
1483 | ' <div class="reviewer_gravatar gravatar">{0}</div>\n'+ |
|
1479 | ' <div class="reviewer_gravatar gravatar">{0}</div>\n'+ | |
1484 | ' <div style="float:left;">{1}</div>\n'+ |
|
1480 | ' <div style="float:left;">{1}</div>\n'+ | |
1485 | ' <input type="hidden" value="{2}" name="review_members" />\n'+ |
|
1481 | ' <input type="hidden" value="{2}" name="review_members" />\n'+ | |
1486 | ' <div class="reviewer_member_remove action_button" onclick="removeReviewMember({2})">\n'+ |
|
1482 | ' <div class="reviewer_member_remove action_button" onclick="removeReviewMember({2})">\n'+ | |
1487 | ' <i class="icon-minus-circled"></i>\n'+ |
|
1483 | ' <i class="icon-minus-circled"></i>\n'+ | |
1488 | ' </div> (add not saved)\n'+ |
|
1484 | ' </div> (add not saved)\n'+ | |
1489 | ' </div>\n'+ |
|
1485 | ' </div>\n'+ | |
1490 | ' </li>\n' |
|
1486 | ' </li>\n' | |
1491 | ).format(gravatarelm, displayname, id); |
|
1487 | ).format(gravatarelm, displayname, id); | |
1492 | // check if we don't have this ID already in |
|
1488 | // check if we don't have this ID already in | |
1493 | var ids = []; |
|
1489 | var ids = []; | |
1494 | $('#review_members').find('li').each(function() { |
|
1490 | $('#review_members').find('li').each(function() { | |
1495 | ids.push(this.id); |
|
1491 | ids.push(this.id); | |
1496 | }); |
|
1492 | }); | |
1497 | if(ids.indexOf('reviewer_'+id) == -1){ |
|
1493 | if(ids.indexOf('reviewer_'+id) == -1){ | |
1498 | //only add if it's not there |
|
1494 | //only add if it's not there | |
1499 | $('#review_members').append(element); |
|
1495 | $('#review_members').append(element); | |
1500 | } |
|
1496 | } | |
1501 | } |
|
1497 | } | |
1502 |
|
1498 | |||
1503 | var removeReviewMember = function(reviewer_id, repo_name, pull_request_id){ |
|
1499 | var removeReviewMember = function(reviewer_id, repo_name, pull_request_id){ | |
1504 | var $li = $('#reviewer_{0}'.format(reviewer_id)); |
|
1500 | var $li = $('#reviewer_{0}'.format(reviewer_id)); | |
1505 | $li.find('div div').css("text-decoration", "line-through"); |
|
1501 | $li.find('div div').css("text-decoration", "line-through"); | |
1506 | $li.find('input').attr('name', 'review_members_removed'); |
|
1502 | $li.find('input').attr('name', 'review_members_removed'); | |
1507 | $li.find('.reviewer_member_remove').replaceWith(' (remove not saved)'); |
|
1503 | $li.find('.reviewer_member_remove').replaceWith(' (remove not saved)'); | |
1508 | } |
|
1504 | } | |
1509 |
|
1505 | |||
1510 | /* activate auto completion of users and groups ... but only used for users as PR reviewers */ |
|
1506 | /* activate auto completion of users and groups ... but only used for users as PR reviewers */ | |
1511 | var PullRequestAutoComplete = function (divid, cont, users_list, groups_list) { |
|
1507 | var PullRequestAutoComplete = function (divid, cont, users_list, groups_list) { | |
1512 | var myUsers = users_list; |
|
1508 | var myUsers = users_list; | |
1513 | var myGroups = groups_list; |
|
1509 | var myGroups = groups_list; | |
1514 |
|
1510 | |||
1515 | // Define a custom search function for the DataSource of users |
|
1511 | // Define a custom search function for the DataSource of users | |
1516 | var matchUsers = function (sQuery) { |
|
1512 | var matchUsers = function (sQuery) { | |
1517 | // Case insensitive matching |
|
1513 | // Case insensitive matching | |
1518 | var query = sQuery.toLowerCase(); |
|
1514 | var query = sQuery.toLowerCase(); | |
1519 | var i = 0; |
|
1515 | var i = 0; | |
1520 | var l = myUsers.length; |
|
1516 | var l = myUsers.length; | |
1521 | var matches = []; |
|
1517 | var matches = []; | |
1522 |
|
1518 | |||
1523 | // Match against each name of each contact |
|
1519 | // Match against each name of each contact | |
1524 | for (; i < l; i++) { |
|
1520 | for (; i < l; i++) { | |
1525 | var contact = myUsers[i]; |
|
1521 | var contact = myUsers[i]; | |
1526 | if (((contact.fname+"").toLowerCase().indexOf(query) > -1) || |
|
1522 | if (((contact.fname+"").toLowerCase().indexOf(query) > -1) || | |
1527 | ((contact.lname+"").toLowerCase().indexOf(query) > -1) || |
|
1523 | ((contact.lname+"").toLowerCase().indexOf(query) > -1) || | |
1528 | ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) { |
|
1524 | ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) { | |
1529 | matches[matches.length] = contact; |
|
1525 | matches[matches.length] = contact; | |
1530 | } |
|
1526 | } | |
1531 | } |
|
1527 | } | |
1532 | return matches; |
|
1528 | return matches; | |
1533 | }; |
|
1529 | }; | |
1534 |
|
1530 | |||
1535 | // Define a custom search function for the DataSource of userGroups |
|
1531 | // Define a custom search function for the DataSource of userGroups | |
1536 | var matchGroups = function (sQuery) { |
|
1532 | var matchGroups = function (sQuery) { | |
1537 | // Case insensitive matching |
|
1533 | // Case insensitive matching | |
1538 | var query = sQuery.toLowerCase(); |
|
1534 | var query = sQuery.toLowerCase(); | |
1539 | var i = 0; |
|
1535 | var i = 0; | |
1540 | var l = myGroups.length; |
|
1536 | var l = myGroups.length; | |
1541 | var matches = []; |
|
1537 | var matches = []; | |
1542 |
|
1538 | |||
1543 | // Match against each name of each contact |
|
1539 | // Match against each name of each contact | |
1544 | for (; i < l; i++) { |
|
1540 | for (; i < l; i++) { | |
1545 | matched_group = myGroups[i]; |
|
1541 | matched_group = myGroups[i]; | |
1546 | if (matched_group.grname.toLowerCase().indexOf(query) > -1) { |
|
1542 | if (matched_group.grname.toLowerCase().indexOf(query) > -1) { | |
1547 | matches[matches.length] = matched_group; |
|
1543 | matches[matches.length] = matched_group; | |
1548 | } |
|
1544 | } | |
1549 | } |
|
1545 | } | |
1550 | return matches; |
|
1546 | return matches; | |
1551 | }; |
|
1547 | }; | |
1552 |
|
1548 | |||
1553 | //match all |
|
1549 | //match all | |
1554 | var matchAll = function (sQuery) { |
|
1550 | var matchAll = function (sQuery) { | |
1555 | return matchUsers(sQuery); |
|
1551 | return matchUsers(sQuery); | |
1556 | }; |
|
1552 | }; | |
1557 |
|
1553 | |||
1558 | // DataScheme for owner |
|
1554 | // DataScheme for owner | |
1559 | var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers); |
|
1555 | var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers); | |
1560 |
|
1556 | |||
1561 | ownerDS.responseSchema = { |
|
1557 | ownerDS.responseSchema = { | |
1562 | fields: ["id", "fname", "lname", "nname", "gravatar_lnk", "gravatar_size"] |
|
1558 | fields: ["id", "fname", "lname", "nname", "gravatar_lnk", "gravatar_size"] | |
1563 | }; |
|
1559 | }; | |
1564 |
|
1560 | |||
1565 | // Instantiate AutoComplete for mentions |
|
1561 | // Instantiate AutoComplete for mentions | |
1566 | var reviewerAC = new YAHOO.widget.AutoComplete(divid, cont, ownerDS); |
|
1562 | var reviewerAC = new YAHOO.widget.AutoComplete(divid, cont, ownerDS); | |
1567 | reviewerAC.useShadow = false; |
|
1563 | reviewerAC.useShadow = false; | |
1568 | reviewerAC.resultTypeList = false; |
|
1564 | reviewerAC.resultTypeList = false; | |
1569 | reviewerAC.suppressInputUpdate = true; |
|
1565 | reviewerAC.suppressInputUpdate = true; | |
1570 | reviewerAC.animVert = false; |
|
1566 | reviewerAC.animVert = false; | |
1571 | reviewerAC.animHoriz = false; |
|
1567 | reviewerAC.animHoriz = false; | |
1572 | reviewerAC.animSpeed = 0.1; |
|
1568 | reviewerAC.animSpeed = 0.1; | |
1573 |
|
1569 | |||
1574 | // Helper highlight function for the formatter |
|
1570 | // Helper highlight function for the formatter | |
1575 | var highlightMatch = function (full, snippet, matchindex) { |
|
1571 | var highlightMatch = function (full, snippet, matchindex) { | |
1576 | return full.substring(0, matchindex) |
|
1572 | return full.substring(0, matchindex) | |
1577 | + "<span class='match'>" |
|
1573 | + "<span class='match'>" | |
1578 | + full.substr(matchindex, snippet.length) |
|
1574 | + full.substr(matchindex, snippet.length) | |
1579 | + "</span>" + full.substring(matchindex + snippet.length); |
|
1575 | + "</span>" + full.substring(matchindex + snippet.length); | |
1580 | }; |
|
1576 | }; | |
1581 |
|
1577 | |||
1582 | // Custom formatter to highlight the matching letters |
|
1578 | // Custom formatter to highlight the matching letters | |
1583 | reviewerAC.formatResult = function (oResultData, sQuery, sResultMatch) { |
|
1579 | reviewerAC.formatResult = function (oResultData, sQuery, sResultMatch) { | |
1584 | var org_sQuery = sQuery; |
|
1580 | var org_sQuery = sQuery; | |
1585 | if(this.dataSource.mentionQuery != null){ |
|
1581 | if(this.dataSource.mentionQuery != null){ | |
1586 | sQuery = this.dataSource.mentionQuery; |
|
1582 | sQuery = this.dataSource.mentionQuery; | |
1587 | } |
|
1583 | } | |
1588 |
|
1584 | |||
1589 | var query = sQuery.toLowerCase(); |
|
1585 | var query = sQuery.toLowerCase(); | |
1590 | var _gravatar = function(res, em, size, group){ |
|
1586 | var _gravatar = function(res, em, size, group){ | |
1591 | var elem = '<img alt="gravatar" class="perm-gravatar-ac" style="width: {0}px; height: {0}px" src="{1}"/>'.format(size, em); |
|
1587 | var elem = '<img alt="gravatar" class="perm-gravatar-ac" style="width: {0}px; height: {0}px" src="{1}"/>'.format(size, em); | |
1592 | if (!em) { |
|
1588 | if (!em) { | |
1593 | elem = '<i class="icon-user perm-gravatar-ac" style="font-size: {0}px;"></i>'.format(size); |
|
1589 | elem = '<i class="icon-user perm-gravatar-ac" style="font-size: {0}px;"></i>'.format(size); | |
1594 | } |
|
1590 | } | |
1595 | if (group !== undefined){ |
|
1591 | if (group !== undefined){ | |
1596 | elem = '<i class="perm-gravatar-ac icon-users"></i>' |
|
1592 | elem = '<i class="perm-gravatar-ac icon-users"></i>' | |
1597 | } |
|
1593 | } | |
1598 | var tmpl = '<div class="ac-container-wrap">{0}{1}</div>' |
|
1594 | var tmpl = '<div class="ac-container-wrap">{0}{1}</div>' | |
1599 | return tmpl.format(elem,res) |
|
1595 | return tmpl.format(elem,res) | |
1600 | } |
|
1596 | } | |
1601 | if (oResultData.nname != undefined) { |
|
1597 | if (oResultData.nname != undefined) { | |
1602 | var fname = oResultData.fname || ""; |
|
1598 | var fname = oResultData.fname || ""; | |
1603 | var lname = oResultData.lname || ""; |
|
1599 | var lname = oResultData.lname || ""; | |
1604 | var nname = oResultData.nname; |
|
1600 | var nname = oResultData.nname; | |
1605 |
|
1601 | |||
1606 | // Guard against null value |
|
1602 | // Guard against null value | |
1607 | var fnameMatchIndex = fname.toLowerCase().indexOf(query), |
|
1603 | var fnameMatchIndex = fname.toLowerCase().indexOf(query), | |
1608 | lnameMatchIndex = lname.toLowerCase().indexOf(query), |
|
1604 | lnameMatchIndex = lname.toLowerCase().indexOf(query), | |
1609 | nnameMatchIndex = nname.toLowerCase().indexOf(query), |
|
1605 | nnameMatchIndex = nname.toLowerCase().indexOf(query), | |
1610 | displayfname, displaylname, displaynname; |
|
1606 | displayfname, displaylname, displaynname; | |
1611 |
|
1607 | |||
1612 | if (fnameMatchIndex > -1) { |
|
1608 | if (fnameMatchIndex > -1) { | |
1613 | displayfname = highlightMatch(fname, query, fnameMatchIndex); |
|
1609 | displayfname = highlightMatch(fname, query, fnameMatchIndex); | |
1614 | } else { |
|
1610 | } else { | |
1615 | displayfname = fname; |
|
1611 | displayfname = fname; | |
1616 | } |
|
1612 | } | |
1617 |
|
1613 | |||
1618 | if (lnameMatchIndex > -1) { |
|
1614 | if (lnameMatchIndex > -1) { | |
1619 | displaylname = highlightMatch(lname, query, lnameMatchIndex); |
|
1615 | displaylname = highlightMatch(lname, query, lnameMatchIndex); | |
1620 | } else { |
|
1616 | } else { | |
1621 | displaylname = lname; |
|
1617 | displaylname = lname; | |
1622 | } |
|
1618 | } | |
1623 |
|
1619 | |||
1624 | if (nnameMatchIndex > -1) { |
|
1620 | if (nnameMatchIndex > -1) { | |
1625 | displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")"; |
|
1621 | displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")"; | |
1626 | } else { |
|
1622 | } else { | |
1627 | displaynname = nname ? "(" + nname + ")" : ""; |
|
1623 | displaynname = nname ? "(" + nname + ")" : ""; | |
1628 | } |
|
1624 | } | |
1629 |
|
1625 | |||
1630 | return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk, oResultData.gravatar_size); |
|
1626 | return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk, oResultData.gravatar_size); | |
1631 | } else { |
|
1627 | } else { | |
1632 | return ''; |
|
1628 | return ''; | |
1633 | } |
|
1629 | } | |
1634 | }; |
|
1630 | }; | |
1635 |
|
1631 | |||
1636 | //members cache to catch duplicates |
|
1632 | //members cache to catch duplicates | |
1637 | reviewerAC.dataSource.cache = []; |
|
1633 | reviewerAC.dataSource.cache = []; | |
1638 | // hack into select event |
|
1634 | // hack into select event | |
1639 | if(reviewerAC.itemSelectEvent){ |
|
1635 | if(reviewerAC.itemSelectEvent){ | |
1640 | reviewerAC.itemSelectEvent.subscribe(function (sType, aArgs) { |
|
1636 | reviewerAC.itemSelectEvent.subscribe(function (sType, aArgs) { | |
1641 |
|
1637 | |||
1642 | var myAC = aArgs[0]; // reference back to the AC instance |
|
1638 | var myAC = aArgs[0]; // reference back to the AC instance | |
1643 | var elLI = aArgs[1]; // reference to the selected LI element |
|
1639 | var elLI = aArgs[1]; // reference to the selected LI element | |
1644 | var oData = aArgs[2]; // object literal of selected item's result data |
|
1640 | var oData = aArgs[2]; // object literal of selected item's result data | |
1645 |
|
1641 | |||
1646 | //fill the autocomplete with value |
|
1642 | //fill the autocomplete with value | |
1647 | if (oData.nname != undefined) { |
|
1643 | if (oData.nname != undefined) { | |
1648 | addReviewMember(oData.id, oData.fname, oData.lname, oData.nname, |
|
1644 | addReviewMember(oData.id, oData.fname, oData.lname, oData.nname, | |
1649 | oData.gravatar_lnk, oData.gravatar_size); |
|
1645 | oData.gravatar_lnk, oData.gravatar_size); | |
1650 | myAC.dataSource.cache.push(oData.id); |
|
1646 | myAC.dataSource.cache.push(oData.id); | |
1651 | $('#user').val(''); |
|
1647 | $('#user').val(''); | |
1652 | } |
|
1648 | } | |
1653 | }); |
|
1649 | }); | |
1654 | } |
|
1650 | } | |
1655 | } |
|
1651 | } | |
1656 |
|
1652 | |||
1657 | /** |
|
1653 | /** | |
1658 | * Activate .quick_repo_menu |
|
1654 | * Activate .quick_repo_menu | |
1659 | */ |
|
1655 | */ | |
1660 | var quick_repo_menu = function(){ |
|
1656 | var quick_repo_menu = function(){ | |
1661 | $(".quick_repo_menu").mouseenter(function(e) { |
|
1657 | $(".quick_repo_menu").mouseenter(function(e) { | |
1662 | var $menu = $(e.currentTarget).children().first().children().first(); |
|
1658 | var $menu = $(e.currentTarget).children().first().children().first(); | |
1663 | if($menu.hasClass('hidden')){ |
|
1659 | if($menu.hasClass('hidden')){ | |
1664 | $menu.removeClass('hidden').addClass('active'); |
|
1660 | $menu.removeClass('hidden').addClass('active'); | |
1665 | $(e.currentTarget).removeClass('hidden').addClass('active'); |
|
1661 | $(e.currentTarget).removeClass('hidden').addClass('active'); | |
1666 | } |
|
1662 | } | |
1667 | }) |
|
1663 | }) | |
1668 | $(".quick_repo_menu").mouseleave(function(e) { |
|
1664 | $(".quick_repo_menu").mouseleave(function(e) { | |
1669 | var $menu = $(e.currentTarget).children().first().children().first(); |
|
1665 | var $menu = $(e.currentTarget).children().first().children().first(); | |
1670 | if($menu.hasClass('active')){ |
|
1666 | if($menu.hasClass('active')){ | |
1671 | $menu.removeClass('active').addClass('hidden'); |
|
1667 | $menu.removeClass('active').addClass('hidden'); | |
1672 | $(e.currentTarget).removeClass('active').addClass('hidden'); |
|
1668 | $(e.currentTarget).removeClass('active').addClass('hidden'); | |
1673 | } |
|
1669 | } | |
1674 | }) |
|
1670 | }) | |
1675 | }; |
|
1671 | }; | |
1676 |
|
1672 | |||
1677 |
|
1673 | |||
1678 | /** |
|
1674 | /** | |
1679 | * TABLE SORTING |
|
1675 | * TABLE SORTING | |
1680 | */ |
|
1676 | */ | |
1681 |
|
1677 | |||
1682 | var revisionSort = function(a, b, desc, field) { |
|
1678 | var revisionSort = function(a, b, desc, field) { | |
1683 | var a_ = parseInt(a.getData('last_rev_raw') || 0); |
|
1679 | var a_ = parseInt(a.getData('last_rev_raw') || 0); | |
1684 | var b_ = parseInt(b.getData('last_rev_raw') || 0); |
|
1680 | var b_ = parseInt(b.getData('last_rev_raw') || 0); | |
1685 |
|
1681 | |||
1686 | return YAHOO.util.Sort.compare(a_, b_, desc); |
|
1682 | return YAHOO.util.Sort.compare(a_, b_, desc); | |
1687 | }; |
|
1683 | }; | |
1688 |
|
1684 | |||
1689 | var ageSort = function(a, b, desc, field) { |
|
1685 | var ageSort = function(a, b, desc, field) { | |
1690 | // data is like: <span class="tooltip" date="2014-06-04 18:18:55.325474" title="Wed, 04 Jun 2014 18:18:55">1 day and 23 hours ago</span> |
|
1686 | // data is like: <span class="tooltip" date="2014-06-04 18:18:55.325474" title="Wed, 04 Jun 2014 18:18:55">1 day and 23 hours ago</span> | |
1691 | var a_ = $(a.getData(field)).attr('date'); |
|
1687 | var a_ = $(a.getData(field)).attr('date'); | |
1692 | var b_ = $(b.getData(field)).attr('date'); |
|
1688 | var b_ = $(b.getData(field)).attr('date'); | |
1693 |
|
1689 | |||
1694 | return YAHOO.util.Sort.compare(a_, b_, desc); |
|
1690 | return YAHOO.util.Sort.compare(a_, b_, desc); | |
1695 | }; |
|
1691 | }; | |
1696 |
|
1692 | |||
1697 | var lastLoginSort = function(a, b, desc, field) { |
|
1693 | var lastLoginSort = function(a, b, desc, field) { | |
1698 | var a_ = parseFloat(a.getData('last_login_raw') || 0); |
|
1694 | var a_ = parseFloat(a.getData('last_login_raw') || 0); | |
1699 | var b_ = parseFloat(b.getData('last_login_raw') || 0); |
|
1695 | var b_ = parseFloat(b.getData('last_login_raw') || 0); | |
1700 |
|
1696 | |||
1701 | return YAHOO.util.Sort.compare(a_, b_, desc); |
|
1697 | return YAHOO.util.Sort.compare(a_, b_, desc); | |
1702 | }; |
|
1698 | }; | |
1703 |
|
1699 | |||
1704 | var nameSort = function(a, b, desc, field) { |
|
1700 | var nameSort = function(a, b, desc, field) { | |
1705 | var a_ = a.getData('raw_name') || 0; |
|
1701 | var a_ = a.getData('raw_name') || 0; | |
1706 | var b_ = b.getData('raw_name') || 0; |
|
1702 | var b_ = b.getData('raw_name') || 0; | |
1707 |
|
1703 | |||
1708 | return YAHOO.util.Sort.compare(a_, b_, desc); |
|
1704 | return YAHOO.util.Sort.compare(a_, b_, desc); | |
1709 | }; |
|
1705 | }; | |
1710 |
|
1706 | |||
1711 | var dateSort = function(a, b, desc, field) { |
|
1707 | var dateSort = function(a, b, desc, field) { | |
1712 | var a_ = parseFloat(a.getData('raw_date') || 0); |
|
1708 | var a_ = parseFloat(a.getData('raw_date') || 0); | |
1713 | var b_ = parseFloat(b.getData('raw_date') || 0); |
|
1709 | var b_ = parseFloat(b.getData('raw_date') || 0); | |
1714 |
|
1710 | |||
1715 | return YAHOO.util.Sort.compare(a_, b_, desc); |
|
1711 | return YAHOO.util.Sort.compare(a_, b_, desc); | |
1716 | }; |
|
1712 | }; | |
1717 |
|
1713 | |||
1718 | var addPermAction = function(_html, users_list, groups_list){ |
|
1714 | var addPermAction = function(_html, users_list, groups_list){ | |
1719 | var $last_node = $('.last_new_member').last(); // empty tr between last and add |
|
1715 | var $last_node = $('.last_new_member').last(); // empty tr between last and add | |
1720 | var next_id = $('.new_members').length; |
|
1716 | var next_id = $('.new_members').length; | |
1721 | $last_node.before($('<tr class="new_members">').append(_html.format(next_id))); |
|
1717 | $last_node.before($('<tr class="new_members">').append(_html.format(next_id))); | |
1722 | _MembersAutoComplete("perm_new_member_name_"+next_id, |
|
1718 | _MembersAutoComplete("perm_new_member_name_"+next_id, | |
1723 | "perm_container_"+next_id, users_list, groups_list); |
|
1719 | "perm_container_"+next_id, users_list, groups_list); | |
1724 | } |
|
1720 | } | |
1725 |
|
1721 | |||
1726 | function ajaxActionRevokePermission(url, obj_id, obj_type, field_id, extra_data) { |
|
1722 | function ajaxActionRevokePermission(url, obj_id, obj_type, field_id, extra_data) { | |
1727 | var success = function (o) { |
|
1723 | var success = function (o) { | |
1728 | $('#' + field_id).remove(); |
|
1724 | $('#' + field_id).remove(); | |
1729 | }; |
|
1725 | }; | |
1730 | var failure = function (o) { |
|
1726 | var failure = function (o) { | |
1731 | alert(_TM['Failed to revoke permission'] + ": " + o.status); |
|
1727 | alert(_TM['Failed to revoke permission'] + ": " + o.status); | |
1732 | }; |
|
1728 | }; | |
1733 | var query_params = { |
|
1729 | var query_params = { | |
1734 | '_method': 'delete' |
|
1730 | '_method': 'delete' | |
1735 | } |
|
1731 | } | |
1736 | // put extra data into POST |
|
1732 | // put extra data into POST | |
1737 | if (extra_data !== undefined && (typeof extra_data === 'object')){ |
|
1733 | if (extra_data !== undefined && (typeof extra_data === 'object')){ | |
1738 | for(var k in extra_data){ |
|
1734 | for(var k in extra_data){ | |
1739 | query_params[k] = extra_data[k]; |
|
1735 | query_params[k] = extra_data[k]; | |
1740 | } |
|
1736 | } | |
1741 | } |
|
1737 | } | |
1742 |
|
1738 | |||
1743 | if (obj_type=='user'){ |
|
1739 | if (obj_type=='user'){ | |
1744 | query_params['user_id'] = obj_id; |
|
1740 | query_params['user_id'] = obj_id; | |
1745 | query_params['obj_type'] = 'user'; |
|
1741 | query_params['obj_type'] = 'user'; | |
1746 | } |
|
1742 | } | |
1747 | else if (obj_type=='user_group'){ |
|
1743 | else if (obj_type=='user_group'){ | |
1748 | query_params['user_group_id'] = obj_id; |
|
1744 | query_params['user_group_id'] = obj_id; | |
1749 | query_params['obj_type'] = 'user_group'; |
|
1745 | query_params['obj_type'] = 'user_group'; | |
1750 | } |
|
1746 | } | |
1751 |
|
1747 | |||
1752 | ajaxPOST(url, query_params, success, failure); |
|
1748 | ajaxPOST(url, query_params, success, failure); | |
1753 | }; |
|
1749 | }; | |
1754 |
|
1750 | |||
1755 | /* Multi selectors */ |
|
1751 | /* Multi selectors */ | |
1756 |
|
1752 | |||
1757 | var MultiSelectWidget = function(selected_id, available_id, form_id){ |
|
1753 | var MultiSelectWidget = function(selected_id, available_id, form_id){ | |
1758 | var $availableselect = $('#' + available_id); |
|
1754 | var $availableselect = $('#' + available_id); | |
1759 | var $selectedselect = $('#' + selected_id); |
|
1755 | var $selectedselect = $('#' + selected_id); | |
1760 |
|
1756 | |||
1761 | //fill available only with those not in selected |
|
1757 | //fill available only with those not in selected | |
1762 | var $selectedoptions = $selectedselect.children('option'); |
|
1758 | var $selectedoptions = $selectedselect.children('option'); | |
1763 | $availableselect.children('option').filter(function(i, e){ |
|
1759 | $availableselect.children('option').filter(function(i, e){ | |
1764 | for(var j = 0, node; node = $selectedoptions[j]; j++){ |
|
1760 | for(var j = 0, node; node = $selectedoptions[j]; j++){ | |
1765 | if(node.value == e.value){ |
|
1761 | if(node.value == e.value){ | |
1766 | return true; |
|
1762 | return true; | |
1767 | } |
|
1763 | } | |
1768 | } |
|
1764 | } | |
1769 | return false; |
|
1765 | return false; | |
1770 | }).remove(); |
|
1766 | }).remove(); | |
1771 |
|
1767 | |||
1772 | $('#add_element').click(function(e){ |
|
1768 | $('#add_element').click(function(e){ | |
1773 | $selectedselect.append($availableselect.children('option:selected')); |
|
1769 | $selectedselect.append($availableselect.children('option:selected')); | |
1774 | }); |
|
1770 | }); | |
1775 | $('#remove_element').click(function(e){ |
|
1771 | $('#remove_element').click(function(e){ | |
1776 | $availableselect.append($selectedselect.children('option:selected')); |
|
1772 | $availableselect.append($selectedselect.children('option:selected')); | |
1777 | }); |
|
1773 | }); | |
1778 |
|
1774 | |||
1779 | $('#'+form_id).submit(function(){ |
|
1775 | $('#'+form_id).submit(function(){ | |
1780 | $selectedselect.children('option').each(function(i, e){ |
|
1776 | $selectedselect.children('option').each(function(i, e){ | |
1781 | e.selected = 'selected'; |
|
1777 | e.selected = 'selected'; | |
1782 | }); |
|
1778 | }); | |
1783 | }); |
|
1779 | }); | |
1784 | } |
|
1780 | } | |
1785 |
|
1781 | |||
1786 | // custom paginator |
|
1782 | // custom paginator | |
1787 | var YUI_paginator = function(links_per_page, containers){ |
|
1783 | var YUI_paginator = function(links_per_page, containers){ | |
1788 |
|
1784 | |||
1789 | (function () { |
|
1785 | (function () { | |
1790 |
|
1786 | |||
1791 | var Paginator = YAHOO.widget.Paginator, |
|
1787 | var Paginator = YAHOO.widget.Paginator, | |
1792 | l = YAHOO.lang, |
|
1788 | l = YAHOO.lang, | |
1793 | setId = YAHOO.util.Dom.generateId; |
|
1789 | setId = YAHOO.util.Dom.generateId; | |
1794 |
|
1790 | |||
1795 | Paginator.ui.MyFirstPageLink = function (p) { |
|
1791 | Paginator.ui.MyFirstPageLink = function (p) { | |
1796 | this.paginator = p; |
|
1792 | this.paginator = p; | |
1797 |
|
1793 | |||
1798 | p.subscribe('recordOffsetChange',this.update,this,true); |
|
1794 | p.subscribe('recordOffsetChange',this.update,this,true); | |
1799 | p.subscribe('rowsPerPageChange',this.update,this,true); |
|
1795 | p.subscribe('rowsPerPageChange',this.update,this,true); | |
1800 | p.subscribe('totalRecordsChange',this.update,this,true); |
|
1796 | p.subscribe('totalRecordsChange',this.update,this,true); | |
1801 | p.subscribe('destroy',this.destroy,this,true); |
|
1797 | p.subscribe('destroy',this.destroy,this,true); | |
1802 |
|
1798 | |||
1803 | // TODO: make this work |
|
1799 | // TODO: make this work | |
1804 | p.subscribe('firstPageLinkLabelChange',this.update,this,true); |
|
1800 | p.subscribe('firstPageLinkLabelChange',this.update,this,true); | |
1805 | p.subscribe('firstPageLinkClassChange',this.update,this,true); |
|
1801 | p.subscribe('firstPageLinkClassChange',this.update,this,true); | |
1806 | }; |
|
1802 | }; | |
1807 |
|
1803 | |||
1808 | Paginator.ui.MyFirstPageLink.init = function (p) { |
|
1804 | Paginator.ui.MyFirstPageLink.init = function (p) { | |
1809 | p.setAttributeConfig('firstPageLinkLabel', { |
|
1805 | p.setAttributeConfig('firstPageLinkLabel', { | |
1810 | value : 1, |
|
1806 | value : 1, | |
1811 | validator : l.isString |
|
1807 | validator : l.isString | |
1812 | }); |
|
1808 | }); | |
1813 | p.setAttributeConfig('firstPageLinkClass', { |
|
1809 | p.setAttributeConfig('firstPageLinkClass', { | |
1814 | value : 'yui-pg-first', |
|
1810 | value : 'yui-pg-first', | |
1815 | validator : l.isString |
|
1811 | validator : l.isString | |
1816 | }); |
|
1812 | }); | |
1817 | p.setAttributeConfig('firstPageLinkTitle', { |
|
1813 | p.setAttributeConfig('firstPageLinkTitle', { | |
1818 | value : 'First Page', |
|
1814 | value : 'First Page', | |
1819 | validator : l.isString |
|
1815 | validator : l.isString | |
1820 | }); |
|
1816 | }); | |
1821 | }; |
|
1817 | }; | |
1822 |
|
1818 | |||
1823 | // Instance members and methods |
|
1819 | // Instance members and methods | |
1824 | Paginator.ui.MyFirstPageLink.prototype = { |
|
1820 | Paginator.ui.MyFirstPageLink.prototype = { | |
1825 | current : null, |
|
1821 | current : null, | |
1826 | leftmost_page: null, |
|
1822 | leftmost_page: null, | |
1827 | rightmost_page: null, |
|
1823 | rightmost_page: null, | |
1828 | link : null, |
|
1824 | link : null, | |
1829 | span : null, |
|
1825 | span : null, | |
1830 | dotdot : null, |
|
1826 | dotdot : null, | |
1831 | getPos : function(cur_page, max_page, items){ |
|
1827 | getPos : function(cur_page, max_page, items){ | |
1832 | var edge = parseInt(items / 2) + 1; |
|
1828 | var edge = parseInt(items / 2) + 1; | |
1833 | if (cur_page <= edge){ |
|
1829 | if (cur_page <= edge){ | |
1834 | var radius = Math.max(parseInt(items / 2), items - cur_page); |
|
1830 | var radius = Math.max(parseInt(items / 2), items - cur_page); | |
1835 | } |
|
1831 | } | |
1836 | else if ((max_page - cur_page) < edge) { |
|
1832 | else if ((max_page - cur_page) < edge) { | |
1837 | var radius = (items - 1) - (max_page - cur_page); |
|
1833 | var radius = (items - 1) - (max_page - cur_page); | |
1838 | } |
|
1834 | } | |
1839 | else{ |
|
1835 | else{ | |
1840 | var radius = parseInt(items / 2); |
|
1836 | var radius = parseInt(items / 2); | |
1841 | } |
|
1837 | } | |
1842 |
|
1838 | |||
1843 | var left = Math.max(1, (cur_page - (radius))) |
|
1839 | var left = Math.max(1, (cur_page - (radius))) | |
1844 | var right = Math.min(max_page, cur_page + (radius)) |
|
1840 | var right = Math.min(max_page, cur_page + (radius)) | |
1845 | return [left, cur_page, right] |
|
1841 | return [left, cur_page, right] | |
1846 | }, |
|
1842 | }, | |
1847 | render : function (id_base) { |
|
1843 | render : function (id_base) { | |
1848 | var p = this.paginator, |
|
1844 | var p = this.paginator, | |
1849 | c = p.get('firstPageLinkClass'), |
|
1845 | c = p.get('firstPageLinkClass'), | |
1850 | label = p.get('firstPageLinkLabel'), |
|
1846 | label = p.get('firstPageLinkLabel'), | |
1851 | title = p.get('firstPageLinkTitle'); |
|
1847 | title = p.get('firstPageLinkTitle'); | |
1852 |
|
1848 | |||
1853 | this.link = document.createElement('a'); |
|
1849 | this.link = document.createElement('a'); | |
1854 | this.span = document.createElement('span'); |
|
1850 | this.span = document.createElement('span'); | |
1855 | $(this.span).hide(); |
|
1851 | $(this.span).hide(); | |
1856 |
|
1852 | |||
1857 | var _pos = this.getPos(p.getCurrentPage(), p.getTotalPages(), 5); |
|
1853 | var _pos = this.getPos(p.getCurrentPage(), p.getTotalPages(), 5); | |
1858 | this.leftmost_page = _pos[0]; |
|
1854 | this.leftmost_page = _pos[0]; | |
1859 | this.rightmost_page = _pos[2]; |
|
1855 | this.rightmost_page = _pos[2]; | |
1860 |
|
1856 | |||
1861 | setId(this.link, id_base + '-first-link'); |
|
1857 | setId(this.link, id_base + '-first-link'); | |
1862 | this.link.href = '#'; |
|
1858 | this.link.href = '#'; | |
1863 | this.link.className = c; |
|
1859 | this.link.className = c; | |
1864 | this.link.innerHTML = label; |
|
1860 | this.link.innerHTML = label; | |
1865 | this.link.title = title; |
|
1861 | this.link.title = title; | |
1866 | YUE.on(this.link,'click',this.onClick,this,true); |
|
1862 | YUE.on(this.link,'click',this.onClick,this,true); | |
1867 |
|
1863 | |||
1868 | setId(this.span, id_base + '-first-span'); |
|
1864 | setId(this.span, id_base + '-first-span'); | |
1869 | this.span.className = c; |
|
1865 | this.span.className = c; | |
1870 | this.span.innerHTML = label; |
|
1866 | this.span.innerHTML = label; | |
1871 |
|
1867 | |||
1872 | this.current = p.getCurrentPage() > 1 ? this.link : this.span; |
|
1868 | this.current = p.getCurrentPage() > 1 ? this.link : this.span; | |
1873 | return this.current; |
|
1869 | return this.current; | |
1874 | }, |
|
1870 | }, | |
1875 | update : function (e) { |
|
1871 | update : function (e) { | |
1876 | var p = this.paginator; |
|
1872 | var p = this.paginator; | |
1877 | var _pos = this.getPos(p.getCurrentPage(), p.getTotalPages(), 5); |
|
1873 | var _pos = this.getPos(p.getCurrentPage(), p.getTotalPages(), 5); | |
1878 | this.leftmost_page = _pos[0]; |
|
1874 | this.leftmost_page = _pos[0]; | |
1879 | this.rightmost_page = _pos[2]; |
|
1875 | this.rightmost_page = _pos[2]; | |
1880 |
|
1876 | |||
1881 | if (e && e.prevValue === e.newValue) { |
|
1877 | if (e && e.prevValue === e.newValue) { | |
1882 | return; |
|
1878 | return; | |
1883 | } |
|
1879 | } | |
1884 |
|
1880 | |||
1885 | var par = this.current ? this.current.parentNode : null; |
|
1881 | var par = this.current ? this.current.parentNode : null; | |
1886 | if (this.leftmost_page > 1) { |
|
1882 | if (this.leftmost_page > 1) { | |
1887 | if (par && this.current === this.span) { |
|
1883 | if (par && this.current === this.span) { | |
1888 | par.replaceChild(this.link,this.current); |
|
1884 | par.replaceChild(this.link,this.current); | |
1889 | this.current = this.link; |
|
1885 | this.current = this.link; | |
1890 | } |
|
1886 | } | |
1891 | } else { |
|
1887 | } else { | |
1892 | if (par && this.current === this.link) { |
|
1888 | if (par && this.current === this.link) { | |
1893 | par.replaceChild(this.span,this.current); |
|
1889 | par.replaceChild(this.span,this.current); | |
1894 | this.current = this.span; |
|
1890 | this.current = this.span; | |
1895 | } |
|
1891 | } | |
1896 | } |
|
1892 | } | |
1897 | }, |
|
1893 | }, | |
1898 | destroy : function () { |
|
1894 | destroy : function () { | |
1899 | YUE.purgeElement(this.link); |
|
1895 | YUE.purgeElement(this.link); | |
1900 | this.current.parentNode.removeChild(this.current); |
|
1896 | this.current.parentNode.removeChild(this.current); | |
1901 | this.link = this.span = null; |
|
1897 | this.link = this.span = null; | |
1902 | }, |
|
1898 | }, | |
1903 | onClick : function (e) { |
|
1899 | onClick : function (e) { | |
1904 | YUE.stopEvent(e); |
|
1900 | YUE.stopEvent(e); | |
1905 | this.paginator.setPage(1); |
|
1901 | this.paginator.setPage(1); | |
1906 | } |
|
1902 | } | |
1907 | }; |
|
1903 | }; | |
1908 |
|
1904 | |||
1909 | })(); |
|
1905 | })(); | |
1910 |
|
1906 | |||
1911 | (function () { |
|
1907 | (function () { | |
1912 |
|
1908 | |||
1913 | var Paginator = YAHOO.widget.Paginator, |
|
1909 | var Paginator = YAHOO.widget.Paginator, | |
1914 | l = YAHOO.lang, |
|
1910 | l = YAHOO.lang, | |
1915 | setId = YAHOO.util.Dom.generateId; |
|
1911 | setId = YAHOO.util.Dom.generateId; | |
1916 |
|
1912 | |||
1917 | Paginator.ui.MyLastPageLink = function (p) { |
|
1913 | Paginator.ui.MyLastPageLink = function (p) { | |
1918 | this.paginator = p; |
|
1914 | this.paginator = p; | |
1919 |
|
1915 | |||
1920 | p.subscribe('recordOffsetChange',this.update,this,true); |
|
1916 | p.subscribe('recordOffsetChange',this.update,this,true); | |
1921 | p.subscribe('rowsPerPageChange',this.update,this,true); |
|
1917 | p.subscribe('rowsPerPageChange',this.update,this,true); | |
1922 | p.subscribe('totalRecordsChange',this.update,this,true); |
|
1918 | p.subscribe('totalRecordsChange',this.update,this,true); | |
1923 | p.subscribe('destroy',this.destroy,this,true); |
|
1919 | p.subscribe('destroy',this.destroy,this,true); | |
1924 |
|
1920 | |||
1925 | // TODO: make this work |
|
1921 | // TODO: make this work | |
1926 | p.subscribe('lastPageLinkLabelChange',this.update,this,true); |
|
1922 | p.subscribe('lastPageLinkLabelChange',this.update,this,true); | |
1927 | p.subscribe('lastPageLinkClassChange', this.update,this,true); |
|
1923 | p.subscribe('lastPageLinkClassChange', this.update,this,true); | |
1928 | }; |
|
1924 | }; | |
1929 |
|
1925 | |||
1930 | Paginator.ui.MyLastPageLink.init = function (p) { |
|
1926 | Paginator.ui.MyLastPageLink.init = function (p) { | |
1931 | p.setAttributeConfig('lastPageLinkLabel', { |
|
1927 | p.setAttributeConfig('lastPageLinkLabel', { | |
1932 | value : -1, |
|
1928 | value : -1, | |
1933 | validator : l.isString |
|
1929 | validator : l.isString | |
1934 | }); |
|
1930 | }); | |
1935 | p.setAttributeConfig('lastPageLinkClass', { |
|
1931 | p.setAttributeConfig('lastPageLinkClass', { | |
1936 | value : 'yui-pg-last', |
|
1932 | value : 'yui-pg-last', | |
1937 | validator : l.isString |
|
1933 | validator : l.isString | |
1938 | }); |
|
1934 | }); | |
1939 | p.setAttributeConfig('lastPageLinkTitle', { |
|
1935 | p.setAttributeConfig('lastPageLinkTitle', { | |
1940 | value : 'Last Page', |
|
1936 | value : 'Last Page', | |
1941 | validator : l.isString |
|
1937 | validator : l.isString | |
1942 | }); |
|
1938 | }); | |
1943 |
|
1939 | |||
1944 | }; |
|
1940 | }; | |
1945 |
|
1941 | |||
1946 | Paginator.ui.MyLastPageLink.prototype = { |
|
1942 | Paginator.ui.MyLastPageLink.prototype = { | |
1947 |
|
1943 | |||
1948 | current : null, |
|
1944 | current : null, | |
1949 | leftmost_page: null, |
|
1945 | leftmost_page: null, | |
1950 | rightmost_page: null, |
|
1946 | rightmost_page: null, | |
1951 | link : null, |
|
1947 | link : null, | |
1952 | span : null, |
|
1948 | span : null, | |
1953 | dotdot : null, |
|
1949 | dotdot : null, | |
1954 | na : null, |
|
1950 | na : null, | |
1955 | getPos : function(cur_page, max_page, items){ |
|
1951 | getPos : function(cur_page, max_page, items){ | |
1956 | var edge = parseInt(items / 2) + 1; |
|
1952 | var edge = parseInt(items / 2) + 1; | |
1957 | if (cur_page <= edge){ |
|
1953 | if (cur_page <= edge){ | |
1958 | var radius = Math.max(parseInt(items / 2), items - cur_page); |
|
1954 | var radius = Math.max(parseInt(items / 2), items - cur_page); | |
1959 | } |
|
1955 | } | |
1960 | else if ((max_page - cur_page) < edge) { |
|
1956 | else if ((max_page - cur_page) < edge) { | |
1961 | var radius = (items - 1) - (max_page - cur_page); |
|
1957 | var radius = (items - 1) - (max_page - cur_page); | |
1962 | } |
|
1958 | } | |
1963 | else{ |
|
1959 | else{ | |
1964 | var radius = parseInt(items / 2); |
|
1960 | var radius = parseInt(items / 2); | |
1965 | } |
|
1961 | } | |
1966 |
|
1962 | |||
1967 | var left = Math.max(1, (cur_page - (radius))) |
|
1963 | var left = Math.max(1, (cur_page - (radius))) | |
1968 | var right = Math.min(max_page, cur_page + (radius)) |
|
1964 | var right = Math.min(max_page, cur_page + (radius)) | |
1969 | return [left, cur_page, right] |
|
1965 | return [left, cur_page, right] | |
1970 | }, |
|
1966 | }, | |
1971 | render : function (id_base) { |
|
1967 | render : function (id_base) { | |
1972 | var p = this.paginator, |
|
1968 | var p = this.paginator, | |
1973 | c = p.get('lastPageLinkClass'), |
|
1969 | c = p.get('lastPageLinkClass'), | |
1974 | label = p.get('lastPageLinkLabel'), |
|
1970 | label = p.get('lastPageLinkLabel'), | |
1975 | last = p.getTotalPages(), |
|
1971 | last = p.getTotalPages(), | |
1976 | title = p.get('lastPageLinkTitle'); |
|
1972 | title = p.get('lastPageLinkTitle'); | |
1977 |
|
1973 | |||
1978 | var _pos = this.getPos(p.getCurrentPage(), p.getTotalPages(), 5); |
|
1974 | var _pos = this.getPos(p.getCurrentPage(), p.getTotalPages(), 5); | |
1979 | this.leftmost_page = _pos[0]; |
|
1975 | this.leftmost_page = _pos[0]; | |
1980 | this.rightmost_page = _pos[2]; |
|
1976 | this.rightmost_page = _pos[2]; | |
1981 |
|
1977 | |||
1982 | this.link = document.createElement('a'); |
|
1978 | this.link = document.createElement('a'); | |
1983 | this.span = document.createElement('span'); |
|
1979 | this.span = document.createElement('span'); | |
1984 | $(this.span).hide(); |
|
1980 | $(this.span).hide(); | |
1985 |
|
1981 | |||
1986 | this.na = this.span.cloneNode(false); |
|
1982 | this.na = this.span.cloneNode(false); | |
1987 |
|
1983 | |||
1988 | setId(this.link, id_base + '-last-link'); |
|
1984 | setId(this.link, id_base + '-last-link'); | |
1989 | this.link.href = '#'; |
|
1985 | this.link.href = '#'; | |
1990 | this.link.className = c; |
|
1986 | this.link.className = c; | |
1991 | this.link.innerHTML = label; |
|
1987 | this.link.innerHTML = label; | |
1992 | this.link.title = title; |
|
1988 | this.link.title = title; | |
1993 | YUE.on(this.link,'click',this.onClick,this,true); |
|
1989 | YUE.on(this.link,'click',this.onClick,this,true); | |
1994 |
|
1990 | |||
1995 | setId(this.span, id_base + '-last-span'); |
|
1991 | setId(this.span, id_base + '-last-span'); | |
1996 | this.span.className = c; |
|
1992 | this.span.className = c; | |
1997 | this.span.innerHTML = label; |
|
1993 | this.span.innerHTML = label; | |
1998 |
|
1994 | |||
1999 | setId(this.na, id_base + '-last-na'); |
|
1995 | setId(this.na, id_base + '-last-na'); | |
2000 |
|
1996 | |||
2001 | if (this.rightmost_page < p.getTotalPages()){ |
|
1997 | if (this.rightmost_page < p.getTotalPages()){ | |
2002 | this.current = this.link; |
|
1998 | this.current = this.link; | |
2003 | } |
|
1999 | } | |
2004 | else{ |
|
2000 | else{ | |
2005 | this.current = this.span; |
|
2001 | this.current = this.span; | |
2006 | } |
|
2002 | } | |
2007 |
|
2003 | |||
2008 | this.current.innerHTML = p.getTotalPages(); |
|
2004 | this.current.innerHTML = p.getTotalPages(); | |
2009 | return this.current; |
|
2005 | return this.current; | |
2010 | }, |
|
2006 | }, | |
2011 |
|
2007 | |||
2012 | update : function (e) { |
|
2008 | update : function (e) { | |
2013 | var p = this.paginator; |
|
2009 | var p = this.paginator; | |
2014 |
|
2010 | |||
2015 | var _pos = this.getPos(p.getCurrentPage(), p.getTotalPages(), 5); |
|
2011 | var _pos = this.getPos(p.getCurrentPage(), p.getTotalPages(), 5); | |
2016 | this.leftmost_page = _pos[0]; |
|
2012 | this.leftmost_page = _pos[0]; | |
2017 | this.rightmost_page = _pos[2]; |
|
2013 | this.rightmost_page = _pos[2]; | |
2018 |
|
2014 | |||
2019 | if (e && e.prevValue === e.newValue) { |
|
2015 | if (e && e.prevValue === e.newValue) { | |
2020 | return; |
|
2016 | return; | |
2021 | } |
|
2017 | } | |
2022 |
|
2018 | |||
2023 | var par = this.current ? this.current.parentNode : null, |
|
2019 | var par = this.current ? this.current.parentNode : null, | |
2024 | after = this.link; |
|
2020 | after = this.link; | |
2025 | if (par) { |
|
2021 | if (par) { | |
2026 |
|
2022 | |||
2027 | // only show the last page if the rightmost one is |
|
2023 | // only show the last page if the rightmost one is | |
2028 | // lower, so we don't have doubled entries at the end |
|
2024 | // lower, so we don't have doubled entries at the end | |
2029 | if (!(this.rightmost_page < p.getTotalPages())){ |
|
2025 | if (!(this.rightmost_page < p.getTotalPages())){ | |
2030 | after = this.span |
|
2026 | after = this.span | |
2031 | } |
|
2027 | } | |
2032 |
|
2028 | |||
2033 | if (this.current !== after) { |
|
2029 | if (this.current !== after) { | |
2034 | par.replaceChild(after,this.current); |
|
2030 | par.replaceChild(after,this.current); | |
2035 | this.current = after; |
|
2031 | this.current = after; | |
2036 | } |
|
2032 | } | |
2037 | } |
|
2033 | } | |
2038 | this.current.innerHTML = this.paginator.getTotalPages(); |
|
2034 | this.current.innerHTML = this.paginator.getTotalPages(); | |
2039 |
|
2035 | |||
2040 | }, |
|
2036 | }, | |
2041 | destroy : function () { |
|
2037 | destroy : function () { | |
2042 | YUE.purgeElement(this.link); |
|
2038 | YUE.purgeElement(this.link); | |
2043 | this.current.parentNode.removeChild(this.current); |
|
2039 | this.current.parentNode.removeChild(this.current); | |
2044 | this.link = this.span = null; |
|
2040 | this.link = this.span = null; | |
2045 | }, |
|
2041 | }, | |
2046 | onClick : function (e) { |
|
2042 | onClick : function (e) { | |
2047 | YUE.stopEvent(e); |
|
2043 | YUE.stopEvent(e); | |
2048 | this.paginator.setPage(this.paginator.getTotalPages()); |
|
2044 | this.paginator.setPage(this.paginator.getTotalPages()); | |
2049 | } |
|
2045 | } | |
2050 | }; |
|
2046 | }; | |
2051 |
|
2047 | |||
2052 | })(); |
|
2048 | })(); | |
2053 |
|
2049 | |||
2054 | var pagi = new YAHOO.widget.Paginator({ |
|
2050 | var pagi = new YAHOO.widget.Paginator({ | |
2055 | rowsPerPage: links_per_page, |
|
2051 | rowsPerPage: links_per_page, | |
2056 | alwaysVisible: false, |
|
2052 | alwaysVisible: false, | |
2057 | template : "{PreviousPageLink} {MyFirstPageLink} {PageLinks} {MyLastPageLink} {NextPageLink}", |
|
2053 | template : "{PreviousPageLink} {MyFirstPageLink} {PageLinks} {MyLastPageLink} {NextPageLink}", | |
2058 | pageLinks: 5, |
|
2054 | pageLinks: 5, | |
2059 | containerClass: 'pagination-wh', |
|
2055 | containerClass: 'pagination-wh', | |
2060 | currentPageClass: 'pager_curpage', |
|
2056 | currentPageClass: 'pager_curpage', | |
2061 | pageLinkClass: 'pager_link', |
|
2057 | pageLinkClass: 'pager_link', | |
2062 | nextPageLinkLabel: '>', |
|
2058 | nextPageLinkLabel: '>', | |
2063 | previousPageLinkLabel: '<', |
|
2059 | previousPageLinkLabel: '<', | |
2064 | containers:containers |
|
2060 | containers:containers | |
2065 | }) |
|
2061 | }) | |
2066 |
|
2062 | |||
2067 | return pagi |
|
2063 | return pagi | |
2068 | } |
|
2064 | } | |
2069 |
|
2065 | |||
2070 | var YUI_datatable = function(data, fields, columns, countnode, sortkey, rows){ |
|
2066 | var YUI_datatable = function(data, fields, columns, countnode, sortkey, rows){ | |
2071 | var myDataSource = new YAHOO.util.DataSource(data); |
|
2067 | var myDataSource = new YAHOO.util.DataSource(data); | |
2072 | myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON; |
|
2068 | myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON; | |
2073 | myDataSource.responseSchema = { |
|
2069 | myDataSource.responseSchema = { | |
2074 | resultsList: "records", |
|
2070 | resultsList: "records", | |
2075 | fields: fields |
|
2071 | fields: fields | |
2076 | }; |
|
2072 | }; | |
2077 | myDataSource.doBeforeCallback = function(req, raw, res, cb) { |
|
2073 | myDataSource.doBeforeCallback = function(req, raw, res, cb) { | |
2078 | // This is the filter function |
|
2074 | // This is the filter function | |
2079 | var data = res.results || [], |
|
2075 | var data = res.results || [], | |
2080 | filtered = [], |
|
2076 | filtered = [], | |
2081 | i, l; |
|
2077 | i, l; | |
2082 |
|
2078 | |||
2083 | if (req) { |
|
2079 | if (req) { | |
2084 | req = req.toLowerCase(); |
|
2080 | req = req.toLowerCase(); | |
2085 | for (i = 0; i<data.length; i++) { |
|
2081 | for (i = 0; i<data.length; i++) { | |
2086 | var pos = data[i].raw_name.toLowerCase().indexOf(req) |
|
2082 | var pos = data[i].raw_name.toLowerCase().indexOf(req) | |
2087 | if (pos != -1) { |
|
2083 | if (pos != -1) { | |
2088 | filtered.push(data[i]); |
|
2084 | filtered.push(data[i]); | |
2089 | } |
|
2085 | } | |
2090 | } |
|
2086 | } | |
2091 | res.results = filtered; |
|
2087 | res.results = filtered; | |
2092 | } |
|
2088 | } | |
2093 | $(countnode).html(res.results.length); |
|
2089 | $(countnode).html(res.results.length); | |
2094 | return res; |
|
2090 | return res; | |
2095 | } |
|
2091 | } | |
2096 |
|
2092 | |||
2097 | var myDataTable = new YAHOO.widget.DataTable("datatable_list_wrap", columns, myDataSource, { |
|
2093 | var myDataTable = new YAHOO.widget.DataTable("datatable_list_wrap", columns, myDataSource, { | |
2098 | sortedBy: {key:sortkey, dir:"asc"}, |
|
2094 | sortedBy: {key:sortkey, dir:"asc"}, | |
2099 | paginator: YUI_paginator(rows !== undefined && rows ? rows : 25, ['user-paginator']), |
|
2095 | paginator: YUI_paginator(rows !== undefined && rows ? rows : 25, ['user-paginator']), | |
2100 | MSG_SORTASC: _TM['MSG_SORTASC'], |
|
2096 | MSG_SORTASC: _TM['MSG_SORTASC'], | |
2101 | MSG_SORTDESC: _TM['MSG_SORTDESC'], |
|
2097 | MSG_SORTDESC: _TM['MSG_SORTDESC'], | |
2102 | MSG_EMPTY: _TM['MSG_EMPTY'], |
|
2098 | MSG_EMPTY: _TM['MSG_EMPTY'], | |
2103 | MSG_ERROR: _TM['MSG_ERROR'], |
|
2099 | MSG_ERROR: _TM['MSG_ERROR'], | |
2104 | MSG_LOADING: _TM['MSG_LOADING'] |
|
2100 | MSG_LOADING: _TM['MSG_LOADING'] | |
2105 | }); |
|
2101 | }); | |
2106 | myDataTable.subscribe('postRenderEvent',function(oArgs) { |
|
2102 | myDataTable.subscribe('postRenderEvent',function(oArgs) { | |
2107 | tooltip_activate(); |
|
2103 | tooltip_activate(); | |
2108 | quick_repo_menu(); |
|
2104 | quick_repo_menu(); | |
2109 | }); |
|
2105 | }); | |
2110 |
|
2106 | |||
2111 | var filterTimeout = null; |
|
2107 | var filterTimeout = null; | |
2112 | var $q_filter = $('#q_filter'); |
|
2108 | var $q_filter = $('#q_filter'); | |
2113 |
|
2109 | |||
2114 | var updateFilter = function () { |
|
2110 | var updateFilter = function () { | |
2115 | // Reset timeout |
|
2111 | // Reset timeout | |
2116 | filterTimeout = null; |
|
2112 | filterTimeout = null; | |
2117 |
|
2113 | |||
2118 | // Reset sort |
|
2114 | // Reset sort | |
2119 | var state = myDataTable.getState(); |
|
2115 | var state = myDataTable.getState(); | |
2120 | state.sortedBy = {key:sortkey, dir:YAHOO.widget.DataTable.CLASS_ASC}; |
|
2116 | state.sortedBy = {key:sortkey, dir:YAHOO.widget.DataTable.CLASS_ASC}; | |
2121 |
|
2117 | |||
2122 | // Get filtered data |
|
2118 | // Get filtered data | |
2123 | myDataSource.sendRequest($q_filter.val(), { |
|
2119 | myDataSource.sendRequest($q_filter.val(), { | |
2124 | success : myDataTable.onDataReturnInitializeTable, |
|
2120 | success : myDataTable.onDataReturnInitializeTable, | |
2125 | failure : myDataTable.onDataReturnInitializeTable, |
|
2121 | failure : myDataTable.onDataReturnInitializeTable, | |
2126 | scope : myDataTable, |
|
2122 | scope : myDataTable, | |
2127 | argument: state}); |
|
2123 | argument: state}); | |
2128 | }; |
|
2124 | }; | |
2129 |
|
2125 | |||
2130 | $q_filter.click(function(){ |
|
2126 | $q_filter.click(function(){ | |
2131 | if(!$q_filter.hasClass('loaded')){ |
|
2127 | if(!$q_filter.hasClass('loaded')){ | |
2132 | //TODO: load here full list later to do search within groups |
|
2128 | //TODO: load here full list later to do search within groups | |
2133 | $q_filter.addClass('loaded'); |
|
2129 | $q_filter.addClass('loaded'); | |
2134 | } |
|
2130 | } | |
2135 | }); |
|
2131 | }); | |
2136 |
|
2132 | |||
2137 | $q_filter.keyup(function (e) { |
|
2133 | $q_filter.keyup(function (e) { | |
2138 | clearTimeout(filterTimeout); |
|
2134 | clearTimeout(filterTimeout); | |
2139 | filterTimeout = setTimeout(updateFilter, 600); |
|
2135 | filterTimeout = setTimeout(updateFilter, 600); | |
2140 | }); |
|
2136 | }); | |
2141 | } |
|
2137 | } | |
2142 |
|
2138 | |||
2143 | // global hooks after DOM is loaded |
|
2139 | // global hooks after DOM is loaded | |
2144 |
|
2140 | |||
2145 | $(document).ready(function(){ |
|
2141 | $(document).ready(function(){ | |
2146 | $('.diff-collapse-button').click(function(e) { |
|
2142 | $('.diff-collapse-button').click(function(e) { | |
2147 | var $button = $(e.currentTarget); |
|
2143 | var $button = $(e.currentTarget); | |
2148 | var $target = $('#' + $button.attr('target')); |
|
2144 | var $target = $('#' + $button.attr('target')); | |
2149 | if($target.hasClass('hidden')){ |
|
2145 | if($target.hasClass('hidden')){ | |
2150 | $target.removeClass('hidden'); |
|
2146 | $target.removeClass('hidden'); | |
2151 | $button.html("↑ {0} ↑".format(_TM['Collapse Diff'])); |
|
2147 | $button.html("↑ {0} ↑".format(_TM['Collapse Diff'])); | |
2152 | } |
|
2148 | } | |
2153 | else if(!$target.hasClass('hidden')){ |
|
2149 | else if(!$target.hasClass('hidden')){ | |
2154 | $target.addClass('hidden'); |
|
2150 | $target.addClass('hidden'); | |
2155 | $button.html("↓ {0} ↓".format(_TM['Expand Diff'])); |
|
2151 | $button.html("↓ {0} ↓".format(_TM['Expand Diff'])); | |
2156 | } |
|
2152 | } | |
2157 | }); |
|
2153 | }); | |
2158 | }); |
|
2154 | }); |
@@ -1,95 +1,94 b'' | |||||
1 | <h3>${_('Parent')}</h3> |
|
1 | <h3>${_('Parent')}</h3> | |
2 | ${h.form(url('edit_repo_advanced_fork', repo_name=c.repo_info.repo_name), method='put')} |
|
2 | ${h.form(url('edit_repo_advanced_fork', repo_name=c.repo_info.repo_name), method='put')} | |
3 | <div class="form"> |
|
3 | <div class="form"> | |
4 | ${h.select('id_fork_of','',c.repos_list,class_="medium")} |
|
4 | ${h.select('id_fork_of','',c.repos_list,class_="medium")} | |
5 | ${h.submit('set_as_fork_%s' % c.repo_info.repo_name,_('Set'),class_="btn btn-small")} |
|
5 | ${h.submit('set_as_fork_%s' % c.repo_info.repo_name,_('Set'),class_="btn btn-small")} | |
6 | <div class="field" style="border:none;color:#888"> |
|
6 | <div class="field" style="border:none;color:#888"> | |
7 | <ul> |
|
7 | <ul> | |
8 | <li>${_('''Manually set this repository as a fork of another from the list.''')}</li> |
|
8 | <li>${_('''Manually set this repository as a fork of another from the list.''')}</li> | |
9 | </ul> |
|
9 | </ul> | |
10 | </div> |
|
10 | </div> | |
11 | </div> |
|
11 | </div> | |
12 | ${h.end_form()} |
|
12 | ${h.end_form()} | |
13 |
|
13 | |||
14 | <script> |
|
14 | <script> | |
15 | $(document).ready(function(){ |
|
15 | $(document).ready(function(){ | |
16 | $("#id_fork_of").select2({ |
|
16 | $("#id_fork_of").select2({ | |
17 | 'dropdownAutoWidth': true |
|
17 | 'dropdownAutoWidth': true | |
18 | }); |
|
18 | }); | |
19 | }) |
|
19 | }) | |
20 | </script> |
|
20 | </script> | |
21 |
|
21 | |||
22 | <h3>${_('Public Journal Visibility')}</h3> |
|
22 | <h3>${_('Public Journal Visibility')}</h3> | |
23 | ${h.form(url('edit_repo_advanced_journal', repo_name=c.repo_info.repo_name), method='put')} |
|
23 | ${h.form(url('edit_repo_advanced_journal', repo_name=c.repo_info.repo_name), method='put')} | |
24 | <div class="form"> |
|
24 | <div class="form"> | |
25 | ${h.hidden('auth_token',str(h.get_token()))} |
|
|||
26 | <div class="field"> |
|
25 | <div class="field"> | |
27 | %if c.in_public_journal: |
|
26 | %if c.in_public_journal: | |
28 | <button class="btn btn-small" type="submit"> |
|
27 | <button class="btn btn-small" type="submit"> | |
29 | <i class="icon-minus"></i> |
|
28 | <i class="icon-minus"></i> | |
30 | ${_('Remove from public journal')} |
|
29 | ${_('Remove from public journal')} | |
31 | </button> |
|
30 | </button> | |
32 | %else: |
|
31 | %else: | |
33 | <button class="btn btn-small" type="submit"> |
|
32 | <button class="btn btn-small" type="submit"> | |
34 | <i class="icon-plus"></i> |
|
33 | <i class="icon-plus"></i> | |
35 | ${_('Add to Public Journal')} |
|
34 | ${_('Add to Public Journal')} | |
36 | </button> |
|
35 | </button> | |
37 | %endif |
|
36 | %endif | |
38 | </div> |
|
37 | </div> | |
39 | <div class="field" style="border:none;color:#888"> |
|
38 | <div class="field" style="border:none;color:#888"> | |
40 | <ul> |
|
39 | <ul> | |
41 | <li>${_('All actions done in this repository will be visible to everyone in the public journal.')}</li> |
|
40 | <li>${_('All actions done in this repository will be visible to everyone in the public journal.')}</li> | |
42 | </ul> |
|
41 | </ul> | |
43 | </div> |
|
42 | </div> | |
44 | </div> |
|
43 | </div> | |
45 | ${h.end_form()} |
|
44 | ${h.end_form()} | |
46 |
|
45 | |||
47 | <h3>${_('Change Locking')}</h3> |
|
46 | <h3>${_('Change Locking')}</h3> | |
48 | ${h.form(url('edit_repo_advanced_locking', repo_name=c.repo_info.repo_name), method='put')} |
|
47 | ${h.form(url('edit_repo_advanced_locking', repo_name=c.repo_info.repo_name), method='put')} | |
49 | <div class="form"> |
|
48 | <div class="form"> | |
50 | %if c.repo_info.locked[0]: |
|
49 | %if c.repo_info.locked[0]: | |
51 | ${h.hidden('set_unlock', '1')} |
|
50 | ${h.hidden('set_unlock', '1')} | |
52 | <button class="btn btn-small" type="submit" |
|
51 | <button class="btn btn-small" type="submit" | |
53 | onclick="return confirm('${_('Confirm to unlock repository.')}');"> |
|
52 | onclick="return confirm('${_('Confirm to unlock repository.')}');"> | |
54 | <i class="icon-lock-open-alt"></i> |
|
53 | <i class="icon-lock-open-alt"></i> | |
55 | ${_('Unlock Repository')} |
|
54 | ${_('Unlock Repository')} | |
56 | </button> |
|
55 | </button> | |
57 | ${'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])))} |
|
56 | ${'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])))} | |
58 | %else: |
|
57 | %else: | |
59 | ${h.hidden('set_lock', '1')} |
|
58 | ${h.hidden('set_lock', '1')} | |
60 | <button class="btn btn-small" type="submit" |
|
59 | <button class="btn btn-small" type="submit" | |
61 | onclick="return confirm('${_('Confirm to lock repository.')}');"> |
|
60 | onclick="return confirm('${_('Confirm to lock repository.')}');"> | |
62 | <i class="icon-lock"></i> |
|
61 | <i class="icon-lock"></i> | |
63 | ${_('Lock Repository')} |
|
62 | ${_('Lock Repository')} | |
64 | </button> |
|
63 | </button> | |
65 | ${_('Repository is not locked')} |
|
64 | ${_('Repository is not locked')} | |
66 | %endif |
|
65 | %endif | |
67 | <div class="field" style="border:none;color:#888"> |
|
66 | <div class="field" style="border:none;color:#888"> | |
68 | <ul> |
|
67 | <ul> | |
69 | <li>${_('Force locking on the repository. Works only when anonymous access is disabled. Triggering a pull locks the repository. The user who is pulling locks the repository; only the user who pulled and locked it can unlock it by doing a push.')} |
|
68 | <li>${_('Force locking on the repository. Works only when anonymous access is disabled. Triggering a pull locks the repository. The user who is pulling locks the repository; only the user who pulled and locked it can unlock it by doing a push.')} | |
70 | </li> |
|
69 | </li> | |
71 | </ul> |
|
70 | </ul> | |
72 | </div> |
|
71 | </div> | |
73 | </div> |
|
72 | </div> | |
74 | ${h.end_form()} |
|
73 | ${h.end_form()} | |
75 |
|
74 | |||
76 | <h3>${_('Delete')}</h3> |
|
75 | <h3>${_('Delete')}</h3> | |
77 | ${h.form(url('repo', repo_name=c.repo_name),method='delete')} |
|
76 | ${h.form(url('repo', repo_name=c.repo_name),method='delete')} | |
78 | <div class="form"> |
|
77 | <div class="form"> | |
79 | <button class="btn btn-small btn-danger" type="submit" |
|
78 | <button class="btn btn-small btn-danger" type="submit" | |
80 | onclick="return confirm('${_('Confirm to delete this repository: %s') % c.repo_name}');"> |
|
79 | onclick="return confirm('${_('Confirm to delete this repository: %s') % c.repo_name}');"> | |
81 | <i class="icon-minus-circled"></i> |
|
80 | <i class="icon-minus-circled"></i> | |
82 | ${_('Delete this Repository')} |
|
81 | ${_('Delete this Repository')} | |
83 | </button> |
|
82 | </button> | |
84 | %if c.repo_info.forks.count(): |
|
83 | %if c.repo_info.forks.count(): | |
85 | ${ungettext('This repository has %s fork', 'This repository has %s forks', c.repo_info.forks.count()) % c.repo_info.forks.count()} |
|
84 | ${ungettext('This repository has %s fork', 'This repository has %s forks', c.repo_info.forks.count()) % c.repo_info.forks.count()} | |
86 | <input type="radio" name="forks" value="detach_forks" checked="checked"/> <label for="forks">${_('Detach forks')}</label> |
|
85 | <input type="radio" name="forks" value="detach_forks" checked="checked"/> <label for="forks">${_('Detach forks')}</label> | |
87 | <input type="radio" name="forks" value="delete_forks" /> <label for="forks">${_('Delete forks')}</label> |
|
86 | <input type="radio" name="forks" value="delete_forks" /> <label for="forks">${_('Delete forks')}</label> | |
88 | %endif |
|
87 | %endif | |
89 | <div class="field" style="border:none;color:#888"> |
|
88 | <div class="field" style="border:none;color:#888"> | |
90 | <ul> |
|
89 | <ul> | |
91 | <li>${_('The deleted repository will be moved away and hidden until the administrator expires it. The administrator can both permanently delete it or restore it.')}</li> |
|
90 | <li>${_('The deleted repository will be moved away and hidden until the administrator expires it. The administrator can both permanently delete it or restore it.')}</li> | |
92 | </ul> |
|
91 | </ul> | |
93 | </div> |
|
92 | </div> | |
94 | </div> |
|
93 | </div> | |
95 | ${h.end_form()} |
|
94 | ${h.end_form()} |
@@ -1,596 +1,596 b'' | |||||
1 | ## -*- coding: utf-8 -*- |
|
1 | ## -*- coding: utf-8 -*- | |
2 | <%inherit file="root.html"/> |
|
2 | <%inherit file="root.html"/> | |
3 |
|
3 | |||
4 | <!-- CONTENT --> |
|
4 | <!-- CONTENT --> | |
5 | <div id="content"> |
|
5 | <div id="content"> | |
6 | ${self.flash_msg()} |
|
6 | ${self.flash_msg()} | |
7 | <div id="main"> |
|
7 | <div id="main"> | |
8 | ${next.main()} |
|
8 | ${next.main()} | |
9 | </div> |
|
9 | </div> | |
10 | </div> |
|
10 | </div> | |
11 | <!-- END CONTENT --> |
|
11 | <!-- END CONTENT --> | |
12 |
|
12 | |||
13 | <!-- FOOTER --> |
|
13 | <!-- FOOTER --> | |
14 | <div id="footer"> |
|
14 | <div id="footer"> | |
15 | <div id="footer-inner" class="title"> |
|
15 | <div id="footer-inner" class="title"> | |
16 | <div> |
|
16 | <div> | |
17 | <p class="footer-link"> |
|
17 | <p class="footer-link"> | |
18 | ${_('Server instance: %s') % c.instance_id if c.instance_id else ''} |
|
18 | ${_('Server instance: %s') % c.instance_id if c.instance_id else ''} | |
19 | </p> |
|
19 | </p> | |
20 | <p class="footer-link-right"> |
|
20 | <p class="footer-link-right"> | |
21 | This site is powered by |
|
21 | This site is powered by | |
22 | %if c.visual.show_version: |
|
22 | %if c.visual.show_version: | |
23 | <a href="${h.url('kallithea_project_url')}" target="_blank">Kallithea</a> ${c.kallithea_version}, |
|
23 | <a href="${h.url('kallithea_project_url')}" target="_blank">Kallithea</a> ${c.kallithea_version}, | |
24 | %else: |
|
24 | %else: | |
25 | <a href="${h.url('kallithea_project_url')}" target="_blank">Kallithea</a>, |
|
25 | <a href="${h.url('kallithea_project_url')}" target="_blank">Kallithea</a>, | |
26 | %endif |
|
26 | %endif | |
27 | which is |
|
27 | which is | |
28 | <a href="${h.canonical_url('about')}#copyright">© 2010–2015 by various authors & licensed under GPLv3</a>. |
|
28 | <a href="${h.canonical_url('about')}#copyright">© 2010–2015 by various authors & licensed under GPLv3</a>. | |
29 | %if c.issues_url: |
|
29 | %if c.issues_url: | |
30 | – <a href="${c.issues_url}" target="_blank">${_('Support')}</a> |
|
30 | – <a href="${c.issues_url}" target="_blank">${_('Support')}</a> | |
31 | %endif |
|
31 | %endif | |
32 | </p> |
|
32 | </p> | |
33 | </div> |
|
33 | </div> | |
34 | </div> |
|
34 | </div> | |
35 | </div> |
|
35 | </div> | |
36 |
|
36 | |||
37 | <!-- END FOOTER --> |
|
37 | <!-- END FOOTER --> | |
38 |
|
38 | |||
39 | ### MAKO DEFS ### |
|
39 | ### MAKO DEFS ### | |
40 |
|
40 | |||
41 | <%block name="branding_title"> |
|
41 | <%block name="branding_title"> | |
42 | %if c.site_name: |
|
42 | %if c.site_name: | |
43 | · ${c.site_name} |
|
43 | · ${c.site_name} | |
44 | %endif |
|
44 | %endif | |
45 | </%block> |
|
45 | </%block> | |
46 |
|
46 | |||
47 | <%def name="flash_msg()"> |
|
47 | <%def name="flash_msg()"> | |
48 | <%include file="/base/flash_msg.html"/> |
|
48 | <%include file="/base/flash_msg.html"/> | |
49 | </%def> |
|
49 | </%def> | |
50 |
|
50 | |||
51 | <%def name="breadcrumbs()"> |
|
51 | <%def name="breadcrumbs()"> | |
52 | <div class="breadcrumbs"> |
|
52 | <div class="breadcrumbs"> | |
53 | ${self.breadcrumbs_links()} |
|
53 | ${self.breadcrumbs_links()} | |
54 | </div> |
|
54 | </div> | |
55 | </%def> |
|
55 | </%def> | |
56 |
|
56 | |||
57 | <%def name="admin_menu()"> |
|
57 | <%def name="admin_menu()"> | |
58 | <ul class="admin_menu"> |
|
58 | <ul class="admin_menu"> | |
59 | <li><a href="${h.url('admin_home')}"><i class="icon-book"></i> ${_('Admin Journal')}</a></li> |
|
59 | <li><a href="${h.url('admin_home')}"><i class="icon-book"></i> ${_('Admin Journal')}</a></li> | |
60 | <li><a href="${h.url('repos')}"><i class="icon-database"></i> ${_('Repositories')}</a></li> |
|
60 | <li><a href="${h.url('repos')}"><i class="icon-database"></i> ${_('Repositories')}</a></li> | |
61 | <li><a href="${h.url('repos_groups')}"><i class="icon-folder"></i> ${_('Repository Groups')}</a></li> |
|
61 | <li><a href="${h.url('repos_groups')}"><i class="icon-folder"></i> ${_('Repository Groups')}</a></li> | |
62 | <li><a href="${h.url('users')}"><i class="icon-user"></i> ${_('Users')}</a></li> |
|
62 | <li><a href="${h.url('users')}"><i class="icon-user"></i> ${_('Users')}</a></li> | |
63 | <li><a href="${h.url('users_groups')}"><i class="icon-users"></i> ${_('User Groups')}</a></li> |
|
63 | <li><a href="${h.url('users_groups')}"><i class="icon-users"></i> ${_('User Groups')}</a></li> | |
64 | <li><a href="${h.url('admin_permissions')}"><i class="icon-block"></i> ${_('Permissions')}</a></li> |
|
64 | <li><a href="${h.url('admin_permissions')}"><i class="icon-block"></i> ${_('Permissions')}</a></li> | |
65 | <li><a href="${h.url('auth_home')}"><i class="icon-key"></i> ${_('Authentication')}</a></li> |
|
65 | <li><a href="${h.url('auth_home')}"><i class="icon-key"></i> ${_('Authentication')}</a></li> | |
66 | <li><a href="${h.url('defaults')}"><i class="icon-wrench"></i> ${_('Defaults')}</a></li> |
|
66 | <li><a href="${h.url('defaults')}"><i class="icon-wrench"></i> ${_('Defaults')}</a></li> | |
67 | <li class="last"><a href="${h.url('admin_settings')}"><i class="icon-gear"></i> ${_('Settings')}</a></li> |
|
67 | <li class="last"><a href="${h.url('admin_settings')}"><i class="icon-gear"></i> ${_('Settings')}</a></li> | |
68 | </ul> |
|
68 | </ul> | |
69 |
|
69 | |||
70 | </%def> |
|
70 | </%def> | |
71 |
|
71 | |||
72 |
|
72 | |||
73 | ## admin menu used for people that have some admin resources |
|
73 | ## admin menu used for people that have some admin resources | |
74 | <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)"> |
|
74 | <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)"> | |
75 | <ul> |
|
75 | <ul> | |
76 | %if repositories: |
|
76 | %if repositories: | |
77 | <li><a href="${h.url('repos')}"><i class="icon-database"></i> ${_('Repositories')}</a></li> |
|
77 | <li><a href="${h.url('repos')}"><i class="icon-database"></i> ${_('Repositories')}</a></li> | |
78 | %endif |
|
78 | %endif | |
79 | %if repository_groups: |
|
79 | %if repository_groups: | |
80 | <li><a href="${h.url('repos_groups')}"><i class="icon-folder"></i> ${_('Repository Groups')}</a></li> |
|
80 | <li><a href="${h.url('repos_groups')}"><i class="icon-folder"></i> ${_('Repository Groups')}</a></li> | |
81 | %endif |
|
81 | %endif | |
82 | %if user_groups: |
|
82 | %if user_groups: | |
83 | <li><a href="${h.url('users_groups')}"><i class="icon-users"></i> ${_('User Groups')}</a></li> |
|
83 | <li><a href="${h.url('users_groups')}"><i class="icon-users"></i> ${_('User Groups')}</a></li> | |
84 | %endif |
|
84 | %endif | |
85 | </ul> |
|
85 | </ul> | |
86 | </%def> |
|
86 | </%def> | |
87 |
|
87 | |||
88 | <%def name="repotag(repo)"> |
|
88 | <%def name="repotag(repo)"> | |
89 | %if h.is_hg(repo): |
|
89 | %if h.is_hg(repo): | |
90 | <span class="repotag" title="${_('Mercurial repository')}">hg</span> |
|
90 | <span class="repotag" title="${_('Mercurial repository')}">hg</span> | |
91 | %endif |
|
91 | %endif | |
92 | %if h.is_git(repo): |
|
92 | %if h.is_git(repo): | |
93 | <span class="repotag" title="${_('Git repository')}">git</span> |
|
93 | <span class="repotag" title="${_('Git repository')}">git</span> | |
94 | %endif |
|
94 | %endif | |
95 | </%def> |
|
95 | </%def> | |
96 |
|
96 | |||
97 | <%def name="repo_context_bar(current=None, rev=None)"> |
|
97 | <%def name="repo_context_bar(current=None, rev=None)"> | |
98 | <% rev = None if rev == 'tip' else rev %> |
|
98 | <% rev = None if rev == 'tip' else rev %> | |
99 | <% |
|
99 | <% | |
100 | def follow_class(): |
|
100 | def follow_class(): | |
101 | if c.repository_following: |
|
101 | if c.repository_following: | |
102 | return h.literal('following') |
|
102 | return h.literal('following') | |
103 | else: |
|
103 | else: | |
104 | return h.literal('follow') |
|
104 | return h.literal('follow') | |
105 | %> |
|
105 | %> | |
106 | <% |
|
106 | <% | |
107 | def is_current(selected): |
|
107 | def is_current(selected): | |
108 | if selected == current: |
|
108 | if selected == current: | |
109 | return h.literal('class="current"') |
|
109 | return h.literal('class="current"') | |
110 | %> |
|
110 | %> | |
111 |
|
111 | |||
112 | <!--- CONTEXT BAR --> |
|
112 | <!--- CONTEXT BAR --> | |
113 | <div id="context-bar" class="box"> |
|
113 | <div id="context-bar" class="box"> | |
114 | <h2> |
|
114 | <h2> | |
115 | ${repotag(c.db_repo)} |
|
115 | ${repotag(c.db_repo)} | |
116 |
|
116 | |||
117 | ## public/private |
|
117 | ## public/private | |
118 | %if c.db_repo.private: |
|
118 | %if c.db_repo.private: | |
119 | <i class="icon-keyhole-circled"></i> |
|
119 | <i class="icon-keyhole-circled"></i> | |
120 | %else: |
|
120 | %else: | |
121 | <i class="icon-globe"></i> |
|
121 | <i class="icon-globe"></i> | |
122 | %endif |
|
122 | %endif | |
123 | ${h.repo_link(c.db_repo.groups_and_repo)} |
|
123 | ${h.repo_link(c.db_repo.groups_and_repo)} | |
124 |
|
124 | |||
125 | %if current == 'createfork': |
|
125 | %if current == 'createfork': | |
126 | - ${_('Create Fork')} |
|
126 | - ${_('Create Fork')} | |
127 | %endif |
|
127 | %endif | |
128 | </h2> |
|
128 | </h2> | |
129 | <!-- |
|
129 | <!-- | |
130 | <div id="breadcrumbs"> |
|
130 | <div id="breadcrumbs"> | |
131 | ${h.link_to(_(u'Repositories'),h.url('home'))} |
|
131 | ${h.link_to(_(u'Repositories'),h.url('home'))} | |
132 | » |
|
132 | » | |
133 | ${h.repo_link(c.db_repo.groups_and_repo)} |
|
133 | ${h.repo_link(c.db_repo.groups_and_repo)} | |
134 | </div> |
|
134 | </div> | |
135 | --> |
|
135 | --> | |
136 | <ul id="context-pages" class="horizontal-list"> |
|
136 | <ul id="context-pages" class="horizontal-list"> | |
137 | <li ${is_current('summary')}><a href="${h.url('summary_home', repo_name=c.repo_name)}"><i class="icon-doc-text"></i> ${_('Summary')}</a></li> |
|
137 | <li ${is_current('summary')}><a href="${h.url('summary_home', repo_name=c.repo_name)}"><i class="icon-doc-text"></i> ${_('Summary')}</a></li> | |
138 | %if rev: |
|
138 | %if rev: | |
139 | <li ${is_current('changelog')}><a href="${h.url('changelog_file_home', repo_name=c.repo_name, revision=rev, f_path='')}"><i class="icon-clock"></i> ${_('Changelog')}</a></li> |
|
139 | <li ${is_current('changelog')}><a href="${h.url('changelog_file_home', repo_name=c.repo_name, revision=rev, f_path='')}"><i class="icon-clock"></i> ${_('Changelog')}</a></li> | |
140 | %else: |
|
140 | %else: | |
141 | <li ${is_current('changelog')}><a href="${h.url('changelog_home', repo_name=c.repo_name)}"><i class="icon-clock"></i> ${_('Changelog')}</a></li> |
|
141 | <li ${is_current('changelog')}><a href="${h.url('changelog_home', repo_name=c.repo_name)}"><i class="icon-clock"></i> ${_('Changelog')}</a></li> | |
142 | %endif |
|
142 | %endif | |
143 | <li ${is_current('files')}><a href="${h.url('files_home', repo_name=c.repo_name, revision=rev or 'tip')}"><i class="icon-doc-inv"></i> ${_('Files')}</a></li> |
|
143 | <li ${is_current('files')}><a href="${h.url('files_home', repo_name=c.repo_name, revision=rev or 'tip')}"><i class="icon-doc-inv"></i> ${_('Files')}</a></li> | |
144 | <li ${is_current('switch-to')}> |
|
144 | <li ${is_current('switch-to')}> | |
145 | <a href="#" id="branch_tag_switcher_2" class="dropdown"><i class="icon-exchange"></i> ${_('Switch To')}</a> |
|
145 | <a href="#" id="branch_tag_switcher_2" class="dropdown"><i class="icon-exchange"></i> ${_('Switch To')}</a> | |
146 | <ul id="switch_to_list_2" class="switch_to submenu"> |
|
146 | <ul id="switch_to_list_2" class="switch_to submenu"> | |
147 | <li><a href="#">${_('Loading...')}</a></li> |
|
147 | <li><a href="#">${_('Loading...')}</a></li> | |
148 | </ul> |
|
148 | </ul> | |
149 | </li> |
|
149 | </li> | |
150 | <li ${is_current('options')}> |
|
150 | <li ${is_current('options')}> | |
151 | %if h.HasRepoPermissionAll('repository.admin')(c.repo_name): |
|
151 | %if h.HasRepoPermissionAll('repository.admin')(c.repo_name): | |
152 | <a href="${h.url('edit_repo',repo_name=c.repo_name)}" class="dropdown"><i class="icon-wrench"></i> ${_('Options')}</a> |
|
152 | <a href="${h.url('edit_repo',repo_name=c.repo_name)}" class="dropdown"><i class="icon-wrench"></i> ${_('Options')}</a> | |
153 | %else: |
|
153 | %else: | |
154 | <a href="#" class="dropdown"><i class="icon-wrench"></i> ${_('Options')}</a> |
|
154 | <a href="#" class="dropdown"><i class="icon-wrench"></i> ${_('Options')}</a> | |
155 | %endif |
|
155 | %endif | |
156 | <ul> |
|
156 | <ul> | |
157 | %if h.HasRepoPermissionAll('repository.admin')(c.repo_name): |
|
157 | %if h.HasRepoPermissionAll('repository.admin')(c.repo_name): | |
158 | <li><a href="${h.url('edit_repo',repo_name=c.repo_name)}"><i class="icon-gear"></i> ${_('Settings')}</a></li> |
|
158 | <li><a href="${h.url('edit_repo',repo_name=c.repo_name)}"><i class="icon-gear"></i> ${_('Settings')}</a></li> | |
159 | %endif |
|
159 | %endif | |
160 | %if c.db_repo.fork: |
|
160 | %if c.db_repo.fork: | |
161 | <li><a href="${h.url('compare_url',repo_name=c.db_repo.fork.repo_name,org_ref_type=c.db_repo.landing_rev[0],org_ref_name=c.db_repo.landing_rev[1], other_repo=c.repo_name,other_ref_type='branch' if request.GET.get('branch') else c.db_repo.landing_rev[0],other_ref_name=request.GET.get('branch') or c.db_repo.landing_rev[1], merge=1)}"> |
|
161 | <li><a href="${h.url('compare_url',repo_name=c.db_repo.fork.repo_name,org_ref_type=c.db_repo.landing_rev[0],org_ref_name=c.db_repo.landing_rev[1], other_repo=c.repo_name,other_ref_type='branch' if request.GET.get('branch') else c.db_repo.landing_rev[0],other_ref_name=request.GET.get('branch') or c.db_repo.landing_rev[1], merge=1)}"> | |
162 | <i class="icon-git-compare"></i> ${_('Compare Fork')}</a></li> |
|
162 | <i class="icon-git-compare"></i> ${_('Compare Fork')}</a></li> | |
163 | %endif |
|
163 | %endif | |
164 | <li><a href="${h.url('compare_home',repo_name=c.repo_name)}"><i class="icon-git-compare"></i> ${_('Compare')}</a></li> |
|
164 | <li><a href="${h.url('compare_home',repo_name=c.repo_name)}"><i class="icon-git-compare"></i> ${_('Compare')}</a></li> | |
165 |
|
165 | |||
166 | <li><a href="${h.url('search_repo',repo_name=c.repo_name)}"><i class="icon-search"></i> ${_('Search')}</a></li> |
|
166 | <li><a href="${h.url('search_repo',repo_name=c.repo_name)}"><i class="icon-search"></i> ${_('Search')}</a></li> | |
167 |
|
167 | |||
168 | %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.db_repo.enable_locking: |
|
168 | %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.db_repo.enable_locking: | |
169 | %if c.db_repo.locked[0]: |
|
169 | %if c.db_repo.locked[0]: | |
170 | <li><a href="${h.url('toggle_locking', repo_name=c.repo_name)}"><i class="icon-lock"></i> ${_('Unlock')}</a></li> |
|
170 | <li><a href="${h.url('toggle_locking', repo_name=c.repo_name)}"><i class="icon-lock"></i> ${_('Unlock')}</a></li> | |
171 | %else: |
|
171 | %else: | |
172 | <li><a href="${h.url('toggle_locking', repo_name=c.repo_name)}"><i class="icon-lock-open-alt"></i> ${_('Lock')}</li> |
|
172 | <li><a href="${h.url('toggle_locking', repo_name=c.repo_name)}"><i class="icon-lock-open-alt"></i> ${_('Lock')}</li> | |
173 | %endif |
|
173 | %endif | |
174 | %endif |
|
174 | %endif | |
175 | ## TODO: this check feels wrong, it would be better to have a check for permissions |
|
175 | ## TODO: this check feels wrong, it would be better to have a check for permissions | |
176 | ## also it feels like a job for the controller |
|
176 | ## also it feels like a job for the controller | |
177 | %if c.authuser.username != 'default': |
|
177 | %if c.authuser.username != 'default': | |
178 | <li> |
|
178 | <li> | |
179 |
<a class="${follow_class()}" onclick="javascript:toggleFollowingRepo(this,${c.db_repo.repo_id} |
|
179 | <a class="${follow_class()}" onclick="javascript:toggleFollowingRepo(this,${c.db_repo.repo_id});"> | |
180 | <span class="show-follow"><i class="icon-heart-empty"></i> ${_('Follow')}</span> |
|
180 | <span class="show-follow"><i class="icon-heart-empty"></i> ${_('Follow')}</span> | |
181 | <span class="show-following"><i class="icon-heart"></i> ${_('Unfollow')}</span> |
|
181 | <span class="show-following"><i class="icon-heart"></i> ${_('Unfollow')}</span> | |
182 | </a> |
|
182 | </a> | |
183 | </li> |
|
183 | </li> | |
184 | <li><a href="${h.url('repo_fork_home',repo_name=c.repo_name)}"><i class="icon-git-pull-request"></i> ${_('Fork')}</a></li> |
|
184 | <li><a href="${h.url('repo_fork_home',repo_name=c.repo_name)}"><i class="icon-git-pull-request"></i> ${_('Fork')}</a></li> | |
185 | <li><a href="${h.url('pullrequest_home',repo_name=c.repo_name)}"><i class="icon-git-pull-request"></i> ${_('Create Pull Request')}</a></li> |
|
185 | <li><a href="${h.url('pullrequest_home',repo_name=c.repo_name)}"><i class="icon-git-pull-request"></i> ${_('Create Pull Request')}</a></li> | |
186 | %endif |
|
186 | %endif | |
187 | </ul> |
|
187 | </ul> | |
188 | </li> |
|
188 | </li> | |
189 | <li ${is_current('showpullrequest')}> |
|
189 | <li ${is_current('showpullrequest')}> | |
190 | <a href="${h.url('pullrequest_show_all',repo_name=c.repo_name)}" title="${_('Show Pull Requests for %s') % c.repo_name}"> <i class="icon-git-pull-request"></i> ${_('Pull Requests')} |
|
190 | <a href="${h.url('pullrequest_show_all',repo_name=c.repo_name)}" title="${_('Show Pull Requests for %s') % c.repo_name}"> <i class="icon-git-pull-request"></i> ${_('Pull Requests')} | |
191 | %if c.repository_pull_requests: |
|
191 | %if c.repository_pull_requests: | |
192 | <span>${c.repository_pull_requests}</span> |
|
192 | <span>${c.repository_pull_requests}</span> | |
193 | %endif |
|
193 | %endif | |
194 | </a> |
|
194 | </a> | |
195 | </li> |
|
195 | </li> | |
196 | </ul> |
|
196 | </ul> | |
197 | </div> |
|
197 | </div> | |
198 | <script type="text/javascript"> |
|
198 | <script type="text/javascript"> | |
199 | YUE.on('branch_tag_switcher_2','mouseover',function(){ |
|
199 | YUE.on('branch_tag_switcher_2','mouseover',function(){ | |
200 | var $branch_tag_switcher_2 = $('#branch_tag_switcher_2'); |
|
200 | var $branch_tag_switcher_2 = $('#branch_tag_switcher_2'); | |
201 | var loaded = $branch_tag_switcher_2.hasClass('loaded'); |
|
201 | var loaded = $branch_tag_switcher_2.hasClass('loaded'); | |
202 | if(!loaded){ |
|
202 | if(!loaded){ | |
203 | $branch_tag_switcher_2.addClass('loaded'); |
|
203 | $branch_tag_switcher_2.addClass('loaded'); | |
204 | asynchtml("${h.url('branch_tag_switcher',repo_name=c.repo_name)}", $('#switch_to_list_2')); |
|
204 | asynchtml("${h.url('branch_tag_switcher',repo_name=c.repo_name)}", $('#switch_to_list_2')); | |
205 | } |
|
205 | } | |
206 | return false; |
|
206 | return false; | |
207 | }); |
|
207 | }); | |
208 | </script> |
|
208 | </script> | |
209 | <!--- END CONTEXT BAR --> |
|
209 | <!--- END CONTEXT BAR --> | |
210 | </%def> |
|
210 | </%def> | |
211 |
|
211 | |||
212 | <%def name="menu(current=None)"> |
|
212 | <%def name="menu(current=None)"> | |
213 | <% |
|
213 | <% | |
214 | def is_current(selected): |
|
214 | def is_current(selected): | |
215 | if selected == current: |
|
215 | if selected == current: | |
216 | return h.literal('class="current"') |
|
216 | return h.literal('class="current"') | |
217 | %> |
|
217 | %> | |
218 |
|
218 | |||
219 | <ul id="quick" class="horizontal-list"> |
|
219 | <ul id="quick" class="horizontal-list"> | |
220 | <!-- repo switcher --> |
|
220 | <!-- repo switcher --> | |
221 | <li ${is_current('repositories')}> |
|
221 | <li ${is_current('repositories')}> | |
222 | <input id="repo_switcher" name="repo_switcher" type="hidden"> |
|
222 | <input id="repo_switcher" name="repo_switcher" type="hidden"> | |
223 | </li> |
|
223 | </li> | |
224 |
|
224 | |||
225 | ##ROOT MENU |
|
225 | ##ROOT MENU | |
226 | %if c.authuser.username != 'default': |
|
226 | %if c.authuser.username != 'default': | |
227 | <li ${is_current('journal')}> |
|
227 | <li ${is_current('journal')}> | |
228 | <a class="menu_link" title="${_('Show recent activity')}" href="${h.url('journal')}"> |
|
228 | <a class="menu_link" title="${_('Show recent activity')}" href="${h.url('journal')}"> | |
229 | <i class="icon-book"></i> ${_('Journal')} |
|
229 | <i class="icon-book"></i> ${_('Journal')} | |
230 | </a> |
|
230 | </a> | |
231 | </li> |
|
231 | </li> | |
232 | %else: |
|
232 | %else: | |
233 | <li ${is_current('journal')}> |
|
233 | <li ${is_current('journal')}> | |
234 | <a class="menu_link" title="${_('Public journal')}" href="${h.url('public_journal')}"> |
|
234 | <a class="menu_link" title="${_('Public journal')}" href="${h.url('public_journal')}"> | |
235 | <i class="icon-book"></i> ${_('Public journal')} |
|
235 | <i class="icon-book"></i> ${_('Public journal')} | |
236 | </a> |
|
236 | </a> | |
237 | </li> |
|
237 | </li> | |
238 | %endif |
|
238 | %endif | |
239 | <li ${is_current('gists')}> |
|
239 | <li ${is_current('gists')}> | |
240 | <a class="menu_link childs" title="${_('Show public gists')}" href="${h.url('gists')}"> |
|
240 | <a class="menu_link childs" title="${_('Show public gists')}" href="${h.url('gists')}"> | |
241 | <i class="icon-clippy"></i> ${_('Gists')} |
|
241 | <i class="icon-clippy"></i> ${_('Gists')} | |
242 | </a> |
|
242 | </a> | |
243 | <ul class="admin_menu"> |
|
243 | <ul class="admin_menu"> | |
244 | <li><a href="${h.url('new_gist', public=1)}"><i class="icon-paste"></i> ${_('Create New Gist')}</a></li> |
|
244 | <li><a href="${h.url('new_gist', public=1)}"><i class="icon-paste"></i> ${_('Create New Gist')}</a></li> | |
245 | <li><a href="${h.url('gists')}"><i class="icon-globe"></i> ${_('All Public Gists')}</a></li> |
|
245 | <li><a href="${h.url('gists')}"><i class="icon-globe"></i> ${_('All Public Gists')}</a></li> | |
246 | %if c.authuser.username != 'default': |
|
246 | %if c.authuser.username != 'default': | |
247 | <li><a href="${h.url('gists', public=1)}"><i class="icon-user"></i> ${_('My Public Gists')}</a></li> |
|
247 | <li><a href="${h.url('gists', public=1)}"><i class="icon-user"></i> ${_('My Public Gists')}</a></li> | |
248 | <li><a href="${h.url('gists', private=1)}"><i class="icon-keyhole-circled"></i> ${_('My Private Gists')}</a></li> |
|
248 | <li><a href="${h.url('gists', private=1)}"><i class="icon-keyhole-circled"></i> ${_('My Private Gists')}</a></li> | |
249 | %endif |
|
249 | %endif | |
250 | </ul> |
|
250 | </ul> | |
251 | </li> |
|
251 | </li> | |
252 | <li ${is_current('search')}> |
|
252 | <li ${is_current('search')}> | |
253 | <a class="menu_link" title="${_('Search in repositories')}" href="${h.url('search')}"> |
|
253 | <a class="menu_link" title="${_('Search in repositories')}" href="${h.url('search')}"> | |
254 | <i class="icon-search"></i> ${_('Search')} |
|
254 | <i class="icon-search"></i> ${_('Search')} | |
255 | </a> |
|
255 | </a> | |
256 | </li> |
|
256 | </li> | |
257 | % if h.HasPermissionAll('hg.admin')('access admin main page'): |
|
257 | % if h.HasPermissionAll('hg.admin')('access admin main page'): | |
258 | <li ${is_current('admin')}> |
|
258 | <li ${is_current('admin')}> | |
259 | <a class="menu_link childs" title="${_('Admin')}" href="${h.url('admin_home')}"> |
|
259 | <a class="menu_link childs" title="${_('Admin')}" href="${h.url('admin_home')}"> | |
260 | <i class="icon-gear"></i> ${_('Admin')} |
|
260 | <i class="icon-gear"></i> ${_('Admin')} | |
261 | </a> |
|
261 | </a> | |
262 | ${admin_menu()} |
|
262 | ${admin_menu()} | |
263 | </li> |
|
263 | </li> | |
264 | % elif c.authuser.repositories_admin or c.authuser.repository_groups_admin or c.authuser.user_groups_admin: |
|
264 | % elif c.authuser.repositories_admin or c.authuser.repository_groups_admin or c.authuser.user_groups_admin: | |
265 | <li ${is_current('admin')}> |
|
265 | <li ${is_current('admin')}> | |
266 | <a class="menu_link childs" title="${_('Admin')}"> |
|
266 | <a class="menu_link childs" title="${_('Admin')}"> | |
267 | <i class="icon-gear"></i> ${_('Admin')} |
|
267 | <i class="icon-gear"></i> ${_('Admin')} | |
268 | </a> |
|
268 | </a> | |
269 | ${admin_menu_simple(c.authuser.repositories_admin, |
|
269 | ${admin_menu_simple(c.authuser.repositories_admin, | |
270 | c.authuser.repository_groups_admin, |
|
270 | c.authuser.repository_groups_admin, | |
271 | c.authuser.user_groups_admin or h.HasPermissionAny('hg.usergroup.create.true')())} |
|
271 | c.authuser.user_groups_admin or h.HasPermissionAny('hg.usergroup.create.true')())} | |
272 | </li> |
|
272 | </li> | |
273 | % endif |
|
273 | % endif | |
274 |
|
274 | |||
275 | <li ${is_current('my_pullrequests')}> |
|
275 | <li ${is_current('my_pullrequests')}> | |
276 | <a class="menu_link" title="${_('My Pull Requests')}" href="${h.url('my_pullrequests')}"> |
|
276 | <a class="menu_link" title="${_('My Pull Requests')}" href="${h.url('my_pullrequests')}"> | |
277 | <i class="icon-git-pull-request"></i> ${_('My Pull Requests')} |
|
277 | <i class="icon-git-pull-request"></i> ${_('My Pull Requests')} | |
278 | %if c.my_pr_count != 0: |
|
278 | %if c.my_pr_count != 0: | |
279 | <span class="menu_link_notifications">${c.my_pr_count}</span> |
|
279 | <span class="menu_link_notifications">${c.my_pr_count}</span> | |
280 | %endif |
|
280 | %endif | |
281 | </a> |
|
281 | </a> | |
282 | </li> |
|
282 | </li> | |
283 |
|
283 | |||
284 | ## USER MENU |
|
284 | ## USER MENU | |
285 | <li> |
|
285 | <li> | |
286 | <a class="menu_link childs" id="quick_login_link"> |
|
286 | <a class="menu_link childs" id="quick_login_link"> | |
287 | <span class="icon"> |
|
287 | <span class="icon"> | |
288 | ${h.gravatar(c.authuser.email, size=20)} |
|
288 | ${h.gravatar(c.authuser.email, size=20)} | |
289 | </span> |
|
289 | </span> | |
290 | %if c.authuser.username != 'default': |
|
290 | %if c.authuser.username != 'default': | |
291 | <span class="menu_link_user">${c.authuser.username}</span> |
|
291 | <span class="menu_link_user">${c.authuser.username}</span> | |
292 | %if c.unread_notifications != 0: |
|
292 | %if c.unread_notifications != 0: | |
293 | <span class="menu_link_notifications">${c.unread_notifications}</span> |
|
293 | <span class="menu_link_notifications">${c.unread_notifications}</span> | |
294 | %endif |
|
294 | %endif | |
295 | %else: |
|
295 | %else: | |
296 | <span>${_('Not Logged In')}</span> |
|
296 | <span>${_('Not Logged In')}</span> | |
297 | %endif |
|
297 | %endif | |
298 | </a> |
|
298 | </a> | |
299 |
|
299 | |||
300 | <div class="user-menu"> |
|
300 | <div class="user-menu"> | |
301 | <div id="quick_login"> |
|
301 | <div id="quick_login"> | |
302 | %if c.authuser.username == 'default' or c.authuser.user_id is None: |
|
302 | %if c.authuser.username == 'default' or c.authuser.user_id is None: | |
303 | <h4>${_('Login to Your Account')}</h4> |
|
303 | <h4>${_('Login to Your Account')}</h4> | |
304 | ${h.form(h.url('login_home',came_from=h.url.current()))} |
|
304 | ${h.form(h.url('login_home',came_from=h.url.current()))} | |
305 | <div class="form"> |
|
305 | <div class="form"> | |
306 | <div class="fields"> |
|
306 | <div class="fields"> | |
307 | <div class="field"> |
|
307 | <div class="field"> | |
308 | <div class="label"> |
|
308 | <div class="label"> | |
309 | <label for="username">${_('Username')}:</label> |
|
309 | <label for="username">${_('Username')}:</label> | |
310 | </div> |
|
310 | </div> | |
311 | <div class="input"> |
|
311 | <div class="input"> | |
312 | ${h.text('username',class_='focus')} |
|
312 | ${h.text('username',class_='focus')} | |
313 | </div> |
|
313 | </div> | |
314 |
|
314 | |||
315 | </div> |
|
315 | </div> | |
316 | <div class="field"> |
|
316 | <div class="field"> | |
317 | <div class="label"> |
|
317 | <div class="label"> | |
318 | <label for="password">${_('Password')}:</label> |
|
318 | <label for="password">${_('Password')}:</label> | |
319 | </div> |
|
319 | </div> | |
320 | <div class="input"> |
|
320 | <div class="input"> | |
321 | ${h.password('password',class_='focus')} |
|
321 | ${h.password('password',class_='focus')} | |
322 | </div> |
|
322 | </div> | |
323 |
|
323 | |||
324 | </div> |
|
324 | </div> | |
325 | <div class="buttons"> |
|
325 | <div class="buttons"> | |
326 | <div class="password_forgoten">${h.link_to(_('Forgot password ?'),h.url('reset_password'))}</div> |
|
326 | <div class="password_forgoten">${h.link_to(_('Forgot password ?'),h.url('reset_password'))}</div> | |
327 | <div class="register"> |
|
327 | <div class="register"> | |
328 | %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')(): |
|
328 | %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')(): | |
329 | ${h.link_to(_("Don't have an account ?"),h.url('register'))} |
|
329 | ${h.link_to(_("Don't have an account ?"),h.url('register'))} | |
330 | %endif |
|
330 | %endif | |
331 | </div> |
|
331 | </div> | |
332 | <div class="submit"> |
|
332 | <div class="submit"> | |
333 | ${h.submit('sign_in',_('Log In'),class_="btn btn-mini")} |
|
333 | ${h.submit('sign_in',_('Log In'),class_="btn btn-mini")} | |
334 | </div> |
|
334 | </div> | |
335 | </div> |
|
335 | </div> | |
336 | </div> |
|
336 | </div> | |
337 | </div> |
|
337 | </div> | |
338 | ${h.end_form()} |
|
338 | ${h.end_form()} | |
339 | %else: |
|
339 | %else: | |
340 | <div class="links_left"> |
|
340 | <div class="links_left"> | |
341 | <div class="big_gravatar"> |
|
341 | <div class="big_gravatar"> | |
342 | ${h.gravatar(c.authuser.email, size=48)} |
|
342 | ${h.gravatar(c.authuser.email, size=48)} | |
343 | </div> |
|
343 | </div> | |
344 | <div class="full_name">${c.authuser.full_name_or_username}</div> |
|
344 | <div class="full_name">${c.authuser.full_name_or_username}</div> | |
345 | <div class="email">${c.authuser.email}</div> |
|
345 | <div class="email">${c.authuser.email}</div> | |
346 | </div> |
|
346 | </div> | |
347 | <div class="links_right"> |
|
347 | <div class="links_right"> | |
348 | <ol class="links"> |
|
348 | <ol class="links"> | |
349 | <li><a href="${h.url('notifications')}">${_('Notifications')}: ${c.unread_notifications}</a></li> |
|
349 | <li><a href="${h.url('notifications')}">${_('Notifications')}: ${c.unread_notifications}</a></li> | |
350 | <li>${h.link_to(_(u'My Account'),h.url('my_account'))}</li> |
|
350 | <li>${h.link_to(_(u'My Account'),h.url('my_account'))}</li> | |
351 | <li class="logout">${h.link_to(_(u'Log Out'),h.url('logout_home'))}</li> |
|
351 | <li class="logout">${h.link_to(_(u'Log Out'),h.url('logout_home'))}</li> | |
352 | </ol> |
|
352 | </ol> | |
353 | </div> |
|
353 | </div> | |
354 | %endif |
|
354 | %endif | |
355 | </div> |
|
355 | </div> | |
356 | </div> |
|
356 | </div> | |
357 | </li> |
|
357 | </li> | |
358 |
|
358 | |||
359 | <script type="text/javascript"> |
|
359 | <script type="text/javascript"> | |
360 | var visual_show_public_icon = "${c.visual.show_public_icon}" == "True"; |
|
360 | var visual_show_public_icon = "${c.visual.show_public_icon}" == "True"; | |
361 | var cache = {} |
|
361 | var cache = {} | |
362 | /*format the look of items in the list*/ |
|
362 | /*format the look of items in the list*/ | |
363 | var format = function(state){ |
|
363 | var format = function(state){ | |
364 | if (!state.id){ |
|
364 | if (!state.id){ | |
365 | return state.text; // optgroup |
|
365 | return state.text; // optgroup | |
366 | } |
|
366 | } | |
367 | var obj_dict = state.obj; |
|
367 | var obj_dict = state.obj; | |
368 | var tmpl = ''; |
|
368 | var tmpl = ''; | |
369 |
|
369 | |||
370 | if(obj_dict && state.type == 'repo'){ |
|
370 | if(obj_dict && state.type == 'repo'){ | |
371 | tmpl += '<span class="repo-icons">'; |
|
371 | tmpl += '<span class="repo-icons">'; | |
372 | if(obj_dict['repo_type'] === 'hg'){ |
|
372 | if(obj_dict['repo_type'] === 'hg'){ | |
373 | tmpl += '<span class="repotag">hg</span> '; |
|
373 | tmpl += '<span class="repotag">hg</span> '; | |
374 | } |
|
374 | } | |
375 | else if(obj_dict['repo_type'] === 'git'){ |
|
375 | else if(obj_dict['repo_type'] === 'git'){ | |
376 | tmpl += '<span class="repotag">git</span> '; |
|
376 | tmpl += '<span class="repotag">git</span> '; | |
377 | } |
|
377 | } | |
378 | if(obj_dict['private']){ |
|
378 | if(obj_dict['private']){ | |
379 | tmpl += '<i class="icon-keyhole-circled"></i> '; |
|
379 | tmpl += '<i class="icon-keyhole-circled"></i> '; | |
380 | } |
|
380 | } | |
381 | else if(visual_show_public_icon){ |
|
381 | else if(visual_show_public_icon){ | |
382 | tmpl += '<i class="icon-globe"></i> '; |
|
382 | tmpl += '<i class="icon-globe"></i> '; | |
383 | } |
|
383 | } | |
384 | tmpl += '</span>'; |
|
384 | tmpl += '</span>'; | |
385 | } |
|
385 | } | |
386 | if(obj_dict && state.type == 'group'){ |
|
386 | if(obj_dict && state.type == 'group'){ | |
387 | tmpl += '<i class="icon-folder"></i> '; |
|
387 | tmpl += '<i class="icon-folder"></i> '; | |
388 | } |
|
388 | } | |
389 | tmpl += state.text; |
|
389 | tmpl += state.text; | |
390 | return tmpl; |
|
390 | return tmpl; | |
391 | } |
|
391 | } | |
392 |
|
392 | |||
393 | $("#repo_switcher").select2({ |
|
393 | $("#repo_switcher").select2({ | |
394 | placeholder: '<i class="icon-database"></i> ${_('Repositories')}', |
|
394 | placeholder: '<i class="icon-database"></i> ${_('Repositories')}', | |
395 | dropdownAutoWidth: true, |
|
395 | dropdownAutoWidth: true, | |
396 | formatResult: format, |
|
396 | formatResult: format, | |
397 | formatSelection: format, |
|
397 | formatSelection: format, | |
398 | formatNoMatches: function(term){ |
|
398 | formatNoMatches: function(term){ | |
399 | return "${_('No matches found')}"; |
|
399 | return "${_('No matches found')}"; | |
400 | }, |
|
400 | }, | |
401 | containerCssClass: "repo-switcher", |
|
401 | containerCssClass: "repo-switcher", | |
402 | dropdownCssClass: "repo-switcher-dropdown", |
|
402 | dropdownCssClass: "repo-switcher-dropdown", | |
403 | escapeMarkup: function(m){ |
|
403 | escapeMarkup: function(m){ | |
404 | // don't escape our custom placeholder |
|
404 | // don't escape our custom placeholder | |
405 | if(m.substr(0,29) == '<i class="icon-database"></i>'){ |
|
405 | if(m.substr(0,29) == '<i class="icon-database"></i>'){ | |
406 | return m; |
|
406 | return m; | |
407 | } |
|
407 | } | |
408 |
|
408 | |||
409 | return Select2.util.escapeMarkup(m); |
|
409 | return Select2.util.escapeMarkup(m); | |
410 | }, |
|
410 | }, | |
411 | query: function(query){ |
|
411 | query: function(query){ | |
412 | var key = 'cache'; |
|
412 | var key = 'cache'; | |
413 | var cached = cache[key] ; |
|
413 | var cached = cache[key] ; | |
414 | if(cached) { |
|
414 | if(cached) { | |
415 | var data = {results: []}; |
|
415 | var data = {results: []}; | |
416 | //filter results |
|
416 | //filter results | |
417 | $.each(cached.results, function(){ |
|
417 | $.each(cached.results, function(){ | |
418 | var section = this.text; |
|
418 | var section = this.text; | |
419 | var children = []; |
|
419 | var children = []; | |
420 | $.each(this.children, function(){ |
|
420 | $.each(this.children, function(){ | |
421 | if(query.term.length == 0 || this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0 ){ |
|
421 | if(query.term.length == 0 || this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0 ){ | |
422 | children.push({'id': this.id, 'text': this.text, 'type': this.type, 'obj': this.obj}) |
|
422 | children.push({'id': this.id, 'text': this.text, 'type': this.type, 'obj': this.obj}) | |
423 | } |
|
423 | } | |
424 | }) |
|
424 | }) | |
425 | if(children.length !== 0){ |
|
425 | if(children.length !== 0){ | |
426 | data.results.push({'text': section, 'children': children}) |
|
426 | data.results.push({'text': section, 'children': children}) | |
427 | } |
|
427 | } | |
428 |
|
428 | |||
429 | }); |
|
429 | }); | |
430 | query.callback(data); |
|
430 | query.callback(data); | |
431 | }else{ |
|
431 | }else{ | |
432 | $.ajax({ |
|
432 | $.ajax({ | |
433 | url: "${h.url('repo_switcher_data')}", |
|
433 | url: "${h.url('repo_switcher_data')}", | |
434 | data: {}, |
|
434 | data: {}, | |
435 | dataType: 'json', |
|
435 | dataType: 'json', | |
436 | type: 'GET', |
|
436 | type: 'GET', | |
437 | success: function(data) { |
|
437 | success: function(data) { | |
438 | cache[key] = data; |
|
438 | cache[key] = data; | |
439 | query.callback({results: data.results}); |
|
439 | query.callback({results: data.results}); | |
440 | } |
|
440 | } | |
441 | }) |
|
441 | }) | |
442 | } |
|
442 | } | |
443 | } |
|
443 | } | |
444 | }); |
|
444 | }); | |
445 |
|
445 | |||
446 | $("#repo_switcher").on('select2-selecting', function(e){ |
|
446 | $("#repo_switcher").on('select2-selecting', function(e){ | |
447 | e.preventDefault(); |
|
447 | e.preventDefault(); | |
448 | window.location = pyroutes.url('summary_home', {'repo_name': e.val}) |
|
448 | window.location = pyroutes.url('summary_home', {'repo_name': e.val}) | |
449 | }) |
|
449 | }) | |
450 |
|
450 | |||
451 | ## Global mouse bindings ## |
|
451 | ## Global mouse bindings ## | |
452 |
|
452 | |||
453 | // general help "?" |
|
453 | // general help "?" | |
454 | Mousetrap.bind(['?'], function(e) { |
|
454 | Mousetrap.bind(['?'], function(e) { | |
455 | $('#help_kb').modal({}) |
|
455 | $('#help_kb').modal({}) | |
456 | }); |
|
456 | }); | |
457 |
|
457 | |||
458 | // / open the quick filter |
|
458 | // / open the quick filter | |
459 | Mousetrap.bind(['/'], function(e) { |
|
459 | Mousetrap.bind(['/'], function(e) { | |
460 | $("#repo_switcher").select2("open"); |
|
460 | $("#repo_switcher").select2("open"); | |
461 |
|
461 | |||
462 | // return false to prevent default browser behavior |
|
462 | // return false to prevent default browser behavior | |
463 | // and stop event from bubbling |
|
463 | // and stop event from bubbling | |
464 | return false; |
|
464 | return false; | |
465 | }); |
|
465 | }); | |
466 |
|
466 | |||
467 | // ctrl/command+b, show the the main bar |
|
467 | // ctrl/command+b, show the the main bar | |
468 | Mousetrap.bind(['command+b', 'ctrl+b'], function(e) { |
|
468 | Mousetrap.bind(['command+b', 'ctrl+b'], function(e) { | |
469 | if($('#header-inner').hasClass('hover') && $('#content').hasClass('hover')){ |
|
469 | if($('#header-inner').hasClass('hover') && $('#content').hasClass('hover')){ | |
470 | $('#header-inner').removeClass('hover'); |
|
470 | $('#header-inner').removeClass('hover'); | |
471 | $('#content').removeClass('hover'); |
|
471 | $('#content').removeClass('hover'); | |
472 | } |
|
472 | } | |
473 | else{ |
|
473 | else{ | |
474 | $('#header-inner').addClass('hover'); |
|
474 | $('#header-inner').addClass('hover'); | |
475 | $('#content').addClass('hover'); |
|
475 | $('#content').addClass('hover'); | |
476 | } |
|
476 | } | |
477 | return false; |
|
477 | return false; | |
478 | }); |
|
478 | }); | |
479 |
|
479 | |||
480 | // general nav g + action |
|
480 | // general nav g + action | |
481 | Mousetrap.bind(['g h'], function(e) { |
|
481 | Mousetrap.bind(['g h'], function(e) { | |
482 | window.location = pyroutes.url('home'); |
|
482 | window.location = pyroutes.url('home'); | |
483 | }); |
|
483 | }); | |
484 | Mousetrap.bind(['g g'], function(e) { |
|
484 | Mousetrap.bind(['g g'], function(e) { | |
485 | window.location = pyroutes.url('gists', {'private':1}); |
|
485 | window.location = pyroutes.url('gists', {'private':1}); | |
486 | }); |
|
486 | }); | |
487 | Mousetrap.bind(['g G'], function(e) { |
|
487 | Mousetrap.bind(['g G'], function(e) { | |
488 | window.location = pyroutes.url('gists', {'public':1}); |
|
488 | window.location = pyroutes.url('gists', {'public':1}); | |
489 | }); |
|
489 | }); | |
490 | Mousetrap.bind(['n g'], function(e) { |
|
490 | Mousetrap.bind(['n g'], function(e) { | |
491 | window.location = pyroutes.url('new_gist'); |
|
491 | window.location = pyroutes.url('new_gist'); | |
492 | }); |
|
492 | }); | |
493 | Mousetrap.bind(['n r'], function(e) { |
|
493 | Mousetrap.bind(['n r'], function(e) { | |
494 | window.location = pyroutes.url('new_repo'); |
|
494 | window.location = pyroutes.url('new_repo'); | |
495 | }); |
|
495 | }); | |
496 |
|
496 | |||
497 | % if hasattr(c, 'repo_name') and hasattr(c, 'db_repo'): |
|
497 | % if hasattr(c, 'repo_name') and hasattr(c, 'db_repo'): | |
498 | // nav in repo context |
|
498 | // nav in repo context | |
499 | Mousetrap.bind(['g s'], function(e) { |
|
499 | Mousetrap.bind(['g s'], function(e) { | |
500 | window.location = pyroutes.url('summary_home', {'repo_name': REPO_NAME}); |
|
500 | window.location = pyroutes.url('summary_home', {'repo_name': REPO_NAME}); | |
501 | }); |
|
501 | }); | |
502 | Mousetrap.bind(['g c'], function(e) { |
|
502 | Mousetrap.bind(['g c'], function(e) { | |
503 | window.location = pyroutes.url('changelog_home', {'repo_name': REPO_NAME}); |
|
503 | window.location = pyroutes.url('changelog_home', {'repo_name': REPO_NAME}); | |
504 | }); |
|
504 | }); | |
505 | Mousetrap.bind(['g F'], function(e) { |
|
505 | Mousetrap.bind(['g F'], function(e) { | |
506 | window.location = pyroutes.url('files_home', {'repo_name': REPO_NAME, 'revision': '${c.db_repo.landing_rev[1]}', 'f_path': '', 'search': '1'}); |
|
506 | window.location = pyroutes.url('files_home', {'repo_name': REPO_NAME, 'revision': '${c.db_repo.landing_rev[1]}', 'f_path': '', 'search': '1'}); | |
507 | }); |
|
507 | }); | |
508 | Mousetrap.bind(['g f'], function(e) { |
|
508 | Mousetrap.bind(['g f'], function(e) { | |
509 | window.location = pyroutes.url('files_home', {'repo_name': REPO_NAME, 'revision': '${c.db_repo.landing_rev[1]}', 'f_path': ''}); |
|
509 | window.location = pyroutes.url('files_home', {'repo_name': REPO_NAME, 'revision': '${c.db_repo.landing_rev[1]}', 'f_path': ''}); | |
510 | }); |
|
510 | }); | |
511 | Mousetrap.bind(['g o'], function(e) { |
|
511 | Mousetrap.bind(['g o'], function(e) { | |
512 | window.location = pyroutes.url('edit_repo', {'repo_name': REPO_NAME}); |
|
512 | window.location = pyroutes.url('edit_repo', {'repo_name': REPO_NAME}); | |
513 | }); |
|
513 | }); | |
514 | Mousetrap.bind(['g O'], function(e) { |
|
514 | Mousetrap.bind(['g O'], function(e) { | |
515 | window.location = pyroutes.url('edit_repo_perms', {'repo_name': REPO_NAME}); |
|
515 | window.location = pyroutes.url('edit_repo_perms', {'repo_name': REPO_NAME}); | |
516 | }); |
|
516 | }); | |
517 | % endif |
|
517 | % endif | |
518 |
|
518 | |||
519 | </script> |
|
519 | </script> | |
520 | </%def> |
|
520 | </%def> | |
521 |
|
521 | |||
522 | %if 0: |
|
522 | %if 0: | |
523 | <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> |
|
523 | <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> | |
524 | <div class="modal-dialog"> |
|
524 | <div class="modal-dialog"> | |
525 | <div class="modal-content"> |
|
525 | <div class="modal-content"> | |
526 | <div class="modal-header"> |
|
526 | <div class="modal-header"> | |
527 | <button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="icon-cancel-circled"></i></button> |
|
527 | <button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="icon-cancel-circled"></i></button> | |
528 | <h4 class="modal-title">${_('Keyboard shortcuts')}</h4> |
|
528 | <h4 class="modal-title">${_('Keyboard shortcuts')}</h4> | |
529 | </div> |
|
529 | </div> | |
530 | <div class="modal-body"> |
|
530 | <div class="modal-body"> | |
531 | <div class="row"> |
|
531 | <div class="row"> | |
532 | <div class="col-md-5"> |
|
532 | <div class="col-md-5"> | |
533 | <table class="keyboard-mappings"> |
|
533 | <table class="keyboard-mappings"> | |
534 | <tbody> |
|
534 | <tbody> | |
535 | <tr> |
|
535 | <tr> | |
536 | <th></th> |
|
536 | <th></th> | |
537 | <th>${_('Site-wide shortcuts')}</th> |
|
537 | <th>${_('Site-wide shortcuts')}</th> | |
538 | </tr> |
|
538 | </tr> | |
539 | <% |
|
539 | <% | |
540 | elems = [ |
|
540 | elems = [ | |
541 | ('/', 'Open quick search box'), |
|
541 | ('/', 'Open quick search box'), | |
542 | ('ctrl/cmd+b', 'Show main settings bar'), |
|
542 | ('ctrl/cmd+b', 'Show main settings bar'), | |
543 | ('g h', 'Goto home page'), |
|
543 | ('g h', 'Goto home page'), | |
544 | ('g g', 'Goto my private gists page'), |
|
544 | ('g g', 'Goto my private gists page'), | |
545 | ('g G', 'Goto my public gists page'), |
|
545 | ('g G', 'Goto my public gists page'), | |
546 | ('n r', 'New repository page'), |
|
546 | ('n r', 'New repository page'), | |
547 | ('n g', 'New gist page') |
|
547 | ('n g', 'New gist page') | |
548 | ] |
|
548 | ] | |
549 | %> |
|
549 | %> | |
550 | %for key, desc in elems: |
|
550 | %for key, desc in elems: | |
551 | <tr> |
|
551 | <tr> | |
552 | <td class="keys"> |
|
552 | <td class="keys"> | |
553 | <span class="key">${key}</span> |
|
553 | <span class="key">${key}</span> | |
554 | </td> |
|
554 | </td> | |
555 | <td>${desc}</td> |
|
555 | <td>${desc}</td> | |
556 | </tr> |
|
556 | </tr> | |
557 | %endfor |
|
557 | %endfor | |
558 | </tbody> |
|
558 | </tbody> | |
559 | </table> |
|
559 | </table> | |
560 | </div> |
|
560 | </div> | |
561 | <div class="col-md-offset-5"> |
|
561 | <div class="col-md-offset-5"> | |
562 | <table class="keyboard-mappings"> |
|
562 | <table class="keyboard-mappings"> | |
563 | <tbody> |
|
563 | <tbody> | |
564 | <tr> |
|
564 | <tr> | |
565 | <th></th> |
|
565 | <th></th> | |
566 | <th>${_('Repositories')}</th> |
|
566 | <th>${_('Repositories')}</th> | |
567 | </tr> |
|
567 | </tr> | |
568 | <% |
|
568 | <% | |
569 | elems = [ |
|
569 | elems = [ | |
570 | ('g s', 'Goto summary page'), |
|
570 | ('g s', 'Goto summary page'), | |
571 | ('g c', 'Goto changelog page'), |
|
571 | ('g c', 'Goto changelog page'), | |
572 | ('g f', 'Goto files page'), |
|
572 | ('g f', 'Goto files page'), | |
573 | ('g F', 'Goto files page with file search activated'), |
|
573 | ('g F', 'Goto files page with file search activated'), | |
574 | ('g o', 'Goto repository settings'), |
|
574 | ('g o', 'Goto repository settings'), | |
575 | ('g O', 'Goto repository permissions settings') |
|
575 | ('g O', 'Goto repository permissions settings') | |
576 | ] |
|
576 | ] | |
577 | %> |
|
577 | %> | |
578 | %for key, desc in elems: |
|
578 | %for key, desc in elems: | |
579 | <tr> |
|
579 | <tr> | |
580 | <td class="keys"> |
|
580 | <td class="keys"> | |
581 | <span class="key">${key}</span> |
|
581 | <span class="key">${key}</span> | |
582 | </td> |
|
582 | </td> | |
583 | <td>${desc}</td> |
|
583 | <td>${desc}</td> | |
584 | </tr> |
|
584 | </tr> | |
585 | %endfor |
|
585 | %endfor | |
586 | </tbody> |
|
586 | </tbody> | |
587 | </table> |
|
587 | </table> | |
588 | </div> |
|
588 | </div> | |
589 | </div> |
|
589 | </div> | |
590 | </div> |
|
590 | </div> | |
591 | <div class="modal-footer"> |
|
591 | <div class="modal-footer"> | |
592 | </div> |
|
592 | </div> | |
593 | </div><!-- /.modal-content --> |
|
593 | </div><!-- /.modal-content --> | |
594 | </div><!-- /.modal-dialog --> |
|
594 | </div><!-- /.modal-dialog --> | |
595 | </div><!-- /.modal --> |
|
595 | </div><!-- /.modal --> | |
596 | %endif |
|
596 | %endif |
@@ -1,217 +1,217 b'' | |||||
1 | ## DATA TABLE RE USABLE ELEMENTS |
|
1 | ## DATA TABLE RE USABLE ELEMENTS | |
2 | ## usage: |
|
2 | ## usage: | |
3 | ## <%namespace name="dt" file="/data_table/_dt_elements.html"/> |
|
3 | ## <%namespace name="dt" file="/data_table/_dt_elements.html"/> | |
4 |
|
4 | |||
5 | <%namespace name="base" file="/base/base.html"/> |
|
5 | <%namespace name="base" file="/base/base.html"/> | |
6 |
|
6 | |||
7 | <%def name="quick_menu(repo_name)"> |
|
7 | <%def name="quick_menu(repo_name)"> | |
8 | <ul class="menu_items hidden"> |
|
8 | <ul class="menu_items hidden"> | |
9 | ##<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu"> |
|
9 | ##<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu"> | |
10 |
|
10 | |||
11 | <li style="border-top:1px solid #577632; margin-left: 21px; padding-left: -99px;"></li> |
|
11 | <li style="border-top:1px solid #577632; margin-left: 21px; padding-left: -99px;"></li> | |
12 | <li> |
|
12 | <li> | |
13 | <a title="${_('Summary')}" href="${h.url('summary_home',repo_name=repo_name)}"> |
|
13 | <a title="${_('Summary')}" href="${h.url('summary_home',repo_name=repo_name)}"> | |
14 | <span class="icon"> |
|
14 | <span class="icon"> | |
15 | <i class="icon-doc-text-inv"></i> |
|
15 | <i class="icon-doc-text-inv"></i> | |
16 | </span> |
|
16 | </span> | |
17 | <span>${_('Summary')}</span> |
|
17 | <span>${_('Summary')}</span> | |
18 | </a> |
|
18 | </a> | |
19 | </li> |
|
19 | </li> | |
20 | <li> |
|
20 | <li> | |
21 | <a title="${_('Changelog')}" href="${h.url('changelog_home',repo_name=repo_name)}"> |
|
21 | <a title="${_('Changelog')}" href="${h.url('changelog_home',repo_name=repo_name)}"> | |
22 | <span class="icon"> |
|
22 | <span class="icon"> | |
23 | <i class="icon-clock"></i> |
|
23 | <i class="icon-clock"></i> | |
24 | </span> |
|
24 | </span> | |
25 | <span>${_('Changelog')}</span> |
|
25 | <span>${_('Changelog')}</span> | |
26 | </a> |
|
26 | </a> | |
27 | </li> |
|
27 | </li> | |
28 | <li> |
|
28 | <li> | |
29 | <a title="${_('Files')}" href="${h.url('files_home',repo_name=repo_name)}"> |
|
29 | <a title="${_('Files')}" href="${h.url('files_home',repo_name=repo_name)}"> | |
30 | <span class="icon"> |
|
30 | <span class="icon"> | |
31 | <i class="icon-docs"></i> |
|
31 | <i class="icon-docs"></i> | |
32 | </span> |
|
32 | </span> | |
33 | <span>${_('Files')}</span> |
|
33 | <span>${_('Files')}</span> | |
34 | </a> |
|
34 | </a> | |
35 | </li> |
|
35 | </li> | |
36 | <li> |
|
36 | <li> | |
37 | <a title="${_('Fork')}" href="${h.url('repo_fork_home',repo_name=repo_name)}"> |
|
37 | <a title="${_('Fork')}" href="${h.url('repo_fork_home',repo_name=repo_name)}"> | |
38 | <span class="icon"> |
|
38 | <span class="icon"> | |
39 | <i class="icon-fork"></i> |
|
39 | <i class="icon-fork"></i> | |
40 | </span> |
|
40 | </span> | |
41 | <span>${_('Fork')}</span> |
|
41 | <span>${_('Fork')}</span> | |
42 | </a> |
|
42 | </a> | |
43 | </li> |
|
43 | </li> | |
44 | <li> |
|
44 | <li> | |
45 | <a title="${_('Settings')}" href="${h.url('edit_repo',repo_name=repo_name)}"> |
|
45 | <a title="${_('Settings')}" href="${h.url('edit_repo',repo_name=repo_name)}"> | |
46 | <span class="icon"> |
|
46 | <span class="icon"> | |
47 | <i class="icon-gear"></i> |
|
47 | <i class="icon-gear"></i> | |
48 | </span> |
|
48 | </span> | |
49 | <span>${_('Settings')}</span> |
|
49 | <span>${_('Settings')}</span> | |
50 | </a> |
|
50 | </a> | |
51 | </li> |
|
51 | </li> | |
52 | </ul> |
|
52 | </ul> | |
53 | </%def> |
|
53 | </%def> | |
54 |
|
54 | |||
55 | <%def name="repo_name(name,rtype,rstate,private,fork_of,short_name=False,admin=False)"> |
|
55 | <%def name="repo_name(name,rtype,rstate,private,fork_of,short_name=False,admin=False)"> | |
56 | <% |
|
56 | <% | |
57 | def get_name(name,short_name=short_name): |
|
57 | def get_name(name,short_name=short_name): | |
58 | if short_name: |
|
58 | if short_name: | |
59 | return name.split('/')[-1] |
|
59 | return name.split('/')[-1] | |
60 | else: |
|
60 | else: | |
61 | return name |
|
61 | return name | |
62 | %> |
|
62 | %> | |
63 | <div class="dt_repo ${'dt_repo_pending' if rstate == 'repo_state_pending' else ''}"> |
|
63 | <div class="dt_repo ${'dt_repo_pending' if rstate == 'repo_state_pending' else ''}"> | |
64 | ##NAME |
|
64 | ##NAME | |
65 | <a href="${h.url('edit_repo' if admin else 'summary_home', repo_name=name)}"> |
|
65 | <a href="${h.url('edit_repo' if admin else 'summary_home', repo_name=name)}"> | |
66 |
|
66 | |||
67 | ##TYPE OF REPO |
|
67 | ##TYPE OF REPO | |
68 | ${base.repotag(rtype)} |
|
68 | ${base.repotag(rtype)} | |
69 |
|
69 | |||
70 | ##PRIVATE/PUBLIC |
|
70 | ##PRIVATE/PUBLIC | |
71 | %if private and c.visual.show_private_icon: |
|
71 | %if private and c.visual.show_private_icon: | |
72 | <i class="icon-keyhole-circled" title="${_('Private repository')}"></i> |
|
72 | <i class="icon-keyhole-circled" title="${_('Private repository')}"></i> | |
73 | %elif not private and c.visual.show_public_icon: |
|
73 | %elif not private and c.visual.show_public_icon: | |
74 | <i class="icon-globe" title="${_('Public repository')}"></i> |
|
74 | <i class="icon-globe" title="${_('Public repository')}"></i> | |
75 | %else: |
|
75 | %else: | |
76 | <span style="margin: 0px 8px 0px 8px"></span> |
|
76 | <span style="margin: 0px 8px 0px 8px"></span> | |
77 | %endif |
|
77 | %endif | |
78 | <span class="dt_repo_name">${get_name(name)}</span> |
|
78 | <span class="dt_repo_name">${get_name(name)}</span> | |
79 | </a> |
|
79 | </a> | |
80 | %if fork_of: |
|
80 | %if fork_of: | |
81 | <a href="${h.url('summary_home',repo_name=fork_of.repo_name)}"><i class="icon-fork"></i></a> |
|
81 | <a href="${h.url('summary_home',repo_name=fork_of.repo_name)}"><i class="icon-fork"></i></a> | |
82 | %endif |
|
82 | %endif | |
83 | %if rstate == 'repo_state_pending': |
|
83 | %if rstate == 'repo_state_pending': | |
84 | <i class="icon-wrench" title="${_('Repository creation in progress...')}"></i> |
|
84 | <i class="icon-wrench" title="${_('Repository creation in progress...')}"></i> | |
85 | %endif |
|
85 | %endif | |
86 | </div> |
|
86 | </div> | |
87 | </%def> |
|
87 | </%def> | |
88 |
|
88 | |||
89 | <%def name="last_change(last_change)"> |
|
89 | <%def name="last_change(last_change)"> | |
90 | <span class="tooltip" date="${last_change}" title="${h.tooltip(h.fmt_date(last_change))}">${h.age(last_change)}</span> |
|
90 | <span class="tooltip" date="${last_change}" title="${h.tooltip(h.fmt_date(last_change))}">${h.age(last_change)}</span> | |
91 | </%def> |
|
91 | </%def> | |
92 |
|
92 | |||
93 | <%def name="revision(name,rev,tip,author,last_msg)"> |
|
93 | <%def name="revision(name,rev,tip,author,last_msg)"> | |
94 | <div> |
|
94 | <div> | |
95 | %if rev >= 0: |
|
95 | %if rev >= 0: | |
96 | <a title="${h.tooltip('%s:\n\n%s' % (author,last_msg))}" class="tooltip revision-link" href="${h.url('changeset_home',repo_name=name,revision=tip)}">${'r%s:%s' % (rev,h.short_id(tip))}</a> |
|
96 | <a title="${h.tooltip('%s:\n\n%s' % (author,last_msg))}" class="tooltip revision-link" href="${h.url('changeset_home',repo_name=name,revision=tip)}">${'r%s:%s' % (rev,h.short_id(tip))}</a> | |
97 | %else: |
|
97 | %else: | |
98 | ${_('No changesets yet')} |
|
98 | ${_('No changesets yet')} | |
99 | %endif |
|
99 | %endif | |
100 | </div> |
|
100 | </div> | |
101 | </%def> |
|
101 | </%def> | |
102 |
|
102 | |||
103 | <%def name="rss(name)"> |
|
103 | <%def name="rss(name)"> | |
104 | %if c.authuser.username != 'default': |
|
104 | %if c.authuser.username != 'default': | |
105 | <a title="${_('Subscribe to %s rss feed')% name}" href="${h.url('rss_feed_home',repo_name=name,api_key=c.authuser.api_key)}"><i class="icon-rss-squared"></i></a> |
|
105 | <a title="${_('Subscribe to %s rss feed')% name}" href="${h.url('rss_feed_home',repo_name=name,api_key=c.authuser.api_key)}"><i class="icon-rss-squared"></i></a> | |
106 | %else: |
|
106 | %else: | |
107 | <a title="${_('Subscribe to %s rss feed')% name}" href="${h.url('rss_feed_home',repo_name=name)}"><i class="icon-rss-squared"></i></a> |
|
107 | <a title="${_('Subscribe to %s rss feed')% name}" href="${h.url('rss_feed_home',repo_name=name)}"><i class="icon-rss-squared"></i></a> | |
108 | %endif |
|
108 | %endif | |
109 | </%def> |
|
109 | </%def> | |
110 |
|
110 | |||
111 | <%def name="atom(name)"> |
|
111 | <%def name="atom(name)"> | |
112 | %if c.authuser.username != 'default': |
|
112 | %if c.authuser.username != 'default': | |
113 | <a title="${_('Subscribe to %s atom feed')% name}" href="${h.url('atom_feed_home',repo_name=name,api_key=c.authuser.api_key)}"><i class="icon-rss-squared"></i></a> |
|
113 | <a title="${_('Subscribe to %s atom feed')% name}" href="${h.url('atom_feed_home',repo_name=name,api_key=c.authuser.api_key)}"><i class="icon-rss-squared"></i></a> | |
114 | %else: |
|
114 | %else: | |
115 | <a title="${_('Subscribe to %s atom feed')% name}" href="${h.url('atom_feed_home',repo_name=name)}"><i class="icon-rss-squared"></i></a> |
|
115 | <a title="${_('Subscribe to %s atom feed')% name}" href="${h.url('atom_feed_home',repo_name=name)}"><i class="icon-rss-squared"></i></a> | |
116 | %endif |
|
116 | %endif | |
117 | </%def> |
|
117 | </%def> | |
118 |
|
118 | |||
119 | <%def name="repo_actions(repo_name, super_user=True)"> |
|
119 | <%def name="repo_actions(repo_name, super_user=True)"> | |
120 | <div> |
|
120 | <div> | |
121 | <div style="float:left; margin-right:5px;" class="grid_edit"> |
|
121 | <div style="float:left; margin-right:5px;" class="grid_edit"> | |
122 | <a href="${h.url('edit_repo',repo_name=repo_name)}" title="${_('edit')}"> |
|
122 | <a href="${h.url('edit_repo',repo_name=repo_name)}" title="${_('edit')}"> | |
123 | <i class="icon-pencil"></i> ${h.submit('edit_%s' % repo_name,_('edit'),class_="action_button")} |
|
123 | <i class="icon-pencil"></i> ${h.submit('edit_%s' % repo_name,_('edit'),class_="action_button")} | |
124 | </a> |
|
124 | </a> | |
125 | </div> |
|
125 | </div> | |
126 | <div style="float:left" class="grid_delete"> |
|
126 | <div style="float:left" class="grid_delete"> | |
127 | ${h.form(h.url('repo', repo_name=repo_name),method='delete')} |
|
127 | ${h.form(h.url('repo', repo_name=repo_name),method='delete')} | |
128 | <i class="icon-minus-circled" style="color:#FF4444"></i> |
|
128 | <i class="icon-minus-circled" style="color:#FF4444"></i> | |
129 | ${h.submit('remove_%s' % repo_name,_('delete'),class_="action_button", |
|
129 | ${h.submit('remove_%s' % repo_name,_('delete'),class_="action_button", | |
130 | onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo_name+"');")} |
|
130 | onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo_name+"');")} | |
131 | ${h.end_form()} |
|
131 | ${h.end_form()} | |
132 | </div> |
|
132 | </div> | |
133 | </div> |
|
133 | </div> | |
134 | </%def> |
|
134 | </%def> | |
135 |
|
135 | |||
136 | <%def name="repo_state(repo_state)"> |
|
136 | <%def name="repo_state(repo_state)"> | |
137 | <div> |
|
137 | <div> | |
138 | %if repo_state == 'repo_state_pending': |
|
138 | %if repo_state == 'repo_state_pending': | |
139 | <div class="btn btn-mini btn-info disabled">${_('Creating')}</div> |
|
139 | <div class="btn btn-mini btn-info disabled">${_('Creating')}</div> | |
140 | %elif repo_state == 'repo_state_created': |
|
140 | %elif repo_state == 'repo_state_created': | |
141 | <div class="btn btn-mini btn-success disabled">${_('Created')}</div> |
|
141 | <div class="btn btn-mini btn-success disabled">${_('Created')}</div> | |
142 | %else: |
|
142 | %else: | |
143 | <div class="btn btn-mini btn-danger disabled" title="${repo_state}">invalid</div> |
|
143 | <div class="btn btn-mini btn-danger disabled" title="${repo_state}">invalid</div> | |
144 | %endif |
|
144 | %endif | |
145 | </div> |
|
145 | </div> | |
146 | </%def> |
|
146 | </%def> | |
147 |
|
147 | |||
148 | <%def name="user_actions(user_id, username)"> |
|
148 | <%def name="user_actions(user_id, username)"> | |
149 | <div style="float:left" class="grid_edit"> |
|
149 | <div style="float:left" class="grid_edit"> | |
150 | <a href="${h.url('edit_user',id=user_id)}" title="${_('edit')}"> |
|
150 | <a href="${h.url('edit_user',id=user_id)}" title="${_('edit')}"> | |
151 | <i class="icon-pencil"></i> ${h.submit('edit_%s' % username,_('edit'),class_="action_button")} |
|
151 | <i class="icon-pencil"></i> ${h.submit('edit_%s' % username,_('edit'),class_="action_button")} | |
152 | </a> |
|
152 | </a> | |
153 | </div> |
|
153 | </div> | |
154 | <div style="float:left" class="grid_delete"> |
|
154 | <div style="float:left" class="grid_delete"> | |
155 | ${h.form(h.url('delete_user', id=user_id),method='delete')} |
|
155 | ${h.form(h.url('delete_user', id=user_id),method='delete')} | |
156 | <i class="icon-minus-circled" style="color:#FF4444"></i> |
|
156 | <i class="icon-minus-circled" style="color:#FF4444"></i> | |
157 | ${h.submit('remove_',_('delete'),id="remove_user_%s" % user_id, class_="action_button", |
|
157 | ${h.submit('remove_',_('delete'),id="remove_user_%s" % user_id, class_="action_button", | |
158 | onclick="return confirm('"+_('Confirm to delete this user: %s') % username+"');")} |
|
158 | onclick="return confirm('"+_('Confirm to delete this user: %s') % username+"');")} | |
159 | ${h.end_form()} |
|
159 | ${h.end_form()} | |
160 | </div> |
|
160 | </div> | |
161 | </%def> |
|
161 | </%def> | |
162 |
|
162 | |||
163 | <%def name="user_group_actions(user_group_id, user_group_name)"> |
|
163 | <%def name="user_group_actions(user_group_id, user_group_name)"> | |
164 | <div style="float:left" class="grid_edit"> |
|
164 | <div style="float:left" class="grid_edit"> | |
165 | <a href="${h.url('edit_users_group', id=user_group_id)}" title="${_('Edit')}"> |
|
165 | <a href="${h.url('edit_users_group', id=user_group_id)}" title="${_('Edit')}"> | |
166 | <i class="icon-pencil"></i> |
|
166 | <i class="icon-pencil"></i> | |
167 | ${h.submit('edit_%s' % user_group_name,_('edit'),class_="action_button", id_="submit_user_group_edit")} |
|
167 | ${h.submit('edit_%s' % user_group_name,_('edit'),class_="action_button", id_="submit_user_group_edit")} | |
168 | </a> |
|
168 | </a> | |
169 | </div> |
|
169 | </div> | |
170 | <div style="float:left" class="grid_delete"> |
|
170 | <div style="float:left" class="grid_delete"> | |
171 | ${h.form(h.url('users_group', id=user_group_id),method='delete')} |
|
171 | ${h.form(h.url('users_group', id=user_group_id),method='delete')} | |
172 | <i class="icon-minus-circled" style="color:#FF4444"></i> |
|
172 | <i class="icon-minus-circled" style="color:#FF4444"></i> | |
173 | ${h.submit('remove_',_('delete'),id="remove_group_%s" % user_group_id, class_="action_button", |
|
173 | ${h.submit('remove_',_('delete'),id="remove_group_%s" % user_group_id, class_="action_button", | |
174 | onclick="return confirm('"+_('Confirm to delete this user group: %s') % user_group_name+"');")} |
|
174 | onclick="return confirm('"+_('Confirm to delete this user group: %s') % user_group_name+"');")} | |
175 | ${h.end_form()} |
|
175 | ${h.end_form()} | |
176 | </div> |
|
176 | </div> | |
177 | </%def> |
|
177 | </%def> | |
178 |
|
178 | |||
179 | <%def name="repo_group_actions(repo_group_id, repo_group_name, gr_count)"> |
|
179 | <%def name="repo_group_actions(repo_group_id, repo_group_name, gr_count)"> | |
180 | <div style="float:left" class="grid_edit"> |
|
180 | <div style="float:left" class="grid_edit"> | |
181 | <a href="${h.url('edit_repo_group',group_name=repo_group_name)}" title="${_('Edit')}"> |
|
181 | <a href="${h.url('edit_repo_group',group_name=repo_group_name)}" title="${_('Edit')}"> | |
182 | <i class="icon-pencil"></i> |
|
182 | <i class="icon-pencil"></i> | |
183 | ${h.submit('edit_%s' % repo_group_name, _('edit'),class_="action_button")} |
|
183 | ${h.submit('edit_%s' % repo_group_name, _('edit'),class_="action_button")} | |
184 | </a> |
|
184 | </a> | |
185 | </div> |
|
185 | </div> | |
186 | <div style="float:left" class="grid_delete"> |
|
186 | <div style="float:left" class="grid_delete"> | |
187 | ${h.form(h.url('repos_group', group_name=repo_group_name),method='delete')} |
|
187 | ${h.form(h.url('repos_group', group_name=repo_group_name),method='delete')} | |
188 | <i class="icon-minus-circled" style="color:#FF4444"></i> |
|
188 | <i class="icon-minus-circled" style="color:#FF4444"></i> | |
189 | ${h.submit('remove_%s' % repo_group_name,_('delete'),class_="action_button", |
|
189 | ${h.submit('remove_%s' % repo_group_name,_('delete'),class_="action_button", | |
190 | onclick="return confirm('"+ungettext('Confirm to delete this group: %s with %s repository','Confirm to delete this group: %s with %s repositories',gr_count) % (repo_group_name, gr_count)+"');")} |
|
190 | onclick="return confirm('"+ungettext('Confirm to delete this group: %s with %s repository','Confirm to delete this group: %s with %s repositories',gr_count) % (repo_group_name, gr_count)+"');")} | |
191 | ${h.end_form()} |
|
191 | ${h.end_form()} | |
192 | </div> |
|
192 | </div> | |
193 | </%def> |
|
193 | </%def> | |
194 |
|
194 | |||
195 | <%def name="user_name(user_id, username)"> |
|
195 | <%def name="user_name(user_id, username)"> | |
196 | ${h.link_to(username,h.url('edit_user', id=user_id))} |
|
196 | ${h.link_to(username,h.url('edit_user', id=user_id))} | |
197 | </%def> |
|
197 | </%def> | |
198 |
|
198 | |||
199 | <%def name="repo_group_name(repo_group_name, children_groups)"> |
|
199 | <%def name="repo_group_name(repo_group_name, children_groups)"> | |
200 | <div style="white-space: nowrap"> |
|
200 | <div style="white-space: nowrap"> | |
201 | <a href="${h.url('repos_group_home',group_name=repo_group_name)}"> |
|
201 | <a href="${h.url('repos_group_home',group_name=repo_group_name)}"> | |
202 | <i class="icon-folder" title="${_('Repository group')}"></i> ${h.literal(' » '.join(children_groups))}</a> |
|
202 | <i class="icon-folder" title="${_('Repository group')}"></i> ${h.literal(' » '.join(children_groups))}</a> | |
203 | </div> |
|
203 | </div> | |
204 | </%def> |
|
204 | </%def> | |
205 |
|
205 | |||
206 | <%def name="user_group_name(user_group_id, user_group_name)"> |
|
206 | <%def name="user_group_name(user_group_id, user_group_name)"> | |
207 | <div style="white-space: nowrap"> |
|
207 | <div style="white-space: nowrap"> | |
208 | <a href="${h.url('edit_users_group', id=user_group_id)}"> |
|
208 | <a href="${h.url('edit_users_group', id=user_group_id)}"> | |
209 | <i class="icon-users" title="${_('User group')}"></i> ${user_group_name}</a> |
|
209 | <i class="icon-users" title="${_('User group')}"></i> ${user_group_name}</a> | |
210 | </div> |
|
210 | </div> | |
211 | </%def> |
|
211 | </%def> | |
212 |
|
212 | |||
213 | <%def name="toggle_follow(repo_id)"> |
|
213 | <%def name="toggle_follow(repo_id)"> | |
214 | <span id="follow_toggle_${repo_id}" class="following" title="${_('Stop following this repository')}" |
|
214 | <span id="follow_toggle_${repo_id}" class="following" title="${_('Stop following this repository')}" | |
215 |
onclick="javascript:toggleFollowingRepo(this, ${repo_id} |
|
215 | onclick="javascript:toggleFollowingRepo(this, ${repo_id})"> | |
216 | </span> |
|
216 | </span> | |
217 | </%def> |
|
217 | </%def> |
@@ -1,400 +1,400 b'' | |||||
1 | <%inherit file="/base/base.html"/> |
|
1 | <%inherit file="/base/base.html"/> | |
2 |
|
2 | |||
3 | <%block name="title"> |
|
3 | <%block name="title"> | |
4 | ${_('%s Summary') % c.repo_name} |
|
4 | ${_('%s Summary') % c.repo_name} | |
5 | </%block> |
|
5 | </%block> | |
6 |
|
6 | |||
7 | <%def name="breadcrumbs_links()"> |
|
7 | <%def name="breadcrumbs_links()"> | |
8 | ${_('Summary')} |
|
8 | ${_('Summary')} | |
9 |
|
9 | |||
10 | ## locking icon |
|
10 | ## locking icon | |
11 | %if c.db_repo.enable_locking: |
|
11 | %if c.db_repo.enable_locking: | |
12 | %if c.db_repo.locked[0]: |
|
12 | %if c.db_repo.locked[0]: | |
13 | <span class="locking_locked tooltip icon-block" title="${_('Repository locked by %s') % h.person_by_id(c.db_repo.locked[0])}"></span> |
|
13 | <span class="locking_locked tooltip icon-block" title="${_('Repository locked by %s') % h.person_by_id(c.db_repo.locked[0])}"></span> | |
14 | %else: |
|
14 | %else: | |
15 | <span class="locking_unlocked tooltip icon-ok" title="${_('Repository unlocked')}"></span> |
|
15 | <span class="locking_unlocked tooltip icon-ok" title="${_('Repository unlocked')}"></span> | |
16 | %endif |
|
16 | %endif | |
17 | %endif |
|
17 | %endif | |
18 |
|
18 | |||
19 | ##FORK |
|
19 | ##FORK | |
20 | %if c.db_repo.fork: |
|
20 | %if c.db_repo.fork: | |
21 | <span> |
|
21 | <span> | |
22 | - <i class="icon-fork"></i> ${_('Fork of')} "<a href="${h.url('summary_home',repo_name=c.db_repo.fork.repo_name)}">${c.db_repo.fork.repo_name}</a>" |
|
22 | - <i class="icon-fork"></i> ${_('Fork of')} "<a href="${h.url('summary_home',repo_name=c.db_repo.fork.repo_name)}">${c.db_repo.fork.repo_name}</a>" | |
23 | </span> |
|
23 | </span> | |
24 | %endif |
|
24 | %endif | |
25 |
|
25 | |||
26 | ##REMOTE |
|
26 | ##REMOTE | |
27 | %if c.db_repo.clone_uri: |
|
27 | %if c.db_repo.clone_uri: | |
28 | <span> |
|
28 | <span> | |
29 | - <i class="icon-fork"></i> ${_('Clone from')} "<a href="${h.url(str(h.hide_credentials(c.db_repo.clone_uri)))}">${h.hide_credentials(c.db_repo.clone_uri)}</a>" |
|
29 | - <i class="icon-fork"></i> ${_('Clone from')} "<a href="${h.url(str(h.hide_credentials(c.db_repo.clone_uri)))}">${h.hide_credentials(c.db_repo.clone_uri)}</a>" | |
30 | <span> |
|
30 | <span> | |
31 | %endif |
|
31 | %endif | |
32 | </%def> |
|
32 | </%def> | |
33 |
|
33 | |||
34 | <%block name="header_menu"> |
|
34 | <%block name="header_menu"> | |
35 | ${self.menu('repositories')} |
|
35 | ${self.menu('repositories')} | |
36 | </%block> |
|
36 | </%block> | |
37 |
|
37 | |||
38 | <%block name="head_extra"> |
|
38 | <%block name="head_extra"> | |
39 | <link href="${h.url('atom_feed_home',repo_name=c.db_repo.repo_name,api_key=c.authuser.api_key)}" rel="alternate" title="${_('%s ATOM feed') % c.repo_name}" type="application/atom+xml" /> |
|
39 | <link href="${h.url('atom_feed_home',repo_name=c.db_repo.repo_name,api_key=c.authuser.api_key)}" rel="alternate" title="${_('%s ATOM feed') % c.repo_name}" type="application/atom+xml" /> | |
40 | <link href="${h.url('rss_feed_home',repo_name=c.db_repo.repo_name,api_key=c.authuser.api_key)}" rel="alternate" title="${_('%s RSS feed') % c.repo_name}" type="application/rss+xml" /> |
|
40 | <link href="${h.url('rss_feed_home',repo_name=c.db_repo.repo_name,api_key=c.authuser.api_key)}" rel="alternate" title="${_('%s RSS feed') % c.repo_name}" type="application/rss+xml" /> | |
41 |
|
41 | |||
42 | <script> |
|
42 | <script> | |
43 | redirect_hash_branch = function(){ |
|
43 | redirect_hash_branch = function(){ | |
44 | var branch = window.location.hash.replace(/^#(.*)/, '$1'); |
|
44 | var branch = window.location.hash.replace(/^#(.*)/, '$1'); | |
45 | if (branch){ |
|
45 | if (branch){ | |
46 | window.location = "${h.url('changelog_home',repo_name=c.repo_name,branch='__BRANCH__')}" |
|
46 | window.location = "${h.url('changelog_home',repo_name=c.repo_name,branch='__BRANCH__')}" | |
47 | .replace('__BRANCH__',branch); |
|
47 | .replace('__BRANCH__',branch); | |
48 | } |
|
48 | } | |
49 | } |
|
49 | } | |
50 | redirect_hash_branch(); |
|
50 | redirect_hash_branch(); | |
51 | window.onhashchange = function() { |
|
51 | window.onhashchange = function() { | |
52 | redirect_hash_branch(); |
|
52 | redirect_hash_branch(); | |
53 | }; |
|
53 | }; | |
54 | </script> |
|
54 | </script> | |
55 | </%block> |
|
55 | </%block> | |
56 |
|
56 | |||
57 | <%def name="main()"> |
|
57 | <%def name="main()"> | |
58 | ${self.repo_context_bar('summary')} |
|
58 | ${self.repo_context_bar('summary')} | |
59 | <% |
|
59 | <% | |
60 | summary = lambda n:{False:'summary-short'}.get(n) |
|
60 | summary = lambda n:{False:'summary-short'}.get(n) | |
61 | %> |
|
61 | %> | |
62 | <div class="box"> |
|
62 | <div class="box"> | |
63 | <!-- box / title --> |
|
63 | <!-- box / title --> | |
64 | <div class="title"> |
|
64 | <div class="title"> | |
65 | ${self.breadcrumbs()} |
|
65 | ${self.breadcrumbs()} | |
66 | </div> |
|
66 | </div> | |
67 | <!-- end box / title --> |
|
67 | <!-- end box / title --> | |
68 | <div class="form"> |
|
68 | <div class="form"> | |
69 | <div id="summary" class="fields"> |
|
69 | <div id="summary" class="fields"> | |
70 | <div class="field"> |
|
70 | <div class="field"> | |
71 | <div class="label-summary"> |
|
71 | <div class="label-summary"> | |
72 | <label>${_('Clone URL')}:</label> |
|
72 | <label>${_('Clone URL')}:</label> | |
73 | </div> |
|
73 | </div> | |
74 | <div class="input ${summary(c.show_stats)}"> |
|
74 | <div class="input ${summary(c.show_stats)}"> | |
75 | ${self.repotag(c.db_repo)} |
|
75 | ${self.repotag(c.db_repo)} | |
76 | <input style="width:80%" type="text" id="clone_url" readonly="readonly" value="${c.clone_repo_url}"/> |
|
76 | <input style="width:80%" type="text" id="clone_url" readonly="readonly" value="${c.clone_repo_url}"/> | |
77 | <input style="display:none;width:80%" type="text" id="clone_url_id" readonly="readonly" value="${c.clone_repo_url_id}"/> |
|
77 | <input style="display:none;width:80%" type="text" id="clone_url_id" readonly="readonly" value="${c.clone_repo_url_id}"/> | |
78 | <div style="display:none" id="clone_by_name" class="btn btn-small">${_('Show by Name')}</div> |
|
78 | <div style="display:none" id="clone_by_name" class="btn btn-small">${_('Show by Name')}</div> | |
79 | <div id="clone_by_id" class="btn btn-small">${_('Show by ID')}</div> |
|
79 | <div id="clone_by_id" class="btn btn-small">${_('Show by ID')}</div> | |
80 | </div> |
|
80 | </div> | |
81 | </div> |
|
81 | </div> | |
82 |
|
82 | |||
83 | <div class="field"> |
|
83 | <div class="field"> | |
84 | <div class="label-summary"> |
|
84 | <div class="label-summary"> | |
85 | <label>${_('Description')}:</label> |
|
85 | <label>${_('Description')}:</label> | |
86 | </div> |
|
86 | </div> | |
87 | %if c.visual.stylify_metatags: |
|
87 | %if c.visual.stylify_metatags: | |
88 | <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(h.desc_stylize(c.db_repo.description))}</div> |
|
88 | <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(h.desc_stylize(c.db_repo.description))}</div> | |
89 | %else: |
|
89 | %else: | |
90 | <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(c.db_repo.description)}</div> |
|
90 | <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(c.db_repo.description)}</div> | |
91 | %endif |
|
91 | %endif | |
92 | </div> |
|
92 | </div> | |
93 |
|
93 | |||
94 | <div class="field"> |
|
94 | <div class="field"> | |
95 | <div class="label-summary"> |
|
95 | <div class="label-summary"> | |
96 | <label>${_('Trending files')}:</label> |
|
96 | <label>${_('Trending files')}:</label> | |
97 | </div> |
|
97 | </div> | |
98 | <div class="input ${summary(c.show_stats)}"> |
|
98 | <div class="input ${summary(c.show_stats)}"> | |
99 | %if c.show_stats: |
|
99 | %if c.show_stats: | |
100 | <div id="lang_stats"></div> |
|
100 | <div id="lang_stats"></div> | |
101 | %else: |
|
101 | %else: | |
102 | ${_('Statistics are disabled for this repository')} |
|
102 | ${_('Statistics are disabled for this repository')} | |
103 | %if h.HasPermissionAll('hg.admin')('enable stats on from summary'): |
|
103 | %if h.HasPermissionAll('hg.admin')('enable stats on from summary'): | |
104 | ${h.link_to(_('Enable'),h.url('edit_repo',repo_name=c.repo_name, anchor='repo_enable_statistics'),class_="btn btn-mini")} |
|
104 | ${h.link_to(_('Enable'),h.url('edit_repo',repo_name=c.repo_name, anchor='repo_enable_statistics'),class_="btn btn-mini")} | |
105 | %endif |
|
105 | %endif | |
106 | %endif |
|
106 | %endif | |
107 | </div> |
|
107 | </div> | |
108 | </div> |
|
108 | </div> | |
109 |
|
109 | |||
110 | <div class="field"> |
|
110 | <div class="field"> | |
111 | <div class="label-summary"> |
|
111 | <div class="label-summary"> | |
112 | <label>${_('Download')}:</label> |
|
112 | <label>${_('Download')}:</label> | |
113 | </div> |
|
113 | </div> | |
114 | <div class="input ${summary(c.show_stats)}"> |
|
114 | <div class="input ${summary(c.show_stats)}"> | |
115 | %if len(c.db_repo_scm_instance.revisions) == 0: |
|
115 | %if len(c.db_repo_scm_instance.revisions) == 0: | |
116 | ${_('There are no downloads yet')} |
|
116 | ${_('There are no downloads yet')} | |
117 | %elif not c.enable_downloads: |
|
117 | %elif not c.enable_downloads: | |
118 | ${_('Downloads are disabled for this repository')} |
|
118 | ${_('Downloads are disabled for this repository')} | |
119 | %if h.HasPermissionAll('hg.admin')('enable downloads on from summary'): |
|
119 | %if h.HasPermissionAll('hg.admin')('enable downloads on from summary'): | |
120 | ${h.link_to(_('Enable'),h.url('edit_repo',repo_name=c.repo_name, anchor='repo_enable_downloads'),class_="btn btn-mini")} |
|
120 | ${h.link_to(_('Enable'),h.url('edit_repo',repo_name=c.repo_name, anchor='repo_enable_downloads'),class_="btn btn-mini")} | |
121 | %endif |
|
121 | %endif | |
122 | %else: |
|
122 | %else: | |
123 | <span id="${'zip_link'}"> |
|
123 | <span id="${'zip_link'}"> | |
124 | <a class="btn btn-small" href="${h.url('files_archive_home',repo_name=c.db_repo.repo_name,fname='tip.zip')}"><i class="icon-file-zip"></i> ${_('Download as zip')}</a> |
|
124 | <a class="btn btn-small" href="${h.url('files_archive_home',repo_name=c.db_repo.repo_name,fname='tip.zip')}"><i class="icon-file-zip"></i> ${_('Download as zip')}</a> | |
125 | </span> |
|
125 | </span> | |
126 | ${h.hidden('download_options')} |
|
126 | ${h.hidden('download_options')} | |
127 | <span style="vertical-align: bottom"> |
|
127 | <span style="vertical-align: bottom"> | |
128 | <input id="archive_subrepos" type="checkbox" name="subrepos" /> |
|
128 | <input id="archive_subrepos" type="checkbox" name="subrepos" /> | |
129 | <label for="archive_subrepos" class="tooltip" title="${h.tooltip(_('Check this to download archive with subrepos'))}" >${_('with subrepos')}</label> |
|
129 | <label for="archive_subrepos" class="tooltip" title="${h.tooltip(_('Check this to download archive with subrepos'))}" >${_('with subrepos')}</label> | |
130 | </span> |
|
130 | </span> | |
131 | %endif |
|
131 | %endif | |
132 | </div> |
|
132 | </div> | |
133 | </div> |
|
133 | </div> | |
134 | </div> |
|
134 | </div> | |
135 | <div id="summary-menu-stats"> |
|
135 | <div id="summary-menu-stats"> | |
136 | <ul> |
|
136 | <ul> | |
137 | <li> |
|
137 | <li> | |
138 | <a title="${_('Owner')} ${c.db_repo.user.email}"> |
|
138 | <a title="${_('Owner')} ${c.db_repo.user.email}"> | |
139 | <i class="icon-user"></i> ${c.db_repo.user.username} |
|
139 | <i class="icon-user"></i> ${c.db_repo.user.username} | |
140 | <div class="gravatar" style="float: right; margin: 0px 0px 0px 0px" title="${c.db_repo.user.name} ${c.db_repo.user.lastname}"> |
|
140 | <div class="gravatar" style="float: right; margin: 0px 0px 0px 0px" title="${c.db_repo.user.name} ${c.db_repo.user.lastname}"> | |
141 | ${h.gravatar(c.db_repo.user.email, size=18)} |
|
141 | ${h.gravatar(c.db_repo.user.email, size=18)} | |
142 | </div> |
|
142 | </div> | |
143 | </a> |
|
143 | </a> | |
144 | </li> |
|
144 | </li> | |
145 | <li> |
|
145 | <li> | |
146 | <a title="${_('Followers')}" href="${h.url('repo_followers_home',repo_name=c.repo_name)}"> |
|
146 | <a title="${_('Followers')}" href="${h.url('repo_followers_home',repo_name=c.repo_name)}"> | |
147 | <i class="icon-heart"></i> ${_('Followers')} |
|
147 | <i class="icon-heart"></i> ${_('Followers')} | |
148 | <span class="stats-bullet" id="current_followers_count">${c.repository_followers}</span> |
|
148 | <span class="stats-bullet" id="current_followers_count">${c.repository_followers}</span> | |
149 | </a> |
|
149 | </a> | |
150 | </li> |
|
150 | </li> | |
151 | <li> |
|
151 | <li> | |
152 | <a title="${_('Forks')}" href="${h.url('repo_forks_home',repo_name=c.repo_name)}"> |
|
152 | <a title="${_('Forks')}" href="${h.url('repo_forks_home',repo_name=c.repo_name)}"> | |
153 | <i class="icon-fork"></i> ${_('Forks')} |
|
153 | <i class="icon-fork"></i> ${_('Forks')} | |
154 | <span class="stats-bullet">${c.repository_forks}</span> |
|
154 | <span class="stats-bullet">${c.repository_forks}</span> | |
155 | </a> |
|
155 | </a> | |
156 | </li> |
|
156 | </li> | |
157 |
|
157 | |||
158 | %if c.authuser.username != 'default': |
|
158 | %if c.authuser.username != 'default': | |
159 | <li class="repo_size"> |
|
159 | <li class="repo_size"> | |
160 |
<a href="#" onclick="javascript:showRepoSize('repo_size_2','${c.db_repo.repo_name}' |
|
160 | <a href="#" onclick="javascript:showRepoSize('repo_size_2','${c.db_repo.repo_name}')"><i class="icon-ruler"></i> ${_('Repository Size')}</a> | |
161 | <span class="stats-bullet" id="repo_size_2"></span> |
|
161 | <span class="stats-bullet" id="repo_size_2"></span> | |
162 | </li> |
|
162 | </li> | |
163 | %endif |
|
163 | %endif | |
164 |
|
164 | |||
165 | <li> |
|
165 | <li> | |
166 | %if c.authuser.username != 'default': |
|
166 | %if c.authuser.username != 'default': | |
167 | <a href="${h.url('atom_feed_home',repo_name=c.db_repo.repo_name,api_key=c.authuser.api_key)}"><i class="icon-rss-squared"></i> ${_('Feed')}</a> |
|
167 | <a href="${h.url('atom_feed_home',repo_name=c.db_repo.repo_name,api_key=c.authuser.api_key)}"><i class="icon-rss-squared"></i> ${_('Feed')}</a> | |
168 | %else: |
|
168 | %else: | |
169 | <a href="${h.url('atom_feed_home',repo_name=c.db_repo.repo_name)}"><i class="icon-rss-squared"></i> ${_('Feed')}</a> |
|
169 | <a href="${h.url('atom_feed_home',repo_name=c.db_repo.repo_name)}"><i class="icon-rss-squared"></i> ${_('Feed')}</a> | |
170 | %endif |
|
170 | %endif | |
171 | </li> |
|
171 | </li> | |
172 |
|
172 | |||
173 | %if c.show_stats: |
|
173 | %if c.show_stats: | |
174 | <li> |
|
174 | <li> | |
175 | <a title="${_('Statistics')}" href="${h.url('repo_stats_home',repo_name=c.repo_name)}"> |
|
175 | <a title="${_('Statistics')}" href="${h.url('repo_stats_home',repo_name=c.repo_name)}"> | |
176 | <i class="icon-graph"></i> ${_('Statistics')} |
|
176 | <i class="icon-graph"></i> ${_('Statistics')} | |
177 | </a> |
|
177 | </a> | |
178 | </li> |
|
178 | </li> | |
179 | %endif |
|
179 | %endif | |
180 | </ul> |
|
180 | </ul> | |
181 | </div> |
|
181 | </div> | |
182 | </div> |
|
182 | </div> | |
183 | </div> |
|
183 | </div> | |
184 |
|
184 | |||
185 |
|
185 | |||
186 | <div class="box"> |
|
186 | <div class="box"> | |
187 | <div class="title"> |
|
187 | <div class="title"> | |
188 | <div class="breadcrumbs"> |
|
188 | <div class="breadcrumbs"> | |
189 | %if c.repo_changesets: |
|
189 | %if c.repo_changesets: | |
190 | ${h.link_to(_('Latest Changes'),h.url('changelog_home',repo_name=c.repo_name))} |
|
190 | ${h.link_to(_('Latest Changes'),h.url('changelog_home',repo_name=c.repo_name))} | |
191 | %else: |
|
191 | %else: | |
192 | ${_('Quick Start')} |
|
192 | ${_('Quick Start')} | |
193 | %endif |
|
193 | %endif | |
194 | </div> |
|
194 | </div> | |
195 | </div> |
|
195 | </div> | |
196 | <div class="table"> |
|
196 | <div class="table"> | |
197 | <div id="shortlog_data"> |
|
197 | <div id="shortlog_data"> | |
198 | <%include file='../changelog/changelog_summary_data.html'/> |
|
198 | <%include file='../changelog/changelog_summary_data.html'/> | |
199 | </div> |
|
199 | </div> | |
200 | </div> |
|
200 | </div> | |
201 | </div> |
|
201 | </div> | |
202 |
|
202 | |||
203 | %if c.readme_data: |
|
203 | %if c.readme_data: | |
204 | <div id="readme" class="anchor"> |
|
204 | <div id="readme" class="anchor"> | |
205 | <div class="box" style="background-color: #FAFAFA"> |
|
205 | <div class="box" style="background-color: #FAFAFA"> | |
206 | <div class="title" title="${_('Readme file from revision %s:%s') % (c.db_repo.landing_rev[0], c.db_repo.landing_rev[1])}"> |
|
206 | <div class="title" title="${_('Readme file from revision %s:%s') % (c.db_repo.landing_rev[0], c.db_repo.landing_rev[1])}"> | |
207 | <div class="breadcrumbs"> |
|
207 | <div class="breadcrumbs"> | |
208 | <a href="${h.url('files_home',repo_name=c.repo_name,revision='tip',f_path=c.readme_file)}">${c.readme_file}</a> |
|
208 | <a href="${h.url('files_home',repo_name=c.repo_name,revision='tip',f_path=c.readme_file)}">${c.readme_file}</a> | |
209 | </div> |
|
209 | </div> | |
210 | </div> |
|
210 | </div> | |
211 | <div class="readme"> |
|
211 | <div class="readme"> | |
212 | <div class="readme_box"> |
|
212 | <div class="readme_box"> | |
213 | ${c.readme_data|n} |
|
213 | ${c.readme_data|n} | |
214 | </div> |
|
214 | </div> | |
215 | </div> |
|
215 | </div> | |
216 | </div> |
|
216 | </div> | |
217 | </div> |
|
217 | </div> | |
218 | %endif |
|
218 | %endif | |
219 |
|
219 | |||
220 | <script type="text/javascript"> |
|
220 | <script type="text/javascript"> | |
221 | $(document).ready(function(){ |
|
221 | $(document).ready(function(){ | |
222 | var $clone_url = $('#clone_url'); |
|
222 | var $clone_url = $('#clone_url'); | |
223 | var $clone_url_id = $('#clone_url_id'); |
|
223 | var $clone_url_id = $('#clone_url_id'); | |
224 | var $clone_by_name = $('#clone_by_name'); |
|
224 | var $clone_by_name = $('#clone_by_name'); | |
225 | var $clone_by_id = $('#clone_by_id'); |
|
225 | var $clone_by_id = $('#clone_by_id'); | |
226 | $clone_url.click(function(e){ |
|
226 | $clone_url.click(function(e){ | |
227 | if($clone_url.hasClass('selected')){ |
|
227 | if($clone_url.hasClass('selected')){ | |
228 | return ; |
|
228 | return ; | |
229 | }else{ |
|
229 | }else{ | |
230 | $clone_url.addClass('selected'); |
|
230 | $clone_url.addClass('selected'); | |
231 | $clone_url.select(); |
|
231 | $clone_url.select(); | |
232 | } |
|
232 | } | |
233 | }); |
|
233 | }); | |
234 |
|
234 | |||
235 | $clone_by_name.click(function(e){ |
|
235 | $clone_by_name.click(function(e){ | |
236 | // show url by name and hide name button |
|
236 | // show url by name and hide name button | |
237 | $clone_url.show(); |
|
237 | $clone_url.show(); | |
238 | $clone_by_name.hide(); |
|
238 | $clone_by_name.hide(); | |
239 |
|
239 | |||
240 | // hide url by id and show name button |
|
240 | // hide url by id and show name button | |
241 | $clone_by_id.show(); |
|
241 | $clone_by_id.show(); | |
242 | $clone_url_id.hide(); |
|
242 | $clone_url_id.hide(); | |
243 | }); |
|
243 | }); | |
244 |
|
244 | |||
245 | $clone_by_id.click(function(e){ |
|
245 | $clone_by_id.click(function(e){ | |
246 | // show url by id and hide id button |
|
246 | // show url by id and hide id button | |
247 | $clone_by_id.hide(); |
|
247 | $clone_by_id.hide(); | |
248 | $clone_url_id.show(); |
|
248 | $clone_url_id.show(); | |
249 |
|
249 | |||
250 | // hide url by name and show id button |
|
250 | // hide url by name and show id button | |
251 | $clone_by_name.show(); |
|
251 | $clone_by_name.show(); | |
252 | $clone_url.hide(); |
|
252 | $clone_url.hide(); | |
253 | }); |
|
253 | }); | |
254 |
|
254 | |||
255 | var cache = {} |
|
255 | var cache = {} | |
256 | $("#download_options").select2({ |
|
256 | $("#download_options").select2({ | |
257 | placeholder: _TM['Select changeset'], |
|
257 | placeholder: _TM['Select changeset'], | |
258 | dropdownAutoWidth: true, |
|
258 | dropdownAutoWidth: true, | |
259 | query: function(query){ |
|
259 | query: function(query){ | |
260 | var key = 'cache'; |
|
260 | var key = 'cache'; | |
261 | var cached = cache[key] ; |
|
261 | var cached = cache[key] ; | |
262 | if(cached) { |
|
262 | if(cached) { | |
263 | var data = {results: []}; |
|
263 | var data = {results: []}; | |
264 | //filter results |
|
264 | //filter results | |
265 | $.each(cached.results, function(){ |
|
265 | $.each(cached.results, function(){ | |
266 | var section = this.text; |
|
266 | var section = this.text; | |
267 | var children = []; |
|
267 | var children = []; | |
268 | $.each(this.children, function(){ |
|
268 | $.each(this.children, function(){ | |
269 | if(query.term.length == 0 || this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0 ){ |
|
269 | if(query.term.length == 0 || this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0 ){ | |
270 | children.push({'id': this.id, 'text': this.text}) |
|
270 | children.push({'id': this.id, 'text': this.text}) | |
271 | } |
|
271 | } | |
272 | }) |
|
272 | }) | |
273 | data.results.push({'text': section, 'children': children}) |
|
273 | data.results.push({'text': section, 'children': children}) | |
274 | }); |
|
274 | }); | |
275 | query.callback(data); |
|
275 | query.callback(data); | |
276 | }else{ |
|
276 | }else{ | |
277 | $.ajax({ |
|
277 | $.ajax({ | |
278 | url: pyroutes.url('repo_refs_data', {'repo_name': '${c.repo_name}'}), |
|
278 | url: pyroutes.url('repo_refs_data', {'repo_name': '${c.repo_name}'}), | |
279 | data: {}, |
|
279 | data: {}, | |
280 | dataType: 'json', |
|
280 | dataType: 'json', | |
281 | type: 'GET', |
|
281 | type: 'GET', | |
282 | success: function(data) { |
|
282 | success: function(data) { | |
283 | cache[key] = data; |
|
283 | cache[key] = data; | |
284 | query.callback({results: data.results}); |
|
284 | query.callback({results: data.results}); | |
285 | } |
|
285 | } | |
286 | }) |
|
286 | }) | |
287 | } |
|
287 | } | |
288 | } |
|
288 | } | |
289 | }); |
|
289 | }); | |
290 | // on change of download options |
|
290 | // on change of download options | |
291 | $('#download_options').change(function(e){ |
|
291 | $('#download_options').change(function(e){ | |
292 | var new_cs = e.added |
|
292 | var new_cs = e.added | |
293 |
|
293 | |||
294 | for(k in tmpl_links){ |
|
294 | for(k in tmpl_links){ | |
295 | var s = $('#'+k+'_link'); |
|
295 | var s = $('#'+k+'_link'); | |
296 | if(s){ |
|
296 | if(s){ | |
297 | var title_tmpl = "${_('Download %s as %s') % ('__CS_NAME__','__CS_EXT__')}"; |
|
297 | var title_tmpl = "${_('Download %s as %s') % ('__CS_NAME__','__CS_EXT__')}"; | |
298 | title_tmpl= title_tmpl.replace('__CS_NAME__',new_cs.text); |
|
298 | title_tmpl= title_tmpl.replace('__CS_NAME__',new_cs.text); | |
299 | title_tmpl = title_tmpl.replace('__CS_EXT__',k); |
|
299 | title_tmpl = title_tmpl.replace('__CS_EXT__',k); | |
300 | title_tmpl = '<i class="icon-file-zip"></i> '+ title_tmpl; |
|
300 | title_tmpl = '<i class="icon-file-zip"></i> '+ title_tmpl; | |
301 | var url = tmpl_links[k].replace('__CS__',new_cs.id); |
|
301 | var url = tmpl_links[k].replace('__CS__',new_cs.id); | |
302 | var subrepos = $('#archive_subrepos').is(':checked'); |
|
302 | var subrepos = $('#archive_subrepos').is(':checked'); | |
303 | url = url.replace('__SUB__',subrepos); |
|
303 | url = url.replace('__SUB__',subrepos); | |
304 | url = url.replace('__NAME__',title_tmpl); |
|
304 | url = url.replace('__NAME__',title_tmpl); | |
305 |
|
305 | |||
306 | s.html(url) |
|
306 | s.html(url) | |
307 | } |
|
307 | } | |
308 | } |
|
308 | } | |
309 | }); |
|
309 | }); | |
310 |
|
310 | |||
311 | var tmpl_links = {}; |
|
311 | var tmpl_links = {}; | |
312 | %for cnt,archive in enumerate(c.db_repo_scm_instance._get_archives()): |
|
312 | %for cnt,archive in enumerate(c.db_repo_scm_instance._get_archives()): | |
313 | tmpl_links["${archive['type']}"] = '${h.link_to('__NAME__', h.url('files_archive_home',repo_name=c.db_repo.repo_name, fname='__CS__'+archive['extension'],subrepos='__SUB__'),class_='btn btn-small')}'; |
|
313 | tmpl_links["${archive['type']}"] = '${h.link_to('__NAME__', h.url('files_archive_home',repo_name=c.db_repo.repo_name, fname='__CS__'+archive['extension'],subrepos='__SUB__'),class_='btn btn-small')}'; | |
314 | %endfor |
|
314 | %endfor | |
315 | }) |
|
315 | }) | |
316 | </script> |
|
316 | </script> | |
317 |
|
317 | |||
318 | %if c.show_stats: |
|
318 | %if c.show_stats: | |
319 | <script type="text/javascript"> |
|
319 | <script type="text/javascript"> | |
320 | $(document).ready(function(){ |
|
320 | $(document).ready(function(){ | |
321 | var data = ${c.trending_languages|n}; |
|
321 | var data = ${c.trending_languages|n}; | |
322 | var total = 0; |
|
322 | var total = 0; | |
323 | var no_data = true; |
|
323 | var no_data = true; | |
324 | var tbl = document.createElement('table'); |
|
324 | var tbl = document.createElement('table'); | |
325 | tbl.setAttribute('class','trending_language_tbl'); |
|
325 | tbl.setAttribute('class','trending_language_tbl'); | |
326 | var cnt = 0; |
|
326 | var cnt = 0; | |
327 | for (var i=0;i<data.length;i++){ |
|
327 | for (var i=0;i<data.length;i++){ | |
328 | total+= data[i][1].count; |
|
328 | total+= data[i][1].count; | |
329 | } |
|
329 | } | |
330 | for (var i=0;i<data.length;i++){ |
|
330 | for (var i=0;i<data.length;i++){ | |
331 | cnt += 1; |
|
331 | cnt += 1; | |
332 | no_data = false; |
|
332 | no_data = false; | |
333 |
|
333 | |||
334 | var hide = cnt>2; |
|
334 | var hide = cnt>2; | |
335 | var tr = document.createElement('tr'); |
|
335 | var tr = document.createElement('tr'); | |
336 | if (hide){ |
|
336 | if (hide){ | |
337 | tr.setAttribute('style','display:none'); |
|
337 | tr.setAttribute('style','display:none'); | |
338 | tr.setAttribute('class','stats_hidden'); |
|
338 | tr.setAttribute('class','stats_hidden'); | |
339 | } |
|
339 | } | |
340 | var k = data[i][0]; |
|
340 | var k = data[i][0]; | |
341 | var obj = data[i][1]; |
|
341 | var obj = data[i][1]; | |
342 | var percentage = Math.round((obj.count/total*100),2); |
|
342 | var percentage = Math.round((obj.count/total*100),2); | |
343 |
|
343 | |||
344 | var td1 = document.createElement('td'); |
|
344 | var td1 = document.createElement('td'); | |
345 | td1.width = 150; |
|
345 | td1.width = 150; | |
346 | var trending_language_label = document.createElement('div'); |
|
346 | var trending_language_label = document.createElement('div'); | |
347 | trending_language_label.innerHTML = obj.desc+" ("+k+")"; |
|
347 | trending_language_label.innerHTML = obj.desc+" ("+k+")"; | |
348 | td1.appendChild(trending_language_label); |
|
348 | td1.appendChild(trending_language_label); | |
349 |
|
349 | |||
350 | var td2 = document.createElement('td'); |
|
350 | var td2 = document.createElement('td'); | |
351 | td2.setAttribute('style','padding-right:14px !important'); |
|
351 | td2.setAttribute('style','padding-right:14px !important'); | |
352 | var trending_language = document.createElement('div'); |
|
352 | var trending_language = document.createElement('div'); | |
353 | var nr_files = obj.count+" ${_('files')}"; |
|
353 | var nr_files = obj.count+" ${_('files')}"; | |
354 |
|
354 | |||
355 | trending_language.title = k+" "+nr_files; |
|
355 | trending_language.title = k+" "+nr_files; | |
356 |
|
356 | |||
357 | if (percentage>22){ |
|
357 | if (percentage>22){ | |
358 | trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"% "+nr_files+ "</b>"; |
|
358 | trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"% "+nr_files+ "</b>"; | |
359 | } |
|
359 | } | |
360 | else{ |
|
360 | else{ | |
361 | trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"%</b>"; |
|
361 | trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"%</b>"; | |
362 | } |
|
362 | } | |
363 |
|
363 | |||
364 | trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner'); |
|
364 | trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner'); | |
365 | trending_language.style.width=percentage+"%"; |
|
365 | trending_language.style.width=percentage+"%"; | |
366 | td2.appendChild(trending_language); |
|
366 | td2.appendChild(trending_language); | |
367 |
|
367 | |||
368 | tr.appendChild(td1); |
|
368 | tr.appendChild(td1); | |
369 | tr.appendChild(td2); |
|
369 | tr.appendChild(td2); | |
370 | tbl.appendChild(tr); |
|
370 | tbl.appendChild(tr); | |
371 | if(cnt == 3){ |
|
371 | if(cnt == 3){ | |
372 | var show_more = document.createElement('tr'); |
|
372 | var show_more = document.createElement('tr'); | |
373 | var td = document.createElement('td'); |
|
373 | var td = document.createElement('td'); | |
374 | lnk = document.createElement('a'); |
|
374 | lnk = document.createElement('a'); | |
375 |
|
375 | |||
376 | lnk.href='#'; |
|
376 | lnk.href='#'; | |
377 | lnk.innerHTML = "${_('Show more')}"; |
|
377 | lnk.innerHTML = "${_('Show more')}"; | |
378 | lnk.id='code_stats_show_more'; |
|
378 | lnk.id='code_stats_show_more'; | |
379 | td.appendChild(lnk); |
|
379 | td.appendChild(lnk); | |
380 |
|
380 | |||
381 | show_more.appendChild(td); |
|
381 | show_more.appendChild(td); | |
382 | show_more.appendChild(document.createElement('td')); |
|
382 | show_more.appendChild(document.createElement('td')); | |
383 | tbl.appendChild(show_more); |
|
383 | tbl.appendChild(show_more); | |
384 | } |
|
384 | } | |
385 |
|
385 | |||
386 | } |
|
386 | } | |
387 | if (data.length == 0) { |
|
387 | if (data.length == 0) { | |
388 | tbl.innerHTML = "<tr><td>${_('No data ready yet')}</td></tr>"; |
|
388 | tbl.innerHTML = "<tr><td>${_('No data ready yet')}</td></tr>"; | |
389 | } |
|
389 | } | |
390 |
|
390 | |||
391 | $('#lang_stats').append(tbl); |
|
391 | $('#lang_stats').append(tbl); | |
392 | $('#code_stats_show_more').click(function(){ |
|
392 | $('#code_stats_show_more').click(function(){ | |
393 | $('.stats_hidden').show(); |
|
393 | $('.stats_hidden').show(); | |
394 | $('#code_stats_show_more').hide(); |
|
394 | $('#code_stats_show_more').hide(); | |
395 | }); |
|
395 | }); | |
396 | }); |
|
396 | }); | |
397 | </script> |
|
397 | </script> | |
398 | %endif |
|
398 | %endif | |
399 |
|
399 | |||
400 | </%def> |
|
400 | </%def> |
@@ -1,39 +1,38 b'' | |||||
1 | from kallithea.tests import * |
|
1 | from kallithea.tests import * | |
2 | import datetime |
|
2 | import datetime | |
3 |
|
3 | |||
4 |
|
4 | |||
5 | class TestJournalController(TestController): |
|
5 | class TestJournalController(TestController): | |
6 |
|
6 | |||
7 | def test_index(self): |
|
7 | def test_index(self): | |
8 | self.log_user() |
|
8 | self.log_user() | |
9 | response = self.app.get(url(controller='journal', action='index')) |
|
9 | response = self.app.get(url(controller='journal', action='index')) | |
10 |
|
10 | |||
11 | response.mustcontain("""<div class="journal_day">%s</div>""" % datetime.date.today()) |
|
11 | response.mustcontain("""<div class="journal_day">%s</div>""" % datetime.date.today()) | |
12 |
|
12 | |||
13 | def test_stop_following_repository(self): |
|
13 | def test_stop_following_repository(self): | |
14 | session = self.log_user() |
|
14 | session = self.log_user() | |
15 | # usr = Session().query(User).filter(User.username == 'test_admin').one() |
|
15 | # usr = Session().query(User).filter(User.username == 'test_admin').one() | |
16 | # repo = Session().query(Repository).filter(Repository.repo_name == HG_REPO).one() |
|
16 | # repo = Session().query(Repository).filter(Repository.repo_name == HG_REPO).one() | |
17 | # |
|
17 | # | |
18 | # followings = Session().query(UserFollowing)\ |
|
18 | # followings = Session().query(UserFollowing)\ | |
19 | # .filter(UserFollowing.user == usr)\ |
|
19 | # .filter(UserFollowing.user == usr)\ | |
20 | # .filter(UserFollowing.follows_repository == repo).all() |
|
20 | # .filter(UserFollowing.follows_repository == repo).all() | |
21 | # |
|
21 | # | |
22 | # assert len(followings) == 1, 'Not following any repository' |
|
22 | # assert len(followings) == 1, 'Not following any repository' | |
23 | # |
|
23 | # | |
24 | # response = self.app.post(url(controller='journal', |
|
24 | # response = self.app.post(url(controller='journal', | |
25 | # action='toggle_following'), |
|
25 | # action='toggle_following'), | |
26 |
# {' |
|
26 | # {'follows_repo_id':repo.repo_id}) | |
27 | # 'follows_repo_id':repo.repo_id}) |
|
|||
28 |
|
27 | |||
29 | def test_start_following_repository(self): |
|
28 | def test_start_following_repository(self): | |
30 | self.log_user() |
|
29 | self.log_user() | |
31 | response = self.app.get(url(controller='journal', action='index'),) |
|
30 | response = self.app.get(url(controller='journal', action='index'),) | |
32 |
|
31 | |||
33 | def test_public_journal_atom(self): |
|
32 | def test_public_journal_atom(self): | |
34 | self.log_user() |
|
33 | self.log_user() | |
35 | response = self.app.get(url(controller='journal', action='public_journal_atom'),) |
|
34 | response = self.app.get(url(controller='journal', action='public_journal_atom'),) | |
36 |
|
35 | |||
37 | def test_public_journal_rss(self): |
|
36 | def test_public_journal_rss(self): | |
38 | self.log_user() |
|
37 | self.log_user() | |
39 | response = self.app.get(url(controller='journal', action='public_journal_rss'),) |
|
38 | response = self.app.get(url(controller='journal', action='public_journal_rss'),) |
General Comments 0
You need to be logged in to leave comments.
Login now