##// END OF EJS Templates
whitespace cleanup
marcink -
r2973:9937afa7 beta
parent child Browse files
Show More
@@ -1,366 +1,366 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.repos_groups
3 rhodecode.controllers.admin.repos_groups
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Repositories groups controller for RhodeCode
6 Repositories groups controller for RhodeCode
7
7
8 :created_on: Mar 23, 2010
8 :created_on: Mar 23, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28 import formencode
28 import formencode
29
29
30 from formencode import htmlfill
30 from formencode import htmlfill
31
31
32 from pylons import request, tmpl_context as c, url
32 from pylons import request, tmpl_context as c, url
33 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
34 from pylons.i18n.translation import _
34 from pylons.i18n.translation import _
35
35
36 from sqlalchemy.exc import IntegrityError
36 from sqlalchemy.exc import IntegrityError
37
37
38 import rhodecode
38 import rhodecode
39 from rhodecode.lib import helpers as h
39 from rhodecode.lib import helpers as h
40 from rhodecode.lib.ext_json import json
40 from rhodecode.lib.ext_json import json
41 from rhodecode.lib.auth import LoginRequired, HasPermissionAnyDecorator,\
41 from rhodecode.lib.auth import LoginRequired, HasPermissionAnyDecorator,\
42 HasReposGroupPermissionAnyDecorator
42 HasReposGroupPermissionAnyDecorator
43 from rhodecode.lib.base import BaseController, render
43 from rhodecode.lib.base import BaseController, render
44 from rhodecode.model.db import RepoGroup, Repository
44 from rhodecode.model.db import RepoGroup, Repository
45 from rhodecode.model.repos_group import ReposGroupModel
45 from rhodecode.model.repos_group import ReposGroupModel
46 from rhodecode.model.forms import ReposGroupForm
46 from rhodecode.model.forms import ReposGroupForm
47 from rhodecode.model.meta import Session
47 from rhodecode.model.meta import Session
48 from rhodecode.model.repo import RepoModel
48 from rhodecode.model.repo import RepoModel
49 from webob.exc import HTTPInternalServerError, HTTPNotFound
49 from webob.exc import HTTPInternalServerError, HTTPNotFound
50 from rhodecode.lib.utils2 import str2bool
50 from rhodecode.lib.utils2 import str2bool
51 from sqlalchemy.sql.expression import func
51 from sqlalchemy.sql.expression import func
52
52
53 log = logging.getLogger(__name__)
53 log = logging.getLogger(__name__)
54
54
55
55
56 class ReposGroupsController(BaseController):
56 class ReposGroupsController(BaseController):
57 """REST Controller styled on the Atom Publishing Protocol"""
57 """REST Controller styled on the Atom Publishing Protocol"""
58 # To properly map this controller, ensure your config/routing.py
58 # To properly map this controller, ensure your config/routing.py
59 # file has a resource setup:
59 # file has a resource setup:
60 # map.resource('repos_group', 'repos_groups')
60 # map.resource('repos_group', 'repos_groups')
61
61
62 @LoginRequired()
62 @LoginRequired()
63 def __before__(self):
63 def __before__(self):
64 super(ReposGroupsController, self).__before__()
64 super(ReposGroupsController, self).__before__()
65
65
66 def __load_defaults(self):
66 def __load_defaults(self):
67 c.repo_groups = RepoGroup.groups_choices()
67 c.repo_groups = RepoGroup.groups_choices()
68 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
68 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
69
69
70 repo_model = RepoModel()
70 repo_model = RepoModel()
71 c.users_array = repo_model.get_users_js()
71 c.users_array = repo_model.get_users_js()
72 c.users_groups_array = repo_model.get_users_groups_js()
72 c.users_groups_array = repo_model.get_users_groups_js()
73
73
74 def __load_data(self, group_id):
74 def __load_data(self, group_id):
75 """
75 """
76 Load defaults settings for edit, and update
76 Load defaults settings for edit, and update
77
77
78 :param group_id:
78 :param group_id:
79 """
79 """
80 self.__load_defaults()
80 self.__load_defaults()
81 repo_group = RepoGroup.get_or_404(group_id)
81 repo_group = RepoGroup.get_or_404(group_id)
82 data = repo_group.get_dict()
82 data = repo_group.get_dict()
83 data['group_name'] = repo_group.name
83 data['group_name'] = repo_group.name
84
84
85 # fill repository users
85 # fill repository users
86 for p in repo_group.repo_group_to_perm:
86 for p in repo_group.repo_group_to_perm:
87 data.update({'u_perm_%s' % p.user.username:
87 data.update({'u_perm_%s' % p.user.username:
88 p.permission.permission_name})
88 p.permission.permission_name})
89
89
90 # fill repository groups
90 # fill repository groups
91 for p in repo_group.users_group_to_perm:
91 for p in repo_group.users_group_to_perm:
92 data.update({'g_perm_%s' % p.users_group.users_group_name:
92 data.update({'g_perm_%s' % p.users_group.users_group_name:
93 p.permission.permission_name})
93 p.permission.permission_name})
94
94
95 return data
95 return data
96
96
97 @HasPermissionAnyDecorator('hg.admin')
97 @HasPermissionAnyDecorator('hg.admin')
98 def index(self, format='html'):
98 def index(self, format='html'):
99 """GET /repos_groups: All items in the collection"""
99 """GET /repos_groups: All items in the collection"""
100 # url('repos_groups')
100 # url('repos_groups')
101 sk = lambda g: g.parents[0].group_name if g.parents else g.group_name
101 sk = lambda g: g.parents[0].group_name if g.parents else g.group_name
102 c.groups = sorted(RepoGroup.query().all(), key=sk)
102 c.groups = sorted(RepoGroup.query().all(), key=sk)
103 return render('admin/repos_groups/repos_groups_show.html')
103 return render('admin/repos_groups/repos_groups_show.html')
104
104
105 @HasPermissionAnyDecorator('hg.admin')
105 @HasPermissionAnyDecorator('hg.admin')
106 def create(self):
106 def create(self):
107 """POST /repos_groups: Create a new item"""
107 """POST /repos_groups: Create a new item"""
108 # url('repos_groups')
108 # url('repos_groups')
109 self.__load_defaults()
109 self.__load_defaults()
110 repos_group_form = ReposGroupForm(available_groups =
110 repos_group_form = ReposGroupForm(available_groups =
111 c.repo_groups_choices)()
111 c.repo_groups_choices)()
112 try:
112 try:
113 form_result = repos_group_form.to_python(dict(request.POST))
113 form_result = repos_group_form.to_python(dict(request.POST))
114 ReposGroupModel().create(
114 ReposGroupModel().create(
115 group_name=form_result['group_name'],
115 group_name=form_result['group_name'],
116 group_description=form_result['group_description'],
116 group_description=form_result['group_description'],
117 parent=form_result['group_parent_id']
117 parent=form_result['group_parent_id']
118 )
118 )
119 Session().commit()
119 Session().commit()
120 h.flash(_('created repos group %s') \
120 h.flash(_('created repos group %s') \
121 % form_result['group_name'], category='success')
121 % form_result['group_name'], category='success')
122 #TODO: in futureaction_logger(, '', '', '', self.sa)
122 #TODO: in futureaction_logger(, '', '', '', self.sa)
123 except formencode.Invalid, errors:
123 except formencode.Invalid, errors:
124
124
125 return htmlfill.render(
125 return htmlfill.render(
126 render('admin/repos_groups/repos_groups_add.html'),
126 render('admin/repos_groups/repos_groups_add.html'),
127 defaults=errors.value,
127 defaults=errors.value,
128 errors=errors.error_dict or {},
128 errors=errors.error_dict or {},
129 prefix_error=False,
129 prefix_error=False,
130 encoding="UTF-8")
130 encoding="UTF-8")
131 except Exception:
131 except Exception:
132 log.error(traceback.format_exc())
132 log.error(traceback.format_exc())
133 h.flash(_('error occurred during creation of repos group %s') \
133 h.flash(_('error occurred during creation of repos group %s') \
134 % request.POST.get('group_name'), category='error')
134 % request.POST.get('group_name'), category='error')
135
135
136 return redirect(url('repos_groups'))
136 return redirect(url('repos_groups'))
137
137
138 @HasPermissionAnyDecorator('hg.admin')
138 @HasPermissionAnyDecorator('hg.admin')
139 def new(self, format='html'):
139 def new(self, format='html'):
140 """GET /repos_groups/new: Form to create a new item"""
140 """GET /repos_groups/new: Form to create a new item"""
141 # url('new_repos_group')
141 # url('new_repos_group')
142 self.__load_defaults()
142 self.__load_defaults()
143 return render('admin/repos_groups/repos_groups_add.html')
143 return render('admin/repos_groups/repos_groups_add.html')
144
144
145 @HasPermissionAnyDecorator('hg.admin')
145 @HasPermissionAnyDecorator('hg.admin')
146 def update(self, id):
146 def update(self, id):
147 """PUT /repos_groups/id: Update an existing item"""
147 """PUT /repos_groups/id: Update an existing item"""
148 # Forms posted to this method should contain a hidden field:
148 # Forms posted to this method should contain a hidden field:
149 # <input type="hidden" name="_method" value="PUT" />
149 # <input type="hidden" name="_method" value="PUT" />
150 # Or using helpers:
150 # Or using helpers:
151 # h.form(url('repos_group', id=ID),
151 # h.form(url('repos_group', id=ID),
152 # method='put')
152 # method='put')
153 # url('repos_group', id=ID)
153 # url('repos_group', id=ID)
154
154
155 self.__load_defaults()
155 self.__load_defaults()
156 c.repos_group = RepoGroup.get(id)
156 c.repos_group = RepoGroup.get(id)
157
157
158 repos_group_form = ReposGroupForm(
158 repos_group_form = ReposGroupForm(
159 edit=True,
159 edit=True,
160 old_data=c.repos_group.get_dict(),
160 old_data=c.repos_group.get_dict(),
161 available_groups=c.repo_groups_choices
161 available_groups=c.repo_groups_choices
162 )()
162 )()
163 try:
163 try:
164 form_result = repos_group_form.to_python(dict(request.POST))
164 form_result = repos_group_form.to_python(dict(request.POST))
165 ReposGroupModel().update(id, form_result)
165 ReposGroupModel().update(id, form_result)
166 Session().commit()
166 Session().commit()
167 h.flash(_('updated repos group %s') \
167 h.flash(_('updated repos group %s') \
168 % form_result['group_name'], category='success')
168 % form_result['group_name'], category='success')
169 #TODO: in future action_logger(, '', '', '', self.sa)
169 #TODO: in future action_logger(, '', '', '', self.sa)
170 except formencode.Invalid, errors:
170 except formencode.Invalid, errors:
171
171
172 return htmlfill.render(
172 return htmlfill.render(
173 render('admin/repos_groups/repos_groups_edit.html'),
173 render('admin/repos_groups/repos_groups_edit.html'),
174 defaults=errors.value,
174 defaults=errors.value,
175 errors=errors.error_dict or {},
175 errors=errors.error_dict or {},
176 prefix_error=False,
176 prefix_error=False,
177 encoding="UTF-8")
177 encoding="UTF-8")
178 except Exception:
178 except Exception:
179 log.error(traceback.format_exc())
179 log.error(traceback.format_exc())
180 h.flash(_('error occurred during update of repos group %s') \
180 h.flash(_('error occurred during update of repos group %s') \
181 % request.POST.get('group_name'), category='error')
181 % request.POST.get('group_name'), category='error')
182
182
183 return redirect(url('edit_repos_group', id=id))
183 return redirect(url('edit_repos_group', id=id))
184
184
185 @HasPermissionAnyDecorator('hg.admin')
185 @HasPermissionAnyDecorator('hg.admin')
186 def delete(self, id):
186 def delete(self, id):
187 """DELETE /repos_groups/id: Delete an existing item"""
187 """DELETE /repos_groups/id: Delete an existing item"""
188 # Forms posted to this method should contain a hidden field:
188 # Forms posted to this method should contain a hidden field:
189 # <input type="hidden" name="_method" value="DELETE" />
189 # <input type="hidden" name="_method" value="DELETE" />
190 # Or using helpers:
190 # Or using helpers:
191 # h.form(url('repos_group', id=ID),
191 # h.form(url('repos_group', id=ID),
192 # method='delete')
192 # method='delete')
193 # url('repos_group', id=ID)
193 # url('repos_group', id=ID)
194
194
195 gr = RepoGroup.get(id)
195 gr = RepoGroup.get(id)
196 repos = gr.repositories.all()
196 repos = gr.repositories.all()
197 if repos:
197 if repos:
198 h.flash(_('This group contains %s repositores and cannot be '
198 h.flash(_('This group contains %s repositores and cannot be '
199 'deleted') % len(repos),
199 'deleted') % len(repos),
200 category='error')
200 category='error')
201 return redirect(url('repos_groups'))
201 return redirect(url('repos_groups'))
202
202
203 try:
203 try:
204 ReposGroupModel().delete(id)
204 ReposGroupModel().delete(id)
205 Session().commit()
205 Session().commit()
206 h.flash(_('removed repos group %s') % gr.group_name,
206 h.flash(_('removed repos group %s') % gr.group_name,
207 category='success')
207 category='success')
208 #TODO: in future action_logger(, '', '', '', self.sa)
208 #TODO: in future action_logger(, '', '', '', self.sa)
209 except IntegrityError, e:
209 except IntegrityError, e:
210 if str(e.message).find('groups_group_parent_id_fkey') != -1:
210 if str(e.message).find('groups_group_parent_id_fkey') != -1:
211 log.error(traceback.format_exc())
211 log.error(traceback.format_exc())
212 h.flash(_('Cannot delete this group it still contains '
212 h.flash(_('Cannot delete this group it still contains '
213 'subgroups'),
213 'subgroups'),
214 category='warning')
214 category='warning')
215 else:
215 else:
216 log.error(traceback.format_exc())
216 log.error(traceback.format_exc())
217 h.flash(_('error occurred during deletion of repos '
217 h.flash(_('error occurred during deletion of repos '
218 'group %s') % gr.group_name, category='error')
218 'group %s') % gr.group_name, category='error')
219
219
220 except Exception:
220 except Exception:
221 log.error(traceback.format_exc())
221 log.error(traceback.format_exc())
222 h.flash(_('error occurred during deletion of repos '
222 h.flash(_('error occurred during deletion of repos '
223 'group %s') % gr.group_name, category='error')
223 'group %s') % gr.group_name, category='error')
224
224
225 return redirect(url('repos_groups'))
225 return redirect(url('repos_groups'))
226
226
227 @HasReposGroupPermissionAnyDecorator('group.admin')
227 @HasReposGroupPermissionAnyDecorator('group.admin')
228 def delete_repos_group_user_perm(self, group_name):
228 def delete_repos_group_user_perm(self, group_name):
229 """
229 """
230 DELETE an existing repositories group permission user
230 DELETE an existing repositories group permission user
231
231
232 :param group_name:
232 :param group_name:
233 """
233 """
234 try:
234 try:
235 recursive = str2bool(request.POST.get('recursive', False))
235 recursive = str2bool(request.POST.get('recursive', False))
236 ReposGroupModel().delete_permission(
236 ReposGroupModel().delete_permission(
237 repos_group=group_name, obj=request.POST['user_id'],
237 repos_group=group_name, obj=request.POST['user_id'],
238 obj_type='user', recursive=recursive
238 obj_type='user', recursive=recursive
239 )
239 )
240 Session().commit()
240 Session().commit()
241 except Exception:
241 except Exception:
242 log.error(traceback.format_exc())
242 log.error(traceback.format_exc())
243 h.flash(_('An error occurred during deletion of group user'),
243 h.flash(_('An error occurred during deletion of group user'),
244 category='error')
244 category='error')
245 raise HTTPInternalServerError()
245 raise HTTPInternalServerError()
246
246
247 @HasReposGroupPermissionAnyDecorator('group.admin')
247 @HasReposGroupPermissionAnyDecorator('group.admin')
248 def delete_repos_group_users_group_perm(self, group_name):
248 def delete_repos_group_users_group_perm(self, group_name):
249 """
249 """
250 DELETE an existing repositories group permission users group
250 DELETE an existing repositories group permission users group
251
251
252 :param group_name:
252 :param group_name:
253 """
253 """
254
254
255 try:
255 try:
256 recursive = str2bool(request.POST.get('recursive', False))
256 recursive = str2bool(request.POST.get('recursive', False))
257 ReposGroupModel().delete_permission(
257 ReposGroupModel().delete_permission(
258 repos_group=group_name, obj=request.POST['users_group_id'],
258 repos_group=group_name, obj=request.POST['users_group_id'],
259 obj_type='users_group', recursive=recursive
259 obj_type='users_group', recursive=recursive
260 )
260 )
261 Session().commit()
261 Session().commit()
262 except Exception:
262 except Exception:
263 log.error(traceback.format_exc())
263 log.error(traceback.format_exc())
264 h.flash(_('An error occurred during deletion of group'
264 h.flash(_('An error occurred during deletion of group'
265 ' users groups'),
265 ' users groups'),
266 category='error')
266 category='error')
267 raise HTTPInternalServerError()
267 raise HTTPInternalServerError()
268
268
269 def show_by_name(self, group_name):
269 def show_by_name(self, group_name):
270 """
270 """
271 This is a proxy that does a lookup group_name -> id, and shows
271 This is a proxy that does a lookup group_name -> id, and shows
272 the group by id view instead
272 the group by id view instead
273 """
273 """
274 group_name = group_name.rstrip('/')
274 group_name = group_name.rstrip('/')
275 id_ = RepoGroup.get_by_group_name(group_name)
275 id_ = RepoGroup.get_by_group_name(group_name)
276 if id_:
276 if id_:
277 return self.show(id_.group_id)
277 return self.show(id_.group_id)
278 raise HTTPNotFound
278 raise HTTPNotFound
279
279
280 @HasReposGroupPermissionAnyDecorator('group.read', 'group.write',
280 @HasReposGroupPermissionAnyDecorator('group.read', 'group.write',
281 'group.admin')
281 'group.admin')
282 def show(self, id, format='html'):
282 def show(self, id, format='html'):
283 """GET /repos_groups/id: Show a specific item"""
283 """GET /repos_groups/id: Show a specific item"""
284 # url('repos_group', id=ID)
284 # url('repos_group', id=ID)
285
285
286 c.group = RepoGroup.get_or_404(id)
286 c.group = RepoGroup.get_or_404(id)
287 c.group_repos = c.group.repositories.all()
287 c.group_repos = c.group.repositories.all()
288
288
289 #overwrite our cached list with current filter
289 #overwrite our cached list with current filter
290 gr_filter = c.group_repos
290 gr_filter = c.group_repos
291 c.repo_cnt = 0
291 c.repo_cnt = 0
292
292
293 groups = RepoGroup.query().order_by(RepoGroup.group_name)\
293 groups = RepoGroup.query().order_by(RepoGroup.group_name)\
294 .filter(RepoGroup.group_parent_id == id).all()
294 .filter(RepoGroup.group_parent_id == id).all()
295 c.groups = self.scm_model.get_repos_groups(groups)
295 c.groups = self.scm_model.get_repos_groups(groups)
296
296
297 if c.visual.lightweight_dashboard is False:
297 if c.visual.lightweight_dashboard is False:
298 c.cached_repo_list = self.scm_model.get_repos(all_repos=gr_filter)
298 c.cached_repo_list = self.scm_model.get_repos(all_repos=gr_filter)
299
299
300 c.repos_list = c.cached_repo_list
300 c.repos_list = c.cached_repo_list
301 ## lightweight version of dashboard
301 ## lightweight version of dashboard
302 else:
302 else:
303 c.repos_list = Repository.query()\
303 c.repos_list = Repository.query()\
304 .filter(Repository.group_id == id)\
304 .filter(Repository.group_id == id)\
305 .order_by(func.lower(Repository.repo_name))\
305 .order_by(func.lower(Repository.repo_name))\
306 .all()
306 .all()
307 repos_data = []
307 repos_data = []
308 total_records = len(c.repos_list)
308 total_records = len(c.repos_list)
309
309
310 _tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
310 _tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
311 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
311 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
312
312
313 quick_menu = lambda repo_name: (template.get_def("quick_menu")
313 quick_menu = lambda repo_name: (template.get_def("quick_menu")
314 .render(repo_name, _=_, h=h, c=c))
314 .render(repo_name, _=_, h=h, c=c))
315 repo_lnk = lambda name, rtype, private, fork_of: (
315 repo_lnk = lambda name, rtype, private, fork_of: (
316 template.get_def("repo_name")
316 template.get_def("repo_name")
317 .render(name, rtype, private, fork_of, short_name=False,
317 .render(name, rtype, private, fork_of, short_name=False,
318 admin=False, _=_, h=h, c=c))
318 admin=False, _=_, h=h, c=c))
319 last_change = lambda last_change: (template.get_def("last_change")
319 last_change = lambda last_change: (template.get_def("last_change")
320 .render(last_change, _=_, h=h, c=c))
320 .render(last_change, _=_, h=h, c=c))
321 rss_lnk = lambda repo_name: (template.get_def("rss")
321 rss_lnk = lambda repo_name: (template.get_def("rss")
322 .render(repo_name, _=_, h=h, c=c))
322 .render(repo_name, _=_, h=h, c=c))
323 atom_lnk = lambda repo_name: (template.get_def("atom")
323 atom_lnk = lambda repo_name: (template.get_def("atom")
324 .render(repo_name, _=_, h=h, c=c))
324 .render(repo_name, _=_, h=h, c=c))
325
325
326 for repo in c.repos_list:
326 for repo in c.repos_list:
327 repos_data.append({
327 repos_data.append({
328 "menu": quick_menu(repo.repo_name),
328 "menu": quick_menu(repo.repo_name),
329 "raw_name": repo.repo_name.lower(),
329 "raw_name": repo.repo_name.lower(),
330 "name": repo_lnk(repo.repo_name, repo.repo_type,
330 "name": repo_lnk(repo.repo_name, repo.repo_type,
331 repo.private, repo.fork),
331 repo.private, repo.fork),
332 "last_change": last_change(repo.last_db_change),
332 "last_change": last_change(repo.last_db_change),
333 "desc": repo.description,
333 "desc": repo.description,
334 "owner": h.person(repo.user.username),
334 "owner": h.person(repo.user.username),
335 "rss": rss_lnk(repo.repo_name),
335 "rss": rss_lnk(repo.repo_name),
336 "atom": atom_lnk(repo.repo_name),
336 "atom": atom_lnk(repo.repo_name),
337 })
337 })
338
338
339 c.data = json.dumps({
339 c.data = json.dumps({
340 "totalRecords": total_records,
340 "totalRecords": total_records,
341 "startIndex": 0,
341 "startIndex": 0,
342 "sort": "name",
342 "sort": "name",
343 "dir": "asc",
343 "dir": "asc",
344 "records": repos_data
344 "records": repos_data
345 })
345 })
346
346
347 return render('admin/repos_groups/repos_groups.html')
347 return render('admin/repos_groups/repos_groups.html')
348
348
349 @HasPermissionAnyDecorator('hg.admin')
349 @HasPermissionAnyDecorator('hg.admin')
350 def edit(self, id, format='html'):
350 def edit(self, id, format='html'):
351 """GET /repos_groups/id/edit: Form to edit an existing item"""
351 """GET /repos_groups/id/edit: Form to edit an existing item"""
352 # url('edit_repos_group', id=ID)
352 # url('edit_repos_group', id=ID)
353
353
354 c.repos_group = ReposGroupModel()._get_repos_group(id)
354 c.repos_group = ReposGroupModel()._get_repos_group(id)
355 defaults = self.__load_data(c.repos_group.group_id)
355 defaults = self.__load_data(c.repos_group.group_id)
356
356
357 # we need to exclude this group from the group list for editing
357 # we need to exclude this group from the group list for editing
358 c.repo_groups = filter(lambda x: x[0] != c.repos_group.group_id,
358 c.repo_groups = filter(lambda x: x[0] != c.repos_group.group_id,
359 c.repo_groups)
359 c.repo_groups)
360
360
361 return htmlfill.render(
361 return htmlfill.render(
362 render('admin/repos_groups/repos_groups_edit.html'),
362 render('admin/repos_groups/repos_groups_edit.html'),
363 defaults=defaults,
363 defaults=defaults,
364 encoding="UTF-8",
364 encoding="UTF-8",
365 force_defaults=False
365 force_defaults=False
366 )
366 )
@@ -1,545 +1,545 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.files
3 rhodecode.controllers.files
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Files controller for RhodeCode
6 Files controller for RhodeCode
7
7
8 :created_on: Apr 21, 2010
8 :created_on: Apr 21, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 from __future__ import with_statement
25 from __future__ import with_statement
26 import os
26 import os
27 import logging
27 import logging
28 import traceback
28 import traceback
29 import tempfile
29 import tempfile
30
30
31 from pylons import request, response, tmpl_context as c, url
31 from pylons import request, response, tmpl_context as c, url
32 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
33 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
34 from pylons.decorators import jsonify
34 from pylons.decorators import jsonify
35
35
36 from rhodecode.lib import diffs
36 from rhodecode.lib import diffs
37 from rhodecode.lib import helpers as h
37 from rhodecode.lib import helpers as h
38
38
39 from rhodecode.lib.compat import OrderedDict
39 from rhodecode.lib.compat import OrderedDict
40 from rhodecode.lib.utils2 import convert_line_endings, detect_mode, safe_str,\
40 from rhodecode.lib.utils2 import convert_line_endings, detect_mode, safe_str,\
41 str2bool
41 str2bool
42 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
42 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
43 from rhodecode.lib.base import BaseRepoController, render
43 from rhodecode.lib.base import BaseRepoController, render
44 from rhodecode.lib.vcs.backends.base import EmptyChangeset
44 from rhodecode.lib.vcs.backends.base import EmptyChangeset
45 from rhodecode.lib.vcs.conf import settings
45 from rhodecode.lib.vcs.conf import settings
46 from rhodecode.lib.vcs.exceptions import RepositoryError, \
46 from rhodecode.lib.vcs.exceptions import RepositoryError, \
47 ChangesetDoesNotExistError, EmptyRepositoryError, \
47 ChangesetDoesNotExistError, EmptyRepositoryError, \
48 ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError
48 ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError
49 from rhodecode.lib.vcs.nodes import FileNode
49 from rhodecode.lib.vcs.nodes import FileNode
50
50
51 from rhodecode.model.repo import RepoModel
51 from rhodecode.model.repo import RepoModel
52 from rhodecode.model.scm import ScmModel
52 from rhodecode.model.scm import ScmModel
53 from rhodecode.model.db import Repository
53 from rhodecode.model.db import Repository
54
54
55 from rhodecode.controllers.changeset import anchor_url, _ignorews_url,\
55 from rhodecode.controllers.changeset import anchor_url, _ignorews_url,\
56 _context_url, get_line_ctx, get_ignore_ws
56 _context_url, get_line_ctx, get_ignore_ws
57
57
58
58
59 log = logging.getLogger(__name__)
59 log = logging.getLogger(__name__)
60
60
61
61
62 class FilesController(BaseRepoController):
62 class FilesController(BaseRepoController):
63
63
64 def __before__(self):
64 def __before__(self):
65 super(FilesController, self).__before__()
65 super(FilesController, self).__before__()
66 c.cut_off_limit = self.cut_off_limit
66 c.cut_off_limit = self.cut_off_limit
67
67
68 def __get_cs_or_redirect(self, rev, repo_name, redirect_after=True):
68 def __get_cs_or_redirect(self, rev, repo_name, redirect_after=True):
69 """
69 """
70 Safe way to get changeset if error occur it redirects to tip with
70 Safe way to get changeset if error occur it redirects to tip with
71 proper message
71 proper message
72
72
73 :param rev: revision to fetch
73 :param rev: revision to fetch
74 :param repo_name: repo name to redirect after
74 :param repo_name: repo name to redirect after
75 """
75 """
76
76
77 try:
77 try:
78 return c.rhodecode_repo.get_changeset(rev)
78 return c.rhodecode_repo.get_changeset(rev)
79 except EmptyRepositoryError, e:
79 except EmptyRepositoryError, e:
80 if not redirect_after:
80 if not redirect_after:
81 return None
81 return None
82 url_ = url('files_add_home',
82 url_ = url('files_add_home',
83 repo_name=c.repo_name,
83 repo_name=c.repo_name,
84 revision=0, f_path='')
84 revision=0, f_path='')
85 add_new = '<a href="%s">[%s]</a>' % (url_, _('click here to add new file'))
85 add_new = '<a href="%s">[%s]</a>' % (url_, _('click here to add new file'))
86 h.flash(h.literal(_('There are no files yet %s') % add_new),
86 h.flash(h.literal(_('There are no files yet %s') % add_new),
87 category='warning')
87 category='warning')
88 redirect(h.url('summary_home', repo_name=repo_name))
88 redirect(h.url('summary_home', repo_name=repo_name))
89
89
90 except RepositoryError, e:
90 except RepositoryError, e:
91 h.flash(str(e), category='warning')
91 h.flash(str(e), category='warning')
92 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
92 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
93
93
94 def __get_filenode_or_redirect(self, repo_name, cs, path):
94 def __get_filenode_or_redirect(self, repo_name, cs, path):
95 """
95 """
96 Returns file_node, if error occurs or given path is directory,
96 Returns file_node, if error occurs or given path is directory,
97 it'll redirect to top level path
97 it'll redirect to top level path
98
98
99 :param repo_name: repo_name
99 :param repo_name: repo_name
100 :param cs: given changeset
100 :param cs: given changeset
101 :param path: path to lookup
101 :param path: path to lookup
102 """
102 """
103
103
104 try:
104 try:
105 file_node = cs.get_node(path)
105 file_node = cs.get_node(path)
106 if file_node.is_dir():
106 if file_node.is_dir():
107 raise RepositoryError('given path is a directory')
107 raise RepositoryError('given path is a directory')
108 except RepositoryError, e:
108 except RepositoryError, e:
109 h.flash(str(e), category='warning')
109 h.flash(str(e), category='warning')
110 redirect(h.url('files_home', repo_name=repo_name,
110 redirect(h.url('files_home', repo_name=repo_name,
111 revision=cs.raw_id))
111 revision=cs.raw_id))
112
112
113 return file_node
113 return file_node
114
114
115 @LoginRequired()
115 @LoginRequired()
116 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
116 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
117 'repository.admin')
117 'repository.admin')
118 def index(self, repo_name, revision, f_path, annotate=False):
118 def index(self, repo_name, revision, f_path, annotate=False):
119 # redirect to given revision from form if given
119 # redirect to given revision from form if given
120 post_revision = request.POST.get('at_rev', None)
120 post_revision = request.POST.get('at_rev', None)
121 if post_revision:
121 if post_revision:
122 cs = self.__get_cs_or_redirect(post_revision, repo_name)
122 cs = self.__get_cs_or_redirect(post_revision, repo_name)
123 redirect(url('files_home', repo_name=c.repo_name,
123 redirect(url('files_home', repo_name=c.repo_name,
124 revision=cs.raw_id, f_path=f_path))
124 revision=cs.raw_id, f_path=f_path))
125
125
126 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
126 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
127 c.branch = request.GET.get('branch', None)
127 c.branch = request.GET.get('branch', None)
128 c.f_path = f_path
128 c.f_path = f_path
129 c.annotate = annotate
129 c.annotate = annotate
130 cur_rev = c.changeset.revision
130 cur_rev = c.changeset.revision
131
131
132 # prev link
132 # prev link
133 try:
133 try:
134 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
134 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
135 c.url_prev = url('files_home', repo_name=c.repo_name,
135 c.url_prev = url('files_home', repo_name=c.repo_name,
136 revision=prev_rev.raw_id, f_path=f_path)
136 revision=prev_rev.raw_id, f_path=f_path)
137 if c.branch:
137 if c.branch:
138 c.url_prev += '?branch=%s' % c.branch
138 c.url_prev += '?branch=%s' % c.branch
139 except (ChangesetDoesNotExistError, VCSError):
139 except (ChangesetDoesNotExistError, VCSError):
140 c.url_prev = '#'
140 c.url_prev = '#'
141
141
142 # next link
142 # next link
143 try:
143 try:
144 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
144 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
145 c.url_next = url('files_home', repo_name=c.repo_name,
145 c.url_next = url('files_home', repo_name=c.repo_name,
146 revision=next_rev.raw_id, f_path=f_path)
146 revision=next_rev.raw_id, f_path=f_path)
147 if c.branch:
147 if c.branch:
148 c.url_next += '?branch=%s' % c.branch
148 c.url_next += '?branch=%s' % c.branch
149 except (ChangesetDoesNotExistError, VCSError):
149 except (ChangesetDoesNotExistError, VCSError):
150 c.url_next = '#'
150 c.url_next = '#'
151
151
152 # files or dirs
152 # files or dirs
153 try:
153 try:
154 c.file = c.changeset.get_node(f_path)
154 c.file = c.changeset.get_node(f_path)
155
155
156 if c.file.is_file():
156 if c.file.is_file():
157 _hist = c.rhodecode_repo.get_changeset().get_file_history(f_path)
157 _hist = c.rhodecode_repo.get_changeset().get_file_history(f_path)
158 c.file_changeset = c.changeset
158 c.file_changeset = c.changeset
159 if _hist:
159 if _hist:
160 c.file_changeset = (c.changeset
160 c.file_changeset = (c.changeset
161 if c.changeset.revision < _hist[0].revision
161 if c.changeset.revision < _hist[0].revision
162 else _hist[0])
162 else _hist[0])
163 c.file_history = self._get_node_history(None, f_path, _hist)
163 c.file_history = self._get_node_history(None, f_path, _hist)
164 c.authors = []
164 c.authors = []
165 for a in set([x.author for x in _hist]):
165 for a in set([x.author for x in _hist]):
166 c.authors.append((h.email(a), h.person(a)))
166 c.authors.append((h.email(a), h.person(a)))
167 else:
167 else:
168 c.authors = c.file_history = []
168 c.authors = c.file_history = []
169 except RepositoryError, e:
169 except RepositoryError, e:
170 h.flash(str(e), category='warning')
170 h.flash(str(e), category='warning')
171 redirect(h.url('files_home', repo_name=repo_name,
171 redirect(h.url('files_home', repo_name=repo_name,
172 revision='tip'))
172 revision='tip'))
173
173
174 if request.environ.get('HTTP_X_PARTIAL_XHR'):
174 if request.environ.get('HTTP_X_PARTIAL_XHR'):
175 return render('files/files_ypjax.html')
175 return render('files/files_ypjax.html')
176
176
177 return render('files/files.html')
177 return render('files/files.html')
178
178
179 @LoginRequired()
179 @LoginRequired()
180 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
180 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
181 'repository.admin')
181 'repository.admin')
182 def rawfile(self, repo_name, revision, f_path):
182 def rawfile(self, repo_name, revision, f_path):
183 cs = self.__get_cs_or_redirect(revision, repo_name)
183 cs = self.__get_cs_or_redirect(revision, repo_name)
184 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
184 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
185
185
186 response.content_disposition = 'attachment; filename=%s' % \
186 response.content_disposition = 'attachment; filename=%s' % \
187 safe_str(f_path.split(Repository.url_sep())[-1])
187 safe_str(f_path.split(Repository.url_sep())[-1])
188
188
189 response.content_type = file_node.mimetype
189 response.content_type = file_node.mimetype
190 return file_node.content
190 return file_node.content
191
191
192 @LoginRequired()
192 @LoginRequired()
193 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
193 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
194 'repository.admin')
194 'repository.admin')
195 def raw(self, repo_name, revision, f_path):
195 def raw(self, repo_name, revision, f_path):
196 cs = self.__get_cs_or_redirect(revision, repo_name)
196 cs = self.__get_cs_or_redirect(revision, repo_name)
197 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
197 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
198
198
199 raw_mimetype_mapping = {
199 raw_mimetype_mapping = {
200 # map original mimetype to a mimetype used for "show as raw"
200 # map original mimetype to a mimetype used for "show as raw"
201 # you can also provide a content-disposition to override the
201 # you can also provide a content-disposition to override the
202 # default "attachment" disposition.
202 # default "attachment" disposition.
203 # orig_type: (new_type, new_dispo)
203 # orig_type: (new_type, new_dispo)
204
204
205 # show images inline:
205 # show images inline:
206 'image/x-icon': ('image/x-icon', 'inline'),
206 'image/x-icon': ('image/x-icon', 'inline'),
207 'image/png': ('image/png', 'inline'),
207 'image/png': ('image/png', 'inline'),
208 'image/gif': ('image/gif', 'inline'),
208 'image/gif': ('image/gif', 'inline'),
209 'image/jpeg': ('image/jpeg', 'inline'),
209 'image/jpeg': ('image/jpeg', 'inline'),
210 'image/svg+xml': ('image/svg+xml', 'inline'),
210 'image/svg+xml': ('image/svg+xml', 'inline'),
211 }
211 }
212
212
213 mimetype = file_node.mimetype
213 mimetype = file_node.mimetype
214 try:
214 try:
215 mimetype, dispo = raw_mimetype_mapping[mimetype]
215 mimetype, dispo = raw_mimetype_mapping[mimetype]
216 except KeyError:
216 except KeyError:
217 # we don't know anything special about this, handle it safely
217 # we don't know anything special about this, handle it safely
218 if file_node.is_binary:
218 if file_node.is_binary:
219 # do same as download raw for binary files
219 # do same as download raw for binary files
220 mimetype, dispo = 'application/octet-stream', 'attachment'
220 mimetype, dispo = 'application/octet-stream', 'attachment'
221 else:
221 else:
222 # do not just use the original mimetype, but force text/plain,
222 # do not just use the original mimetype, but force text/plain,
223 # otherwise it would serve text/html and that might be unsafe.
223 # otherwise it would serve text/html and that might be unsafe.
224 # Note: underlying vcs library fakes text/plain mimetype if the
224 # Note: underlying vcs library fakes text/plain mimetype if the
225 # mimetype can not be determined and it thinks it is not
225 # mimetype can not be determined and it thinks it is not
226 # binary.This might lead to erroneous text display in some
226 # binary.This might lead to erroneous text display in some
227 # cases, but helps in other cases, like with text files
227 # cases, but helps in other cases, like with text files
228 # without extension.
228 # without extension.
229 mimetype, dispo = 'text/plain', 'inline'
229 mimetype, dispo = 'text/plain', 'inline'
230
230
231 if dispo == 'attachment':
231 if dispo == 'attachment':
232 dispo = 'attachment; filename=%s' % \
232 dispo = 'attachment; filename=%s' % \
233 safe_str(f_path.split(os.sep)[-1])
233 safe_str(f_path.split(os.sep)[-1])
234
234
235 response.content_disposition = dispo
235 response.content_disposition = dispo
236 response.content_type = mimetype
236 response.content_type = mimetype
237 return file_node.content
237 return file_node.content
238
238
239 @LoginRequired()
239 @LoginRequired()
240 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
240 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
241 def edit(self, repo_name, revision, f_path):
241 def edit(self, repo_name, revision, f_path):
242 repo = Repository.get_by_repo_name(repo_name)
242 repo = Repository.get_by_repo_name(repo_name)
243 if repo.enable_locking and repo.locked[0]:
243 if repo.enable_locking and repo.locked[0]:
244 h.flash(_('This repository is has been locked by %s on %s')
244 h.flash(_('This repository is has been locked by %s on %s')
245 % (h.person_by_id(repo.locked[0]),
245 % (h.person_by_id(repo.locked[0]),
246 h.fmt_date(h.time_to_datetime(repo.locked[1]))),
246 h.fmt_date(h.time_to_datetime(repo.locked[1]))),
247 'warning')
247 'warning')
248 return redirect(h.url('files_home',
248 return redirect(h.url('files_home',
249 repo_name=repo_name, revision='tip'))
249 repo_name=repo_name, revision='tip'))
250
250
251 r_post = request.POST
251 r_post = request.POST
252
252
253 c.cs = self.__get_cs_or_redirect(revision, repo_name)
253 c.cs = self.__get_cs_or_redirect(revision, repo_name)
254 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
254 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
255
255
256 if c.file.is_binary:
256 if c.file.is_binary:
257 return redirect(url('files_home', repo_name=c.repo_name,
257 return redirect(url('files_home', repo_name=c.repo_name,
258 revision=c.cs.raw_id, f_path=f_path))
258 revision=c.cs.raw_id, f_path=f_path))
259
259
260 c.f_path = f_path
260 c.f_path = f_path
261
261
262 if r_post:
262 if r_post:
263
263
264 old_content = c.file.content
264 old_content = c.file.content
265 sl = old_content.splitlines(1)
265 sl = old_content.splitlines(1)
266 first_line = sl[0] if sl else ''
266 first_line = sl[0] if sl else ''
267 # modes: 0 - Unix, 1 - Mac, 2 - DOS
267 # modes: 0 - Unix, 1 - Mac, 2 - DOS
268 mode = detect_mode(first_line, 0)
268 mode = detect_mode(first_line, 0)
269 content = convert_line_endings(r_post.get('content'), mode)
269 content = convert_line_endings(r_post.get('content'), mode)
270
270
271 message = r_post.get('message') or (_('Edited %s via RhodeCode')
271 message = r_post.get('message') or (_('Edited %s via RhodeCode')
272 % (f_path))
272 % (f_path))
273 author = self.rhodecode_user.full_contact
273 author = self.rhodecode_user.full_contact
274
274
275 if content == old_content:
275 if content == old_content:
276 h.flash(_('No changes'),
276 h.flash(_('No changes'),
277 category='warning')
277 category='warning')
278 return redirect(url('changeset_home', repo_name=c.repo_name,
278 return redirect(url('changeset_home', repo_name=c.repo_name,
279 revision='tip'))
279 revision='tip'))
280
280
281 try:
281 try:
282 self.scm_model.commit_change(repo=c.rhodecode_repo,
282 self.scm_model.commit_change(repo=c.rhodecode_repo,
283 repo_name=repo_name, cs=c.cs,
283 repo_name=repo_name, cs=c.cs,
284 user=self.rhodecode_user,
284 user=self.rhodecode_user,
285 author=author, message=message,
285 author=author, message=message,
286 content=content, f_path=f_path)
286 content=content, f_path=f_path)
287 h.flash(_('Successfully committed to %s') % f_path,
287 h.flash(_('Successfully committed to %s') % f_path,
288 category='success')
288 category='success')
289
289
290 except Exception:
290 except Exception:
291 log.error(traceback.format_exc())
291 log.error(traceback.format_exc())
292 h.flash(_('Error occurred during commit'), category='error')
292 h.flash(_('Error occurred during commit'), category='error')
293 return redirect(url('changeset_home',
293 return redirect(url('changeset_home',
294 repo_name=c.repo_name, revision='tip'))
294 repo_name=c.repo_name, revision='tip'))
295
295
296 return render('files/files_edit.html')
296 return render('files/files_edit.html')
297
297
298 @LoginRequired()
298 @LoginRequired()
299 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
299 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
300 def add(self, repo_name, revision, f_path):
300 def add(self, repo_name, revision, f_path):
301
301
302 repo = Repository.get_by_repo_name(repo_name)
302 repo = Repository.get_by_repo_name(repo_name)
303 if repo.enable_locking and repo.locked[0]:
303 if repo.enable_locking and repo.locked[0]:
304 h.flash(_('This repository is has been locked by %s on %s')
304 h.flash(_('This repository is has been locked by %s on %s')
305 % (h.person_by_id(repo.locked[0]),
305 % (h.person_by_id(repo.locked[0]),
306 h.fmt_date(h.time_to_datetime(repo.locked[1]))),
306 h.fmt_date(h.time_to_datetime(repo.locked[1]))),
307 'warning')
307 'warning')
308 return redirect(h.url('files_home',
308 return redirect(h.url('files_home',
309 repo_name=repo_name, revision='tip'))
309 repo_name=repo_name, revision='tip'))
310
310
311 r_post = request.POST
311 r_post = request.POST
312 c.cs = self.__get_cs_or_redirect(revision, repo_name,
312 c.cs = self.__get_cs_or_redirect(revision, repo_name,
313 redirect_after=False)
313 redirect_after=False)
314 if c.cs is None:
314 if c.cs is None:
315 c.cs = EmptyChangeset(alias=c.rhodecode_repo.alias)
315 c.cs = EmptyChangeset(alias=c.rhodecode_repo.alias)
316
316
317 c.f_path = f_path
317 c.f_path = f_path
318
318
319 if r_post:
319 if r_post:
320 unix_mode = 0
320 unix_mode = 0
321 content = convert_line_endings(r_post.get('content'), unix_mode)
321 content = convert_line_endings(r_post.get('content'), unix_mode)
322
322
323 message = r_post.get('message') or (_('Added %s via RhodeCode')
323 message = r_post.get('message') or (_('Added %s via RhodeCode')
324 % (f_path))
324 % (f_path))
325 location = r_post.get('location')
325 location = r_post.get('location')
326 filename = r_post.get('filename')
326 filename = r_post.get('filename')
327 file_obj = r_post.get('upload_file', None)
327 file_obj = r_post.get('upload_file', None)
328
328
329 if file_obj is not None and hasattr(file_obj, 'filename'):
329 if file_obj is not None and hasattr(file_obj, 'filename'):
330 filename = file_obj.filename
330 filename = file_obj.filename
331 content = file_obj.file
331 content = file_obj.file
332
332
333 node_path = os.path.join(location, filename)
333 node_path = os.path.join(location, filename)
334 author = self.rhodecode_user.full_contact
334 author = self.rhodecode_user.full_contact
335
335
336 if not content:
336 if not content:
337 h.flash(_('No content'), category='warning')
337 h.flash(_('No content'), category='warning')
338 return redirect(url('changeset_home', repo_name=c.repo_name,
338 return redirect(url('changeset_home', repo_name=c.repo_name,
339 revision='tip'))
339 revision='tip'))
340 if not filename:
340 if not filename:
341 h.flash(_('No filename'), category='warning')
341 h.flash(_('No filename'), category='warning')
342 return redirect(url('changeset_home', repo_name=c.repo_name,
342 return redirect(url('changeset_home', repo_name=c.repo_name,
343 revision='tip'))
343 revision='tip'))
344
344
345 try:
345 try:
346 self.scm_model.create_node(repo=c.rhodecode_repo,
346 self.scm_model.create_node(repo=c.rhodecode_repo,
347 repo_name=repo_name, cs=c.cs,
347 repo_name=repo_name, cs=c.cs,
348 user=self.rhodecode_user,
348 user=self.rhodecode_user,
349 author=author, message=message,
349 author=author, message=message,
350 content=content, f_path=node_path)
350 content=content, f_path=node_path)
351 h.flash(_('Successfully committed to %s') % node_path,
351 h.flash(_('Successfully committed to %s') % node_path,
352 category='success')
352 category='success')
353 except NodeAlreadyExistsError, e:
353 except NodeAlreadyExistsError, e:
354 h.flash(_(e), category='error')
354 h.flash(_(e), category='error')
355 except Exception:
355 except Exception:
356 log.error(traceback.format_exc())
356 log.error(traceback.format_exc())
357 h.flash(_('Error occurred during commit'), category='error')
357 h.flash(_('Error occurred during commit'), category='error')
358 return redirect(url('changeset_home',
358 return redirect(url('changeset_home',
359 repo_name=c.repo_name, revision='tip'))
359 repo_name=c.repo_name, revision='tip'))
360
360
361 return render('files/files_add.html')
361 return render('files/files_add.html')
362
362
363 @LoginRequired()
363 @LoginRequired()
364 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
364 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
365 'repository.admin')
365 'repository.admin')
366 def archivefile(self, repo_name, fname):
366 def archivefile(self, repo_name, fname):
367
367
368 fileformat = None
368 fileformat = None
369 revision = None
369 revision = None
370 ext = None
370 ext = None
371 subrepos = request.GET.get('subrepos') == 'true'
371 subrepos = request.GET.get('subrepos') == 'true'
372
372
373 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
373 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
374 archive_spec = fname.split(ext_data[1])
374 archive_spec = fname.split(ext_data[1])
375 if len(archive_spec) == 2 and archive_spec[1] == '':
375 if len(archive_spec) == 2 and archive_spec[1] == '':
376 fileformat = a_type or ext_data[1]
376 fileformat = a_type or ext_data[1]
377 revision = archive_spec[0]
377 revision = archive_spec[0]
378 ext = ext_data[1]
378 ext = ext_data[1]
379
379
380 try:
380 try:
381 dbrepo = RepoModel().get_by_repo_name(repo_name)
381 dbrepo = RepoModel().get_by_repo_name(repo_name)
382 if dbrepo.enable_downloads is False:
382 if dbrepo.enable_downloads is False:
383 return _('downloads disabled')
383 return _('downloads disabled')
384
384
385 if c.rhodecode_repo.alias == 'hg':
385 if c.rhodecode_repo.alias == 'hg':
386 # patch and reset hooks section of UI config to not run any
386 # patch and reset hooks section of UI config to not run any
387 # hooks on fetching archives with subrepos
387 # hooks on fetching archives with subrepos
388 for k, v in c.rhodecode_repo._repo.ui.configitems('hooks'):
388 for k, v in c.rhodecode_repo._repo.ui.configitems('hooks'):
389 c.rhodecode_repo._repo.ui.setconfig('hooks', k, None)
389 c.rhodecode_repo._repo.ui.setconfig('hooks', k, None)
390
390
391 cs = c.rhodecode_repo.get_changeset(revision)
391 cs = c.rhodecode_repo.get_changeset(revision)
392 content_type = settings.ARCHIVE_SPECS[fileformat][0]
392 content_type = settings.ARCHIVE_SPECS[fileformat][0]
393 except ChangesetDoesNotExistError:
393 except ChangesetDoesNotExistError:
394 return _('Unknown revision %s') % revision
394 return _('Unknown revision %s') % revision
395 except EmptyRepositoryError:
395 except EmptyRepositoryError:
396 return _('Empty repository')
396 return _('Empty repository')
397 except (ImproperArchiveTypeError, KeyError):
397 except (ImproperArchiveTypeError, KeyError):
398 return _('Unknown archive type')
398 return _('Unknown archive type')
399
399
400 fd, archive = tempfile.mkstemp()
400 fd, archive = tempfile.mkstemp()
401 t = open(archive, 'wb')
401 t = open(archive, 'wb')
402 cs.fill_archive(stream=t, kind=fileformat, subrepos=subrepos)
402 cs.fill_archive(stream=t, kind=fileformat, subrepos=subrepos)
403 t.close()
403 t.close()
404
404
405 def get_chunked_archive(archive):
405 def get_chunked_archive(archive):
406 stream = open(archive, 'rb')
406 stream = open(archive, 'rb')
407 while True:
407 while True:
408 data = stream.read(16 * 1024)
408 data = stream.read(16 * 1024)
409 if not data:
409 if not data:
410 stream.close()
410 stream.close()
411 os.close(fd)
411 os.close(fd)
412 os.remove(archive)
412 os.remove(archive)
413 break
413 break
414 yield data
414 yield data
415
415
416 response.content_disposition = str('attachment; filename=%s-%s%s' \
416 response.content_disposition = str('attachment; filename=%s-%s%s' \
417 % (repo_name, revision[:12], ext))
417 % (repo_name, revision[:12], ext))
418 response.content_type = str(content_type)
418 response.content_type = str(content_type)
419 return get_chunked_archive(archive)
419 return get_chunked_archive(archive)
420
420
421 @LoginRequired()
421 @LoginRequired()
422 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
422 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
423 'repository.admin')
423 'repository.admin')
424 def diff(self, repo_name, f_path):
424 def diff(self, repo_name, f_path):
425 ignore_whitespace = request.GET.get('ignorews') == '1'
425 ignore_whitespace = request.GET.get('ignorews') == '1'
426 line_context = request.GET.get('context', 3)
426 line_context = request.GET.get('context', 3)
427 diff1 = request.GET.get('diff1', '')
427 diff1 = request.GET.get('diff1', '')
428 diff2 = request.GET.get('diff2', '')
428 diff2 = request.GET.get('diff2', '')
429 c.action = request.GET.get('diff')
429 c.action = request.GET.get('diff')
430 c.no_changes = diff1 == diff2
430 c.no_changes = diff1 == diff2
431 c.f_path = f_path
431 c.f_path = f_path
432 c.big_diff = False
432 c.big_diff = False
433 c.anchor_url = anchor_url
433 c.anchor_url = anchor_url
434 c.ignorews_url = _ignorews_url
434 c.ignorews_url = _ignorews_url
435 c.context_url = _context_url
435 c.context_url = _context_url
436 c.changes = OrderedDict()
436 c.changes = OrderedDict()
437 c.changes[diff2] = []
437 c.changes[diff2] = []
438
438
439 #special case if we want a show rev only, it's impl here
439 #special case if we want a show rev only, it's impl here
440 #to reduce JS and callbacks
440 #to reduce JS and callbacks
441 if request.GET.get('show_rev'):
441 if request.GET.get('show_rev'):
442 if str2bool(request.GET.get('annotate', 'False')):
442 if str2bool(request.GET.get('annotate', 'False')):
443 _url = url('files_annotate_home', repo_name=c.repo_name,
443 _url = url('files_annotate_home', repo_name=c.repo_name,
444 revision=diff1, f_path=c.f_path)
444 revision=diff1, f_path=c.f_path)
445 else:
445 else:
446 _url = url('files_home', repo_name=c.repo_name,
446 _url = url('files_home', repo_name=c.repo_name,
447 revision=diff1, f_path=c.f_path)
447 revision=diff1, f_path=c.f_path)
448
448
449 return redirect(_url)
449 return redirect(_url)
450 try:
450 try:
451 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
451 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
452 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
452 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
453 node1 = c.changeset_1.get_node(f_path)
453 node1 = c.changeset_1.get_node(f_path)
454 else:
454 else:
455 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
455 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
456 node1 = FileNode('.', '', changeset=c.changeset_1)
456 node1 = FileNode('.', '', changeset=c.changeset_1)
457
457
458 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
458 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
459 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
459 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
460 node2 = c.changeset_2.get_node(f_path)
460 node2 = c.changeset_2.get_node(f_path)
461 else:
461 else:
462 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
462 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
463 node2 = FileNode('.', '', changeset=c.changeset_2)
463 node2 = FileNode('.', '', changeset=c.changeset_2)
464 except RepositoryError:
464 except RepositoryError:
465 return redirect(url('files_home', repo_name=c.repo_name,
465 return redirect(url('files_home', repo_name=c.repo_name,
466 f_path=f_path))
466 f_path=f_path))
467
467
468 if c.action == 'download':
468 if c.action == 'download':
469 _diff = diffs.get_gitdiff(node1, node2,
469 _diff = diffs.get_gitdiff(node1, node2,
470 ignore_whitespace=ignore_whitespace,
470 ignore_whitespace=ignore_whitespace,
471 context=line_context)
471 context=line_context)
472 diff = diffs.DiffProcessor(_diff, format='gitdiff')
472 diff = diffs.DiffProcessor(_diff, format='gitdiff')
473
473
474 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
474 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
475 response.content_type = 'text/plain'
475 response.content_type = 'text/plain'
476 response.content_disposition = (
476 response.content_disposition = (
477 'attachment; filename=%s' % diff_name
477 'attachment; filename=%s' % diff_name
478 )
478 )
479 return diff.raw_diff()
479 return diff.raw_diff()
480
480
481 elif c.action == 'raw':
481 elif c.action == 'raw':
482 _diff = diffs.get_gitdiff(node1, node2,
482 _diff = diffs.get_gitdiff(node1, node2,
483 ignore_whitespace=ignore_whitespace,
483 ignore_whitespace=ignore_whitespace,
484 context=line_context)
484 context=line_context)
485 diff = diffs.DiffProcessor(_diff, format='gitdiff')
485 diff = diffs.DiffProcessor(_diff, format='gitdiff')
486 response.content_type = 'text/plain'
486 response.content_type = 'text/plain'
487 return diff.raw_diff()
487 return diff.raw_diff()
488
488
489 else:
489 else:
490 fid = h.FID(diff2, node2.path)
490 fid = h.FID(diff2, node2.path)
491 line_context_lcl = get_line_ctx(fid, request.GET)
491 line_context_lcl = get_line_ctx(fid, request.GET)
492 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
492 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
493
493
494 lim = request.GET.get('fulldiff') or self.cut_off_limit
494 lim = request.GET.get('fulldiff') or self.cut_off_limit
495 _, cs1, cs2, diff, st = diffs.wrapped_diff(filenode_old=node1,
495 _, cs1, cs2, diff, st = diffs.wrapped_diff(filenode_old=node1,
496 filenode_new=node2,
496 filenode_new=node2,
497 cut_off_limit=lim,
497 cut_off_limit=lim,
498 ignore_whitespace=ign_whitespace_lcl,
498 ignore_whitespace=ign_whitespace_lcl,
499 line_context=line_context_lcl,
499 line_context=line_context_lcl,
500 enable_comments=False)
500 enable_comments=False)
501
501
502 c.changes = [('', node2, diff, cs1, cs2, st,)]
502 c.changes = [('', node2, diff, cs1, cs2, st,)]
503
503
504 return render('files/file_diff.html')
504 return render('files/file_diff.html')
505
505
506 def _get_node_history(self, cs, f_path, changesets=None):
506 def _get_node_history(self, cs, f_path, changesets=None):
507 if cs is None:
507 if cs is None:
508 # if we pass empty CS calculate history based on tip
508 # if we pass empty CS calculate history based on tip
509 cs = c.rhodecode_repo.get_changeset()
509 cs = c.rhodecode_repo.get_changeset()
510 if changesets is None:
510 if changesets is None:
511 changesets = cs.get_file_history(f_path)
511 changesets = cs.get_file_history(f_path)
512
512
513 hist_l = []
513 hist_l = []
514
514
515 changesets_group = ([], _("Changesets"))
515 changesets_group = ([], _("Changesets"))
516 branches_group = ([], _("Branches"))
516 branches_group = ([], _("Branches"))
517 tags_group = ([], _("Tags"))
517 tags_group = ([], _("Tags"))
518 _hg = cs.repository.alias == 'hg'
518 _hg = cs.repository.alias == 'hg'
519 for chs in changesets:
519 for chs in changesets:
520 _branch = '(%s)' % chs.branch if _hg else ''
520 _branch = '(%s)' % chs.branch if _hg else ''
521 n_desc = 'r%s:%s %s' % (chs.revision, chs.short_id, _branch)
521 n_desc = 'r%s:%s %s' % (chs.revision, chs.short_id, _branch)
522 changesets_group[0].append((chs.raw_id, n_desc,))
522 changesets_group[0].append((chs.raw_id, n_desc,))
523
523
524 hist_l.append(changesets_group)
524 hist_l.append(changesets_group)
525
525
526 for name, chs in c.rhodecode_repo.branches.items():
526 for name, chs in c.rhodecode_repo.branches.items():
527 branches_group[0].append((chs, name),)
527 branches_group[0].append((chs, name),)
528 hist_l.append(branches_group)
528 hist_l.append(branches_group)
529
529
530 for name, chs in c.rhodecode_repo.tags.items():
530 for name, chs in c.rhodecode_repo.tags.items():
531 tags_group[0].append((chs, name),)
531 tags_group[0].append((chs, name),)
532 hist_l.append(tags_group)
532 hist_l.append(tags_group)
533
533
534 return hist_l
534 return hist_l
535
535
536 @LoginRequired()
536 @LoginRequired()
537 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
537 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
538 'repository.admin')
538 'repository.admin')
539 @jsonify
539 @jsonify
540 def nodelist(self, repo_name, revision, f_path):
540 def nodelist(self, repo_name, revision, f_path):
541 if request.environ.get('HTTP_X_PARTIAL_XHR'):
541 if request.environ.get('HTTP_X_PARTIAL_XHR'):
542 cs = self.__get_cs_or_redirect(revision, repo_name)
542 cs = self.__get_cs_or_redirect(revision, repo_name)
543 _d, _f = ScmModel().get_nodes(repo_name, cs.raw_id, f_path,
543 _d, _f = ScmModel().get_nodes(repo_name, cs.raw_id, f_path,
544 flat=False)
544 flat=False)
545 return {'nodes': _d + _f}
545 return {'nodes': _d + _f}
@@ -1,31 +1,31 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.middleware.errormator
3 rhodecode.lib.middleware.errormator
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 middleware to handle errormator publishing of errors
6 middleware to handle errormator publishing of errors
7
7
8 :created_on: October 18, 2012
8 :created_on: October 18, 2012
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 try:
26 try:
27 from errormator_client import make_errormator_middleware
27 from errormator_client import make_errormator_middleware
28 except ImportError:
28 except ImportError:
29 Errormator = None
29 Errormator = None
30 else:
30 else:
31 Errormator = make_errormator_middleware No newline at end of file
31 Errormator = make_errormator_middleware
@@ -1,47 +1,47 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.middleware.sentry
3 rhodecode.lib.middleware.sentry
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 middleware to handle sentry/raven publishing of errors
6 middleware to handle sentry/raven publishing of errors
7
7
8 :created_on: September 18, 2012
8 :created_on: September 18, 2012
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 try:
26 try:
27 from raven.base import Client
27 from raven.base import Client
28 from raven.contrib.pylons import list_from_setting
28 from raven.contrib.pylons import list_from_setting
29 from raven.middleware import Sentry as Middleware
29 from raven.middleware import Sentry as Middleware
30 except ImportError:
30 except ImportError:
31 Sentry = None
31 Sentry = None
32 else:
32 else:
33 class Sentry(Middleware):
33 class Sentry(Middleware):
34 def __init__(self, app, config, client_cls=Client):
34 def __init__(self, app, config, client_cls=Client):
35 client = client_cls(
35 client = client_cls(
36 dsn=config.get('sentry.dsn'),
36 dsn=config.get('sentry.dsn'),
37 servers=list_from_setting(config, 'sentry.servers'),
37 servers=list_from_setting(config, 'sentry.servers'),
38 name=config.get('sentry.name'),
38 name=config.get('sentry.name'),
39 key=config.get('sentry.key'),
39 key=config.get('sentry.key'),
40 public_key=config.get('sentry.public_key'),
40 public_key=config.get('sentry.public_key'),
41 secret_key=config.get('sentry.secret_key'),
41 secret_key=config.get('sentry.secret_key'),
42 project=config.get('sentry.project'),
42 project=config.get('sentry.project'),
43 site=config.get('sentry.site'),
43 site=config.get('sentry.site'),
44 include_paths=list_from_setting(config, 'sentry.include_paths'),
44 include_paths=list_from_setting(config, 'sentry.include_paths'),
45 exclude_paths=list_from_setting(config, 'sentry.exclude_paths'),
45 exclude_paths=list_from_setting(config, 'sentry.exclude_paths'),
46 )
46 )
47 super(Sentry, self).__init__(app, client) No newline at end of file
47 super(Sentry, self).__init__(app, client)
@@ -1,302 +1,302 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2
2
3 <%inherit file="/base/base.html"/>
3 <%inherit file="/base/base.html"/>
4
4
5 <%def name="title()">
5 <%def name="title()">
6 ${_('%s Changelog') % c.repo_name} - ${c.rhodecode_name}
6 ${_('%s Changelog') % c.repo_name} - ${c.rhodecode_name}
7 </%def>
7 </%def>
8
8
9 <%def name="breadcrumbs_links()">
9 <%def name="breadcrumbs_links()">
10 ${h.link_to(_(u'Home'),h.url('/'))}
10 ${h.link_to(_(u'Home'),h.url('/'))}
11 &raquo;
11 &raquo;
12 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
12 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
13 &raquo;
13 &raquo;
14 <% size = c.size if c.size <= c.total_cs else c.total_cs %>
14 <% size = c.size if c.size <= c.total_cs else c.total_cs %>
15 ${_('Changelog')} - ${ungettext('showing %d out of %d revision', 'showing %d out of %d revisions', size) % (size, c.total_cs)}
15 ${_('Changelog')} - ${ungettext('showing %d out of %d revision', 'showing %d out of %d revisions', size) % (size, c.total_cs)}
16 </%def>
16 </%def>
17
17
18 <%def name="page_nav()">
18 <%def name="page_nav()">
19 ${self.menu('changelog')}
19 ${self.menu('changelog')}
20 </%def>
20 </%def>
21
21
22 <%def name="main()">
22 <%def name="main()">
23 <div class="box">
23 <div class="box">
24 <!-- box / title -->
24 <!-- box / title -->
25 <div class="title">
25 <div class="title">
26 ${self.breadcrumbs()}
26 ${self.breadcrumbs()}
27 </div>
27 </div>
28 <div class="table">
28 <div class="table">
29 % if c.pagination:
29 % if c.pagination:
30 <div id="graph">
30 <div id="graph">
31 <div id="graph_nodes">
31 <div id="graph_nodes">
32 <canvas id="graph_canvas"></canvas>
32 <canvas id="graph_canvas"></canvas>
33 </div>
33 </div>
34 <div id="graph_content">
34 <div id="graph_content">
35 <div class="info_box" style="clear: both;padding: 10px 6px;vertical-align: right;text-align: right;">
35 <div class="info_box" style="clear: both;padding: 10px 6px;vertical-align: right;text-align: right;">
36 <a href="#" class="ui-btn small" id="rev_range_container" style="display:none"></a>
36 <a href="#" class="ui-btn small" id="rev_range_container" style="display:none"></a>
37 <a href="#" class="ui-btn small" id="rev_range_clear" style="display:none">${_('Clear selection')}</a>
37 <a href="#" class="ui-btn small" id="rev_range_clear" style="display:none">${_('Clear selection')}</a>
38
38
39 %if c.rhodecode_db_repo.fork:
39 %if c.rhodecode_db_repo.fork:
40 <a title="${_('compare fork with %s' % c.rhodecode_db_repo.fork.repo_name)}" href="${h.url('compare_url',repo_name=c.repo_name,org_ref_type='branch',org_ref=request.GET.get('branch') or 'default',other_ref_type='branch',other_ref='default',repo=c.rhodecode_db_repo.fork.repo_name)}" class="ui-btn small">${_('Compare fork')}</a>
40 <a title="${_('compare fork with %s' % c.rhodecode_db_repo.fork.repo_name)}" href="${h.url('compare_url',repo_name=c.repo_name,org_ref_type='branch',org_ref=request.GET.get('branch') or 'default',other_ref_type='branch',other_ref='default',repo=c.rhodecode_db_repo.fork.repo_name)}" class="ui-btn small">${_('Compare fork')}</a>
41 %endif
41 %endif
42 %if h.is_hg(c.rhodecode_repo):
42 %if h.is_hg(c.rhodecode_repo):
43 <a id="open_new_pr" href="${h.url('pullrequest_home',repo_name=c.repo_name)}" class="ui-btn small">${_('Open new pull request')}</a>
43 <a id="open_new_pr" href="${h.url('pullrequest_home',repo_name=c.repo_name)}" class="ui-btn small">${_('Open new pull request')}</a>
44 %endif
44 %endif
45 </div>
45 </div>
46 <div class="container_header">
46 <div class="container_header">
47 ${h.form(h.url.current(),method='get')}
47 ${h.form(h.url.current(),method='get')}
48 <div class="info_box" style="float:left">
48 <div class="info_box" style="float:left">
49 ${h.submit('set',_('Show'),class_="ui-btn")}
49 ${h.submit('set',_('Show'),class_="ui-btn")}
50 ${h.text('size',size=1,value=c.size)}
50 ${h.text('size',size=1,value=c.size)}
51 ${_('revisions')}
51 ${_('revisions')}
52 </div>
52 </div>
53 ${h.end_form()}
53 ${h.end_form()}
54 <div style="float:right">${h.select('branch_filter',c.branch_name,c.branch_filters)}</div>
54 <div style="float:right">${h.select('branch_filter',c.branch_name,c.branch_filters)}</div>
55 </div>
55 </div>
56
56
57 %for cnt,cs in enumerate(c.pagination):
57 %for cnt,cs in enumerate(c.pagination):
58 <div id="chg_${cnt+1}" class="container ${'tablerow%s' % (cnt%2)}">
58 <div id="chg_${cnt+1}" class="container ${'tablerow%s' % (cnt%2)}">
59 <div class="left">
59 <div class="left">
60 <div>
60 <div>
61 ${h.checkbox(cs.raw_id,class_="changeset_range")}
61 ${h.checkbox(cs.raw_id,class_="changeset_range")}
62 <span class="tooltip" title="${h.tooltip(h.age(cs.date))}"><a href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id)}"><span class="changeset_id">${cs.revision}:<span class="changeset_hash">${h.short_id(cs.raw_id)}</span></span></a></span>
62 <span class="tooltip" title="${h.tooltip(h.age(cs.date))}"><a href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id)}"><span class="changeset_id">${cs.revision}:<span class="changeset_hash">${h.short_id(cs.raw_id)}</span></span></a></span>
63 </div>
63 </div>
64 <div class="author">
64 <div class="author">
65 <div class="gravatar">
65 <div class="gravatar">
66 <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(cs.author),16)}"/>
66 <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(cs.author),16)}"/>
67 </div>
67 </div>
68 <div title="${cs.author}" class="user">${h.shorter(h.person(cs.author),22)}</div>
68 <div title="${cs.author}" class="user">${h.shorter(h.person(cs.author),22)}</div>
69 </div>
69 </div>
70 <div class="date">${h.fmt_date(cs.date)}</div>
70 <div class="date">${h.fmt_date(cs.date)}</div>
71 </div>
71 </div>
72 <div class="mid">
72 <div class="mid">
73 <div class="message">${h.urlify_commit(cs.message, c.repo_name,h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</div>
73 <div class="message">${h.urlify_commit(cs.message, c.repo_name,h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</div>
74 <div class="expand"><span class="expandtext">&darr; ${_('show more')} &darr;</span></div>
74 <div class="expand"><span class="expandtext">&darr; ${_('show more')} &darr;</span></div>
75 </div>
75 </div>
76 <div class="right">
76 <div class="right">
77 <div class="changes">
77 <div class="changes">
78 <div id="changed_total_${cs.raw_id}" style="float:right;" class="changed_total tooltip" title="${h.tooltip(_('Affected number of files, click to show more details'))}">${len(cs.affected_files)}</div>
78 <div id="changed_total_${cs.raw_id}" style="float:right;" class="changed_total tooltip" title="${h.tooltip(_('Affected number of files, click to show more details'))}">${len(cs.affected_files)}</div>
79 <div class="comments-container">
79 <div class="comments-container">
80 %if len(c.comments.get(cs.raw_id,[])) > 0:
80 %if len(c.comments.get(cs.raw_id,[])) > 0:
81 <div class="comments-cnt" title="${('comments')}">
81 <div class="comments-cnt" title="${('comments')}">
82 <a href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id,anchor='comment-%s' % c.comments[cs.raw_id][0].comment_id)}">
82 <a href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id,anchor='comment-%s' % c.comments[cs.raw_id][0].comment_id)}">
83 <div class="comments-cnt">${len(c.comments[cs.raw_id])}</div>
83 <div class="comments-cnt">${len(c.comments[cs.raw_id])}</div>
84 <img src="${h.url('/images/icons/comments.png')}">
84 <img src="${h.url('/images/icons/comments.png')}">
85 </a>
85 </a>
86 </div>
86 </div>
87 %endif
87 %endif
88 </div>
88 </div>
89 <div class="changeset-status-container">
89 <div class="changeset-status-container">
90 %if c.statuses.get(cs.raw_id):
90 %if c.statuses.get(cs.raw_id):
91 <div title="${_('Changeset status')}" class="changeset-status-lbl">${c.statuses.get(cs.raw_id)[1]}</div>
91 <div title="${_('Changeset status')}" class="changeset-status-lbl">${c.statuses.get(cs.raw_id)[1]}</div>
92 <div class="changeset-status-ico">
92 <div class="changeset-status-ico">
93 %if c.statuses.get(cs.raw_id)[2]:
93 %if c.statuses.get(cs.raw_id)[2]:
94 <a class="tooltip" title="${_('Click to open associated pull request')}" href="${h.url('pullrequest_show',repo_name=c.statuses.get(cs.raw_id)[3],pull_request_id=c.statuses.get(cs.raw_id)[2])}"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses.get(cs.raw_id)[0])}" /></a>
94 <a class="tooltip" title="${_('Click to open associated pull request')}" href="${h.url('pullrequest_show',repo_name=c.statuses.get(cs.raw_id)[3],pull_request_id=c.statuses.get(cs.raw_id)[2])}"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses.get(cs.raw_id)[0])}" /></a>
95 %else:
95 %else:
96 <img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses.get(cs.raw_id)[0])}" />
96 <img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses.get(cs.raw_id)[0])}" />
97 %endif
97 %endif
98 </div>
98 </div>
99 %endif
99 %endif
100 </div>
100 </div>
101 </div>
101 </div>
102 %if cs.parents:
102 %if cs.parents:
103 %for p_cs in reversed(cs.parents):
103 %for p_cs in reversed(cs.parents):
104 <div class="parent">${_('Parent')}
104 <div class="parent">${_('Parent')}
105 <span class="changeset_id">${p_cs.revision}:<span class="changeset_hash">${h.link_to(h.short_id(p_cs.raw_id),
105 <span class="changeset_id">${p_cs.revision}:<span class="changeset_hash">${h.link_to(h.short_id(p_cs.raw_id),
106 h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)}</span></span>
106 h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)}</span></span>
107 </div>
107 </div>
108 %endfor
108 %endfor
109 %else:
109 %else:
110 <div class="parent">${_('No parents')}</div>
110 <div class="parent">${_('No parents')}</div>
111 %endif
111 %endif
112
112
113 <span class="logtags">
113 <span class="logtags">
114 %if len(cs.parents)>1:
114 %if len(cs.parents)>1:
115 <span class="merge">${_('merge')}</span>
115 <span class="merge">${_('merge')}</span>
116 %endif
116 %endif
117 %if cs.branch:
117 %if cs.branch:
118 <span class="branchtag" title="${'%s %s' % (_('branch'),cs.branch)}">
118 <span class="branchtag" title="${'%s %s' % (_('branch'),cs.branch)}">
119 ${h.link_to(h.shorter(cs.branch),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}
119 ${h.link_to(h.shorter(cs.branch),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}
120 </span>
120 </span>
121 %endif
121 %endif
122 %if h.is_hg(c.rhodecode_repo):
122 %if h.is_hg(c.rhodecode_repo):
123 %for book in cs.bookmarks:
123 %for book in cs.bookmarks:
124 <span class="bookbook" title="${'%s %s' % (_('bookmark'),book)}">
124 <span class="bookbook" title="${'%s %s' % (_('bookmark'),book)}">
125 ${h.link_to(h.shorter(book),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}
125 ${h.link_to(h.shorter(book),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}
126 </span>
126 </span>
127 %endfor
127 %endfor
128 %endif
128 %endif
129 %for tag in cs.tags:
129 %for tag in cs.tags:
130 <span class="tagtag" title="${'%s %s' % (_('tag'),tag)}">
130 <span class="tagtag" title="${'%s %s' % (_('tag'),tag)}">
131 ${h.link_to(h.shorter(tag),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}</span>
131 ${h.link_to(h.shorter(tag),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}</span>
132 %endfor
132 %endfor
133 </span>
133 </span>
134 </div>
134 </div>
135 </div>
135 </div>
136
136
137 %endfor
137 %endfor
138 <div class="pagination-wh pagination-left">
138 <div class="pagination-wh pagination-left">
139 ${c.pagination.pager('$link_previous ~2~ $link_next')}
139 ${c.pagination.pager('$link_previous ~2~ $link_next')}
140 </div>
140 </div>
141 </div>
141 </div>
142 </div>
142 </div>
143
143
144 <script type="text/javascript" src="${h.url('/js/graph.js')}"></script>
144 <script type="text/javascript" src="${h.url('/js/graph.js')}"></script>
145 <script type="text/javascript">
145 <script type="text/javascript">
146 YAHOO.util.Event.onDOMReady(function(){
146 YAHOO.util.Event.onDOMReady(function(){
147
147
148 //Monitor range checkboxes and build a link to changesets
148 //Monitor range checkboxes and build a link to changesets
149 //ranges
149 //ranges
150 var checkboxes = YUD.getElementsByClassName('changeset_range');
150 var checkboxes = YUD.getElementsByClassName('changeset_range');
151 var url_tmpl = "${h.url('changeset_home',repo_name=c.repo_name,revision='__REVRANGE__')}";
151 var url_tmpl = "${h.url('changeset_home',repo_name=c.repo_name,revision='__REVRANGE__')}";
152 YUE.on(checkboxes,'click',function(e){
152 YUE.on(checkboxes,'click',function(e){
153 var clicked_cb = e.currentTarget;
153 var clicked_cb = e.currentTarget;
154 var checked_checkboxes = [];
154 var checked_checkboxes = [];
155 for (pos in checkboxes){
155 for (pos in checkboxes){
156 if(checkboxes[pos].checked){
156 if(checkboxes[pos].checked){
157 checked_checkboxes.push(checkboxes[pos]);
157 checked_checkboxes.push(checkboxes[pos]);
158 }
158 }
159 }
159 }
160
160
161 if(checked_checkboxes.length>0){
161 if(checked_checkboxes.length>0){
162 // modify open pull request to show we have selected cs
162 // modify open pull request to show we have selected cs
163 YUD.get('open_new_pr').innerHTML = _TM['Open new pull request for selected changesets'];
163 YUD.get('open_new_pr').innerHTML = _TM['Open new pull request for selected changesets'];
164
164
165 }else{
165 }else{
166 YUD.get('open_new_pr').innerHTML = _TM['Open new pull request'];
166 YUD.get('open_new_pr').innerHTML = _TM['Open new pull request'];
167 }
167 }
168
168
169 if(checked_checkboxes.length>1){
169 if(checked_checkboxes.length>1){
170 var rev_end = checked_checkboxes[0].name;
170 var rev_end = checked_checkboxes[0].name;
171 var rev_start = checked_checkboxes[checked_checkboxes.length-1].name;
171 var rev_start = checked_checkboxes[checked_checkboxes.length-1].name;
172
172
173 // now select all checkboxes in the middle.
173 // now select all checkboxes in the middle.
174 var checked = false;
174 var checked = false;
175 for (var i=0; i<checkboxes.length; i++){
175 for (var i=0; i<checkboxes.length; i++){
176 var cb = checkboxes[i];
176 var cb = checkboxes[i];
177 var rev = cb.name;
177 var rev = cb.name;
178
178
179 if (rev == rev_end){
179 if (rev == rev_end){
180 checked = true;
180 checked = true;
181 }
181 }
182 if (checked){
182 if (checked){
183 cb.checked = true;
183 cb.checked = true;
184 }
184 }
185 else{
185 else{
186 cb.checked = false;
186 cb.checked = false;
187 }
187 }
188 if (rev == rev_start){
188 if (rev == rev_start){
189 checked = false;
189 checked = false;
190 }
190 }
191
191
192 }
192 }
193
193
194 var url = url_tmpl.replace('__REVRANGE__',
194 var url = url_tmpl.replace('__REVRANGE__',
195 rev_start+'...'+rev_end);
195 rev_start+'...'+rev_end);
196
196
197 var link = _TM['Show selected changes __S -> __E'];
197 var link = _TM['Show selected changes __S -> __E'];
198 link = link.replace('__S',rev_start.substr(0,6));
198 link = link.replace('__S',rev_start.substr(0,6));
199 link = link.replace('__E',rev_end.substr(0,6));
199 link = link.replace('__E',rev_end.substr(0,6));
200 YUD.get('rev_range_container').href = url;
200 YUD.get('rev_range_container').href = url;
201 YUD.get('rev_range_container').innerHTML = link;
201 YUD.get('rev_range_container').innerHTML = link;
202 YUD.setStyle('rev_range_container','display','');
202 YUD.setStyle('rev_range_container','display','');
203 YUD.setStyle('rev_range_clear','display','');
203 YUD.setStyle('rev_range_clear','display','');
204
204
205 }
205 }
206 else{
206 else{
207 YUD.setStyle('rev_range_container','display','none');
207 YUD.setStyle('rev_range_container','display','none');
208 YUD.setStyle('rev_range_clear','display','none');
208 YUD.setStyle('rev_range_clear','display','none');
209 }
209 }
210 });
210 });
211 YUE.on('rev_range_clear','click',function(e){
211 YUE.on('rev_range_clear','click',function(e){
212 for (var i=0; i<checkboxes.length; i++){
212 for (var i=0; i<checkboxes.length; i++){
213 var cb = checkboxes[i];
213 var cb = checkboxes[i];
214 cb.checked = false;
214 cb.checked = false;
215 }
215 }
216 YUE.preventDefault(e);
216 YUE.preventDefault(e);
217 })
217 })
218 var msgs = YUQ('.message');
218 var msgs = YUQ('.message');
219 // get first element height
219 // get first element height
220 var el = YUQ('#graph_content .container')[0];
220 var el = YUQ('#graph_content .container')[0];
221 var row_h = el.clientHeight;
221 var row_h = el.clientHeight;
222 for(var i=0;i<msgs.length;i++){
222 for(var i=0;i<msgs.length;i++){
223 var m = msgs[i];
223 var m = msgs[i];
224
224
225 var h = m.clientHeight;
225 var h = m.clientHeight;
226 var pad = YUD.getStyle(m,'padding');
226 var pad = YUD.getStyle(m,'padding');
227 if(h > row_h){
227 if(h > row_h){
228 var offset = row_h - (h+12);
228 var offset = row_h - (h+12);
229 YUD.setStyle(m.nextElementSibling,'display','block');
229 YUD.setStyle(m.nextElementSibling,'display','block');
230 YUD.setStyle(m.nextElementSibling,'margin-top',offset+'px');
230 YUD.setStyle(m.nextElementSibling,'margin-top',offset+'px');
231 };
231 };
232 }
232 }
233 YUE.on(YUQ('.expand'),'click',function(e){
233 YUE.on(YUQ('.expand'),'click',function(e){
234 var elem = e.currentTarget.parentNode.parentNode;
234 var elem = e.currentTarget.parentNode.parentNode;
235 YUD.setStyle(e.currentTarget,'display','none');
235 YUD.setStyle(e.currentTarget,'display','none');
236 YUD.setStyle(elem,'height','auto');
236 YUD.setStyle(elem,'height','auto');
237
237
238 //redraw the graph, line_count and jsdata are global vars
238 //redraw the graph, line_count and jsdata are global vars
239 set_canvas(100);
239 set_canvas(100);
240
240
241 var r = new BranchRenderer();
241 var r = new BranchRenderer();
242 r.render(jsdata,100,line_count);
242 r.render(jsdata,100,line_count);
243
243
244 })
244 })
245
245
246 // Fetch changeset details
246 // Fetch changeset details
247 YUE.on(YUD.getElementsByClassName('changed_total'),'click',function(e){
247 YUE.on(YUD.getElementsByClassName('changed_total'),'click',function(e){
248 var id = e.currentTarget.id;
248 var id = e.currentTarget.id;
249 var url = "${h.url('changelog_details',repo_name=c.repo_name,cs='__CS__')}";
249 var url = "${h.url('changelog_details',repo_name=c.repo_name,cs='__CS__')}";
250 var url = url.replace('__CS__',id.replace('changed_total_',''));
250 var url = url.replace('__CS__',id.replace('changed_total_',''));
251 ypjax(url,id,function(){tooltip_activate()});
251 ypjax(url,id,function(){tooltip_activate()});
252 });
252 });
253
253
254 // change branch filter
254 // change branch filter
255 YUE.on(YUD.get('branch_filter'),'change',function(e){
255 YUE.on(YUD.get('branch_filter'),'change',function(e){
256 var selected_branch = e.currentTarget.options[e.currentTarget.selectedIndex].value;
256 var selected_branch = e.currentTarget.options[e.currentTarget.selectedIndex].value;
257 var url_main = "${h.url('changelog_home',repo_name=c.repo_name)}";
257 var url_main = "${h.url('changelog_home',repo_name=c.repo_name)}";
258 var url = "${h.url('changelog_home',repo_name=c.repo_name,branch='__BRANCH__')}";
258 var url = "${h.url('changelog_home',repo_name=c.repo_name,branch='__BRANCH__')}";
259 var url = url.replace('__BRANCH__',selected_branch);
259 var url = url.replace('__BRANCH__',selected_branch);
260 if(selected_branch != ''){
260 if(selected_branch != ''){
261 window.location = url;
261 window.location = url;
262 }else{
262 }else{
263 window.location = url_main;
263 window.location = url_main;
264 }
264 }
265
265
266 });
266 });
267
267
268 function set_canvas(width) {
268 function set_canvas(width) {
269 var c = document.getElementById('graph_nodes');
269 var c = document.getElementById('graph_nodes');
270 var t = document.getElementById('graph_content');
270 var t = document.getElementById('graph_content');
271 canvas = document.getElementById('graph_canvas');
271 canvas = document.getElementById('graph_canvas');
272 var div_h = t.clientHeight;
272 var div_h = t.clientHeight;
273 c.style.height=div_h+'px';
273 c.style.height=div_h+'px';
274 canvas.setAttribute('height',div_h);
274 canvas.setAttribute('height',div_h);
275 c.style.height=width+'px';
275 c.style.height=width+'px';
276 canvas.setAttribute('width',width);
276 canvas.setAttribute('width',width);
277 };
277 };
278 var heads = 1;
278 var heads = 1;
279 var line_count = 0;
279 var line_count = 0;
280 var jsdata = ${c.jsdata|n};
280 var jsdata = ${c.jsdata|n};
281
281
282 for (var i=0;i<jsdata.length;i++) {
282 for (var i=0;i<jsdata.length;i++) {
283 var in_l = jsdata[i][2];
283 var in_l = jsdata[i][2];
284 for (var j in in_l) {
284 for (var j in in_l) {
285 var m = in_l[j][1];
285 var m = in_l[j][1];
286 if (m > line_count)
286 if (m > line_count)
287 line_count = m;
287 line_count = m;
288 }
288 }
289 }
289 }
290 set_canvas(100);
290 set_canvas(100);
291
291
292 var r = new BranchRenderer();
292 var r = new BranchRenderer();
293 r.render(jsdata,100,line_count);
293 r.render(jsdata,100,line_count);
294
294
295 });
295 });
296 </script>
296 </script>
297 %else:
297 %else:
298 ${_('There are no changes yet')}
298 ${_('There are no changes yet')}
299 %endif
299 %endif
300 </div>
300 </div>
301 </div>
301 </div>
302 </%def>
302 </%def>
@@ -1,122 +1,122 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('%s Changesets') % c.repo_name} - r${c.cs_ranges[0].revision}:${h.short_id(c.cs_ranges[0].raw_id)} -> r${c.cs_ranges[-1].revision}:${h.short_id(c.cs_ranges[-1].raw_id)} - ${c.rhodecode_name}
5 ${_('%s Changesets') % c.repo_name} - r${c.cs_ranges[0].revision}:${h.short_id(c.cs_ranges[0].raw_id)} -> r${c.cs_ranges[-1].revision}:${h.short_id(c.cs_ranges[-1].raw_id)} - ${c.rhodecode_name}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${h.link_to(_(u'Home'),h.url('/'))}
9 ${h.link_to(_(u'Home'),h.url('/'))}
10 &raquo;
10 &raquo;
11 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
11 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
12 &raquo;
12 &raquo;
13 ${_('Changesets')} - r${c.cs_ranges[0].revision}:${h.short_id(c.cs_ranges[0].raw_id)} -> r${c.cs_ranges[-1].revision}:${h.short_id(c.cs_ranges[-1].raw_id)}
13 ${_('Changesets')} - r${c.cs_ranges[0].revision}:${h.short_id(c.cs_ranges[0].raw_id)} -> r${c.cs_ranges[-1].revision}:${h.short_id(c.cs_ranges[-1].raw_id)}
14 </%def>
14 </%def>
15
15
16 <%def name="page_nav()">
16 <%def name="page_nav()">
17 ${self.menu('changelog')}
17 ${self.menu('changelog')}
18 </%def>
18 </%def>
19
19
20 <%def name="main()">
20 <%def name="main()">
21 <div class="box">
21 <div class="box">
22 <!-- box / title -->
22 <!-- box / title -->
23 <div class="title">
23 <div class="title">
24 ${self.breadcrumbs()}
24 ${self.breadcrumbs()}
25 </div>
25 </div>
26 <div class="table">
26 <div class="table">
27 <div id="body" class="diffblock">
27 <div id="body" class="diffblock">
28 <div class="code-header cv">
28 <div class="code-header cv">
29 <h3 class="code-header-title">${_('Compare View')}</h3>
29 <h3 class="code-header-title">${_('Compare View')}</h3>
30 <div>
30 <div>
31 ${_('Changesets')} - r${c.cs_ranges[0].revision}:${h.short_id(c.cs_ranges[0].raw_id)} -> r${c.cs_ranges[-1].revision}:${h.short_id(c.cs_ranges[-1].raw_id)}
31 ${_('Changesets')} - r${c.cs_ranges[0].revision}:${h.short_id(c.cs_ranges[0].raw_id)} -> r${c.cs_ranges[-1].revision}:${h.short_id(c.cs_ranges[-1].raw_id)}
32 </div>
32 </div>
33 </div>
33 </div>
34 </div>
34 </div>
35 <div id="changeset_compare_view_content">
35 <div id="changeset_compare_view_content">
36 <div class="container">
36 <div class="container">
37 <table class="compare_view_commits noborder">
37 <table class="compare_view_commits noborder">
38 %for cnt,cs in enumerate(c.cs_ranges):
38 %for cnt,cs in enumerate(c.cs_ranges):
39 <tr>
39 <tr>
40 <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(h.email_or_none(cs.author),14)}"/></div></td>
40 <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(h.email_or_none(cs.author),14)}"/></div></td>
41 <td>${h.link_to('r%s:%s' % (cs.revision,h.short_id(cs.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</td>
41 <td>${h.link_to('r%s:%s' % (cs.revision,h.short_id(cs.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</td>
42 <td><div class="author">${h.person(cs.author)}</div></td>
42 <td><div class="author">${h.person(cs.author)}</div></td>
43 <td><span class="tooltip" title="${h.age(cs.date)}">${cs.date}</span></td>
43 <td><span class="tooltip" title="${h.age(cs.date)}">${cs.date}</span></td>
44 <td>
44 <td>
45 %if c.statuses:
45 %if c.statuses:
46 <div title="${h.tooltip(_('Changeset status'))}" class="changeset-status-ico"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses[cnt])}" /></div>
46 <div title="${h.tooltip(_('Changeset status'))}" class="changeset-status-ico"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses[cnt])}" /></div>
47 %endif
47 %endif
48 </td>
48 </td>
49 <td><div class="message">${h.urlify_commit(h.wrap_paragraphs(cs.message),c.repo_name)}</div></td>
49 <td><div class="message">${h.urlify_commit(h.wrap_paragraphs(cs.message),c.repo_name)}</div></td>
50 </tr>
50 </tr>
51 %endfor
51 %endfor
52 </table>
52 </table>
53 </div>
53 </div>
54 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Files affected')}</div>
54 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Files affected')}</div>
55 <div class="cs_files">
55 <div class="cs_files">
56 %for cs in c.cs_ranges:
56 %for cs in c.cs_ranges:
57 <div class="cur_cs">${h.link_to('r%s:%s' % (cs.revision,h.short_id(cs.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</div>
57 <div class="cur_cs">${h.link_to('r%s:%s' % (cs.revision,h.short_id(cs.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</div>
58 %for change,filenode,diff,cs1,cs2,st in c.changes[cs.raw_id]:
58 %for change,filenode,diff,cs1,cs2,st in c.changes[cs.raw_id]:
59 <div class="cs_${change}">${h.link_to(h.safe_unicode(filenode.path),h.url.current(anchor=h.FID(cs.raw_id,filenode.path)))}</div>
59 <div class="cs_${change}">${h.link_to(h.safe_unicode(filenode.path),h.url.current(anchor=h.FID(cs.raw_id,filenode.path)))}</div>
60 %endfor
60 %endfor
61 %endfor
61 %endfor
62 </div>
62 </div>
63 </div>
63 </div>
64
64
65 </div>
65 </div>
66 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
66 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
67 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
67 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
68 %for cs in c.cs_ranges:
68 %for cs in c.cs_ranges:
69 ##${comment.comment_inline_form(cs)}
69 ##${comment.comment_inline_form(cs)}
70 ## diff block
70 ## diff block
71 <div class="h3">
71 <div class="h3">
72 <a class="tooltip" title="${h.tooltip(cs.message)}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id)}">${'r%s:%s' % (cs.revision,h.short_id(cs.raw_id))}</a>
72 <a class="tooltip" title="${h.tooltip(cs.message)}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id)}">${'r%s:%s' % (cs.revision,h.short_id(cs.raw_id))}</a>
73 <div class="gravatar">
73 <div class="gravatar">
74 <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(cs.author),20)}"/>
74 <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(cs.author),20)}"/>
75 </div>
75 </div>
76 <div class="right">
76 <div class="right">
77 <span class="logtags">
77 <span class="logtags">
78 %if len(cs.parents)>1:
78 %if len(cs.parents)>1:
79 <span class="merge">${_('merge')}</span>
79 <span class="merge">${_('merge')}</span>
80 %endif
80 %endif
81 %if cs.branch:
81 %if cs.branch:
82 <span class="branchtag" title="${'%s %s' % (_('branch'),cs.branch)}">
82 <span class="branchtag" title="${'%s %s' % (_('branch'),cs.branch)}">
83 ${h.link_to(h.shorter(cs.branch),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}
83 ${h.link_to(h.shorter(cs.branch),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}
84 </span>
84 </span>
85 %endif
85 %endif
86 %if h.is_hg(c.rhodecode_repo):
86 %if h.is_hg(c.rhodecode_repo):
87 %for book in cs.bookmarks:
87 %for book in cs.bookmarks:
88 <span class="bookbook" title="${'%s %s' % (_('bookmark'),book)}">
88 <span class="bookbook" title="${'%s %s' % (_('bookmark'),book)}">
89 ${h.link_to(h.shorter(book),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}
89 ${h.link_to(h.shorter(book),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}
90 </span>
90 </span>
91 %endfor
91 %endfor
92 %endif
92 %endif
93 %for tag in cs.tags:
93 %for tag in cs.tags:
94 <span class="tagtag" title="${'%s %s' % (_('tag'),tag)}">
94 <span class="tagtag" title="${'%s %s' % (_('tag'),tag)}">
95 ${h.link_to(h.shorter(tag),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}</span>
95 ${h.link_to(h.shorter(tag),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}</span>
96 %endfor
96 %endfor
97 </span>
97 </span>
98 </div>
98 </div>
99 </div>
99 </div>
100 ${diff_block.diff_block(c.changes[cs.raw_id])}
100 ${diff_block.diff_block(c.changes[cs.raw_id])}
101 ##${comment.comments(cs)}
101 ##${comment.comments(cs)}
102
102
103 %endfor
103 %endfor
104 <script type="text/javascript">
104 <script type="text/javascript">
105
105
106 YUE.onDOMReady(function(){
106 YUE.onDOMReady(function(){
107
107
108 YUE.on(YUQ('.diff-menu-activate'),'click',function(e){
108 YUE.on(YUQ('.diff-menu-activate'),'click',function(e){
109 var act = e.currentTarget.nextElementSibling;
109 var act = e.currentTarget.nextElementSibling;
110
110
111 if(YUD.hasClass(act,'active')){
111 if(YUD.hasClass(act,'active')){
112 YUD.removeClass(act,'active');
112 YUD.removeClass(act,'active');
113 YUD.setStyle(act,'display','none');
113 YUD.setStyle(act,'display','none');
114 }else{
114 }else{
115 YUD.addClass(act,'active');
115 YUD.addClass(act,'active');
116 YUD.setStyle(act,'display','');
116 YUD.setStyle(act,'display','');
117 }
117 }
118 });
118 });
119 })
119 })
120 </script>
120 </script>
121 </div>
121 </div>
122 </%def>
122 </%def>
@@ -1,32 +1,32 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2
2
3 % for f in c.followers_pager:
3 % for f in c.followers_pager:
4 <div>
4 <div>
5 <div class="follower_user">
5 <div class="follower_user">
6 <div class="gravatar">
6 <div class="gravatar">
7 <img alt="gravatar" src="${h.gravatar_url(f.user.email,24)}"/>
7 <img alt="gravatar" src="${h.gravatar_url(f.user.email,24)}"/>
8 </div>
8 </div>
9 <span style="font-size: 20px"> <b>${f.user.username}</b> (${f.user.name} ${f.user.lastname})</span>
9 <span style="font-size: 20px"> <b>${f.user.username}</b> (${f.user.name} ${f.user.lastname})</span>
10 </div>
10 </div>
11 <div style="clear:both;padding-top: 10px"></div>
11 <div style="clear:both;padding-top: 10px"></div>
12 <div class="follower_date">${_('Started following -')}
12 <div class="follower_date">${_('Started following -')}
13 <span class="tooltip" title="${h.tooltip(f.follows_from)}"> ${h.age(f.follows_from)}</span></div>
13 <span class="tooltip" title="${h.tooltip(f.follows_from)}"> ${h.age(f.follows_from)}</span></div>
14 <div style="border-bottom: 1px solid #DDD;margin:10px 0px 10px 0px"></div>
14 <div style="border-bottom: 1px solid #DDD;margin:10px 0px 10px 0px"></div>
15 </div>
15 </div>
16 % endfor
16 % endfor
17
17
18 <div class="pagination-wh pagination-left">
18 <div class="pagination-wh pagination-left">
19 <script type="text/javascript">
19 <script type="text/javascript">
20 YUE.onDOMReady(function(){
20 YUE.onDOMReady(function(){
21 YUE.delegate("followers","click",function(e, matchedEl, container){
21 YUE.delegate("followers","click",function(e, matchedEl, container){
22 ypjax(e.target.href,"followers",function(){
22 ypjax(e.target.href,"followers",function(){
23 show_more_event();
23 show_more_event();
24 tooltip_activate();
24 tooltip_activate();
25 show_changeset_tooltip();
25 show_changeset_tooltip();
26 });
26 });
27 YUE.preventDefault(e);
27 YUE.preventDefault(e);
28 },'.pager_link');
28 },'.pager_link');
29 });
29 });
30 </script>
30 </script>
31 ${c.followers_pager.pager('$link_previous ~2~ $link_next')}
31 ${c.followers_pager.pager('$link_previous ~2~ $link_next')}
32 </div>
32 </div>
@@ -1,43 +1,43 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2
2
3 % if c.forks_pager:
3 % if c.forks_pager:
4 % for f in c.forks_pager:
4 % for f in c.forks_pager:
5 <div>
5 <div>
6 <div class="fork_user">
6 <div class="fork_user">
7 <div class="gravatar">
7 <div class="gravatar">
8 <img alt="gravatar" src="${h.gravatar_url(f.user.email,24)}"/>
8 <img alt="gravatar" src="${h.gravatar_url(f.user.email,24)}"/>
9 </div>
9 </div>
10 <span style="font-size: 20px">
10 <span style="font-size: 20px">
11 <b>${f.user.username}</b> (${f.user.name} ${f.user.lastname}) /
11 <b>${f.user.username}</b> (${f.user.name} ${f.user.lastname}) /
12 ${h.link_to(f.repo_name,h.url('summary_home',repo_name=f.repo_name))}
12 ${h.link_to(f.repo_name,h.url('summary_home',repo_name=f.repo_name))}
13 </span>
13 </span>
14 <div style="padding:5px 3px 3px 42px;">${f.description}</div>
14 <div style="padding:5px 3px 3px 42px;">${f.description}</div>
15 </div>
15 </div>
16 <div style="clear:both;padding-top: 10px"></div>
16 <div style="clear:both;padding-top: 10px"></div>
17 <div class="follower_date">${_('forked')} -
17 <div class="follower_date">${_('forked')} -
18 <span class="tooltip" title="${h.tooltip(h.fmt_date(f.created_on))}"> ${h.age(f.created_on)}</span>
18 <span class="tooltip" title="${h.tooltip(h.fmt_date(f.created_on))}"> ${h.age(f.created_on)}</span>
19 <a title="${_('compare fork with %s' % c.repo_name)}"
19 <a title="${_('compare fork with %s' % c.repo_name)}"
20 href="${h.url('compare_url',repo_name=f.repo_name,org_ref_type='branch',org_ref='default',other_ref_type='branch',other_ref='default', repo=c.repo_name)}"
20 href="${h.url('compare_url',repo_name=f.repo_name,org_ref_type='branch',org_ref='default',other_ref_type='branch',other_ref='default', repo=c.repo_name)}"
21 class="ui-btn small">${_('Compare fork')}</a>
21 class="ui-btn small">${_('Compare fork')}</a>
22 </div>
22 </div>
23 <div style="border-bottom: 1px solid #DDD;margin:10px 0px 10px 0px"></div>
23 <div style="border-bottom: 1px solid #DDD;margin:10px 0px 10px 0px"></div>
24 </div>
24 </div>
25 % endfor
25 % endfor
26 <div class="pagination-wh pagination-left">
26 <div class="pagination-wh pagination-left">
27 <script type="text/javascript">
27 <script type="text/javascript">
28 YUE.onDOMReady(function(){
28 YUE.onDOMReady(function(){
29 YUE.delegate("forks","click",function(e, matchedEl, container){
29 YUE.delegate("forks","click",function(e, matchedEl, container){
30 ypjax(e.target.href,"forks",function(){
30 ypjax(e.target.href,"forks",function(){
31 show_more_event();
31 show_more_event();
32 tooltip_activate();
32 tooltip_activate();
33 show_changeset_tooltip();
33 show_changeset_tooltip();
34 });
34 });
35 YUE.preventDefault(e);
35 YUE.preventDefault(e);
36 },'.pager_link');
36 },'.pager_link');
37 });
37 });
38 </script>
38 </script>
39 ${c.forks_pager.pager('$link_previous ~2~ $link_next')}
39 ${c.forks_pager.pager('$link_previous ~2~ $link_next')}
40 </div>
40 </div>
41 % else:
41 % else:
42 ${_('There are no forks yet')}
42 ${_('There are no forks yet')}
43 % endif
43 % endif
@@ -1,332 +1,331 b''
1 <%page args="parent" />
1 <%page args="parent" />
2 <div class="box">
2 <div class="box">
3 <!-- box / title -->
3 <!-- box / title -->
4 <div class="title">
4 <div class="title">
5 <h5>
5 <h5>
6 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}"/> ${parent.breadcrumbs()} <span id="repo_count">0</span> ${_('repositories')}
6 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}"/> ${parent.breadcrumbs()} <span id="repo_count">0</span> ${_('repositories')}
7 </h5>
7 </h5>
8 %if c.rhodecode_user.username != 'default':
8 %if c.rhodecode_user.username != 'default':
9 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
9 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
10 <ul class="links">
10 <ul class="links">
11 <li>
11 <li>
12 %if c.group:
12 %if c.group:
13 <span>${h.link_to(_('ADD REPOSITORY'),h.url('admin_settings_create_repository',parent_group=c.group.group_id))}</span>
13 <span>${h.link_to(_('ADD REPOSITORY'),h.url('admin_settings_create_repository',parent_group=c.group.group_id))}</span>
14 %else:
14 %else:
15 <span>${h.link_to(_('ADD REPOSITORY'),h.url('admin_settings_create_repository'))}</span>
15 <span>${h.link_to(_('ADD REPOSITORY'),h.url('admin_settings_create_repository'))}</span>
16 %endif
16 %endif
17 </li>
17 </li>
18 </ul>
18 </ul>
19 %endif
19 %endif
20 %endif
20 %endif
21 </div>
21 </div>
22 <!-- end box / title -->
22 <!-- end box / title -->
23 <div class="table">
23 <div class="table">
24 % if c.groups:
24 % if c.groups:
25 <div id='groups_list_wrap' class="yui-skin-sam">
25 <div id='groups_list_wrap' class="yui-skin-sam">
26 <table id="groups_list">
26 <table id="groups_list">
27 <thead>
27 <thead>
28 <tr>
28 <tr>
29 <th class="left"><a href="#">${_('Group name')}</a></th>
29 <th class="left"><a href="#">${_('Group name')}</a></th>
30 <th class="left"><a href="#">${_('Description')}</a></th>
30 <th class="left"><a href="#">${_('Description')}</a></th>
31 ##<th class="left"><a href="#">${_('Number of repositories')}</a></th>
31 ##<th class="left"><a href="#">${_('Number of repositories')}</a></th>
32 </tr>
32 </tr>
33 </thead>
33 </thead>
34
34
35 ## REPO GROUPS
35 ## REPO GROUPS
36 % for gr in c.groups:
36 % for gr in c.groups:
37 <tr>
37 <tr>
38 <td>
38 <td>
39 <div style="white-space: nowrap">
39 <div style="white-space: nowrap">
40 <img class="icon" alt="${_('Repositories group')}" src="${h.url('/images/icons/database_link.png')}"/>
40 <img class="icon" alt="${_('Repositories group')}" src="${h.url('/images/icons/database_link.png')}"/>
41 ${h.link_to(gr.name,url('repos_group_home',group_name=gr.group_name))}
41 ${h.link_to(gr.name,url('repos_group_home',group_name=gr.group_name))}
42 </div>
42 </div>
43 </td>
43 </td>
44 %if c.visual.stylify_metatags:
44 %if c.visual.stylify_metatags:
45 <td>${h.urlify_text(h.desc_stylize(gr.group_description))}</td>
45 <td>${h.urlify_text(h.desc_stylize(gr.group_description))}</td>
46 %else:
46 %else:
47 <td>${gr.group_description}</td>
47 <td>${gr.group_description}</td>
48 %endif
48 %endif
49 ## this is commented out since for multi nested repos can be HEAVY!
49 ## this is commented out since for multi nested repos can be HEAVY!
50 ## in number of executed queries during traversing uncomment at will
50 ## in number of executed queries during traversing uncomment at will
51 ##<td><b>${gr.repositories_recursive_count}</b></td>
51 ##<td><b>${gr.repositories_recursive_count}</b></td>
52 </tr>
52 </tr>
53 % endfor
53 % endfor
54
54
55 </table>
55 </table>
56 </div>
56 </div>
57 <div style="height: 20px"></div>
57 <div style="height: 20px"></div>
58 % endif
58 % endif
59 <div id="welcome" style="display:none;text-align:center">
59 <div id="welcome" style="display:none;text-align:center">
60 <h1><a href="${h.url('home')}">${c.rhodecode_name} ${c.rhodecode_version}</a></h1>
60 <h1><a href="${h.url('home')}">${c.rhodecode_name} ${c.rhodecode_version}</a></h1>
61 </div>
61 </div>
62 <%cnt=0%>
62 <%cnt=0%>
63 <%namespace name="dt" file="/data_table/_dt_elements.html"/>
63 <%namespace name="dt" file="/data_table/_dt_elements.html"/>
64 % if c.visual.lightweight_dashboard is False:
64 % if c.visual.lightweight_dashboard is False:
65 ## old full detailed version
65 ## old full detailed version
66 <div id='repos_list_wrap' class="yui-skin-sam">
66 <div id='repos_list_wrap' class="yui-skin-sam">
67 <table id="repos_list">
67 <table id="repos_list">
68 <thead>
68 <thead>
69 <tr>
69 <tr>
70 <th class="left"></th>
70 <th class="left"></th>
71 <th class="left">${_('Name')}</th>
71 <th class="left">${_('Name')}</th>
72 <th class="left">${_('Description')}</th>
72 <th class="left">${_('Description')}</th>
73 <th class="left">${_('Last change')}</th>
73 <th class="left">${_('Last change')}</th>
74 <th class="left">${_('Tip')}</th>
74 <th class="left">${_('Tip')}</th>
75 <th class="left">${_('Owner')}</th>
75 <th class="left">${_('Owner')}</th>
76 <th class="left">${_('RSS')}</th>
76 <th class="left">${_('RSS')}</th>
77 <th class="left">${_('Atom')}</th>
77 <th class="left">${_('Atom')}</th>
78 </tr>
78 </tr>
79 </thead>
79 </thead>
80 <tbody>
80 <tbody>
81 %for cnt,repo in enumerate(c.repos_list):
81 %for cnt,repo in enumerate(c.repos_list):
82 <tr class="parity${(cnt+1)%2}">
82 <tr class="parity${(cnt+1)%2}">
83 ##QUICK MENU
83 ##QUICK MENU
84 <td class="quick_repo_menu">
84 <td class="quick_repo_menu">
85 ${dt.quick_menu(repo['name'])}
85 ${dt.quick_menu(repo['name'])}
86 </td>
86 </td>
87 ##REPO NAME AND ICONS
87 ##REPO NAME AND ICONS
88 <td class="reponame">
88 <td class="reponame">
89 ${dt.repo_name(repo['name'],repo['dbrepo']['repo_type'],repo['dbrepo']['private'],h.AttributeDict(repo['dbrepo_fork']),pageargs.get('short_repo_names'))}
89 ${dt.repo_name(repo['name'],repo['dbrepo']['repo_type'],repo['dbrepo']['private'],h.AttributeDict(repo['dbrepo_fork']),pageargs.get('short_repo_names'))}
90 </td>
90 </td>
91 ##DESCRIPTION
91 ##DESCRIPTION
92 <td><span class="tooltip" title="${h.tooltip(repo['description'])}">
92 <td><span class="tooltip" title="${h.tooltip(repo['description'])}">
93 %if c.visual.stylify_metatags:
93 %if c.visual.stylify_metatags:
94 ${h.urlify_text(h.desc_stylize(h.truncate(repo['description'],60)))}</span>
94 ${h.urlify_text(h.desc_stylize(h.truncate(repo['description'],60)))}</span>
95 %else:
95 %else:
96 ${h.truncate(repo['description'],60)}</span>
96 ${h.truncate(repo['description'],60)}</span>
97 %endif
97 %endif
98 </td>
98 </td>
99 ##LAST CHANGE DATE
99 ##LAST CHANGE DATE
100 <td>
100 <td>
101 ${dt.last_change(repo['last_change'])}
101 ${dt.last_change(repo['last_change'])}
102 </td>
102 </td>
103 ##LAST REVISION
103 ##LAST REVISION
104 <td>
104 <td>
105 ${dt.revision(repo['name'],repo['rev'],repo['tip'],repo['author'],repo['last_msg'])}
105 ${dt.revision(repo['name'],repo['rev'],repo['tip'],repo['author'],repo['last_msg'])}
106 </td>
106 </td>
107 ##
107 ##
108 <td title="${repo['contact']}">${h.person(repo['contact'])}</td>
108 <td title="${repo['contact']}">${h.person(repo['contact'])}</td>
109 <td>
109 <td>
110 ${dt.rss(repo['name'])}
110 ${dt.rss(repo['name'])}
111 </td>
111 </td>
112 <td>
112 <td>
113 ${dt.atom(repo['name'])}
113 ${dt.atom(repo['name'])}
114 </td>
114 </td>
115 </tr>
115 </tr>
116 %endfor
116 %endfor
117 </tbody>
117 </tbody>
118 </table>
118 </table>
119 </div>
119 </div>
120 % else:
120 % else:
121 ## lightweight version
121 ## lightweight version
122 <div class="yui-skin-sam" id="repos_list_wrap"></div>
122 <div class="yui-skin-sam" id="repos_list_wrap"></div>
123 <div id="user-paginator" style="padding: 0px 0px 0px 0px"></div>
123 <div id="user-paginator" style="padding: 0px 0px 0px 0px"></div>
124 % endif
124 % endif
125 </div>
125 </div>
126 </div>
126 </div>
127 % if c.visual.lightweight_dashboard is False:
127 % if c.visual.lightweight_dashboard is False:
128 <script>
128 <script>
129 YUD.get('repo_count').innerHTML = ${cnt+1 if cnt else 0};
129 YUD.get('repo_count').innerHTML = ${cnt+1 if cnt else 0};
130 var func = function(node){
130 var func = function(node){
131 return node.parentNode.parentNode.parentNode.parentNode;
131 return node.parentNode.parentNode.parentNode.parentNode;
132 }
132 }
133
133
134 // groups table sorting
134 // groups table sorting
135 var myColumnDefs = [
135 var myColumnDefs = [
136 {key:"name",label:"${_('Group name')}",sortable:true,
136 {key:"name",label:"${_('Group name')}",sortable:true,
137 sortOptions: { sortFunction: groupNameSort }},
137 sortOptions: { sortFunction: groupNameSort }},
138 {key:"desc",label:"${_('Description')}",sortable:true},
138 {key:"desc",label:"${_('Description')}",sortable:true},
139 ];
139 ];
140
140
141 var myDataSource = new YAHOO.util.DataSource(YUD.get("groups_list"));
141 var myDataSource = new YAHOO.util.DataSource(YUD.get("groups_list"));
142
142
143 myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
143 myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
144 myDataSource.responseSchema = {
144 myDataSource.responseSchema = {
145 fields: [
145 fields: [
146 {key:"name"},
146 {key:"name"},
147 {key:"desc"},
147 {key:"desc"},
148 ]
148 ]
149 };
149 };
150
150
151 var myDataTable = new YAHOO.widget.DataTable("groups_list_wrap", myColumnDefs, myDataSource,{
151 var myDataTable = new YAHOO.widget.DataTable("groups_list_wrap", myColumnDefs, myDataSource,{
152 sortedBy:{key:"name",dir:"asc"},
152 sortedBy:{key:"name",dir:"asc"},
153 paginator: new YAHOO.widget.Paginator({
153 paginator: new YAHOO.widget.Paginator({
154 rowsPerPage: 5,
154 rowsPerPage: 5,
155 alwaysVisible: false,
155 alwaysVisible: false,
156 template : "{PreviousPageLink} {FirstPageLink} {PageLinks} {LastPageLink} {NextPageLink}",
156 template : "{PreviousPageLink} {FirstPageLink} {PageLinks} {LastPageLink} {NextPageLink}",
157 pageLinks: 5,
157 pageLinks: 5,
158 containerClass: 'pagination-wh',
158 containerClass: 'pagination-wh',
159 currentPageClass: 'pager_curpage',
159 currentPageClass: 'pager_curpage',
160 pageLinkClass: 'pager_link',
160 pageLinkClass: 'pager_link',
161 nextPageLinkLabel: '&gt;',
161 nextPageLinkLabel: '&gt;',
162 previousPageLinkLabel: '&lt;',
162 previousPageLinkLabel: '&lt;',
163 firstPageLinkLabel: '&lt;&lt;',
163 firstPageLinkLabel: '&lt;&lt;',
164 lastPageLinkLabel: '&gt;&gt;',
164 lastPageLinkLabel: '&gt;&gt;',
165 containers:['user-paginator']
165 containers:['user-paginator']
166 }),
166 }),
167 MSG_SORTASC:"${_('Click to sort ascending')}",
167 MSG_SORTASC:"${_('Click to sort ascending')}",
168 MSG_SORTDESC:"${_('Click to sort descending')}"
168 MSG_SORTDESC:"${_('Click to sort descending')}"
169 });
169 });
170
170
171 // main table sorting
171 // main table sorting
172 var myColumnDefs = [
172 var myColumnDefs = [
173 {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"},
173 {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"},
174 {key:"name",label:"${_('Name')}",sortable:true,
174 {key:"name",label:"${_('Name')}",sortable:true,
175 sortOptions: { sortFunction: nameSort }},
175 sortOptions: { sortFunction: nameSort }},
176 {key:"desc",label:"${_('Description')}",sortable:true},
176 {key:"desc",label:"${_('Description')}",sortable:true},
177 {key:"last_change",label:"${_('Last Change')}",sortable:true,
177 {key:"last_change",label:"${_('Last Change')}",sortable:true,
178 sortOptions: { sortFunction: ageSort }},
178 sortOptions: { sortFunction: ageSort }},
179 {key:"tip",label:"${_('Tip')}",sortable:true,
179 {key:"tip",label:"${_('Tip')}",sortable:true,
180 sortOptions: { sortFunction: revisionSort }},
180 sortOptions: { sortFunction: revisionSort }},
181 {key:"owner",label:"${_('Owner')}",sortable:true},
181 {key:"owner",label:"${_('Owner')}",sortable:true},
182 {key:"rss",label:"",sortable:false},
182 {key:"rss",label:"",sortable:false},
183 {key:"atom",label:"",sortable:false},
183 {key:"atom",label:"",sortable:false},
184 ];
184 ];
185
185
186 var myDataSource = new YAHOO.util.DataSource(YUD.get("repos_list"));
186 var myDataSource = new YAHOO.util.DataSource(YUD.get("repos_list"));
187
187
188 myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
188 myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
189
189
190 myDataSource.responseSchema = {
190 myDataSource.responseSchema = {
191 fields: [
191 fields: [
192 {key:"menu"},
192 {key:"menu"},
193 //{key:"raw_name"},
193 //{key:"raw_name"},
194 {key:"name"},
194 {key:"name"},
195 {key:"desc"},
195 {key:"desc"},
196 {key:"last_change"},
196 {key:"last_change"},
197 {key:"tip"},
197 {key:"tip"},
198 {key:"owner"},
198 {key:"owner"},
199 {key:"rss"},
199 {key:"rss"},
200 {key:"atom"},
200 {key:"atom"},
201 ]
201 ]
202 };
202 };
203
203
204 var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,
204 var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,
205 {
205 {
206 sortedBy:{key:"name",dir:"asc"},
206 sortedBy:{key:"name",dir:"asc"},
207 MSG_SORTASC:"${_('Click to sort ascending')}",
207 MSG_SORTASC:"${_('Click to sort ascending')}",
208 MSG_SORTDESC:"${_('Click to sort descending')}",
208 MSG_SORTDESC:"${_('Click to sort descending')}",
209 MSG_EMPTY:"${_('No records found.')}",
209 MSG_EMPTY:"${_('No records found.')}",
210 MSG_ERROR:"${_('Data error.')}",
210 MSG_ERROR:"${_('Data error.')}",
211 MSG_LOADING:"${_('Loading...')}",
211 MSG_LOADING:"${_('Loading...')}",
212 }
212 }
213 );
213 );
214 myDataTable.subscribe('postRenderEvent',function(oArgs) {
214 myDataTable.subscribe('postRenderEvent',function(oArgs) {
215 tooltip_activate();
215 tooltip_activate();
216 quick_repo_menu();
216 quick_repo_menu();
217 q_filter('q_filter',YUQ('div.table tr td a.repo_name'),func);
217 q_filter('q_filter',YUQ('div.table tr td a.repo_name'),func);
218 });
218 });
219
219
220 </script>
220 </script>
221 % else:
221 % else:
222 <script>
222 <script>
223 //var url = "${h.url('formatted_users', format='json')}";
223 //var url = "${h.url('formatted_users', format='json')}";
224 var data = ${c.data|n};
224 var data = ${c.data|n};
225 var myDataSource = new YAHOO.util.DataSource(data);
225 var myDataSource = new YAHOO.util.DataSource(data);
226 myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
226 myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
227
227
228 myDataSource.responseSchema = {
228 myDataSource.responseSchema = {
229 resultsList: "records",
229 resultsList: "records",
230 fields: [
230 fields: [
231 {key:"menu"},
231 {key:"menu"},
232 {key:"raw_name"},
232 {key:"raw_name"},
233 {key:"name"},
233 {key:"name"},
234 {key:"desc"},
234 {key:"desc"},
235 {key:"last_change"},
235 {key:"last_change"},
236 {key:"owner"},
236 {key:"owner"},
237 {key:"rss"},
237 {key:"rss"},
238 {key:"atom"},
238 {key:"atom"},
239 ]
239 ]
240 };
240 };
241 myDataSource.doBeforeCallback = function(req,raw,res,cb) {
241 myDataSource.doBeforeCallback = function(req,raw,res,cb) {
242 // This is the filter function
242 // This is the filter function
243 var data = res.results || [],
243 var data = res.results || [],
244 filtered = [],
244 filtered = [],
245 i,l;
245 i,l;
246
246
247 if (req) {
247 if (req) {
248 req = req.toLowerCase();
248 req = req.toLowerCase();
249 for (i = 0; i<data.length; i++) {
249 for (i = 0; i<data.length; i++) {
250 var pos = data[i].raw_name.toLowerCase().indexOf(req)
250 var pos = data[i].raw_name.toLowerCase().indexOf(req)
251 if (pos != -1) {
251 if (pos != -1) {
252 filtered.push(data[i]);
252 filtered.push(data[i]);
253 }
253 }
254 }
254 }
255 res.results = filtered;
255 res.results = filtered;
256 }
256 }
257 YUD.get('repo_count').innerHTML = res.results.length;
257 YUD.get('repo_count').innerHTML = res.results.length;
258 return res;
258 return res;
259 }
259 }
260
260
261 // main table sorting
261 // main table sorting
262 var myColumnDefs = [
262 var myColumnDefs = [
263 {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"},
263 {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"},
264 {key:"name",label:"${_('Name')}",sortable:true,
264 {key:"name",label:"${_('Name')}",sortable:true,
265 sortOptions: { sortFunction: nameSort }},
265 sortOptions: { sortFunction: nameSort }},
266 {key:"desc",label:"${_('Description')}",sortable:true},
266 {key:"desc",label:"${_('Description')}",sortable:true},
267 {key:"last_change",label:"${_('Last Change')}",sortable:true,
267 {key:"last_change",label:"${_('Last Change')}",sortable:true,
268 sortOptions: { sortFunction: ageSort }},
268 sortOptions: { sortFunction: ageSort }},
269 {key:"owner",label:"${_('Owner')}",sortable:true},
269 {key:"owner",label:"${_('Owner')}",sortable:true},
270 {key:"rss",label:"",sortable:false},
270 {key:"rss",label:"",sortable:false},
271 {key:"atom",label:"",sortable:false},
271 {key:"atom",label:"",sortable:false},
272 ];
272 ];
273
273
274 var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,{
274 var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,{
275 sortedBy:{key:"name",dir:"asc"},
275 sortedBy:{key:"name",dir:"asc"},
276 paginator: new YAHOO.widget.Paginator({
276 paginator: new YAHOO.widget.Paginator({
277 rowsPerPage: 15,
277 rowsPerPage: 15,
278 alwaysVisible: false,
278 alwaysVisible: false,
279 template : "{PreviousPageLink} {FirstPageLink} {PageLinks} {LastPageLink} {NextPageLink}",
279 template : "{PreviousPageLink} {FirstPageLink} {PageLinks} {LastPageLink} {NextPageLink}",
280 pageLinks: 5,
280 pageLinks: 5,
281 containerClass: 'pagination-wh',
281 containerClass: 'pagination-wh',
282 currentPageClass: 'pager_curpage',
282 currentPageClass: 'pager_curpage',
283 pageLinkClass: 'pager_link',
283 pageLinkClass: 'pager_link',
284 nextPageLinkLabel: '&gt;',
284 nextPageLinkLabel: '&gt;',
285 previousPageLinkLabel: '&lt;',
285 previousPageLinkLabel: '&lt;',
286 firstPageLinkLabel: '&lt;&lt;',
286 firstPageLinkLabel: '&lt;&lt;',
287 lastPageLinkLabel: '&gt;&gt;',
287 lastPageLinkLabel: '&gt;&gt;',
288 containers:['user-paginator']
288 containers:['user-paginator']
289 }),
289 }),
290
290
291 MSG_SORTASC:"${_('Click to sort ascending')}",
291 MSG_SORTASC:"${_('Click to sort ascending')}",
292 MSG_SORTDESC:"${_('Click to sort descending')}",
292 MSG_SORTDESC:"${_('Click to sort descending')}",
293 MSG_EMPTY:"${_('No records found.')}",
293 MSG_EMPTY:"${_('No records found.')}",
294 MSG_ERROR:"${_('Data error.')}",
294 MSG_ERROR:"${_('Data error.')}",
295 MSG_LOADING:"${_('Loading...')}",
295 MSG_LOADING:"${_('Loading...')}",
296 }
296 }
297 );
297 );
298 myDataTable.subscribe('postRenderEvent',function(oArgs) {
298 myDataTable.subscribe('postRenderEvent',function(oArgs) {
299 tooltip_activate();
299 tooltip_activate();
300 quick_repo_menu();
300 quick_repo_menu();
301 });
301 });
302
302
303 var filterTimeout = null;
303 var filterTimeout = null;
304
304
305 updateFilter = function () {
305 updateFilter = function () {
306 // Reset timeout
306 // Reset timeout
307 filterTimeout = null;
307 filterTimeout = null;
308
308
309 // Reset sort
309 // Reset sort
310 var state = myDataTable.getState();
310 var state = myDataTable.getState();
311 state.sortedBy = {key:'name', dir:YAHOO.widget.DataTable.CLASS_ASC};
311 state.sortedBy = {key:'name', dir:YAHOO.widget.DataTable.CLASS_ASC};
312
312
313 // Get filtered data
313 // Get filtered data
314 myDataSource.sendRequest(YUD.get('q_filter').value,{
314 myDataSource.sendRequest(YUD.get('q_filter').value,{
315 success : myDataTable.onDataReturnInitializeTable,
315 success : myDataTable.onDataReturnInitializeTable,
316 failure : myDataTable.onDataReturnInitializeTable,
316 failure : myDataTable.onDataReturnInitializeTable,
317 scope : myDataTable,
317 scope : myDataTable,
318 argument: state
318 argument: state
319 });
319 });
320
320
321 };
321 };
322 YUE.on('q_filter','click',function(){
322 YUE.on('q_filter','click',function(){
323 YUD.get('q_filter').value = '';
323 YUD.get('q_filter').value = '';
324 });
324 });
325
325
326 YUE.on('q_filter','keyup',function (e) {
326 YUE.on('q_filter','keyup',function (e) {
327 clearTimeout(filterTimeout);
327 clearTimeout(filterTimeout);
328 filterTimeout = setTimeout(updateFilter,600);
328 filterTimeout = setTimeout(updateFilter,600);
329 });
329 });
330 </script>
330 </script>
331 % endif
331 % endif
332 No newline at end of file
@@ -1,210 +1,210 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3 <%def name="title()">
3 <%def name="title()">
4 ${_('Journal')} - ${c.rhodecode_name}
4 ${_('Journal')} - ${c.rhodecode_name}
5 </%def>
5 </%def>
6 <%def name="breadcrumbs()">
6 <%def name="breadcrumbs()">
7 ${c.rhodecode_name}
7 ${c.rhodecode_name}
8 </%def>
8 </%def>
9 <%def name="page_nav()">
9 <%def name="page_nav()">
10 ${self.menu('home')}
10 ${self.menu('home')}
11 </%def>
11 </%def>
12 <%def name="head_extra()">
12 <%def name="head_extra()">
13 <link href="${h.url('journal_atom', api_key=c.rhodecode_user.api_key)}" rel="alternate" title="${_('ATOM journal feed')}" type="application/atom+xml" />
13 <link href="${h.url('journal_atom', api_key=c.rhodecode_user.api_key)}" rel="alternate" title="${_('ATOM journal feed')}" type="application/atom+xml" />
14 <link href="${h.url('journal_rss', api_key=c.rhodecode_user.api_key)}" rel="alternate" title="${_('RSS journal feed')}" type="application/rss+xml" />
14 <link href="${h.url('journal_rss', api_key=c.rhodecode_user.api_key)}" rel="alternate" title="${_('RSS journal feed')}" type="application/rss+xml" />
15 </%def>
15 </%def>
16 <%def name="main()">
16 <%def name="main()">
17
17
18 <div class="box box-left">
18 <div class="box box-left">
19 <!-- box / title -->
19 <!-- box / title -->
20 <div class="title">
20 <div class="title">
21 <h5>${_('Journal')}</h5>
21 <h5>${_('Journal')}</h5>
22 <ul class="links">
22 <ul class="links">
23 <li>
23 <li>
24 <span><a id="refresh" href="${h.url('journal')}"><img class="icon" title="${_('Refresh')}" alt="${_('Refresh')}" src="${h.url('/images/icons/arrow_refresh.png')}"/></a></span>
24 <span><a id="refresh" href="${h.url('journal')}"><img class="icon" title="${_('Refresh')}" alt="${_('Refresh')}" src="${h.url('/images/icons/arrow_refresh.png')}"/></a></span>
25 </li>
25 </li>
26 <li>
26 <li>
27 <span><a href="${h.url('journal_rss', api_key=c.rhodecode_user.api_key)}"><img class="icon" title="${_('RSS feed')}" alt="${_('RSS feed')}" src="${h.url('/images/icons/rss_16.png')}"/></a></span>
27 <span><a href="${h.url('journal_rss', api_key=c.rhodecode_user.api_key)}"><img class="icon" title="${_('RSS feed')}" alt="${_('RSS feed')}" src="${h.url('/images/icons/rss_16.png')}"/></a></span>
28 </li>
28 </li>
29 <li>
29 <li>
30 <span><a href="${h.url('journal_atom', api_key=c.rhodecode_user.api_key)}"><img class="icon" title="${_('ATOM feed')}" alt="${_('ATOM feed')}" src="${h.url('/images/icons/atom.png')}"/></a></span>
30 <span><a href="${h.url('journal_atom', api_key=c.rhodecode_user.api_key)}"><img class="icon" title="${_('ATOM feed')}" alt="${_('ATOM feed')}" src="${h.url('/images/icons/atom.png')}"/></a></span>
31 </li>
31 </li>
32 </ul>
32 </ul>
33 </div>
33 </div>
34 <div id="journal">${c.journal_data}</div>
34 <div id="journal">${c.journal_data}</div>
35 </div>
35 </div>
36 <div class="box box-right">
36 <div class="box box-right">
37 <!-- box / title -->
37 <!-- box / title -->
38 <div class="title">
38 <div class="title">
39 <h5>
39 <h5>
40 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}"/>
40 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}"/>
41 <a id="show_watched" class="link-white" href="#watched">${_('Watched')}</a> / <a id="show_my" class="link-white" href="#my">${_('My repos')}</a>
41 <a id="show_watched" class="link-white" href="#watched">${_('Watched')}</a> / <a id="show_my" class="link-white" href="#my">${_('My repos')}</a>
42 </h5>
42 </h5>
43 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
43 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
44 <ul class="links">
44 <ul class="links">
45 <li>
45 <li>
46 <span>${h.link_to(_('ADD'),h.url('admin_settings_create_repository'))}</span>
46 <span>${h.link_to(_('ADD'),h.url('admin_settings_create_repository'))}</span>
47 </li>
47 </li>
48 </ul>
48 </ul>
49 %endif
49 %endif
50 </div>
50 </div>
51 <!-- end box / title -->
51 <!-- end box / title -->
52 <div id="my" class="table" style="display:none">
52 <div id="my" class="table" style="display:none">
53 ## loaded via AJAX
53 ## loaded via AJAX
54 ${_('Loading...')}
54 ${_('Loading...')}
55 </div>
55 </div>
56
56
57 <div id="watched" class="table">
57 <div id="watched" class="table">
58 %if c.following:
58 %if c.following:
59 <table>
59 <table>
60 <thead>
60 <thead>
61 <tr>
61 <tr>
62 <th class="left">${_('Name')}</th>
62 <th class="left">${_('Name')}</th>
63 </thead>
63 </thead>
64 <tbody>
64 <tbody>
65 %for entry in c.following:
65 %for entry in c.following:
66 <tr>
66 <tr>
67 <td>
67 <td>
68 %if entry.follows_user_id:
68 %if entry.follows_user_id:
69 <img title="${_('following user')}" alt="${_('user')}" src="${h.url('/images/icons/user.png')}"/>
69 <img title="${_('following user')}" alt="${_('user')}" src="${h.url('/images/icons/user.png')}"/>
70 ${entry.follows_user.full_contact}
70 ${entry.follows_user.full_contact}
71 %endif
71 %endif
72
72
73 %if entry.follows_repo_id:
73 %if entry.follows_repo_id:
74 <div style="float:right;padding-right:5px">
74 <div style="float:right;padding-right:5px">
75 <span id="follow_toggle_${entry.follows_repository.repo_id}" class="following" title="${_('Stop following this repository')}"
75 <span id="follow_toggle_${entry.follows_repository.repo_id}" class="following" title="${_('Stop following this repository')}"
76 onclick="javascript:toggleFollowingRepo(this,${entry.follows_repository.repo_id},'${str(h.get_token())}')">
76 onclick="javascript:toggleFollowingRepo(this,${entry.follows_repository.repo_id},'${str(h.get_token())}')">
77 </span>
77 </span>
78 </div>
78 </div>
79
79
80 %if h.is_hg(entry.follows_repository):
80 %if h.is_hg(entry.follows_repository):
81 <img class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url('/images/icons/hgicon.png')}"/>
81 <img class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url('/images/icons/hgicon.png')}"/>
82 %elif h.is_git(entry.follows_repository):
82 %elif h.is_git(entry.follows_repository):
83 <img class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url('/images/icons/giticon.png')}"/>
83 <img class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url('/images/icons/giticon.png')}"/>
84 %endif
84 %endif
85
85
86 %if entry.follows_repository.private and c.visual.show_private_icon:
86 %if entry.follows_repository.private and c.visual.show_private_icon:
87 <img class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url('/images/icons/lock.png')}"/>
87 <img class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url('/images/icons/lock.png')}"/>
88 %elif not entry.follows_repository.private and c.visual.show_public_icon:
88 %elif not entry.follows_repository.private and c.visual.show_public_icon:
89 <img class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url('/images/icons/lock_open.png')}"/>
89 <img class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url('/images/icons/lock_open.png')}"/>
90 %endif
90 %endif
91 <span class="watched_repo">
91 <span class="watched_repo">
92 ${h.link_to(entry.follows_repository.repo_name,h.url('summary_home',repo_name=entry.follows_repository.repo_name))}
92 ${h.link_to(entry.follows_repository.repo_name,h.url('summary_home',repo_name=entry.follows_repository.repo_name))}
93 </span>
93 </span>
94 %endif
94 %endif
95 </td>
95 </td>
96 </tr>
96 </tr>
97 %endfor
97 %endfor
98 </tbody>
98 </tbody>
99 </table>
99 </table>
100 %else:
100 %else:
101 <div style="padding:5px 0px 10px 0px;">
101 <div style="padding:5px 0px 10px 0px;">
102 ${_('You are not following any users or repositories')}
102 ${_('You are not following any users or repositories')}
103 </div>
103 </div>
104 %endif
104 %endif
105 </div>
105 </div>
106 </div>
106 </div>
107
107
108 <script type="text/javascript">
108 <script type="text/javascript">
109 var show_my = function(e){
109 var show_my = function(e){
110 YUD.setStyle('watched','display','none');
110 YUD.setStyle('watched','display','none');
111 YUD.setStyle('my','display','');
111 YUD.setStyle('my','display','');
112
112
113 var url = "${h.url('admin_settings_my_repos')}";
113 var url = "${h.url('admin_settings_my_repos')}";
114 ypjax(url, 'my', function(){
114 ypjax(url, 'my', function(){
115 tooltip_activate();
115 tooltip_activate();
116 quick_repo_menu();
116 quick_repo_menu();
117 var nodes = YUQ('#my tr td a.repo_name');
117 var nodes = YUQ('#my tr td a.repo_name');
118 var func = function(node){
118 var func = function(node){
119 return node.parentNode.parentNode.parentNode;
119 return node.parentNode.parentNode.parentNode;
120 }
120 }
121 q_filter('q_filter',nodes,func);
121 q_filter('q_filter',nodes,func);
122 });
122 });
123
123
124 }
124 }
125 YUE.on('show_my','click',function(e){
125 YUE.on('show_my','click',function(e){
126 show_my(e);
126 show_my(e);
127 })
127 })
128 var show_watched = function(e){
128 var show_watched = function(e){
129 YUD.setStyle('my','display','none');
129 YUD.setStyle('my','display','none');
130 YUD.setStyle('watched','display','');
130 YUD.setStyle('watched','display','');
131 var nodes = YUQ('#watched .watched_repo a');
131 var nodes = YUQ('#watched .watched_repo a');
132 var target = 'q_filter';
132 var target = 'q_filter';
133 var func = function(node){
133 var func = function(node){
134 return node.parentNode.parentNode;
134 return node.parentNode.parentNode;
135 }
135 }
136 q_filter(target,nodes,func);
136 q_filter(target,nodes,func);
137 }
137 }
138 YUE.on('show_watched','click',function(e){
138 YUE.on('show_watched','click',function(e){
139 show_watched(e);
139 show_watched(e);
140 })
140 })
141 //init watched
141 //init watched
142 show_watched();
142 show_watched();
143
143
144 var tabs = {
144 var tabs = {
145 'watched': show_watched,
145 'watched': show_watched,
146 'my': show_my,
146 'my': show_my,
147 }
147 }
148 var url = location.href.split('#');
148 var url = location.href.split('#');
149 if (url[1]) {
149 if (url[1]) {
150 //We have a hash
150 //We have a hash
151 var tabHash = url[1];
151 var tabHash = url[1];
152 tabs[tabHash]();
152 tabs[tabHash]();
153 }
153 }
154
154
155 YUE.on('refresh','click',function(e){
155 YUE.on('refresh','click',function(e){
156 ypjax(e.currentTarget.href,"journal",function(){
156 ypjax(e.currentTarget.href,"journal",function(){
157 show_more_event();
157 show_more_event();
158 tooltip_activate();
158 tooltip_activate();
159 show_changeset_tooltip();
159 show_changeset_tooltip();
160 });
160 });
161 YUE.preventDefault(e);
161 YUE.preventDefault(e);
162 });
162 });
163
163
164
164
165 // main table sorting
165 // main table sorting
166 var myColumnDefs = [
166 var myColumnDefs = [
167 {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"},
167 {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"},
168 {key:"name",label:"${_('Name')}",sortable:true,
168 {key:"name",label:"${_('Name')}",sortable:true,
169 sortOptions: { sortFunction: nameSort }},
169 sortOptions: { sortFunction: nameSort }},
170 {key:"tip",label:"${_('Tip')}",sortable:true,
170 {key:"tip",label:"${_('Tip')}",sortable:true,
171 sortOptions: { sortFunction: revisionSort }},
171 sortOptions: { sortFunction: revisionSort }},
172 {key:"action1",label:"",sortable:false},
172 {key:"action1",label:"",sortable:false},
173 {key:"action2",label:"",sortable:false},
173 {key:"action2",label:"",sortable:false},
174 ];
174 ];
175
175
176 var myDataSource = new YAHOO.util.DataSource(YUD.get("repos_list"));
176 var myDataSource = new YAHOO.util.DataSource(YUD.get("repos_list"));
177
177
178 myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
178 myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
179
179
180 myDataSource.responseSchema = {
180 myDataSource.responseSchema = {
181 fields: [
181 fields: [
182 {key:"menu"},
182 {key:"menu"},
183 {key:"name"},
183 {key:"name"},
184 {key:"tip"},
184 {key:"tip"},
185 {key:"action1"},
185 {key:"action1"},
186 {key:"action2"}
186 {key:"action2"}
187 ]
187 ]
188 };
188 };
189
189
190 var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,
190 var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,
191 {
191 {
192 sortedBy:{key:"name",dir:"asc"},
192 sortedBy:{key:"name",dir:"asc"},
193 MSG_SORTASC:"${_('Click to sort ascending')}",
193 MSG_SORTASC:"${_('Click to sort ascending')}",
194 MSG_SORTDESC:"${_('Click to sort descending')}",
194 MSG_SORTDESC:"${_('Click to sort descending')}",
195 MSG_EMPTY:"${_('No records found.')}",
195 MSG_EMPTY:"${_('No records found.')}",
196 MSG_ERROR:"${_('Data error.')}",
196 MSG_ERROR:"${_('Data error.')}",
197 MSG_LOADING:"${_('Loading...')}",
197 MSG_LOADING:"${_('Loading...')}",
198 }
198 }
199 );
199 );
200 myDataTable.subscribe('postRenderEvent',function(oArgs) {
200 myDataTable.subscribe('postRenderEvent',function(oArgs) {
201 tooltip_activate();
201 tooltip_activate();
202 quick_repo_menu();
202 quick_repo_menu();
203 var func = function(node){
203 var func = function(node){
204 return node.parentNode.parentNode.parentNode.parentNode;
204 return node.parentNode.parentNode.parentNode.parentNode;
205 }
205 }
206 q_filter('q_filter',YUQ('#my tr td a.repo_name'),func);
206 q_filter('q_filter',YUQ('#my tr td a.repo_name'),func);
207 });
207 });
208
208
209 </script>
209 </script>
210 </%def>
210 </%def>
@@ -1,47 +1,47 b''
1 %if c.user_repos:
1 %if c.user_repos:
2 <div id='repos_list_wrap' class="yui-skin-sam">
2 <div id='repos_list_wrap' class="yui-skin-sam">
3 <table id="repos_list">
3 <table id="repos_list">
4 <thead>
4 <thead>
5 <tr>
5 <tr>
6 <th></th>
6 <th></th>
7 <th class="left">${_('Name')}</th>
7 <th class="left">${_('Name')}</th>
8 <th class="left">${_('Revision')}</th>
8 <th class="left">${_('Revision')}</th>
9 <th class="left">${_('Action')}</th>
9 <th class="left">${_('Action')}</th>
10 <th class="left">${_('Action')}</th>
10 <th class="left">${_('Action')}</th>
11 </thead>
11 </thead>
12 <tbody>
12 <tbody>
13 <%namespace name="dt" file="/data_table/_dt_elements.html"/>
13 <%namespace name="dt" file="/data_table/_dt_elements.html"/>
14 %for repo in c.user_repos:
14 %for repo in c.user_repos:
15 <tr>
15 <tr>
16 ##QUICK MENU
16 ##QUICK MENU
17 <td class="quick_repo_menu">
17 <td class="quick_repo_menu">
18 ${dt.quick_menu(repo['name'])}
18 ${dt.quick_menu(repo['name'])}
19 </td>
19 </td>
20 ##REPO NAME AND ICONS
20 ##REPO NAME AND ICONS
21 <td class="reponame">
21 <td class="reponame">
22 ${dt.repo_name(repo['name'],repo['dbrepo']['repo_type'],repo['dbrepo']['private'],h.AttributeDict(repo['dbrepo_fork']))}
22 ${dt.repo_name(repo['name'],repo['dbrepo']['repo_type'],repo['dbrepo']['private'],h.AttributeDict(repo['dbrepo_fork']))}
23 </td>
23 </td>
24 ##LAST REVISION
24 ##LAST REVISION
25 <td>
25 <td>
26 ${dt.revision(repo['name'],repo['rev'],repo['tip'],repo['author'],repo['last_msg'])}
26 ${dt.revision(repo['name'],repo['rev'],repo['tip'],repo['author'],repo['last_msg'])}
27 </td>
27 </td>
28 ##
28 ##
29 <td><a href="${h.url('repo_settings_home',repo_name=repo['name'])}" title="${_('edit')}"><img class="icon" alt="${_('private')}" src="${h.url('/images/icons/application_form_edit.png')}"/></a></td>
29 <td><a href="${h.url('repo_settings_home',repo_name=repo['name'])}" title="${_('edit')}"><img class="icon" alt="${_('private')}" src="${h.url('/images/icons/application_form_edit.png')}"/></a></td>
30 <td>
30 <td>
31 ${h.form(url('repo_settings_delete', repo_name=repo['name']),method='delete')}
31 ${h.form(url('repo_settings_delete', repo_name=repo['name']),method='delete')}
32 ${h.submit('remove_%s' % repo['name'],'',class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo['name']+"');")}
32 ${h.submit('remove_%s' % repo['name'],'',class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo['name']+"');")}
33 ${h.end_form()}
33 ${h.end_form()}
34 </td>
34 </td>
35 </tr>
35 </tr>
36 %endfor
36 %endfor
37 </tbody>
37 </tbody>
38 </table>
38 </table>
39 </div>
39 </div>
40 %else:
40 %else:
41 <div style="padding:5px 0px 10px 0px;">
41 <div style="padding:5px 0px 10px 0px;">
42 ${_('No repositories yet')}
42 ${_('No repositories yet')}
43 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
43 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
44 ${h.link_to(_('create one now'),h.url('admin_settings_create_repository'),class_="ui-btn")}
44 ${h.link_to(_('create one now'),h.url('admin_settings_create_repository'),class_="ui-btn")}
45 %endif
45 %endif
46 </div>
46 </div>
47 %endif No newline at end of file
47 %endif
@@ -1,207 +1,207 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${c.repo_name} ${_('Pull request #%s') % c.pull_request.pull_request_id}
4 ${c.repo_name} ${_('Pull request #%s') % c.pull_request.pull_request_id}
5 </%def>
5 </%def>
6
6
7 <%def name="breadcrumbs_links()">
7 <%def name="breadcrumbs_links()">
8 ${h.link_to(_(u'Home'),h.url('/'))}
8 ${h.link_to(_(u'Home'),h.url('/'))}
9 &raquo;
9 &raquo;
10 ${h.link_to(c.repo_name,h.url('changelog_home',repo_name=c.repo_name))}
10 ${h.link_to(c.repo_name,h.url('changelog_home',repo_name=c.repo_name))}
11 &raquo;
11 &raquo;
12 ${_('Pull request #%s') % c.pull_request.pull_request_id}
12 ${_('Pull request #%s') % c.pull_request.pull_request_id}
13 </%def>
13 </%def>
14
14
15 <%def name="main()">
15 <%def name="main()">
16
16
17 <div class="box">
17 <div class="box">
18 <!-- box / title -->
18 <!-- box / title -->
19 <div class="title">
19 <div class="title">
20 ${self.breadcrumbs()}
20 ${self.breadcrumbs()}
21 </div>
21 </div>
22 %if c.pull_request.is_closed():
22 %if c.pull_request.is_closed():
23 <div style="padding:10px; font-size:22px;width:100%;text-align: center; color:#88D882">${_('Closed %s') % (h.age(c.pull_request.updated_on))} ${_('with status %s') % h.changeset_status_lbl(c.current_changeset_status)}</div>
23 <div style="padding:10px; font-size:22px;width:100%;text-align: center; color:#88D882">${_('Closed %s') % (h.age(c.pull_request.updated_on))} ${_('with status %s') % h.changeset_status_lbl(c.current_changeset_status)}</div>
24 %endif
24 %endif
25 <h3>${_('Title')}: ${c.pull_request.title}</h3>
25 <h3>${_('Title')}: ${c.pull_request.title}</h3>
26
26
27 <div class="form">
27 <div class="form">
28 <div id="summary" class="fields">
28 <div id="summary" class="fields">
29 <div class="field">
29 <div class="field">
30 <div class="label-summary">
30 <div class="label-summary">
31 <label>${_('Status')}:</label>
31 <label>${_('Status')}:</label>
32 </div>
32 </div>
33 <div class="input">
33 <div class="input">
34 <div class="changeset-status-container" style="float:none;clear:both">
34 <div class="changeset-status-container" style="float:none;clear:both">
35 %if c.current_changeset_status:
35 %if c.current_changeset_status:
36 <div title="${_('Pull request status')}" class="changeset-status-lbl">[${h.changeset_status_lbl(c.current_changeset_status)}]</div>
36 <div title="${_('Pull request status')}" class="changeset-status-lbl">[${h.changeset_status_lbl(c.current_changeset_status)}]</div>
37 <div class="changeset-status-ico" style="padding:1px 4px"><img src="${h.url('/images/icons/flag_status_%s.png' % c.current_changeset_status)}" /></div>
37 <div class="changeset-status-ico" style="padding:1px 4px"><img src="${h.url('/images/icons/flag_status_%s.png' % c.current_changeset_status)}" /></div>
38 %endif
38 %endif
39 </div>
39 </div>
40 </div>
40 </div>
41 </div>
41 </div>
42 <div class="field">
42 <div class="field">
43 <div class="label-summary">
43 <div class="label-summary">
44 <label>${_('Still not reviewed by')}:</label>
44 <label>${_('Still not reviewed by')}:</label>
45 </div>
45 </div>
46 <div class="input">
46 <div class="input">
47 % if len(c.pull_request_pending_reviewers) > 0:
47 % if len(c.pull_request_pending_reviewers) > 0:
48 <div class="tooltip" title="${h.tooltip(','.join([x.username for x in c.pull_request_pending_reviewers]))}">${ungettext('%d reviewer', '%d reviewers',len(c.pull_request_pending_reviewers)) % len(c.pull_request_pending_reviewers)}</div>
48 <div class="tooltip" title="${h.tooltip(','.join([x.username for x in c.pull_request_pending_reviewers]))}">${ungettext('%d reviewer', '%d reviewers',len(c.pull_request_pending_reviewers)) % len(c.pull_request_pending_reviewers)}</div>
49 %else:
49 %else:
50 <div>${_('pull request was reviewed by all reviewers')}</div>
50 <div>${_('pull request was reviewed by all reviewers')}</div>
51 %endif
51 %endif
52 </div>
52 </div>
53 </div>
53 </div>
54 </div>
54 </div>
55 </div>
55 </div>
56 <div style="white-space:pre-wrap;padding:3px 3px 5px 20px">${h.literal(c.pull_request.description)}</div>
56 <div style="white-space:pre-wrap;padding:3px 3px 5px 20px">${h.literal(c.pull_request.description)}</div>
57 <div style="padding:4px 4px 10px 20px">
57 <div style="padding:4px 4px 10px 20px">
58 <div>${_('Created on')}: ${h.fmt_date(c.pull_request.created_on)}</div>
58 <div>${_('Created on')}: ${h.fmt_date(c.pull_request.created_on)}</div>
59 </div>
59 </div>
60
60
61 <div style="min-height:160px">
61 <div style="min-height:160px">
62 ##DIFF
62 ##DIFF
63 <div class="table" style="float:left;clear:none">
63 <div class="table" style="float:left;clear:none">
64 <div id="body" class="diffblock">
64 <div id="body" class="diffblock">
65 <div style="white-space:pre-wrap;padding:5px">${_('Compare view')}</div>
65 <div style="white-space:pre-wrap;padding:5px">${_('Compare view')}</div>
66 </div>
66 </div>
67 <div id="changeset_compare_view_content">
67 <div id="changeset_compare_view_content">
68 ##CS
68 ##CS
69 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Incoming changesets')}</div>
69 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Incoming changesets')}</div>
70 <%include file="/compare/compare_cs.html" />
70 <%include file="/compare/compare_cs.html" />
71
71
72 ## FILES
72 ## FILES
73 <div id="affected_files">
73 <div id="affected_files">
74 % if c.files:
74 % if c.files:
75 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Files affected')}</div>
75 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Files affected')}</div>
76 <div class="cs_files">
76 <div class="cs_files">
77 %for fid, change, f, stat in c.files:
77 %for fid, change, f, stat in c.files:
78 <div class="cs_${change}">
78 <div class="cs_${change}">
79 <div class="node">${h.link_to(h.safe_unicode(f),h.url.current(anchor=fid))}</div>
79 <div class="node">${h.link_to(h.safe_unicode(f),h.url.current(anchor=fid))}</div>
80 <div class="changes">${h.fancy_file_stats(stat)}</div>
80 <div class="changes">${h.fancy_file_stats(stat)}</div>
81 </div>
81 </div>
82 %endfor
82 %endfor
83 </div>
83 </div>
84 %else:
84 %else:
85 <div class="ui-btn" style="text-align: center;margin-top:5px">${_('Click to load diff details')}</div>
85 <div class="ui-btn" style="text-align: center;margin-top:5px">${_('Click to load diff details')}</div>
86 %endif
86 %endif
87 </div>
87 </div>
88 </div>
88 </div>
89 </div>
89 </div>
90 ## REVIEWERS
90 ## REVIEWERS
91 <div style="float:left; border-left:1px dashed #eee">
91 <div style="float:left; border-left:1px dashed #eee">
92 <h4>${_('Pull request reviewers')}</h4>
92 <h4>${_('Pull request reviewers')}</h4>
93 <div id="reviewers" style="padding:0px 0px 0px 15px">
93 <div id="reviewers" style="padding:0px 0px 0px 15px">
94 ## members goes here !
94 ## members goes here !
95 <div class="group_members_wrap">
95 <div class="group_members_wrap">
96 <ul id="review_members" class="group_members">
96 <ul id="review_members" class="group_members">
97 %for member,status in c.pull_request_reviewers:
97 %for member,status in c.pull_request_reviewers:
98 <li id="reviewer_${member.user_id}">
98 <li id="reviewer_${member.user_id}">
99 <div class="reviewers_member">
99 <div class="reviewers_member">
100 <div style="float:left;padding:0px 3px 0px 0px" class="tooltip" title="${h.tooltip(h.changeset_status_lbl(status[0][1].status if status else 'not_reviewed'))}">
100 <div style="float:left;padding:0px 3px 0px 0px" class="tooltip" title="${h.tooltip(h.changeset_status_lbl(status[0][1].status if status else 'not_reviewed'))}">
101 <img src="${h.url(str('/images/icons/flag_status_%s.png' % (status[0][1].status if status else 'not_reviewed')))}"/>
101 <img src="${h.url(str('/images/icons/flag_status_%s.png' % (status[0][1].status if status else 'not_reviewed')))}"/>
102 </div>
102 </div>
103 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(member.email,14)}"/> </div>
103 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(member.email,14)}"/> </div>
104 <div style="float:left">${member.full_name} (${_('owner')})</div>
104 <div style="float:left">${member.full_name} (${_('owner')})</div>
105 <input type="hidden" value="${member.user_id}" name="review_members" />
105 <input type="hidden" value="${member.user_id}" name="review_members" />
106 %if not c.pull_request.is_closed() and (h.HasPermissionAny('hg.admin', 'repository.admin')() or c.pull_request.author.user_id == c.rhodecode_user.user_id):
106 %if not c.pull_request.is_closed() and (h.HasPermissionAny('hg.admin', 'repository.admin')() or c.pull_request.author.user_id == c.rhodecode_user.user_id):
107 <span class="delete_icon action_button" onclick="removeReviewer(${member.user_id})"></span>
107 <span class="delete_icon action_button" onclick="removeReviewer(${member.user_id})"></span>
108 %endif
108 %endif
109 </div>
109 </div>
110 </li>
110 </li>
111 %endfor
111 %endfor
112 </ul>
112 </ul>
113 </div>
113 </div>
114 %if not c.pull_request.is_closed():
114 %if not c.pull_request.is_closed():
115 <div class='ac'>
115 <div class='ac'>
116 %if h.HasPermissionAny('hg.admin', 'repository.admin')() or c.pull_request.author.user_id == c.rhodecode_user.user_id:
116 %if h.HasPermissionAny('hg.admin', 'repository.admin')() or c.pull_request.author.user_id == c.rhodecode_user.user_id:
117 <div class="reviewer_ac">
117 <div class="reviewer_ac">
118 ${h.text('user', class_='yui-ac-input')}
118 ${h.text('user', class_='yui-ac-input')}
119 <span class="help-block">${_('Add reviewer to this pull request.')}</span>
119 <span class="help-block">${_('Add reviewer to this pull request.')}</span>
120 <div id="reviewers_container"></div>
120 <div id="reviewers_container"></div>
121 </div>
121 </div>
122 <div style="padding:0px 10px">
122 <div style="padding:0px 10px">
123 <span id="update_pull_request" class="ui-btn xsmall">${_('save')}</span>
123 <span id="update_pull_request" class="ui-btn xsmall">${_('save')}</span>
124 </div>
124 </div>
125 %endif
125 %endif
126 </div>
126 </div>
127 %endif
127 %endif
128 </div>
128 </div>
129 </div>
129 </div>
130 </div>
130 </div>
131 <script>
131 <script>
132 var _USERS_AC_DATA = ${c.users_array|n};
132 var _USERS_AC_DATA = ${c.users_array|n};
133 var _GROUPS_AC_DATA = ${c.users_groups_array|n};
133 var _GROUPS_AC_DATA = ${c.users_groups_array|n};
134 AJAX_COMMENT_URL = "${url('pullrequest_comment',repo_name=c.repo_name,pull_request_id=c.pull_request.pull_request_id)}";
134 AJAX_COMMENT_URL = "${url('pullrequest_comment',repo_name=c.repo_name,pull_request_id=c.pull_request.pull_request_id)}";
135 AJAX_COMMENT_DELETE_URL = "${url('pullrequest_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}";
135 AJAX_COMMENT_DELETE_URL = "${url('pullrequest_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}";
136 AJAX_UPDATE_PULLREQUEST = "${url('pullrequest_update',repo_name=c.repo_name,pull_request_id=c.pull_request.pull_request_id)}"
136 AJAX_UPDATE_PULLREQUEST = "${url('pullrequest_update',repo_name=c.repo_name,pull_request_id=c.pull_request.pull_request_id)}"
137 </script>
137 </script>
138
138
139 ## diff block
139 ## diff block
140 <div id="diff_block_container" style="clear:both;">
140 <div id="diff_block_container" style="clear:both;">
141 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
141 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
142 %for fid, change, f, stat in c.files:
142 %for fid, change, f, stat in c.files:
143 ${diff_block.diff_block_simple([c.changes[fid]])}
143 ${diff_block.diff_block_simple([c.changes[fid]])}
144 %endfor
144 %endfor
145 </div>
145 </div>
146
146
147 ## template for inline comment form
147 ## template for inline comment form
148 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
148 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
149 ${comment.comment_inline_form()}
149 ${comment.comment_inline_form()}
150
150
151 ## render comments and inlines
151 ## render comments and inlines
152 ${comment.generate_comments()}
152 ${comment.generate_comments()}
153
153
154 % if not c.pull_request.is_closed():
154 % if not c.pull_request.is_closed():
155 ## main comment form and it status
155 ## main comment form and it status
156 ${comment.comments(h.url('pullrequest_comment', repo_name=c.repo_name,
156 ${comment.comments(h.url('pullrequest_comment', repo_name=c.repo_name,
157 pull_request_id=c.pull_request.pull_request_id),
157 pull_request_id=c.pull_request.pull_request_id),
158 c.current_changeset_status,
158 c.current_changeset_status,
159 close_btn=True)}
159 close_btn=True)}
160 %endif
160 %endif
161
161
162 <script type="text/javascript">
162 <script type="text/javascript">
163 YUE.onDOMReady(function(){
163 YUE.onDOMReady(function(){
164 PullRequestAutoComplete('user', 'reviewers_container', _USERS_AC_DATA, _GROUPS_AC_DATA);
164 PullRequestAutoComplete('user', 'reviewers_container', _USERS_AC_DATA, _GROUPS_AC_DATA);
165
165
166 YUE.on(YUQ('.show-inline-comments'),'change',function(e){
166 YUE.on(YUQ('.show-inline-comments'),'change',function(e){
167 var show = 'none';
167 var show = 'none';
168 var target = e.currentTarget;
168 var target = e.currentTarget;
169 if(target.checked){
169 if(target.checked){
170 var show = ''
170 var show = ''
171 }
171 }
172 var boxid = YUD.getAttribute(target,'id_for');
172 var boxid = YUD.getAttribute(target,'id_for');
173 var comments = YUQ('#{0} .inline-comments'.format(boxid));
173 var comments = YUQ('#{0} .inline-comments'.format(boxid));
174 for(c in comments){
174 for(c in comments){
175 YUD.setStyle(comments[c],'display',show);
175 YUD.setStyle(comments[c],'display',show);
176 }
176 }
177 var btns = YUQ('#{0} .inline-comments-button'.format(boxid));
177 var btns = YUQ('#{0} .inline-comments-button'.format(boxid));
178 for(c in btns){
178 for(c in btns){
179 YUD.setStyle(btns[c],'display',show);
179 YUD.setStyle(btns[c],'display',show);
180 }
180 }
181 })
181 })
182
182
183 YUE.on(YUQ('.line'),'click',function(e){
183 YUE.on(YUQ('.line'),'click',function(e){
184 var tr = e.currentTarget;
184 var tr = e.currentTarget;
185 injectInlineForm(tr);
185 injectInlineForm(tr);
186 });
186 });
187
187
188 // inject comments into they proper positions
188 // inject comments into they proper positions
189 var file_comments = YUQ('.inline-comment-placeholder');
189 var file_comments = YUQ('.inline-comment-placeholder');
190 renderInlineComments(file_comments);
190 renderInlineComments(file_comments);
191
191
192 YUE.on(YUD.get('update_pull_request'),'click',function(e){
192 YUE.on(YUD.get('update_pull_request'),'click',function(e){
193
193
194 var reviewers_ids = [];
194 var reviewers_ids = [];
195 var ids = YUQ('#review_members input');
195 var ids = YUQ('#review_members input');
196 for(var i=0; i<ids.length;i++){
196 for(var i=0; i<ids.length;i++){
197 var id = ids[i].value
197 var id = ids[i].value
198 reviewers_ids.push(id);
198 reviewers_ids.push(id);
199 }
199 }
200 updateReviewers(reviewers_ids);
200 updateReviewers(reviewers_ids);
201 })
201 })
202 })
202 })
203 </script>
203 </script>
204
204
205 </div>
205 </div>
206
206
207 </%def>
207 </%def>
@@ -1,719 +1,719 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${_('%s Summary') % c.repo_name} - ${c.rhodecode_name}
4 ${_('%s Summary') % c.repo_name} - ${c.rhodecode_name}
5 </%def>
5 </%def>
6
6
7 <%def name="breadcrumbs_links()">
7 <%def name="breadcrumbs_links()">
8 ${h.link_to(_(u'Home'),h.url('/'))}
8 ${h.link_to(_(u'Home'),h.url('/'))}
9 &raquo;
9 &raquo;
10 ${h.repo_link(c.dbrepo.groups_and_repo)}
10 ${h.repo_link(c.dbrepo.groups_and_repo)}
11 &raquo;
11 &raquo;
12 ${_('summary')}
12 ${_('summary')}
13 </%def>
13 </%def>
14
14
15 <%def name="page_nav()">
15 <%def name="page_nav()">
16 ${self.menu('summary')}
16 ${self.menu('summary')}
17 </%def>
17 </%def>
18
18
19 <%def name="head_extra()">
19 <%def name="head_extra()">
20 <link href="${h.url('atom_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key)}" rel="alternate" title="${_('repo %s ATOM feed') % c.repo_name}" type="application/atom+xml" />
20 <link href="${h.url('atom_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key)}" rel="alternate" title="${_('repo %s ATOM feed') % c.repo_name}" type="application/atom+xml" />
21 <link href="${h.url('rss_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key)}" rel="alternate" title="${_('repo %s RSS feed') % c.repo_name}" type="application/rss+xml" />
21 <link href="${h.url('rss_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key)}" rel="alternate" title="${_('repo %s RSS feed') % c.repo_name}" type="application/rss+xml" />
22 </%def>
22 </%def>
23
23
24 <%def name="main()">
24 <%def name="main()">
25 <%
25 <%
26 summary = lambda n:{False:'summary-short'}.get(n)
26 summary = lambda n:{False:'summary-short'}.get(n)
27 %>
27 %>
28 %if c.show_stats:
28 %if c.show_stats:
29 <div class="box box-left">
29 <div class="box box-left">
30 %else:
30 %else:
31 <div class="box">
31 <div class="box">
32 %endif
32 %endif
33 <!-- box / title -->
33 <!-- box / title -->
34 <div class="title">
34 <div class="title">
35 ${self.breadcrumbs()}
35 ${self.breadcrumbs()}
36 </div>
36 </div>
37 <!-- end box / title -->
37 <!-- end box / title -->
38 <div class="form">
38 <div class="form">
39 <div id="summary" class="fields">
39 <div id="summary" class="fields">
40
40
41 <div class="field">
41 <div class="field">
42 <div class="label-summary">
42 <div class="label-summary">
43 <label>${_('Name')}:</label>
43 <label>${_('Name')}:</label>
44 </div>
44 </div>
45 <div class="input ${summary(c.show_stats)}">
45 <div class="input ${summary(c.show_stats)}">
46 <div style="float:right;padding:5px 0px 0px 5px">
46 <div style="float:right;padding:5px 0px 0px 5px">
47 %if c.rhodecode_user.username != 'default':
47 %if c.rhodecode_user.username != 'default':
48 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='rss_icon')}
48 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='rss_icon')}
49 ${h.link_to(_('ATOM'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='atom_icon')}
49 ${h.link_to(_('ATOM'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='atom_icon')}
50 %else:
50 %else:
51 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name),class_='rss_icon')}
51 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name),class_='rss_icon')}
52 ${h.link_to(_('ATOM'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name),class_='atom_icon')}
52 ${h.link_to(_('ATOM'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name),class_='atom_icon')}
53 %endif
53 %endif
54 </div>
54 </div>
55 %if c.rhodecode_user.username != 'default':
55 %if c.rhodecode_user.username != 'default':
56 %if c.following:
56 %if c.following:
57 <span id="follow_toggle" class="following tooltip" title="${_('Stop following this repository')}"
57 <span id="follow_toggle" class="following tooltip" title="${_('Stop following this repository')}"
58 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
58 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
59 </span>
59 </span>
60 %else:
60 %else:
61 <span id="follow_toggle" class="follow tooltip" title="${_('Start following this repository')}"
61 <span id="follow_toggle" class="follow tooltip" title="${_('Start following this repository')}"
62 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
62 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
63 </span>
63 </span>
64 %endif
64 %endif
65 %endif:
65 %endif:
66
66
67 ## locking icon
67 ## locking icon
68 %if c.rhodecode_db_repo.enable_locking:
68 %if c.rhodecode_db_repo.enable_locking:
69 %if c.rhodecode_db_repo.locked[0]:
69 %if c.rhodecode_db_repo.locked[0]:
70 <span class="locking_locked tooltip" title="${_('Repository locked by %s') % h.person_by_id(c.rhodecode_db_repo.locked[0])}"></span>
70 <span class="locking_locked tooltip" title="${_('Repository locked by %s') % h.person_by_id(c.rhodecode_db_repo.locked[0])}"></span>
71 %else:
71 %else:
72 <span class="locking_unlocked tooltip" title="${_('Repository unlocked')}"></span>
72 <span class="locking_unlocked tooltip" title="${_('Repository unlocked')}"></span>
73 %endif
73 %endif
74 %endif
74 %endif
75 ##REPO TYPE
75 ##REPO TYPE
76 %if h.is_hg(c.dbrepo):
76 %if h.is_hg(c.dbrepo):
77 <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url('/images/icons/hgicon.png')}"/>
77 <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url('/images/icons/hgicon.png')}"/>
78 %endif
78 %endif
79 %if h.is_git(c.dbrepo):
79 %if h.is_git(c.dbrepo):
80 <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url('/images/icons/giticon.png')}"/>
80 <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url('/images/icons/giticon.png')}"/>
81 %endif
81 %endif
82
82
83 ##PUBLIC/PRIVATE
83 ##PUBLIC/PRIVATE
84 %if c.dbrepo.private:
84 %if c.dbrepo.private:
85 <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url('/images/icons/lock.png')}"/>
85 <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url('/images/icons/lock.png')}"/>
86 %else:
86 %else:
87 <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url('/images/icons/lock_open.png')}"/>
87 <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url('/images/icons/lock_open.png')}"/>
88 %endif
88 %endif
89
89
90 ##REPO NAME
90 ##REPO NAME
91 <span class="repo_name" title="${_('Non changable ID %s') % c.dbrepo.repo_id}">${h.repo_link(c.dbrepo.groups_and_repo)}</span>
91 <span class="repo_name" title="${_('Non changable ID %s') % c.dbrepo.repo_id}">${h.repo_link(c.dbrepo.groups_and_repo)}</span>
92
92
93 ##FORK
93 ##FORK
94 %if c.dbrepo.fork:
94 %if c.dbrepo.fork:
95 <div style="margin-top:5px;clear:both"">
95 <div style="margin-top:5px;clear:both"">
96 <a href="${h.url('summary_home',repo_name=c.dbrepo.fork.repo_name)}"><img class="icon" alt="${_('public')}" title="${_('Fork of')} ${c.dbrepo.fork.repo_name}" src="${h.url('/images/icons/arrow_divide.png')}"/>
96 <a href="${h.url('summary_home',repo_name=c.dbrepo.fork.repo_name)}"><img class="icon" alt="${_('public')}" title="${_('Fork of')} ${c.dbrepo.fork.repo_name}" src="${h.url('/images/icons/arrow_divide.png')}"/>
97 ${_('Fork of')} ${c.dbrepo.fork.repo_name}
97 ${_('Fork of')} ${c.dbrepo.fork.repo_name}
98 </a>
98 </a>
99 </div>
99 </div>
100 %endif
100 %endif
101 ##REMOTE
101 ##REMOTE
102 %if c.dbrepo.clone_uri:
102 %if c.dbrepo.clone_uri:
103 <div style="margin-top:5px;clear:both">
103 <div style="margin-top:5px;clear:both">
104 <a href="${h.url(str(h.hide_credentials(c.dbrepo.clone_uri)))}"><img class="icon" alt="${_('remote clone')}" title="${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}" src="${h.url('/images/icons/connect.png')}"/>
104 <a href="${h.url(str(h.hide_credentials(c.dbrepo.clone_uri)))}"><img class="icon" alt="${_('remote clone')}" title="${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}" src="${h.url('/images/icons/connect.png')}"/>
105 ${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}
105 ${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}
106 </a>
106 </a>
107 </div>
107 </div>
108 %endif
108 %endif
109 </div>
109 </div>
110 </div>
110 </div>
111
111
112 <div class="field">
112 <div class="field">
113 <div class="label-summary">
113 <div class="label-summary">
114 <label>${_('Description')}:</label>
114 <label>${_('Description')}:</label>
115 </div>
115 </div>
116 %if c.visual.stylify_metatags:
116 %if c.visual.stylify_metatags:
117 <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(h.desc_stylize(c.dbrepo.description))}</div>
117 <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(h.desc_stylize(c.dbrepo.description))}</div>
118 %else:
118 %else:
119 <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(c.dbrepo.description)}</div>
119 <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(c.dbrepo.description)}</div>
120 %endif
120 %endif
121 </div>
121 </div>
122
122
123 <div class="field">
123 <div class="field">
124 <div class="label-summary">
124 <div class="label-summary">
125 <label>${_('Contact')}:</label>
125 <label>${_('Contact')}:</label>
126 </div>
126 </div>
127 <div class="input ${summary(c.show_stats)}">
127 <div class="input ${summary(c.show_stats)}">
128 <div class="gravatar">
128 <div class="gravatar">
129 <img alt="gravatar" src="${h.gravatar_url(c.dbrepo.user.email)}"/>
129 <img alt="gravatar" src="${h.gravatar_url(c.dbrepo.user.email)}"/>
130 </div>
130 </div>
131 ${_('Username')}: ${c.dbrepo.user.username}<br/>
131 ${_('Username')}: ${c.dbrepo.user.username}<br/>
132 ${_('Name')}: ${c.dbrepo.user.name} ${c.dbrepo.user.lastname}<br/>
132 ${_('Name')}: ${c.dbrepo.user.name} ${c.dbrepo.user.lastname}<br/>
133 ${_('Email')}: <a href="mailto:${c.dbrepo.user.email}">${c.dbrepo.user.email}</a>
133 ${_('Email')}: <a href="mailto:${c.dbrepo.user.email}">${c.dbrepo.user.email}</a>
134 </div>
134 </div>
135 </div>
135 </div>
136
136
137 <div class="field">
137 <div class="field">
138 <div class="label-summary">
138 <div class="label-summary">
139 <label>${_('Clone url')}:</label>
139 <label>${_('Clone url')}:</label>
140 </div>
140 </div>
141 <div class="input ${summary(c.show_stats)}">
141 <div class="input ${summary(c.show_stats)}">
142 <div style="display:none" id="clone_by_name" class="ui-btn clone">${_('Show by Name')}</div>
142 <div style="display:none" id="clone_by_name" class="ui-btn clone">${_('Show by Name')}</div>
143 <div id="clone_by_id" class="ui-btn clone">${_('Show by ID')}</div>
143 <div id="clone_by_id" class="ui-btn clone">${_('Show by ID')}</div>
144 <input style="width:80%;margin-left:105px" type="text" id="clone_url" readonly="readonly" value="${c.clone_repo_url}"/>
144 <input style="width:80%;margin-left:105px" type="text" id="clone_url" readonly="readonly" value="${c.clone_repo_url}"/>
145 <input style="display:none;width:80%;margin-left:105px" type="text" id="clone_url_id" readonly="readonly" value="${c.clone_repo_url_id}"/>
145 <input style="display:none;width:80%;margin-left:105px" type="text" id="clone_url_id" readonly="readonly" value="${c.clone_repo_url_id}"/>
146 </div>
146 </div>
147 </div>
147 </div>
148
148
149 <div class="field">
149 <div class="field">
150 <div class="label-summary">
150 <div class="label-summary">
151 <label>${_('Trending files')}:</label>
151 <label>${_('Trending files')}:</label>
152 </div>
152 </div>
153 <div class="input ${summary(c.show_stats)}">
153 <div class="input ${summary(c.show_stats)}">
154 %if c.show_stats:
154 %if c.show_stats:
155 <div id="lang_stats"></div>
155 <div id="lang_stats"></div>
156 %else:
156 %else:
157 ${_('Statistics are disabled for this repository')}
157 ${_('Statistics are disabled for this repository')}
158 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
158 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
159 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
159 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
160 %endif
160 %endif
161 %endif
161 %endif
162 </div>
162 </div>
163 </div>
163 </div>
164
164
165 <div class="field">
165 <div class="field">
166 <div class="label-summary">
166 <div class="label-summary">
167 <label>${_('Download')}:</label>
167 <label>${_('Download')}:</label>
168 </div>
168 </div>
169 <div class="input ${summary(c.show_stats)}">
169 <div class="input ${summary(c.show_stats)}">
170 %if len(c.rhodecode_repo.revisions) == 0:
170 %if len(c.rhodecode_repo.revisions) == 0:
171 ${_('There are no downloads yet')}
171 ${_('There are no downloads yet')}
172 %elif c.enable_downloads is False:
172 %elif c.enable_downloads is False:
173 ${_('Downloads are disabled for this repository')}
173 ${_('Downloads are disabled for this repository')}
174 %if h.HasPermissionAll('hg.admin')('enable downloads on from summary'):
174 %if h.HasPermissionAll('hg.admin')('enable downloads on from summary'):
175 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
175 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
176 %endif
176 %endif
177 %else:
177 %else:
178 ${h.select('download_options',c.rhodecode_repo.get_changeset().raw_id,c.download_options)}
178 ${h.select('download_options',c.rhodecode_repo.get_changeset().raw_id,c.download_options)}
179 <span id="${'zip_link'}">${h.link_to(_('Download as zip'), h.url('files_archive_home',repo_name=c.dbrepo.repo_name,fname='tip.zip'),class_="archive_icon ui-btn")}</span>
179 <span id="${'zip_link'}">${h.link_to(_('Download as zip'), h.url('files_archive_home',repo_name=c.dbrepo.repo_name,fname='tip.zip'),class_="archive_icon ui-btn")}</span>
180 <span style="vertical-align: bottom">
180 <span style="vertical-align: bottom">
181 <input id="archive_subrepos" type="checkbox" name="subrepos" />
181 <input id="archive_subrepos" type="checkbox" name="subrepos" />
182 <label for="archive_subrepos" class="tooltip" title="${h.tooltip(_('Check this to download archive with subrepos'))}" >${_('with subrepos')}</label>
182 <label for="archive_subrepos" class="tooltip" title="${h.tooltip(_('Check this to download archive with subrepos'))}" >${_('with subrepos')}</label>
183 </span>
183 </span>
184 %endif
184 %endif
185 </div>
185 </div>
186 </div>
186 </div>
187 </div>
187 </div>
188 </div>
188 </div>
189 </div>
189 </div>
190
190
191 %if c.show_stats:
191 %if c.show_stats:
192 <div class="box box-right" style="min-height:455px">
192 <div class="box box-right" style="min-height:455px">
193 <!-- box / title -->
193 <!-- box / title -->
194 <div class="title">
194 <div class="title">
195 <h5>${_('Commit activity by day / author')}</h5>
195 <h5>${_('Commit activity by day / author')}</h5>
196 </div>
196 </div>
197
197
198 <div class="graph">
198 <div class="graph">
199 <div style="padding:0 10px 10px 17px;">
199 <div style="padding:0 10px 10px 17px;">
200 %if c.no_data:
200 %if c.no_data:
201 ${c.no_data_msg}
201 ${c.no_data_msg}
202 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
202 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
203 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
203 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
204 %endif
204 %endif
205 %else:
205 %else:
206 ${_('Stats gathered: ')} ${c.stats_percentage}%
206 ${_('Stats gathered: ')} ${c.stats_percentage}%
207 %endif
207 %endif
208 </div>
208 </div>
209 <div id="commit_history" style="width:450px;height:300px;float:left"></div>
209 <div id="commit_history" style="width:450px;height:300px;float:left"></div>
210 <div style="clear: both;height: 10px"></div>
210 <div style="clear: both;height: 10px"></div>
211 <div id="overview" style="width:450px;height:100px;float:left"></div>
211 <div id="overview" style="width:450px;height:100px;float:left"></div>
212
212
213 <div id="legend_data" style="clear:both;margin-top:10px;">
213 <div id="legend_data" style="clear:both;margin-top:10px;">
214 <div id="legend_container"></div>
214 <div id="legend_container"></div>
215 <div id="legend_choices">
215 <div id="legend_choices">
216 <table id="legend_choices_tables" class="noborder" style="font-size:smaller;color:#545454"></table>
216 <table id="legend_choices_tables" class="noborder" style="font-size:smaller;color:#545454"></table>
217 </div>
217 </div>
218 </div>
218 </div>
219 </div>
219 </div>
220 </div>
220 </div>
221 %endif
221 %endif
222
222
223 <div class="box">
223 <div class="box">
224 <div class="title">
224 <div class="title">
225 <div class="breadcrumbs">
225 <div class="breadcrumbs">
226 %if c.repo_changesets:
226 %if c.repo_changesets:
227 ${h.link_to(_('Shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}
227 ${h.link_to(_('Shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}
228 %else:
228 %else:
229 ${_('Quick start')}
229 ${_('Quick start')}
230 %endif
230 %endif
231 </div>
231 </div>
232 </div>
232 </div>
233 <div class="table">
233 <div class="table">
234 <div id="shortlog_data">
234 <div id="shortlog_data">
235 <%include file='../shortlog/shortlog_data.html'/>
235 <%include file='../shortlog/shortlog_data.html'/>
236 </div>
236 </div>
237 </div>
237 </div>
238 </div>
238 </div>
239
239
240 %if c.readme_data:
240 %if c.readme_data:
241 <div id="readme" class="box header-pos-fix" style="background-color: #FAFAFA">
241 <div id="readme" class="box header-pos-fix" style="background-color: #FAFAFA">
242 <div id="readme" class="title" title="${_("Readme file at revision '%s'" % c.rhodecode_db_repo.landing_rev)}">
242 <div id="readme" class="title" title="${_("Readme file at revision '%s'" % c.rhodecode_db_repo.landing_rev)}">
243 <div class="breadcrumbs">
243 <div class="breadcrumbs">
244 <a href="${h.url('files_home',repo_name=c.repo_name,revision='tip',f_path=c.readme_file)}">${c.readme_file}</a>
244 <a href="${h.url('files_home',repo_name=c.repo_name,revision='tip',f_path=c.readme_file)}">${c.readme_file}</a>
245 <a class="permalink" href="#readme" title="${_('Permalink to this readme')}">&para;</a>
245 <a class="permalink" href="#readme" title="${_('Permalink to this readme')}">&para;</a>
246 </div>
246 </div>
247 </div>
247 </div>
248 <div id="readme" class="readme">
248 <div id="readme" class="readme">
249 <div class="readme_box">
249 <div class="readme_box">
250 ${c.readme_data|n}
250 ${c.readme_data|n}
251 </div>
251 </div>
252 </div>
252 </div>
253 </div>
253 </div>
254 %endif
254 %endif
255
255
256 <script type="text/javascript">
256 <script type="text/javascript">
257 var clone_url = 'clone_url';
257 var clone_url = 'clone_url';
258 YUE.on(clone_url,'click',function(e){
258 YUE.on(clone_url,'click',function(e){
259 if(YUD.hasClass(clone_url,'selected')){
259 if(YUD.hasClass(clone_url,'selected')){
260 return
260 return
261 }
261 }
262 else{
262 else{
263 YUD.addClass(clone_url,'selected');
263 YUD.addClass(clone_url,'selected');
264 YUD.get(clone_url).select();
264 YUD.get(clone_url).select();
265 }
265 }
266 })
266 })
267
267
268 YUE.on('clone_by_name','click',function(e){
268 YUE.on('clone_by_name','click',function(e){
269 // show url by name and hide name button
269 // show url by name and hide name button
270 YUD.setStyle('clone_url','display','');
270 YUD.setStyle('clone_url','display','');
271 YUD.setStyle('clone_by_name','display','none');
271 YUD.setStyle('clone_by_name','display','none');
272
272
273 // hide url by id and show name button
273 // hide url by id and show name button
274 YUD.setStyle('clone_by_id','display','');
274 YUD.setStyle('clone_by_id','display','');
275 YUD.setStyle('clone_url_id','display','none');
275 YUD.setStyle('clone_url_id','display','none');
276
276
277 })
277 })
278 YUE.on('clone_by_id','click',function(e){
278 YUE.on('clone_by_id','click',function(e){
279
279
280 // show url by id and hide id button
280 // show url by id and hide id button
281 YUD.setStyle('clone_by_id','display','none');
281 YUD.setStyle('clone_by_id','display','none');
282 YUD.setStyle('clone_url_id','display','');
282 YUD.setStyle('clone_url_id','display','');
283
283
284 // hide url by name and show id button
284 // hide url by name and show id button
285 YUD.setStyle('clone_by_name','display','');
285 YUD.setStyle('clone_by_name','display','');
286 YUD.setStyle('clone_url','display','none');
286 YUD.setStyle('clone_url','display','none');
287 })
287 })
288
288
289
289
290 var tmpl_links = {};
290 var tmpl_links = {};
291 %for cnt,archive in enumerate(c.rhodecode_repo._get_archives()):
291 %for cnt,archive in enumerate(c.rhodecode_repo._get_archives()):
292 tmpl_links["${archive['type']}"] = '${h.link_to('__NAME__', h.url('files_archive_home',repo_name=c.dbrepo.repo_name, fname='__CS__'+archive['extension'],subrepos='__SUB__'),class_='archive_icon ui-btn')}';
292 tmpl_links["${archive['type']}"] = '${h.link_to('__NAME__', h.url('files_archive_home',repo_name=c.dbrepo.repo_name, fname='__CS__'+archive['extension'],subrepos='__SUB__'),class_='archive_icon ui-btn')}';
293 %endfor
293 %endfor
294
294
295 YUE.on(['download_options','archive_subrepos'],'change',function(e){
295 YUE.on(['download_options','archive_subrepos'],'change',function(e){
296 var sm = YUD.get('download_options');
296 var sm = YUD.get('download_options');
297 var new_cs = sm.options[sm.selectedIndex];
297 var new_cs = sm.options[sm.selectedIndex];
298
298
299 for(k in tmpl_links){
299 for(k in tmpl_links){
300 var s = YUD.get(k+'_link');
300 var s = YUD.get(k+'_link');
301 if(s){
301 if(s){
302 var title_tmpl = "${_('Download %s as %s') % ('__CS_NAME__','__CS_EXT__')}";
302 var title_tmpl = "${_('Download %s as %s') % ('__CS_NAME__','__CS_EXT__')}";
303 title_tmpl= title_tmpl.replace('__CS_NAME__',new_cs.text);
303 title_tmpl= title_tmpl.replace('__CS_NAME__',new_cs.text);
304 title_tmpl = title_tmpl.replace('__CS_EXT__',k);
304 title_tmpl = title_tmpl.replace('__CS_EXT__',k);
305
305
306 var url = tmpl_links[k].replace('__CS__',new_cs.value);
306 var url = tmpl_links[k].replace('__CS__',new_cs.value);
307 var subrepos = YUD.get('archive_subrepos').checked;
307 var subrepos = YUD.get('archive_subrepos').checked;
308 url = url.replace('__SUB__',subrepos);
308 url = url.replace('__SUB__',subrepos);
309 url = url.replace('__NAME__',title_tmpl);
309 url = url.replace('__NAME__',title_tmpl);
310 s.innerHTML = url
310 s.innerHTML = url
311 }
311 }
312 }
312 }
313 });
313 });
314 </script>
314 </script>
315 %if c.show_stats:
315 %if c.show_stats:
316 <script type="text/javascript">
316 <script type="text/javascript">
317 var data = ${c.trending_languages|n};
317 var data = ${c.trending_languages|n};
318 var total = 0;
318 var total = 0;
319 var no_data = true;
319 var no_data = true;
320 var tbl = document.createElement('table');
320 var tbl = document.createElement('table');
321 tbl.setAttribute('class','trending_language_tbl');
321 tbl.setAttribute('class','trending_language_tbl');
322 var cnt = 0;
322 var cnt = 0;
323 for (var i=0;i<data.length;i++){
323 for (var i=0;i<data.length;i++){
324 total+= data[i][1].count;
324 total+= data[i][1].count;
325 }
325 }
326 for (var i=0;i<data.length;i++){
326 for (var i=0;i<data.length;i++){
327 cnt += 1;
327 cnt += 1;
328 no_data = false;
328 no_data = false;
329
329
330 var hide = cnt>2;
330 var hide = cnt>2;
331 var tr = document.createElement('tr');
331 var tr = document.createElement('tr');
332 if (hide){
332 if (hide){
333 tr.setAttribute('style','display:none');
333 tr.setAttribute('style','display:none');
334 tr.setAttribute('class','stats_hidden');
334 tr.setAttribute('class','stats_hidden');
335 }
335 }
336 var k = data[i][0];
336 var k = data[i][0];
337 var obj = data[i][1];
337 var obj = data[i][1];
338 var percentage = Math.round((obj.count/total*100),2);
338 var percentage = Math.round((obj.count/total*100),2);
339
339
340 var td1 = document.createElement('td');
340 var td1 = document.createElement('td');
341 td1.width = 150;
341 td1.width = 150;
342 var trending_language_label = document.createElement('div');
342 var trending_language_label = document.createElement('div');
343 trending_language_label.innerHTML = obj.desc+" ("+k+")";
343 trending_language_label.innerHTML = obj.desc+" ("+k+")";
344 td1.appendChild(trending_language_label);
344 td1.appendChild(trending_language_label);
345
345
346 var td2 = document.createElement('td');
346 var td2 = document.createElement('td');
347 td2.setAttribute('style','padding-right:14px !important');
347 td2.setAttribute('style','padding-right:14px !important');
348 var trending_language = document.createElement('div');
348 var trending_language = document.createElement('div');
349 var nr_files = obj.count+" ${_('files')}";
349 var nr_files = obj.count+" ${_('files')}";
350
350
351 trending_language.title = k+" "+nr_files;
351 trending_language.title = k+" "+nr_files;
352
352
353 if (percentage>22){
353 if (percentage>22){
354 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"% "+nr_files+ "</b>";
354 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"% "+nr_files+ "</b>";
355 }
355 }
356 else{
356 else{
357 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"%</b>";
357 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"%</b>";
358 }
358 }
359
359
360 trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
360 trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
361 trending_language.style.width=percentage+"%";
361 trending_language.style.width=percentage+"%";
362 td2.appendChild(trending_language);
362 td2.appendChild(trending_language);
363
363
364 tr.appendChild(td1);
364 tr.appendChild(td1);
365 tr.appendChild(td2);
365 tr.appendChild(td2);
366 tbl.appendChild(tr);
366 tbl.appendChild(tr);
367 if(cnt == 3){
367 if(cnt == 3){
368 var show_more = document.createElement('tr');
368 var show_more = document.createElement('tr');
369 var td = document.createElement('td');
369 var td = document.createElement('td');
370 lnk = document.createElement('a');
370 lnk = document.createElement('a');
371
371
372 lnk.href='#';
372 lnk.href='#';
373 lnk.innerHTML = "${_('show more')}";
373 lnk.innerHTML = "${_('show more')}";
374 lnk.id='code_stats_show_more';
374 lnk.id='code_stats_show_more';
375 td.appendChild(lnk);
375 td.appendChild(lnk);
376
376
377 show_more.appendChild(td);
377 show_more.appendChild(td);
378 show_more.appendChild(document.createElement('td'));
378 show_more.appendChild(document.createElement('td'));
379 tbl.appendChild(show_more);
379 tbl.appendChild(show_more);
380 }
380 }
381
381
382 }
382 }
383
383
384 YUD.get('lang_stats').appendChild(tbl);
384 YUD.get('lang_stats').appendChild(tbl);
385 YUE.on('code_stats_show_more','click',function(){
385 YUE.on('code_stats_show_more','click',function(){
386 l = YUD.getElementsByClassName('stats_hidden')
386 l = YUD.getElementsByClassName('stats_hidden')
387 for (e in l){
387 for (e in l){
388 YUD.setStyle(l[e],'display','');
388 YUD.setStyle(l[e],'display','');
389 };
389 };
390 YUD.setStyle(YUD.get('code_stats_show_more'),
390 YUD.setStyle(YUD.get('code_stats_show_more'),
391 'display','none');
391 'display','none');
392 });
392 });
393 </script>
393 </script>
394 <script type="text/javascript">
394 <script type="text/javascript">
395 /**
395 /**
396 * Plots summary graph
396 * Plots summary graph
397 *
397 *
398 * @class SummaryPlot
398 * @class SummaryPlot
399 * @param {from} initial from for detailed graph
399 * @param {from} initial from for detailed graph
400 * @param {to} initial to for detailed graph
400 * @param {to} initial to for detailed graph
401 * @param {dataset}
401 * @param {dataset}
402 * @param {overview_dataset}
402 * @param {overview_dataset}
403 */
403 */
404 function SummaryPlot(from,to,dataset,overview_dataset) {
404 function SummaryPlot(from,to,dataset,overview_dataset) {
405 var initial_ranges = {
405 var initial_ranges = {
406 "xaxis":{
406 "xaxis":{
407 "from":from,
407 "from":from,
408 "to":to,
408 "to":to,
409 },
409 },
410 };
410 };
411 var dataset = dataset;
411 var dataset = dataset;
412 var overview_dataset = [overview_dataset];
412 var overview_dataset = [overview_dataset];
413 var choiceContainer = YUD.get("legend_choices");
413 var choiceContainer = YUD.get("legend_choices");
414 var choiceContainerTable = YUD.get("legend_choices_tables");
414 var choiceContainerTable = YUD.get("legend_choices_tables");
415 var plotContainer = YUD.get('commit_history');
415 var plotContainer = YUD.get('commit_history');
416 var overviewContainer = YUD.get('overview');
416 var overviewContainer = YUD.get('overview');
417
417
418 var plot_options = {
418 var plot_options = {
419 bars: {show:true,align:'center',lineWidth:4},
419 bars: {show:true,align:'center',lineWidth:4},
420 legend: {show:true, container:"legend_container"},
420 legend: {show:true, container:"legend_container"},
421 points: {show:true,radius:0,fill:false},
421 points: {show:true,radius:0,fill:false},
422 yaxis: {tickDecimals:0,},
422 yaxis: {tickDecimals:0,},
423 xaxis: {
423 xaxis: {
424 mode: "time",
424 mode: "time",
425 timeformat: "%d/%m",
425 timeformat: "%d/%m",
426 min:from,
426 min:from,
427 max:to,
427 max:to,
428 },
428 },
429 grid: {
429 grid: {
430 hoverable: true,
430 hoverable: true,
431 clickable: true,
431 clickable: true,
432 autoHighlight:true,
432 autoHighlight:true,
433 color: "#999"
433 color: "#999"
434 },
434 },
435 //selection: {mode: "x"}
435 //selection: {mode: "x"}
436 };
436 };
437 var overview_options = {
437 var overview_options = {
438 legend:{show:false},
438 legend:{show:false},
439 bars: {show:true,barWidth: 2,},
439 bars: {show:true,barWidth: 2,},
440 shadowSize: 0,
440 shadowSize: 0,
441 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
441 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
442 yaxis: {ticks: 3, min: 0,tickDecimals:0,},
442 yaxis: {ticks: 3, min: 0,tickDecimals:0,},
443 grid: {color: "#999",},
443 grid: {color: "#999",},
444 selection: {mode: "x"}
444 selection: {mode: "x"}
445 };
445 };
446
446
447 /**
447 /**
448 *get dummy data needed in few places
448 *get dummy data needed in few places
449 */
449 */
450 function getDummyData(label){
450 function getDummyData(label){
451 return {"label":label,
451 return {"label":label,
452 "data":[{"time":0,
452 "data":[{"time":0,
453 "commits":0,
453 "commits":0,
454 "added":0,
454 "added":0,
455 "changed":0,
455 "changed":0,
456 "removed":0,
456 "removed":0,
457 }],
457 }],
458 "schema":["commits"],
458 "schema":["commits"],
459 "color":'#ffffff',
459 "color":'#ffffff',
460 }
460 }
461 }
461 }
462
462
463 /**
463 /**
464 * generate checkboxes accordindly to data
464 * generate checkboxes accordindly to data
465 * @param keys
465 * @param keys
466 * @returns
466 * @returns
467 */
467 */
468 function generateCheckboxes(data) {
468 function generateCheckboxes(data) {
469 //append checkboxes
469 //append checkboxes
470 var i = 0;
470 var i = 0;
471 choiceContainerTable.innerHTML = '';
471 choiceContainerTable.innerHTML = '';
472 for(var pos in data) {
472 for(var pos in data) {
473
473
474 data[pos].color = i;
474 data[pos].color = i;
475 i++;
475 i++;
476 if(data[pos].label != ''){
476 if(data[pos].label != ''){
477 choiceContainerTable.innerHTML +=
477 choiceContainerTable.innerHTML +=
478 '<tr><td><input type="checkbox" id="id_user_{0}" name="{0}" checked="checked" /> \
478 '<tr><td><input type="checkbox" id="id_user_{0}" name="{0}" checked="checked" /> \
479 <label for="id_user_{0}">{0}</label></td></tr>'.format(data[pos].label);
479 <label for="id_user_{0}">{0}</label></td></tr>'.format(data[pos].label);
480 }
480 }
481 }
481 }
482 }
482 }
483
483
484 /**
484 /**
485 * ToolTip show
485 * ToolTip show
486 */
486 */
487 function showTooltip(x, y, contents) {
487 function showTooltip(x, y, contents) {
488 var div=document.getElementById('tooltip');
488 var div=document.getElementById('tooltip');
489 if(!div) {
489 if(!div) {
490 div = document.createElement('div');
490 div = document.createElement('div');
491 div.id="tooltip";
491 div.id="tooltip";
492 div.style.position="absolute";
492 div.style.position="absolute";
493 div.style.border='1px solid #fdd';
493 div.style.border='1px solid #fdd';
494 div.style.padding='2px';
494 div.style.padding='2px';
495 div.style.backgroundColor='#fee';
495 div.style.backgroundColor='#fee';
496 document.body.appendChild(div);
496 document.body.appendChild(div);
497 }
497 }
498 YUD.setStyle(div, 'opacity', 0);
498 YUD.setStyle(div, 'opacity', 0);
499 div.innerHTML = contents;
499 div.innerHTML = contents;
500 div.style.top=(y + 5) + "px";
500 div.style.top=(y + 5) + "px";
501 div.style.left=(x + 5) + "px";
501 div.style.left=(x + 5) + "px";
502
502
503 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
503 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
504 anim.animate();
504 anim.animate();
505 }
505 }
506
506
507 /**
507 /**
508 * This function will detect if selected period has some changesets
508 * This function will detect if selected period has some changesets
509 for this user if it does this data is then pushed for displaying
509 for this user if it does this data is then pushed for displaying
510 Additionally it will only display users that are selected by the checkbox
510 Additionally it will only display users that are selected by the checkbox
511 */
511 */
512 function getDataAccordingToRanges(ranges) {
512 function getDataAccordingToRanges(ranges) {
513
513
514 var data = [];
514 var data = [];
515 var new_dataset = {};
515 var new_dataset = {};
516 var keys = [];
516 var keys = [];
517 var max_commits = 0;
517 var max_commits = 0;
518 for(var key in dataset){
518 for(var key in dataset){
519
519
520 for(var ds in dataset[key].data){
520 for(var ds in dataset[key].data){
521 commit_data = dataset[key].data[ds];
521 commit_data = dataset[key].data[ds];
522 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
522 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
523
523
524 if(new_dataset[key] === undefined){
524 if(new_dataset[key] === undefined){
525 new_dataset[key] = {data:[],schema:["commits"],label:key};
525 new_dataset[key] = {data:[],schema:["commits"],label:key};
526 }
526 }
527 new_dataset[key].data.push(commit_data);
527 new_dataset[key].data.push(commit_data);
528 }
528 }
529 }
529 }
530 if (new_dataset[key] !== undefined){
530 if (new_dataset[key] !== undefined){
531 data.push(new_dataset[key]);
531 data.push(new_dataset[key]);
532 }
532 }
533 }
533 }
534
534
535 if (data.length > 0){
535 if (data.length > 0){
536 return data;
536 return data;
537 }
537 }
538 else{
538 else{
539 //just return dummy data for graph to plot itself
539 //just return dummy data for graph to plot itself
540 return [getDummyData('')];
540 return [getDummyData('')];
541 }
541 }
542 }
542 }
543
543
544 /**
544 /**
545 * redraw using new checkbox data
545 * redraw using new checkbox data
546 */
546 */
547 function plotchoiced(e,args){
547 function plotchoiced(e,args){
548 var cur_data = args[0];
548 var cur_data = args[0];
549 var cur_ranges = args[1];
549 var cur_ranges = args[1];
550
550
551 var new_data = [];
551 var new_data = [];
552 var inputs = choiceContainer.getElementsByTagName("input");
552 var inputs = choiceContainer.getElementsByTagName("input");
553
553
554 //show only checked labels
554 //show only checked labels
555 for(var i=0; i<inputs.length; i++) {
555 for(var i=0; i<inputs.length; i++) {
556 var checkbox_key = inputs[i].name;
556 var checkbox_key = inputs[i].name;
557
557
558 if(inputs[i].checked){
558 if(inputs[i].checked){
559 for(var d in cur_data){
559 for(var d in cur_data){
560 if(cur_data[d].label == checkbox_key){
560 if(cur_data[d].label == checkbox_key){
561 new_data.push(cur_data[d]);
561 new_data.push(cur_data[d]);
562 }
562 }
563 }
563 }
564 }
564 }
565 else{
565 else{
566 //push dummy data to not hide the label
566 //push dummy data to not hide the label
567 new_data.push(getDummyData(checkbox_key));
567 new_data.push(getDummyData(checkbox_key));
568 }
568 }
569 }
569 }
570
570
571 var new_options = YAHOO.lang.merge(plot_options, {
571 var new_options = YAHOO.lang.merge(plot_options, {
572 xaxis: {
572 xaxis: {
573 min: cur_ranges.xaxis.from,
573 min: cur_ranges.xaxis.from,
574 max: cur_ranges.xaxis.to,
574 max: cur_ranges.xaxis.to,
575 mode:"time",
575 mode:"time",
576 timeformat: "%d/%m",
576 timeformat: "%d/%m",
577 },
577 },
578 });
578 });
579 if (!new_data){
579 if (!new_data){
580 new_data = [[0,1]];
580 new_data = [[0,1]];
581 }
581 }
582 // do the zooming
582 // do the zooming
583 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
583 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
584
584
585 plot.subscribe("plotselected", plotselected);
585 plot.subscribe("plotselected", plotselected);
586
586
587 //resubscribe plothover
587 //resubscribe plothover
588 plot.subscribe("plothover", plothover);
588 plot.subscribe("plothover", plothover);
589
589
590 // don't fire event on the overview to prevent eternal loop
590 // don't fire event on the overview to prevent eternal loop
591 overview.setSelection(cur_ranges, true);
591 overview.setSelection(cur_ranges, true);
592
592
593 }
593 }
594
594
595 /**
595 /**
596 * plot only selected items from overview
596 * plot only selected items from overview
597 * @param ranges
597 * @param ranges
598 * @returns
598 * @returns
599 */
599 */
600 function plotselected(ranges,cur_data) {
600 function plotselected(ranges,cur_data) {
601 //updates the data for new plot
601 //updates the data for new plot
602 var data = getDataAccordingToRanges(ranges);
602 var data = getDataAccordingToRanges(ranges);
603 generateCheckboxes(data);
603 generateCheckboxes(data);
604
604
605 var new_options = YAHOO.lang.merge(plot_options, {
605 var new_options = YAHOO.lang.merge(plot_options, {
606 xaxis: {
606 xaxis: {
607 min: ranges.xaxis.from,
607 min: ranges.xaxis.from,
608 max: ranges.xaxis.to,
608 max: ranges.xaxis.to,
609 mode:"time",
609 mode:"time",
610 timeformat: "%d/%m",
610 timeformat: "%d/%m",
611 },
611 },
612 });
612 });
613 // do the zooming
613 // do the zooming
614 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
614 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
615
615
616 plot.subscribe("plotselected", plotselected);
616 plot.subscribe("plotselected", plotselected);
617
617
618 //resubscribe plothover
618 //resubscribe plothover
619 plot.subscribe("plothover", plothover);
619 plot.subscribe("plothover", plothover);
620
620
621 // don't fire event on the overview to prevent eternal loop
621 // don't fire event on the overview to prevent eternal loop
622 overview.setSelection(ranges, true);
622 overview.setSelection(ranges, true);
623
623
624 //resubscribe choiced
624 //resubscribe choiced
625 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
625 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
626 }
626 }
627
627
628 var previousPoint = null;
628 var previousPoint = null;
629
629
630 function plothover(o) {
630 function plothover(o) {
631 var pos = o.pos;
631 var pos = o.pos;
632 var item = o.item;
632 var item = o.item;
633
633
634 //YUD.get("x").innerHTML = pos.x.toFixed(2);
634 //YUD.get("x").innerHTML = pos.x.toFixed(2);
635 //YUD.get("y").innerHTML = pos.y.toFixed(2);
635 //YUD.get("y").innerHTML = pos.y.toFixed(2);
636 if (item) {
636 if (item) {
637 if (previousPoint != item.datapoint) {
637 if (previousPoint != item.datapoint) {
638 previousPoint = item.datapoint;
638 previousPoint = item.datapoint;
639
639
640 var tooltip = YUD.get("tooltip");
640 var tooltip = YUD.get("tooltip");
641 if(tooltip) {
641 if(tooltip) {
642 tooltip.parentNode.removeChild(tooltip);
642 tooltip.parentNode.removeChild(tooltip);
643 }
643 }
644 var x = item.datapoint.x.toFixed(2);
644 var x = item.datapoint.x.toFixed(2);
645 var y = item.datapoint.y.toFixed(2);
645 var y = item.datapoint.y.toFixed(2);
646
646
647 if (!item.series.label){
647 if (!item.series.label){
648 item.series.label = 'commits';
648 item.series.label = 'commits';
649 }
649 }
650 var d = new Date(x*1000);
650 var d = new Date(x*1000);
651 var fd = d.toDateString()
651 var fd = d.toDateString()
652 var nr_commits = parseInt(y);
652 var nr_commits = parseInt(y);
653
653
654 var cur_data = dataset[item.series.label].data[item.dataIndex];
654 var cur_data = dataset[item.series.label].data[item.dataIndex];
655 var added = cur_data.added;
655 var added = cur_data.added;
656 var changed = cur_data.changed;
656 var changed = cur_data.changed;
657 var removed = cur_data.removed;
657 var removed = cur_data.removed;
658
658
659 var nr_commits_suffix = " ${_('commits')} ";
659 var nr_commits_suffix = " ${_('commits')} ";
660 var added_suffix = " ${_('files added')} ";
660 var added_suffix = " ${_('files added')} ";
661 var changed_suffix = " ${_('files changed')} ";
661 var changed_suffix = " ${_('files changed')} ";
662 var removed_suffix = " ${_('files removed')} ";
662 var removed_suffix = " ${_('files removed')} ";
663
663
664
664
665 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
665 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
666 if(added==1){added_suffix=" ${_('file added')} ";}
666 if(added==1){added_suffix=" ${_('file added')} ";}
667 if(changed==1){changed_suffix=" ${_('file changed')} ";}
667 if(changed==1){changed_suffix=" ${_('file changed')} ";}
668 if(removed==1){removed_suffix=" ${_('file removed')} ";}
668 if(removed==1){removed_suffix=" ${_('file removed')} ";}
669
669
670 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
670 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
671 +'<br/>'+
671 +'<br/>'+
672 nr_commits + nr_commits_suffix+'<br/>'+
672 nr_commits + nr_commits_suffix+'<br/>'+
673 added + added_suffix +'<br/>'+
673 added + added_suffix +'<br/>'+
674 changed + changed_suffix + '<br/>'+
674 changed + changed_suffix + '<br/>'+
675 removed + removed_suffix + '<br/>');
675 removed + removed_suffix + '<br/>');
676 }
676 }
677 }
677 }
678 else {
678 else {
679 var tooltip = YUD.get("tooltip");
679 var tooltip = YUD.get("tooltip");
680
680
681 if(tooltip) {
681 if(tooltip) {
682 tooltip.parentNode.removeChild(tooltip);
682 tooltip.parentNode.removeChild(tooltip);
683 }
683 }
684 previousPoint = null;
684 previousPoint = null;
685 }
685 }
686 }
686 }
687
687
688 /**
688 /**
689 * MAIN EXECUTION
689 * MAIN EXECUTION
690 */
690 */
691
691
692 var data = getDataAccordingToRanges(initial_ranges);
692 var data = getDataAccordingToRanges(initial_ranges);
693 generateCheckboxes(data);
693 generateCheckboxes(data);
694
694
695 //main plot
695 //main plot
696 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
696 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
697
697
698 //overview
698 //overview
699 var overview = YAHOO.widget.Flot(overviewContainer,
699 var overview = YAHOO.widget.Flot(overviewContainer,
700 overview_dataset, overview_options);
700 overview_dataset, overview_options);
701
701
702 //show initial selection on overview
702 //show initial selection on overview
703 overview.setSelection(initial_ranges);
703 overview.setSelection(initial_ranges);
704
704
705 plot.subscribe("plotselected", plotselected);
705 plot.subscribe("plotselected", plotselected);
706 plot.subscribe("plothover", plothover)
706 plot.subscribe("plothover", plothover)
707
707
708 overview.subscribe("plotselected", function (ranges) {
708 overview.subscribe("plotselected", function (ranges) {
709 plot.setSelection(ranges);
709 plot.setSelection(ranges);
710 });
710 });
711
711
712 // user choices on overview
712 // user choices on overview
713 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
713 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
714 }
714 }
715 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
715 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
716 </script>
716 </script>
717 %endif
717 %endif
718
718
719 </%def>
719 </%def>
@@ -1,79 +1,78 b''
1 import time
1 import time
2 from rhodecode.tests import *
2 from rhodecode.tests import *
3 from rhodecode.model.meta import Session
3 from rhodecode.model.meta import Session
4 from rhodecode.model.db import User, RhodeCodeSetting, Repository
4 from rhodecode.model.db import User, RhodeCodeSetting, Repository
5 from rhodecode.lib.utils import set_rhodecode_config
5 from rhodecode.lib.utils import set_rhodecode_config
6
6
7
7
8 class TestHomeController(TestController):
8 class TestHomeController(TestController):
9
9
10 def test_index(self):
10 def test_index(self):
11 self.log_user()
11 self.log_user()
12 response = self.app.get(url(controller='home', action='index'))
12 response = self.app.get(url(controller='home', action='index'))
13 #if global permission is set
13 #if global permission is set
14 response.mustcontain('ADD REPOSITORY')
14 response.mustcontain('ADD REPOSITORY')
15 response.mustcontain('href="/%s/summary"' % HG_REPO)
15 response.mustcontain('href="/%s/summary"' % HG_REPO)
16
16
17 response.mustcontain("""<img class="icon" title="Mercurial repository" """
17 response.mustcontain("""<img class="icon" title="Mercurial repository" """
18 """alt="Mercurial repository" src="/images/icons/hg"""
18 """alt="Mercurial repository" src="/images/icons/hg"""
19 """icon.png"/>""")
19 """icon.png"/>""")
20 response.mustcontain("""<img class="icon" title="public repository" """
20 response.mustcontain("""<img class="icon" title="public repository" """
21 """alt="public repository" src="/images/icons/lock_"""
21 """alt="public repository" src="/images/icons/lock_"""
22 """open.png"/>""")
22 """open.png"/>""")
23
23
24 response.mustcontain(
24 response.mustcontain(
25 """<a title="Marcin Kuzminski &amp;lt;marcin@python-works.com&amp;gt;:\n
25 """<a title="Marcin Kuzminski &amp;lt;marcin@python-works.com&amp;gt;:\n
26 merge" class="tooltip" href="/vcs_test_hg/changeset/27cd5cce30c96924232"""
26 merge" class="tooltip" href="/vcs_test_hg/changeset/27cd5cce30c96924232"""
27 """dffcd24178a07ffeb5dfc">r173:27cd5cce30c9</a>"""
27 """dffcd24178a07ffeb5dfc">r173:27cd5cce30c9</a>"""
28 )
28 )
29
29
30 def test_repo_summary_with_anonymous_access_disabled(self):
30 def test_repo_summary_with_anonymous_access_disabled(self):
31 anon = User.get_by_username('default')
31 anon = User.get_by_username('default')
32 anon.active = False
32 anon.active = False
33 Session().add(anon)
33 Session().add(anon)
34 Session().commit()
34 Session().commit()
35 time.sleep(1.5) # must sleep for cache (1s to expire)
35 time.sleep(1.5) # must sleep for cache (1s to expire)
36 try:
36 try:
37 response = self.app.get(url(controller='summary',
37 response = self.app.get(url(controller='summary',
38 action='index', repo_name=HG_REPO),
38 action='index', repo_name=HG_REPO),
39 status=302)
39 status=302)
40 assert 'login' in response.location
40 assert 'login' in response.location
41
41
42 finally:
42 finally:
43 anon = User.get_by_username('default')
43 anon = User.get_by_username('default')
44 anon.active = True
44 anon.active = True
45 Session().add(anon)
45 Session().add(anon)
46 Session().commit()
46 Session().commit()
47
47
48 def test_index_with_anonymous_access_disabled(self):
48 def test_index_with_anonymous_access_disabled(self):
49 anon = User.get_by_username('default')
49 anon = User.get_by_username('default')
50 anon.active = False
50 anon.active = False
51 Session().add(anon)
51 Session().add(anon)
52 Session().commit()
52 Session().commit()
53 time.sleep(1.5) # must sleep for cache (1s to expire)
53 time.sleep(1.5) # must sleep for cache (1s to expire)
54 try:
54 try:
55 response = self.app.get(url(controller='home', action='index'),
55 response = self.app.get(url(controller='home', action='index'),
56 status=302)
56 status=302)
57 assert 'login' in response.location
57 assert 'login' in response.location
58 finally:
58 finally:
59 anon = User.get_by_username('default')
59 anon = User.get_by_username('default')
60 anon.active = True
60 anon.active = True
61 Session().add(anon)
61 Session().add(anon)
62 Session().commit()
62 Session().commit()
63
63
64 def test_index_with_lightweight_dashboard(self):
64 def test_index_with_lightweight_dashboard(self):
65 self.log_user()
65 self.log_user()
66
66
67 def set_l_dash(set_to):
67 def set_l_dash(set_to):
68 self.app.post(url('admin_setting', setting_id='visual'),
68 self.app.post(url('admin_setting', setting_id='visual'),
69 params=dict(_method='put',
69 params=dict(_method='put',
70 rhodecode_lightweight_dashboard=set_to,))
70 rhodecode_lightweight_dashboard=set_to,))
71
71
72 set_l_dash(True)
72 set_l_dash(True)
73
73
74 try:
74 try:
75 response = self.app.get(url(controller='home', action='index'))
75 response = self.app.get(url(controller='home', action='index'))
76 response.mustcontain("""var data = {"totalRecords": %s""" % len(Repository.getAll()))
76 response.mustcontain("""var data = {"totalRecords": %s""" % len(Repository.getAll()))
77 finally:
77 finally:
78 set_l_dash(False)
78 set_l_dash(False)
79
General Comments 0
You need to be logged in to leave comments. Login now