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