##// END OF EJS Templates
Added more tests for web based file edit and add
marcink -
r3839:9dec8704 beta
parent child Browse files
Show More
@@ -1,650 +1,650 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.files
3 rhodecode.controllers.files
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Files controller for RhodeCode
6 Files controller for RhodeCode
7
7
8 :created_on: Apr 21, 2010
8 :created_on: Apr 21, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 from __future__ import with_statement
25 from __future__ import with_statement
26 import os
26 import os
27 import logging
27 import logging
28 import traceback
28 import traceback
29 import tempfile
29 import tempfile
30 import shutil
30 import shutil
31
31
32 from pylons import request, response, tmpl_context as c, url
32 from pylons import request, response, tmpl_context as c, url
33 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
34 from pylons.controllers.util import redirect
34 from pylons.controllers.util import redirect
35 from rhodecode.lib.utils import jsonify
35 from rhodecode.lib.utils import jsonify
36
36
37 from rhodecode.lib import diffs
37 from rhodecode.lib import diffs
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
39
39
40 from rhodecode.lib.compat import OrderedDict
40 from rhodecode.lib.compat import OrderedDict
41 from rhodecode.lib.utils2 import convert_line_endings, detect_mode, safe_str,\
41 from rhodecode.lib.utils2 import convert_line_endings, detect_mode, safe_str,\
42 str2bool
42 str2bool
43 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
43 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
44 from rhodecode.lib.base import BaseRepoController, render
44 from rhodecode.lib.base import BaseRepoController, render
45 from rhodecode.lib.vcs.backends.base import EmptyChangeset
45 from rhodecode.lib.vcs.backends.base import EmptyChangeset
46 from rhodecode.lib.vcs.conf import settings
46 from rhodecode.lib.vcs.conf import settings
47 from rhodecode.lib.vcs.exceptions import RepositoryError, \
47 from rhodecode.lib.vcs.exceptions import RepositoryError, \
48 ChangesetDoesNotExistError, EmptyRepositoryError, \
48 ChangesetDoesNotExistError, EmptyRepositoryError, \
49 ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError,\
49 ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError,\
50 NodeDoesNotExistError, ChangesetError, NodeError
50 NodeDoesNotExistError, ChangesetError, NodeError
51 from rhodecode.lib.vcs.nodes import FileNode
51 from rhodecode.lib.vcs.nodes import FileNode
52
52
53 from rhodecode.model.repo import RepoModel
53 from rhodecode.model.repo import RepoModel
54 from rhodecode.model.scm import ScmModel
54 from rhodecode.model.scm import ScmModel
55 from rhodecode.model.db import Repository
55 from rhodecode.model.db import Repository
56
56
57 from rhodecode.controllers.changeset import anchor_url, _ignorews_url,\
57 from rhodecode.controllers.changeset import anchor_url, _ignorews_url,\
58 _context_url, get_line_ctx, get_ignore_ws
58 _context_url, get_line_ctx, get_ignore_ws
59 from webob.exc import HTTPNotFound
59 from webob.exc import HTTPNotFound
60
60
61
61
62 log = logging.getLogger(__name__)
62 log = logging.getLogger(__name__)
63
63
64
64
65 class FilesController(BaseRepoController):
65 class FilesController(BaseRepoController):
66
66
67 def __before__(self):
67 def __before__(self):
68 super(FilesController, self).__before__()
68 super(FilesController, self).__before__()
69 c.cut_off_limit = self.cut_off_limit
69 c.cut_off_limit = self.cut_off_limit
70
70
71 def __get_cs_or_redirect(self, rev, repo_name, redirect_after=True):
71 def __get_cs_or_redirect(self, rev, repo_name, redirect_after=True):
72 """
72 """
73 Safe way to get changeset if error occur it redirects to tip with
73 Safe way to get changeset if error occur it redirects to tip with
74 proper message
74 proper message
75
75
76 :param rev: revision to fetch
76 :param rev: revision to fetch
77 :param repo_name: repo name to redirect after
77 :param repo_name: repo name to redirect after
78 """
78 """
79
79
80 try:
80 try:
81 return c.rhodecode_repo.get_changeset(rev)
81 return c.rhodecode_repo.get_changeset(rev)
82 except EmptyRepositoryError, e:
82 except EmptyRepositoryError, e:
83 if not redirect_after:
83 if not redirect_after:
84 return None
84 return None
85 url_ = url('files_add_home',
85 url_ = url('files_add_home',
86 repo_name=c.repo_name,
86 repo_name=c.repo_name,
87 revision=0, f_path='')
87 revision=0, f_path='')
88 add_new = h.link_to(_('Click here to add new file'), url_)
88 add_new = h.link_to(_('Click here to add new file'), url_)
89 h.flash(h.literal(_('There are no files yet %s') % add_new),
89 h.flash(h.literal(_('There are no files yet %s') % add_new),
90 category='warning')
90 category='warning')
91 redirect(h.url('summary_home', repo_name=repo_name))
91 redirect(h.url('summary_home', repo_name=repo_name))
92
92
93 except RepositoryError, e: # including ChangesetDoesNotExistError
93 except RepositoryError, e: # including ChangesetDoesNotExistError
94 h.flash(str(e), category='error')
94 h.flash(str(e), category='error')
95 raise HTTPNotFound()
95 raise HTTPNotFound()
96
96
97 def __get_filenode_or_redirect(self, repo_name, cs, path):
97 def __get_filenode_or_redirect(self, repo_name, cs, path):
98 """
98 """
99 Returns file_node, if error occurs or given path is directory,
99 Returns file_node, if error occurs or given path is directory,
100 it'll redirect to top level path
100 it'll redirect to top level path
101
101
102 :param repo_name: repo_name
102 :param repo_name: repo_name
103 :param cs: given changeset
103 :param cs: given changeset
104 :param path: path to lookup
104 :param path: path to lookup
105 """
105 """
106
106
107 try:
107 try:
108 file_node = cs.get_node(path)
108 file_node = cs.get_node(path)
109 if file_node.is_dir():
109 if file_node.is_dir():
110 raise RepositoryError('given path is a directory')
110 raise RepositoryError('given path is a directory')
111 except RepositoryError, e:
111 except RepositoryError, e:
112 h.flash(str(e), category='error')
112 h.flash(str(e), category='error')
113 raise HTTPNotFound()
113 raise HTTPNotFound()
114
114
115 return file_node
115 return file_node
116
116
117 @LoginRequired()
117 @LoginRequired()
118 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
118 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
119 'repository.admin')
119 'repository.admin')
120 def index(self, repo_name, revision, f_path, annotate=False):
120 def index(self, repo_name, revision, f_path, annotate=False):
121 # redirect to given revision from form if given
121 # redirect to given revision from form if given
122 post_revision = request.POST.get('at_rev', None)
122 post_revision = request.POST.get('at_rev', None)
123 if post_revision:
123 if post_revision:
124 cs = self.__get_cs_or_redirect(post_revision, repo_name)
124 cs = self.__get_cs_or_redirect(post_revision, repo_name)
125
125
126 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
126 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
127 c.branch = request.GET.get('branch', None)
127 c.branch = request.GET.get('branch', None)
128 c.f_path = f_path
128 c.f_path = f_path
129 c.annotate = annotate
129 c.annotate = annotate
130 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
130 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
131 cur_rev = c.changeset.revision
131 cur_rev = c.changeset.revision
132
132
133 # prev link
133 # prev link
134 try:
134 try:
135 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
135 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
136 c.url_prev = url('files_home', repo_name=c.repo_name,
136 c.url_prev = url('files_home', repo_name=c.repo_name,
137 revision=prev_rev.raw_id, f_path=f_path)
137 revision=prev_rev.raw_id, f_path=f_path)
138 if c.branch:
138 if c.branch:
139 c.url_prev += '?branch=%s' % c.branch
139 c.url_prev += '?branch=%s' % c.branch
140 except (ChangesetDoesNotExistError, VCSError):
140 except (ChangesetDoesNotExistError, VCSError):
141 c.url_prev = '#'
141 c.url_prev = '#'
142
142
143 # next link
143 # next link
144 try:
144 try:
145 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
145 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
146 c.url_next = url('files_home', repo_name=c.repo_name,
146 c.url_next = url('files_home', repo_name=c.repo_name,
147 revision=next_rev.raw_id, f_path=f_path)
147 revision=next_rev.raw_id, f_path=f_path)
148 if c.branch:
148 if c.branch:
149 c.url_next += '?branch=%s' % c.branch
149 c.url_next += '?branch=%s' % c.branch
150 except (ChangesetDoesNotExistError, VCSError):
150 except (ChangesetDoesNotExistError, VCSError):
151 c.url_next = '#'
151 c.url_next = '#'
152
152
153 # files or dirs
153 # files or dirs
154 try:
154 try:
155 c.file = c.changeset.get_node(f_path)
155 c.file = c.changeset.get_node(f_path)
156
156
157 if c.file.is_file():
157 if c.file.is_file():
158 c.load_full_history = False
158 c.load_full_history = False
159 file_last_cs = c.file.last_changeset
159 file_last_cs = c.file.last_changeset
160 c.file_changeset = (c.changeset
160 c.file_changeset = (c.changeset
161 if c.changeset.revision < file_last_cs.revision
161 if c.changeset.revision < file_last_cs.revision
162 else file_last_cs)
162 else file_last_cs)
163 #determine if we're on branch head
163 #determine if we're on branch head
164 _branches = c.rhodecode_repo.branches
164 _branches = c.rhodecode_repo.branches
165 c.on_branch_head = revision in _branches.keys() + _branches.values()
165 c.on_branch_head = revision in _branches.keys() + _branches.values()
166 _hist = []
166 _hist = []
167 c.file_history = []
167 c.file_history = []
168 if c.load_full_history:
168 if c.load_full_history:
169 c.file_history, _hist = self._get_node_history(c.changeset, f_path)
169 c.file_history, _hist = self._get_node_history(c.changeset, f_path)
170
170
171 c.authors = []
171 c.authors = []
172 for a in set([x.author for x in _hist]):
172 for a in set([x.author for x in _hist]):
173 c.authors.append((h.email(a), h.person(a)))
173 c.authors.append((h.email(a), h.person(a)))
174 else:
174 else:
175 c.authors = c.file_history = []
175 c.authors = c.file_history = []
176 except RepositoryError, e:
176 except RepositoryError, e:
177 h.flash(str(e), category='error')
177 h.flash(str(e), category='error')
178 raise HTTPNotFound()
178 raise HTTPNotFound()
179
179
180 if request.environ.get('HTTP_X_PARTIAL_XHR'):
180 if request.environ.get('HTTP_X_PARTIAL_XHR'):
181 return render('files/files_ypjax.html')
181 return render('files/files_ypjax.html')
182
182
183 return render('files/files.html')
183 return render('files/files.html')
184
184
185 @LoginRequired()
185 @LoginRequired()
186 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
186 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
187 'repository.admin')
187 'repository.admin')
188 def history(self, repo_name, revision, f_path, annotate=False):
188 def history(self, repo_name, revision, f_path, annotate=False):
189 if request.environ.get('HTTP_X_PARTIAL_XHR'):
189 if request.environ.get('HTTP_X_PARTIAL_XHR'):
190 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
190 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
191 c.f_path = f_path
191 c.f_path = f_path
192 c.annotate = annotate
192 c.annotate = annotate
193 c.file = c.changeset.get_node(f_path)
193 c.file = c.changeset.get_node(f_path)
194 if c.file.is_file():
194 if c.file.is_file():
195 file_last_cs = c.file.last_changeset
195 file_last_cs = c.file.last_changeset
196 c.file_changeset = (c.changeset
196 c.file_changeset = (c.changeset
197 if c.changeset.revision < file_last_cs.revision
197 if c.changeset.revision < file_last_cs.revision
198 else file_last_cs)
198 else file_last_cs)
199 c.file_history, _hist = self._get_node_history(c.changeset, f_path)
199 c.file_history, _hist = self._get_node_history(c.changeset, f_path)
200 c.authors = []
200 c.authors = []
201 for a in set([x.author for x in _hist]):
201 for a in set([x.author for x in _hist]):
202 c.authors.append((h.email(a), h.person(a)))
202 c.authors.append((h.email(a), h.person(a)))
203 return render('files/files_history_box.html')
203 return render('files/files_history_box.html')
204
204
205 @LoginRequired()
205 @LoginRequired()
206 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
206 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
207 'repository.admin')
207 'repository.admin')
208 def rawfile(self, repo_name, revision, f_path):
208 def rawfile(self, repo_name, revision, f_path):
209 cs = self.__get_cs_or_redirect(revision, repo_name)
209 cs = self.__get_cs_or_redirect(revision, repo_name)
210 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
210 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
211
211
212 response.content_disposition = 'attachment; filename=%s' % \
212 response.content_disposition = 'attachment; filename=%s' % \
213 safe_str(f_path.split(Repository.url_sep())[-1])
213 safe_str(f_path.split(Repository.url_sep())[-1])
214
214
215 response.content_type = file_node.mimetype
215 response.content_type = file_node.mimetype
216 return file_node.content
216 return file_node.content
217
217
218 @LoginRequired()
218 @LoginRequired()
219 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
219 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
220 'repository.admin')
220 'repository.admin')
221 def raw(self, repo_name, revision, f_path):
221 def raw(self, repo_name, revision, f_path):
222 cs = self.__get_cs_or_redirect(revision, repo_name)
222 cs = self.__get_cs_or_redirect(revision, repo_name)
223 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
223 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
224
224
225 raw_mimetype_mapping = {
225 raw_mimetype_mapping = {
226 # map original mimetype to a mimetype used for "show as raw"
226 # map original mimetype to a mimetype used for "show as raw"
227 # you can also provide a content-disposition to override the
227 # you can also provide a content-disposition to override the
228 # default "attachment" disposition.
228 # default "attachment" disposition.
229 # orig_type: (new_type, new_dispo)
229 # orig_type: (new_type, new_dispo)
230
230
231 # show images inline:
231 # show images inline:
232 'image/x-icon': ('image/x-icon', 'inline'),
232 'image/x-icon': ('image/x-icon', 'inline'),
233 'image/png': ('image/png', 'inline'),
233 'image/png': ('image/png', 'inline'),
234 'image/gif': ('image/gif', 'inline'),
234 'image/gif': ('image/gif', 'inline'),
235 'image/jpeg': ('image/jpeg', 'inline'),
235 'image/jpeg': ('image/jpeg', 'inline'),
236 'image/svg+xml': ('image/svg+xml', 'inline'),
236 'image/svg+xml': ('image/svg+xml', 'inline'),
237 }
237 }
238
238
239 mimetype = file_node.mimetype
239 mimetype = file_node.mimetype
240 try:
240 try:
241 mimetype, dispo = raw_mimetype_mapping[mimetype]
241 mimetype, dispo = raw_mimetype_mapping[mimetype]
242 except KeyError:
242 except KeyError:
243 # we don't know anything special about this, handle it safely
243 # we don't know anything special about this, handle it safely
244 if file_node.is_binary:
244 if file_node.is_binary:
245 # do same as download raw for binary files
245 # do same as download raw for binary files
246 mimetype, dispo = 'application/octet-stream', 'attachment'
246 mimetype, dispo = 'application/octet-stream', 'attachment'
247 else:
247 else:
248 # do not just use the original mimetype, but force text/plain,
248 # do not just use the original mimetype, but force text/plain,
249 # otherwise it would serve text/html and that might be unsafe.
249 # otherwise it would serve text/html and that might be unsafe.
250 # Note: underlying vcs library fakes text/plain mimetype if the
250 # Note: underlying vcs library fakes text/plain mimetype if the
251 # mimetype can not be determined and it thinks it is not
251 # mimetype can not be determined and it thinks it is not
252 # binary.This might lead to erroneous text display in some
252 # binary.This might lead to erroneous text display in some
253 # cases, but helps in other cases, like with text files
253 # cases, but helps in other cases, like with text files
254 # without extension.
254 # without extension.
255 mimetype, dispo = 'text/plain', 'inline'
255 mimetype, dispo = 'text/plain', 'inline'
256
256
257 if dispo == 'attachment':
257 if dispo == 'attachment':
258 dispo = 'attachment; filename=%s' % \
258 dispo = 'attachment; filename=%s' % \
259 safe_str(f_path.split(os.sep)[-1])
259 safe_str(f_path.split(os.sep)[-1])
260
260
261 response.content_disposition = dispo
261 response.content_disposition = dispo
262 response.content_type = mimetype
262 response.content_type = mimetype
263 return file_node.content
263 return file_node.content
264
264
265 @LoginRequired()
265 @LoginRequired()
266 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
266 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
267 def edit(self, repo_name, revision, f_path):
267 def edit(self, repo_name, revision, f_path):
268 repo = c.rhodecode_db_repo
268 repo = c.rhodecode_db_repo
269 if repo.enable_locking and repo.locked[0]:
269 if repo.enable_locking and repo.locked[0]:
270 h.flash(_('This repository is has been locked by %s on %s')
270 h.flash(_('This repository is has been locked by %s on %s')
271 % (h.person_by_id(repo.locked[0]),
271 % (h.person_by_id(repo.locked[0]),
272 h.fmt_date(h.time_to_datetime(repo.locked[1]))),
272 h.fmt_date(h.time_to_datetime(repo.locked[1]))),
273 'warning')
273 'warning')
274 return redirect(h.url('files_home',
274 return redirect(h.url('files_home',
275 repo_name=repo_name, revision='tip'))
275 repo_name=repo_name, revision='tip'))
276
276
277 # check if revision is a branch identifier- basically we cannot
277 # check if revision is a branch identifier- basically we cannot
278 # create multiple heads via file editing
278 # create multiple heads via file editing
279 _branches = repo.scm_instance.branches
279 _branches = repo.scm_instance.branches
280 # check if revision is a branch name or branch hash
280 # check if revision is a branch name or branch hash
281 if revision not in _branches.keys() + _branches.values():
281 if revision not in _branches.keys() + _branches.values():
282 h.flash(_('You can only edit files with revision '
282 h.flash(_('You can only edit files with revision '
283 'being a valid branch '), category='warning')
283 'being a valid branch '), category='warning')
284 return redirect(h.url('files_home',
284 return redirect(h.url('files_home',
285 repo_name=repo_name, revision='tip',
285 repo_name=repo_name, revision='tip',
286 f_path=f_path))
286 f_path=f_path))
287
287
288 r_post = request.POST
288 r_post = request.POST
289
289
290 c.cs = self.__get_cs_or_redirect(revision, repo_name)
290 c.cs = self.__get_cs_or_redirect(revision, repo_name)
291 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
291 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
292
292
293 if c.file.is_binary:
293 if c.file.is_binary:
294 return redirect(url('files_home', repo_name=c.repo_name,
294 return redirect(url('files_home', repo_name=c.repo_name,
295 revision=c.cs.raw_id, f_path=f_path))
295 revision=c.cs.raw_id, f_path=f_path))
296 c.default_message = _('Edited file %s via RhodeCode') % (f_path)
296 c.default_message = _('Edited file %s via RhodeCode') % (f_path)
297 c.f_path = f_path
297 c.f_path = f_path
298
298
299 if r_post:
299 if r_post:
300
300
301 old_content = c.file.content
301 old_content = c.file.content
302 sl = old_content.splitlines(1)
302 sl = old_content.splitlines(1)
303 first_line = sl[0] if sl else ''
303 first_line = sl[0] if sl else ''
304 # modes: 0 - Unix, 1 - Mac, 2 - DOS
304 # modes: 0 - Unix, 1 - Mac, 2 - DOS
305 mode = detect_mode(first_line, 0)
305 mode = detect_mode(first_line, 0)
306 content = convert_line_endings(r_post.get('content'), mode)
306 content = convert_line_endings(r_post.get('content', ''), mode)
307
307
308 message = r_post.get('message') or c.default_message
308 message = r_post.get('message') or c.default_message
309 author = self.rhodecode_user.full_contact
309 author = self.rhodecode_user.full_contact
310
310
311 if content == old_content:
311 if content == old_content:
312 h.flash(_('No changes'), category='warning')
312 h.flash(_('No changes'), category='warning')
313 return redirect(url('changeset_home', repo_name=c.repo_name,
313 return redirect(url('changeset_home', repo_name=c.repo_name,
314 revision='tip'))
314 revision='tip'))
315 try:
315 try:
316 self.scm_model.commit_change(repo=c.rhodecode_repo,
316 self.scm_model.commit_change(repo=c.rhodecode_repo,
317 repo_name=repo_name, cs=c.cs,
317 repo_name=repo_name, cs=c.cs,
318 user=self.rhodecode_user.user_id,
318 user=self.rhodecode_user.user_id,
319 author=author, message=message,
319 author=author, message=message,
320 content=content, f_path=f_path)
320 content=content, f_path=f_path)
321 h.flash(_('Successfully committed to %s') % f_path,
321 h.flash(_('Successfully committed to %s') % f_path,
322 category='success')
322 category='success')
323
323
324 except Exception:
324 except Exception:
325 log.error(traceback.format_exc())
325 log.error(traceback.format_exc())
326 h.flash(_('Error occurred during commit'), category='error')
326 h.flash(_('Error occurred during commit'), category='error')
327 return redirect(url('changeset_home',
327 return redirect(url('changeset_home',
328 repo_name=c.repo_name, revision='tip'))
328 repo_name=c.repo_name, revision='tip'))
329
329
330 return render('files/files_edit.html')
330 return render('files/files_edit.html')
331
331
332 @LoginRequired()
332 @LoginRequired()
333 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
333 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
334 def add(self, repo_name, revision, f_path):
334 def add(self, repo_name, revision, f_path):
335
335
336 repo = Repository.get_by_repo_name(repo_name)
336 repo = Repository.get_by_repo_name(repo_name)
337 if repo.enable_locking and repo.locked[0]:
337 if repo.enable_locking and repo.locked[0]:
338 h.flash(_('This repository is has been locked by %s on %s')
338 h.flash(_('This repository is has been locked by %s on %s')
339 % (h.person_by_id(repo.locked[0]),
339 % (h.person_by_id(repo.locked[0]),
340 h.fmt_date(h.time_to_datetime(repo.locked[1]))),
340 h.fmt_date(h.time_to_datetime(repo.locked[1]))),
341 'warning')
341 'warning')
342 return redirect(h.url('files_home',
342 return redirect(h.url('files_home',
343 repo_name=repo_name, revision='tip'))
343 repo_name=repo_name, revision='tip'))
344
344
345 r_post = request.POST
345 r_post = request.POST
346 c.cs = self.__get_cs_or_redirect(revision, repo_name,
346 c.cs = self.__get_cs_or_redirect(revision, repo_name,
347 redirect_after=False)
347 redirect_after=False)
348 if c.cs is None:
348 if c.cs is None:
349 c.cs = EmptyChangeset(alias=c.rhodecode_repo.alias)
349 c.cs = EmptyChangeset(alias=c.rhodecode_repo.alias)
350 c.default_message = (_('Added file via RhodeCode'))
350 c.default_message = (_('Added file via RhodeCode'))
351 c.f_path = f_path
351 c.f_path = f_path
352
352
353 if r_post:
353 if r_post:
354 unix_mode = 0
354 unix_mode = 0
355 content = convert_line_endings(r_post.get('content'), unix_mode)
355 content = convert_line_endings(r_post.get('content', ''), unix_mode)
356
356
357 message = r_post.get('message') or c.default_message
357 message = r_post.get('message') or c.default_message
358 filename = r_post.get('filename')
358 filename = r_post.get('filename')
359 location = r_post.get('location')
359 location = r_post.get('location', '')
360 file_obj = r_post.get('upload_file', None)
360 file_obj = r_post.get('upload_file', None)
361
361
362 if file_obj is not None and hasattr(file_obj, 'filename'):
362 if file_obj is not None and hasattr(file_obj, 'filename'):
363 filename = file_obj.filename
363 filename = file_obj.filename
364 content = file_obj.file
364 content = file_obj.file
365
365
366 if not content:
366 if not content:
367 h.flash(_('No content'), category='warning')
367 h.flash(_('No content'), category='warning')
368 return redirect(url('changeset_home', repo_name=c.repo_name,
368 return redirect(url('changeset_home', repo_name=c.repo_name,
369 revision='tip'))
369 revision='tip'))
370 if not filename:
370 if not filename:
371 h.flash(_('No filename'), category='warning')
371 h.flash(_('No filename'), category='warning')
372 return redirect(url('changeset_home', repo_name=c.repo_name,
372 return redirect(url('changeset_home', repo_name=c.repo_name,
373 revision='tip'))
373 revision='tip'))
374 if location.startswith('/') or location.startswith('.') or '../' in location:
374 if location.startswith('/') or location.startswith('.') or '../' in location:
375 h.flash(_('Location must be relative path and must not '
375 h.flash(_('Location must be relative path and must not '
376 'contain .. in path'), category='warning')
376 'contain .. in path'), category='warning')
377 return redirect(url('changeset_home', repo_name=c.repo_name,
377 return redirect(url('changeset_home', repo_name=c.repo_name,
378 revision='tip'))
378 revision='tip'))
379 if location:
379 if location:
380 location = os.path.normpath(location)
380 location = os.path.normpath(location)
381 filename = os.path.basename(filename)
381 filename = os.path.basename(filename)
382 node_path = os.path.join(location, filename)
382 node_path = os.path.join(location, filename)
383 author = self.rhodecode_user.full_contact
383 author = self.rhodecode_user.full_contact
384
384
385 try:
385 try:
386 self.scm_model.create_node(repo=c.rhodecode_repo,
386 self.scm_model.create_node(repo=c.rhodecode_repo,
387 repo_name=repo_name, cs=c.cs,
387 repo_name=repo_name, cs=c.cs,
388 user=self.rhodecode_user.user_id,
388 user=self.rhodecode_user.user_id,
389 author=author, message=message,
389 author=author, message=message,
390 content=content, f_path=node_path)
390 content=content, f_path=node_path)
391 h.flash(_('Successfully committed to %s') % node_path,
391 h.flash(_('Successfully committed to %s') % node_path,
392 category='success')
392 category='success')
393 except (NodeError, NodeAlreadyExistsError), e:
393 except (NodeError, NodeAlreadyExistsError), e:
394 h.flash(_(e), category='error')
394 h.flash(_(e), category='error')
395 except Exception:
395 except Exception:
396 log.error(traceback.format_exc())
396 log.error(traceback.format_exc())
397 h.flash(_('Error occurred during commit'), category='error')
397 h.flash(_('Error occurred during commit'), category='error')
398 return redirect(url('changeset_home',
398 return redirect(url('changeset_home',
399 repo_name=c.repo_name, revision='tip'))
399 repo_name=c.repo_name, revision='tip'))
400
400
401 return render('files/files_add.html')
401 return render('files/files_add.html')
402
402
403 @LoginRequired()
403 @LoginRequired()
404 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
404 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
405 'repository.admin')
405 'repository.admin')
406 def archivefile(self, repo_name, fname):
406 def archivefile(self, repo_name, fname):
407
407
408 fileformat = None
408 fileformat = None
409 revision = None
409 revision = None
410 ext = None
410 ext = None
411 subrepos = request.GET.get('subrepos') == 'true'
411 subrepos = request.GET.get('subrepos') == 'true'
412
412
413 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
413 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
414 archive_spec = fname.split(ext_data[1])
414 archive_spec = fname.split(ext_data[1])
415 if len(archive_spec) == 2 and archive_spec[1] == '':
415 if len(archive_spec) == 2 and archive_spec[1] == '':
416 fileformat = a_type or ext_data[1]
416 fileformat = a_type or ext_data[1]
417 revision = archive_spec[0]
417 revision = archive_spec[0]
418 ext = ext_data[1]
418 ext = ext_data[1]
419
419
420 try:
420 try:
421 dbrepo = RepoModel().get_by_repo_name(repo_name)
421 dbrepo = RepoModel().get_by_repo_name(repo_name)
422 if not dbrepo.enable_downloads:
422 if not dbrepo.enable_downloads:
423 return _('Downloads disabled')
423 return _('Downloads disabled')
424
424
425 if c.rhodecode_repo.alias == 'hg':
425 if c.rhodecode_repo.alias == 'hg':
426 # patch and reset hooks section of UI config to not run any
426 # patch and reset hooks section of UI config to not run any
427 # hooks on fetching archives with subrepos
427 # hooks on fetching archives with subrepos
428 for k, v in c.rhodecode_repo._repo.ui.configitems('hooks'):
428 for k, v in c.rhodecode_repo._repo.ui.configitems('hooks'):
429 c.rhodecode_repo._repo.ui.setconfig('hooks', k, None)
429 c.rhodecode_repo._repo.ui.setconfig('hooks', k, None)
430
430
431 cs = c.rhodecode_repo.get_changeset(revision)
431 cs = c.rhodecode_repo.get_changeset(revision)
432 content_type = settings.ARCHIVE_SPECS[fileformat][0]
432 content_type = settings.ARCHIVE_SPECS[fileformat][0]
433 except ChangesetDoesNotExistError:
433 except ChangesetDoesNotExistError:
434 return _('Unknown revision %s') % revision
434 return _('Unknown revision %s') % revision
435 except EmptyRepositoryError:
435 except EmptyRepositoryError:
436 return _('Empty repository')
436 return _('Empty repository')
437 except (ImproperArchiveTypeError, KeyError):
437 except (ImproperArchiveTypeError, KeyError):
438 return _('Unknown archive type')
438 return _('Unknown archive type')
439 # archive cache
439 # archive cache
440 from rhodecode import CONFIG
440 from rhodecode import CONFIG
441 rev_name = cs.raw_id[:12]
441 rev_name = cs.raw_id[:12]
442 archive_name = '%s-%s%s' % (safe_str(repo_name.replace('/', '_')),
442 archive_name = '%s-%s%s' % (safe_str(repo_name.replace('/', '_')),
443 safe_str(rev_name), ext)
443 safe_str(rev_name), ext)
444
444
445 use_cached_archive = False # defines if we use cached version of archive
445 use_cached_archive = False # defines if we use cached version of archive
446 archive_cache_enabled = CONFIG.get('archive_cache_dir')
446 archive_cache_enabled = CONFIG.get('archive_cache_dir')
447 if not subrepos and archive_cache_enabled:
447 if not subrepos and archive_cache_enabled:
448 #check if we it's ok to write
448 #check if we it's ok to write
449 if not os.path.isdir(CONFIG['archive_cache_dir']):
449 if not os.path.isdir(CONFIG['archive_cache_dir']):
450 os.makedirs(CONFIG['archive_cache_dir'])
450 os.makedirs(CONFIG['archive_cache_dir'])
451 cached_archive_path = os.path.join(CONFIG['archive_cache_dir'], archive_name)
451 cached_archive_path = os.path.join(CONFIG['archive_cache_dir'], archive_name)
452 if os.path.isfile(cached_archive_path):
452 if os.path.isfile(cached_archive_path):
453 log.debug('Found cached archive in %s' % cached_archive_path)
453 log.debug('Found cached archive in %s' % cached_archive_path)
454 fd, archive = None, cached_archive_path
454 fd, archive = None, cached_archive_path
455 use_cached_archive = True
455 use_cached_archive = True
456 else:
456 else:
457 log.debug('Archive %s is not yet cached' % (archive_name))
457 log.debug('Archive %s is not yet cached' % (archive_name))
458
458
459 if not use_cached_archive:
459 if not use_cached_archive:
460 #generate new archive
460 #generate new archive
461 try:
461 try:
462 fd, archive = tempfile.mkstemp()
462 fd, archive = tempfile.mkstemp()
463 t = open(archive, 'wb')
463 t = open(archive, 'wb')
464 log.debug('Creating new temp archive in %s' % archive)
464 log.debug('Creating new temp archive in %s' % archive)
465 cs.fill_archive(stream=t, kind=fileformat, subrepos=subrepos)
465 cs.fill_archive(stream=t, kind=fileformat, subrepos=subrepos)
466 if archive_cache_enabled:
466 if archive_cache_enabled:
467 #if we generated the archive and use cache rename that
467 #if we generated the archive and use cache rename that
468 log.debug('Storing new archive in %s' % cached_archive_path)
468 log.debug('Storing new archive in %s' % cached_archive_path)
469 shutil.move(archive, cached_archive_path)
469 shutil.move(archive, cached_archive_path)
470 archive = cached_archive_path
470 archive = cached_archive_path
471 finally:
471 finally:
472 t.close()
472 t.close()
473
473
474 def get_chunked_archive(archive):
474 def get_chunked_archive(archive):
475 stream = open(archive, 'rb')
475 stream = open(archive, 'rb')
476 while True:
476 while True:
477 data = stream.read(16 * 1024)
477 data = stream.read(16 * 1024)
478 if not data:
478 if not data:
479 stream.close()
479 stream.close()
480 if fd: # fd means we used temporary file
480 if fd: # fd means we used temporary file
481 os.close(fd)
481 os.close(fd)
482 if not archive_cache_enabled:
482 if not archive_cache_enabled:
483 log.debug('Destroing temp archive %s' % archive)
483 log.debug('Destroing temp archive %s' % archive)
484 os.remove(archive)
484 os.remove(archive)
485 break
485 break
486 yield data
486 yield data
487
487
488 response.content_disposition = str('attachment; filename=%s' % (archive_name))
488 response.content_disposition = str('attachment; filename=%s' % (archive_name))
489 response.content_type = str(content_type)
489 response.content_type = str(content_type)
490 return get_chunked_archive(archive)
490 return get_chunked_archive(archive)
491
491
492 @LoginRequired()
492 @LoginRequired()
493 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
493 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
494 'repository.admin')
494 'repository.admin')
495 def diff(self, repo_name, f_path):
495 def diff(self, repo_name, f_path):
496 ignore_whitespace = request.GET.get('ignorews') == '1'
496 ignore_whitespace = request.GET.get('ignorews') == '1'
497 line_context = request.GET.get('context', 3)
497 line_context = request.GET.get('context', 3)
498 diff1 = request.GET.get('diff1', '')
498 diff1 = request.GET.get('diff1', '')
499 diff2 = request.GET.get('diff2', '')
499 diff2 = request.GET.get('diff2', '')
500 c.action = request.GET.get('diff')
500 c.action = request.GET.get('diff')
501 c.no_changes = diff1 == diff2
501 c.no_changes = diff1 == diff2
502 c.f_path = f_path
502 c.f_path = f_path
503 c.big_diff = False
503 c.big_diff = False
504 c.anchor_url = anchor_url
504 c.anchor_url = anchor_url
505 c.ignorews_url = _ignorews_url
505 c.ignorews_url = _ignorews_url
506 c.context_url = _context_url
506 c.context_url = _context_url
507 c.changes = OrderedDict()
507 c.changes = OrderedDict()
508 c.changes[diff2] = []
508 c.changes[diff2] = []
509
509
510 #special case if we want a show rev only, it's impl here
510 #special case if we want a show rev only, it's impl here
511 #to reduce JS and callbacks
511 #to reduce JS and callbacks
512
512
513 if request.GET.get('show_rev'):
513 if request.GET.get('show_rev'):
514 if str2bool(request.GET.get('annotate', 'False')):
514 if str2bool(request.GET.get('annotate', 'False')):
515 _url = url('files_annotate_home', repo_name=c.repo_name,
515 _url = url('files_annotate_home', repo_name=c.repo_name,
516 revision=diff1, f_path=c.f_path)
516 revision=diff1, f_path=c.f_path)
517 else:
517 else:
518 _url = url('files_home', repo_name=c.repo_name,
518 _url = url('files_home', repo_name=c.repo_name,
519 revision=diff1, f_path=c.f_path)
519 revision=diff1, f_path=c.f_path)
520
520
521 return redirect(_url)
521 return redirect(_url)
522 try:
522 try:
523 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
523 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
524 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
524 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
525 try:
525 try:
526 node1 = c.changeset_1.get_node(f_path)
526 node1 = c.changeset_1.get_node(f_path)
527 if node1.is_dir():
527 if node1.is_dir():
528 raise NodeError('%s path is a %s not a file'
528 raise NodeError('%s path is a %s not a file'
529 % (node1, type(node1)))
529 % (node1, type(node1)))
530 except NodeDoesNotExistError:
530 except NodeDoesNotExistError:
531 c.changeset_1 = EmptyChangeset(cs=diff1,
531 c.changeset_1 = EmptyChangeset(cs=diff1,
532 revision=c.changeset_1.revision,
532 revision=c.changeset_1.revision,
533 repo=c.rhodecode_repo)
533 repo=c.rhodecode_repo)
534 node1 = FileNode(f_path, '', changeset=c.changeset_1)
534 node1 = FileNode(f_path, '', changeset=c.changeset_1)
535 else:
535 else:
536 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
536 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
537 node1 = FileNode(f_path, '', changeset=c.changeset_1)
537 node1 = FileNode(f_path, '', changeset=c.changeset_1)
538
538
539 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
539 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
540 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
540 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
541 try:
541 try:
542 node2 = c.changeset_2.get_node(f_path)
542 node2 = c.changeset_2.get_node(f_path)
543 if node2.is_dir():
543 if node2.is_dir():
544 raise NodeError('%s path is a %s not a file'
544 raise NodeError('%s path is a %s not a file'
545 % (node2, type(node2)))
545 % (node2, type(node2)))
546 except NodeDoesNotExistError:
546 except NodeDoesNotExistError:
547 c.changeset_2 = EmptyChangeset(cs=diff2,
547 c.changeset_2 = EmptyChangeset(cs=diff2,
548 revision=c.changeset_2.revision,
548 revision=c.changeset_2.revision,
549 repo=c.rhodecode_repo)
549 repo=c.rhodecode_repo)
550 node2 = FileNode(f_path, '', changeset=c.changeset_2)
550 node2 = FileNode(f_path, '', changeset=c.changeset_2)
551 else:
551 else:
552 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
552 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
553 node2 = FileNode(f_path, '', changeset=c.changeset_2)
553 node2 = FileNode(f_path, '', changeset=c.changeset_2)
554 except (RepositoryError, NodeError):
554 except (RepositoryError, NodeError):
555 log.error(traceback.format_exc())
555 log.error(traceback.format_exc())
556 return redirect(url('files_home', repo_name=c.repo_name,
556 return redirect(url('files_home', repo_name=c.repo_name,
557 f_path=f_path))
557 f_path=f_path))
558
558
559 if c.action == 'download':
559 if c.action == 'download':
560 _diff = diffs.get_gitdiff(node1, node2,
560 _diff = diffs.get_gitdiff(node1, node2,
561 ignore_whitespace=ignore_whitespace,
561 ignore_whitespace=ignore_whitespace,
562 context=line_context)
562 context=line_context)
563 diff = diffs.DiffProcessor(_diff, format='gitdiff')
563 diff = diffs.DiffProcessor(_diff, format='gitdiff')
564
564
565 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
565 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
566 response.content_type = 'text/plain'
566 response.content_type = 'text/plain'
567 response.content_disposition = (
567 response.content_disposition = (
568 'attachment; filename=%s' % diff_name
568 'attachment; filename=%s' % diff_name
569 )
569 )
570 return diff.as_raw()
570 return diff.as_raw()
571
571
572 elif c.action == 'raw':
572 elif c.action == 'raw':
573 _diff = diffs.get_gitdiff(node1, node2,
573 _diff = diffs.get_gitdiff(node1, node2,
574 ignore_whitespace=ignore_whitespace,
574 ignore_whitespace=ignore_whitespace,
575 context=line_context)
575 context=line_context)
576 diff = diffs.DiffProcessor(_diff, format='gitdiff')
576 diff = diffs.DiffProcessor(_diff, format='gitdiff')
577 response.content_type = 'text/plain'
577 response.content_type = 'text/plain'
578 return diff.as_raw()
578 return diff.as_raw()
579
579
580 else:
580 else:
581 fid = h.FID(diff2, node2.path)
581 fid = h.FID(diff2, node2.path)
582 line_context_lcl = get_line_ctx(fid, request.GET)
582 line_context_lcl = get_line_ctx(fid, request.GET)
583 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
583 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
584
584
585 lim = request.GET.get('fulldiff') or self.cut_off_limit
585 lim = request.GET.get('fulldiff') or self.cut_off_limit
586 _, cs1, cs2, diff, st = diffs.wrapped_diff(filenode_old=node1,
586 _, cs1, cs2, diff, st = diffs.wrapped_diff(filenode_old=node1,
587 filenode_new=node2,
587 filenode_new=node2,
588 cut_off_limit=lim,
588 cut_off_limit=lim,
589 ignore_whitespace=ign_whitespace_lcl,
589 ignore_whitespace=ign_whitespace_lcl,
590 line_context=line_context_lcl,
590 line_context=line_context_lcl,
591 enable_comments=False)
591 enable_comments=False)
592 op = ''
592 op = ''
593 filename = node1.path
593 filename = node1.path
594 cs_changes = {
594 cs_changes = {
595 'fid': [cs1, cs2, op, filename, diff, st]
595 'fid': [cs1, cs2, op, filename, diff, st]
596 }
596 }
597 c.changes = cs_changes
597 c.changes = cs_changes
598
598
599 return render('files/file_diff.html')
599 return render('files/file_diff.html')
600
600
601 def _get_node_history(self, cs, f_path, changesets=None):
601 def _get_node_history(self, cs, f_path, changesets=None):
602 """
602 """
603 get changesets history for given node
603 get changesets history for given node
604
604
605 :param cs: changeset to calculate history
605 :param cs: changeset to calculate history
606 :param f_path: path for node to calculate history for
606 :param f_path: path for node to calculate history for
607 :param changesets: if passed don't calculate history and take
607 :param changesets: if passed don't calculate history and take
608 changesets defined in this list
608 changesets defined in this list
609 """
609 """
610 # calculate history based on tip
610 # calculate history based on tip
611 tip_cs = c.rhodecode_repo.get_changeset()
611 tip_cs = c.rhodecode_repo.get_changeset()
612 if changesets is None:
612 if changesets is None:
613 try:
613 try:
614 changesets = tip_cs.get_file_history(f_path)
614 changesets = tip_cs.get_file_history(f_path)
615 except (NodeDoesNotExistError, ChangesetError):
615 except (NodeDoesNotExistError, ChangesetError):
616 #this node is not present at tip !
616 #this node is not present at tip !
617 changesets = cs.get_file_history(f_path)
617 changesets = cs.get_file_history(f_path)
618 hist_l = []
618 hist_l = []
619
619
620 changesets_group = ([], _("Changesets"))
620 changesets_group = ([], _("Changesets"))
621 branches_group = ([], _("Branches"))
621 branches_group = ([], _("Branches"))
622 tags_group = ([], _("Tags"))
622 tags_group = ([], _("Tags"))
623 _hg = cs.repository.alias == 'hg'
623 _hg = cs.repository.alias == 'hg'
624 for chs in changesets:
624 for chs in changesets:
625 #_branch = '(%s)' % chs.branch if _hg else ''
625 #_branch = '(%s)' % chs.branch if _hg else ''
626 _branch = chs.branch
626 _branch = chs.branch
627 n_desc = 'r%s:%s (%s)' % (chs.revision, chs.short_id, _branch)
627 n_desc = 'r%s:%s (%s)' % (chs.revision, chs.short_id, _branch)
628 changesets_group[0].append((chs.raw_id, n_desc,))
628 changesets_group[0].append((chs.raw_id, n_desc,))
629 hist_l.append(changesets_group)
629 hist_l.append(changesets_group)
630
630
631 for name, chs in c.rhodecode_repo.branches.items():
631 for name, chs in c.rhodecode_repo.branches.items():
632 branches_group[0].append((chs, name),)
632 branches_group[0].append((chs, name),)
633 hist_l.append(branches_group)
633 hist_l.append(branches_group)
634
634
635 for name, chs in c.rhodecode_repo.tags.items():
635 for name, chs in c.rhodecode_repo.tags.items():
636 tags_group[0].append((chs, name),)
636 tags_group[0].append((chs, name),)
637 hist_l.append(tags_group)
637 hist_l.append(tags_group)
638
638
639 return hist_l, changesets
639 return hist_l, changesets
640
640
641 @LoginRequired()
641 @LoginRequired()
642 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
642 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
643 'repository.admin')
643 'repository.admin')
644 @jsonify
644 @jsonify
645 def nodelist(self, repo_name, revision, f_path):
645 def nodelist(self, repo_name, revision, f_path):
646 if request.environ.get('HTTP_X_PARTIAL_XHR'):
646 if request.environ.get('HTTP_X_PARTIAL_XHR'):
647 cs = self.__get_cs_or_redirect(revision, repo_name)
647 cs = self.__get_cs_or_redirect(revision, repo_name)
648 _d, _f = ScmModel().get_nodes(repo_name, cs.raw_id, f_path,
648 _d, _f = ScmModel().get_nodes(repo_name, cs.raw_id, f_path,
649 flat=False)
649 flat=False)
650 return {'nodes': _d + _f}
650 return {'nodes': _d + _f}
@@ -1,467 +1,735 b''
1 import os
1 from rhodecode.tests import *
2 from rhodecode.tests import *
2 from rhodecode.model.db import Repository
3 from rhodecode.model.db import Repository
3 from rhodecode.model.meta import Session
4 from rhodecode.model.meta import Session
5 from rhodecode.tests.fixture import Fixture
6
7 fixture = Fixture()
4
8
5 ARCHIVE_SPECS = {
9 ARCHIVE_SPECS = {
6 '.tar.bz2': ('application/x-bzip2', 'tbz2', ''),
10 '.tar.bz2': ('application/x-bzip2', 'tbz2', ''),
7 '.tar.gz': ('application/x-gzip', 'tgz', ''),
11 '.tar.gz': ('application/x-gzip', 'tgz', ''),
8 '.zip': ('application/zip', 'zip', ''),
12 '.zip': ('application/zip', 'zip', ''),
9 }
13 }
10
14
11
15
12 def _set_downloads(repo_name, set_to):
16 def _set_downloads(repo_name, set_to):
13 repo = Repository.get_by_repo_name(repo_name)
17 repo = Repository.get_by_repo_name(repo_name)
14 repo.enable_downloads = set_to
18 repo.enable_downloads = set_to
15 Session().add(repo)
19 Session().add(repo)
16 Session().commit()
20 Session().commit()
17
21
18
22
19 class TestFilesController(TestController):
23 class TestFilesController(TestController):
20
24
21 def test_index(self):
25 def test_index(self):
22 self.log_user()
26 self.log_user()
23 response = self.app.get(url(controller='files', action='index',
27 response = self.app.get(url(controller='files', action='index',
24 repo_name=HG_REPO,
28 repo_name=HG_REPO,
25 revision='tip',
29 revision='tip',
26 f_path='/'))
30 f_path='/'))
27 # Test response...
31 # Test response...
28 response.mustcontain('<a class="browser-dir ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/docs">docs</a>')
32 response.mustcontain('<a class="browser-dir ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/docs">docs</a>')
29 response.mustcontain('<a class="browser-dir ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/vcs">vcs</a>')
33 response.mustcontain('<a class="browser-dir ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/vcs">vcs</a>')
30 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/.gitignore">.gitignore</a>')
34 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/.gitignore">.gitignore</a>')
31 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/.hgignore">.hgignore</a>')
35 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/.hgignore">.hgignore</a>')
32 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/.hgtags">.hgtags</a>')
36 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/.hgtags">.hgtags</a>')
33 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/.travis.yml">.travis.yml</a>')
37 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/.travis.yml">.travis.yml</a>')
34 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/MANIFEST.in">MANIFEST.in</a>')
38 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/MANIFEST.in">MANIFEST.in</a>')
35 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/README.rst">README.rst</a>')
39 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/README.rst">README.rst</a>')
36 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/run_test_and_report.sh">run_test_and_report.sh</a>')
40 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/run_test_and_report.sh">run_test_and_report.sh</a>')
37 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/setup.cfg">setup.cfg</a>')
41 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/setup.cfg">setup.cfg</a>')
38 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/setup.py">setup.py</a>')
42 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/setup.py">setup.py</a>')
39 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/test_and_report.sh">test_and_report.sh</a>')
43 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/test_and_report.sh">test_and_report.sh</a>')
40 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/tox.ini">tox.ini</a>')
44 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/tox.ini">tox.ini</a>')
41
45
42 def test_index_revision(self):
46 def test_index_revision(self):
43 self.log_user()
47 self.log_user()
44
48
45 response = self.app.get(
49 response = self.app.get(
46 url(controller='files', action='index',
50 url(controller='files', action='index',
47 repo_name=HG_REPO,
51 repo_name=HG_REPO,
48 revision='7ba66bec8d6dbba14a2155be32408c435c5f4492',
52 revision='7ba66bec8d6dbba14a2155be32408c435c5f4492',
49 f_path='/')
53 f_path='/')
50 )
54 )
51
55
52 #Test response...
56 #Test response...
53
57
54 response.mustcontain('<a class="browser-dir ypjax-link" href="/vcs_test_hg/files/7ba66bec8d6dbba14a2155be32408c435c5f4492/docs">docs</a>')
58 response.mustcontain('<a class="browser-dir ypjax-link" href="/vcs_test_hg/files/7ba66bec8d6dbba14a2155be32408c435c5f4492/docs">docs</a>')
55 response.mustcontain('<a class="browser-dir ypjax-link" href="/vcs_test_hg/files/7ba66bec8d6dbba14a2155be32408c435c5f4492/tests">tests</a>')
59 response.mustcontain('<a class="browser-dir ypjax-link" href="/vcs_test_hg/files/7ba66bec8d6dbba14a2155be32408c435c5f4492/tests">tests</a>')
56 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/7ba66bec8d6dbba14a2155be32408c435c5f4492/README.rst">README.rst</a>')
60 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/7ba66bec8d6dbba14a2155be32408c435c5f4492/README.rst">README.rst</a>')
57 response.mustcontain('1.1 KiB')
61 response.mustcontain('1.1 KiB')
58 response.mustcontain('text/x-python')
62 response.mustcontain('text/x-python')
59
63
60 def test_index_different_branch(self):
64 def test_index_different_branch(self):
61 self.log_user()
65 self.log_user()
62
66
63 response = self.app.get(url(controller='files', action='index',
67 response = self.app.get(url(controller='files', action='index',
64 repo_name=HG_REPO,
68 repo_name=HG_REPO,
65 revision='97e8b885c04894463c51898e14387d80c30ed1ee',
69 revision='97e8b885c04894463c51898e14387d80c30ed1ee',
66 f_path='/'))
70 f_path='/'))
67
71
68 response.mustcontain("""<span style="text-transform: uppercase;"><a href="#">Branch: git</a></span>""")
72 response.mustcontain("""<span style="text-transform: uppercase;"><a href="#">Branch: git</a></span>""")
69
73
70 def test_index_paging(self):
74 def test_index_paging(self):
71 self.log_user()
75 self.log_user()
72
76
73 for r in [(73, 'a066b25d5df7016b45a41b7e2a78c33b57adc235'),
77 for r in [(73, 'a066b25d5df7016b45a41b7e2a78c33b57adc235'),
74 (92, 'cc66b61b8455b264a7a8a2d8ddc80fcfc58c221e'),
78 (92, 'cc66b61b8455b264a7a8a2d8ddc80fcfc58c221e'),
75 (109, '75feb4c33e81186c87eac740cee2447330288412'),
79 (109, '75feb4c33e81186c87eac740cee2447330288412'),
76 (1, '3d8f361e72ab303da48d799ff1ac40d5ac37c67e'),
80 (1, '3d8f361e72ab303da48d799ff1ac40d5ac37c67e'),
77 (0, 'b986218ba1c9b0d6a259fac9b050b1724ed8e545')]:
81 (0, 'b986218ba1c9b0d6a259fac9b050b1724ed8e545')]:
78
82
79 response = self.app.get(url(controller='files', action='index',
83 response = self.app.get(url(controller='files', action='index',
80 repo_name=HG_REPO,
84 repo_name=HG_REPO,
81 revision=r[1],
85 revision=r[1],
82 f_path='/'))
86 f_path='/'))
83
87
84 response.mustcontain("""@ r%s:%s""" % (r[0], r[1][:12]))
88 response.mustcontain("""@ r%s:%s""" % (r[0], r[1][:12]))
85
89
86 def test_file_source(self):
90 def test_file_source(self):
87 self.log_user()
91 self.log_user()
88 response = self.app.get(url(controller='files', action='index',
92 response = self.app.get(url(controller='files', action='index',
89 repo_name=HG_REPO,
93 repo_name=HG_REPO,
90 revision='8911406ad776fdd3d0b9932a2e89677e57405a48',
94 revision='8911406ad776fdd3d0b9932a2e89677e57405a48',
91 f_path='vcs/nodes.py'))
95 f_path='vcs/nodes.py'))
92
96
93 response.mustcontain("""<div class="commit">Partially implemented <a class="issue-tracker-link" href="https://myissueserver.com/vcs_test_hg/issue/16">#16</a>. filecontent/commit message/author/node name are safe_unicode now.
97 response.mustcontain("""<div class="commit">Partially implemented <a class="issue-tracker-link" href="https://myissueserver.com/vcs_test_hg/issue/16">#16</a>. filecontent/commit message/author/node name are safe_unicode now.
94 In addition some other __str__ are unicode as well
98 In addition some other __str__ are unicode as well
95 Added test for unicode
99 Added test for unicode
96 Improved test to clone into uniq repository.
100 Improved test to clone into uniq repository.
97 removed extra unicode conversion in diff.</div>
101 removed extra unicode conversion in diff.</div>
98 """)
102 """)
99
103
100 response.mustcontain("""<span style="text-transform: uppercase;"><a href="#">Branch: default</a></span>""")
104 response.mustcontain("""<span style="text-transform: uppercase;"><a href="#">Branch: default</a></span>""")
101
105
102 def test_file_source_history(self):
106 def test_file_source_history(self):
103 self.log_user()
107 self.log_user()
104 response = self.app.get(url(controller='files', action='history',
108 response = self.app.get(url(controller='files', action='history',
105 repo_name=HG_REPO,
109 repo_name=HG_REPO,
106 revision='27cd5cce30c96924232dffcd24178a07ffeb5dfc',
110 revision='27cd5cce30c96924232dffcd24178a07ffeb5dfc',
107 f_path='vcs/nodes.py'),
111 f_path='vcs/nodes.py'),
108 extra_environ={'HTTP_X_PARTIAL_XHR': '1'},)
112 extra_environ={'HTTP_X_PARTIAL_XHR': '1'},)
109 #test or history
113 #test or history
110 response.mustcontain("""<optgroup label="Changesets">
114 response.mustcontain("""<optgroup label="Changesets">
111 <option value="dbec37a0d5cab8ff39af4cfc4a4cd3996e4acfc6">r648:dbec37a0d5ca (default)</option>
115 <option value="dbec37a0d5cab8ff39af4cfc4a4cd3996e4acfc6">r648:dbec37a0d5ca (default)</option>
112 <option value="1d20ed9eda9482d46ff0a6af5812550218b3ff15">r639:1d20ed9eda94 (default)</option>
116 <option value="1d20ed9eda9482d46ff0a6af5812550218b3ff15">r639:1d20ed9eda94 (default)</option>
113 <option value="0173395e822797f098799ed95c1a81b6a547a9ad">r547:0173395e8227 (default)</option>
117 <option value="0173395e822797f098799ed95c1a81b6a547a9ad">r547:0173395e8227 (default)</option>
114 <option value="afbb45ade933a8182f1d8ec5d4d1bb2de2572043">r546:afbb45ade933 (default)</option>
118 <option value="afbb45ade933a8182f1d8ec5d4d1bb2de2572043">r546:afbb45ade933 (default)</option>
115 <option value="6f093e30cac34e6b4b11275a9f22f80c5d7ad1f7">r502:6f093e30cac3 (default)</option>
119 <option value="6f093e30cac34e6b4b11275a9f22f80c5d7ad1f7">r502:6f093e30cac3 (default)</option>
116 <option value="c7e2212dd2ae975d1d06534a3d7e317165c06960">r476:c7e2212dd2ae (default)</option>
120 <option value="c7e2212dd2ae975d1d06534a3d7e317165c06960">r476:c7e2212dd2ae (default)</option>
117 <option value="45477506df79f701bf69419aac3e1f0fed3c5bcf">r472:45477506df79 (default)</option>
121 <option value="45477506df79f701bf69419aac3e1f0fed3c5bcf">r472:45477506df79 (default)</option>
118 <option value="5fc76cb25d11e07c60de040f78b8cd265ff10d53">r469:5fc76cb25d11 (default)</option>
122 <option value="5fc76cb25d11e07c60de040f78b8cd265ff10d53">r469:5fc76cb25d11 (default)</option>
119 <option value="b073433cf8994969ee5cd7cce84cbe587bb880b2">r468:b073433cf899 (default)</option>
123 <option value="b073433cf8994969ee5cd7cce84cbe587bb880b2">r468:b073433cf899 (default)</option>
120 <option value="7a74dbfcacd1dbcb58bb9c860b2f29fbb22c4c96">r467:7a74dbfcacd1 (default)</option>
124 <option value="7a74dbfcacd1dbcb58bb9c860b2f29fbb22c4c96">r467:7a74dbfcacd1 (default)</option>
121 <option value="71ee52cc4d629096bdbee036325975dac2af4501">r465:71ee52cc4d62 (default)</option>
125 <option value="71ee52cc4d629096bdbee036325975dac2af4501">r465:71ee52cc4d62 (default)</option>
122 <option value="a5b217d26c5f111e72bae4de672b084ee0fbf75c">r452:a5b217d26c5f (default)</option>
126 <option value="a5b217d26c5f111e72bae4de672b084ee0fbf75c">r452:a5b217d26c5f (default)</option>
123 <option value="47aedd538bf616eedcb0e7d630ea476df0e159c7">r450:47aedd538bf6 (default)</option>
127 <option value="47aedd538bf616eedcb0e7d630ea476df0e159c7">r450:47aedd538bf6 (default)</option>
124 <option value="8e4915fa32d727dcbf09746f637a5f82e539511e">r432:8e4915fa32d7 (default)</option>
128 <option value="8e4915fa32d727dcbf09746f637a5f82e539511e">r432:8e4915fa32d7 (default)</option>
125 <option value="25213a5fbb048dff8ba65d21e466a835536e5b70">r356:25213a5fbb04 (default)</option>
129 <option value="25213a5fbb048dff8ba65d21e466a835536e5b70">r356:25213a5fbb04 (default)</option>
126 <option value="23debcedddc1c23c14be33e713e7786d4a9de471">r351:23debcedddc1 (default)</option>
130 <option value="23debcedddc1c23c14be33e713e7786d4a9de471">r351:23debcedddc1 (default)</option>
127 <option value="61e25b2a90a19e7fffd75dea1e4c7e20df526bbe">r342:61e25b2a90a1 (default)</option>
131 <option value="61e25b2a90a19e7fffd75dea1e4c7e20df526bbe">r342:61e25b2a90a1 (default)</option>
128 <option value="fb95b340e0d03fa51f33c56c991c08077c99303e">r318:fb95b340e0d0 (webvcs)</option>
132 <option value="fb95b340e0d03fa51f33c56c991c08077c99303e">r318:fb95b340e0d0 (webvcs)</option>
129 <option value="bda35e0e564fbbc5cd26fe0a37fb647a254c99fe">r303:bda35e0e564f (default)</option>
133 <option value="bda35e0e564fbbc5cd26fe0a37fb647a254c99fe">r303:bda35e0e564f (default)</option>
130 <option value="97ff74896d7dbf3115a337a421d44b55154acc89">r302:97ff74896d7d (default)</option>
134 <option value="97ff74896d7dbf3115a337a421d44b55154acc89">r302:97ff74896d7d (default)</option>
131 <option value="cec3473c3fdb9599c98067182a075b49bde570f9">r293:cec3473c3fdb (default)</option>
135 <option value="cec3473c3fdb9599c98067182a075b49bde570f9">r293:cec3473c3fdb (default)</option>
132 <option value="0e86c43eef866a013a587666a877c879899599bb">r289:0e86c43eef86 (default)</option>
136 <option value="0e86c43eef866a013a587666a877c879899599bb">r289:0e86c43eef86 (default)</option>
133 <option value="91a27c312808100cf20a602f78befbbff9d89bfd">r288:91a27c312808 (default)</option>
137 <option value="91a27c312808100cf20a602f78befbbff9d89bfd">r288:91a27c312808 (default)</option>
134 <option value="400e36a1670a57d11e3edcb5b07bf82c30006d0b">r287:400e36a1670a (default)</option>
138 <option value="400e36a1670a57d11e3edcb5b07bf82c30006d0b">r287:400e36a1670a (default)</option>
135 <option value="014fb17dfc95b0995e838c565376bf9a993e230a">r261:014fb17dfc95 (default)</option>
139 <option value="014fb17dfc95b0995e838c565376bf9a993e230a">r261:014fb17dfc95 (default)</option>
136 <option value="cca7aebbc4d6125798446b11e69dc8847834a982">r260:cca7aebbc4d6 (default)</option>
140 <option value="cca7aebbc4d6125798446b11e69dc8847834a982">r260:cca7aebbc4d6 (default)</option>
137 <option value="14cdb2957c011a5feba36f50d960d9832ba0f0c1">r258:14cdb2957c01 (workdir)</option>
141 <option value="14cdb2957c011a5feba36f50d960d9832ba0f0c1">r258:14cdb2957c01 (workdir)</option>
138 <option value="34df20118ed74b5987d22a579e8a60e903da5bf8">r245:34df20118ed7 (default)</option>
142 <option value="34df20118ed74b5987d22a579e8a60e903da5bf8">r245:34df20118ed7 (default)</option>
139 <option value="0375d9042a64a1ac1641528f0f0668f9a339e86d">r233:0375d9042a64 (workdir)</option>
143 <option value="0375d9042a64a1ac1641528f0f0668f9a339e86d">r233:0375d9042a64 (workdir)</option>
140 <option value="94aa45fc1806c04d4ba640933edf682c22478453">r222:94aa45fc1806 (workdir)</option>
144 <option value="94aa45fc1806c04d4ba640933edf682c22478453">r222:94aa45fc1806 (workdir)</option>
141 <option value="7ed99bc738818879941e3ce20243f8856a7cfc84">r188:7ed99bc73881 (default)</option>
145 <option value="7ed99bc738818879941e3ce20243f8856a7cfc84">r188:7ed99bc73881 (default)</option>
142 <option value="1e85975528bcebe853732a9e5fb8dbf4461f6bb2">r184:1e85975528bc (default)</option>
146 <option value="1e85975528bcebe853732a9e5fb8dbf4461f6bb2">r184:1e85975528bc (default)</option>
143 <option value="ed30beddde7bbddb26042625be19bcd11576c1dd">r183:ed30beddde7b (default)</option>
147 <option value="ed30beddde7bbddb26042625be19bcd11576c1dd">r183:ed30beddde7b (default)</option>
144 <option value="a6664e18181c6fc81b751a8d01474e7e1a3fe7fc">r177:a6664e18181c (default)</option>
148 <option value="a6664e18181c6fc81b751a8d01474e7e1a3fe7fc">r177:a6664e18181c (default)</option>
145 <option value="8911406ad776fdd3d0b9932a2e89677e57405a48">r167:8911406ad776 (default)</option>
149 <option value="8911406ad776fdd3d0b9932a2e89677e57405a48">r167:8911406ad776 (default)</option>
146 <option value="aa957ed78c35a1541f508d2ec90e501b0a9e3167">r165:aa957ed78c35 (default)</option>
150 <option value="aa957ed78c35a1541f508d2ec90e501b0a9e3167">r165:aa957ed78c35 (default)</option>
147 <option value="48e11b73e94c0db33e736eaeea692f990cb0b5f1">r140:48e11b73e94c (default)</option>
151 <option value="48e11b73e94c0db33e736eaeea692f990cb0b5f1">r140:48e11b73e94c (default)</option>
148 <option value="adf3cbf483298563b968a6c673cd5bde5f7d5eea">r126:adf3cbf48329 (default)</option>
152 <option value="adf3cbf483298563b968a6c673cd5bde5f7d5eea">r126:adf3cbf48329 (default)</option>
149 <option value="6249fd0fb2cfb1411e764129f598e2cf0de79a6f">r113:6249fd0fb2cf (git)</option>
153 <option value="6249fd0fb2cfb1411e764129f598e2cf0de79a6f">r113:6249fd0fb2cf (git)</option>
150 <option value="75feb4c33e81186c87eac740cee2447330288412">r109:75feb4c33e81 (default)</option>
154 <option value="75feb4c33e81186c87eac740cee2447330288412">r109:75feb4c33e81 (default)</option>
151 <option value="9a4dc232ecdc763ef2e98ae2238cfcbba4f6ad8d">r108:9a4dc232ecdc (default)</option>
155 <option value="9a4dc232ecdc763ef2e98ae2238cfcbba4f6ad8d">r108:9a4dc232ecdc (default)</option>
152 <option value="595cce4efa21fda2f2e4eeb4fe5f2a6befe6fa2d">r107:595cce4efa21 (default)</option>
156 <option value="595cce4efa21fda2f2e4eeb4fe5f2a6befe6fa2d">r107:595cce4efa21 (default)</option>
153 <option value="4a8bd421fbc2dfbfb70d85a3fe064075ab2c49da">r104:4a8bd421fbc2 (default)</option>
157 <option value="4a8bd421fbc2dfbfb70d85a3fe064075ab2c49da">r104:4a8bd421fbc2 (default)</option>
154 <option value="57be63fc8f85e65a0106a53187f7316f8c487ffa">r102:57be63fc8f85 (default)</option>
158 <option value="57be63fc8f85e65a0106a53187f7316f8c487ffa">r102:57be63fc8f85 (default)</option>
155 <option value="5530bd87f7e2e124a64d07cb2654c997682128be">r101:5530bd87f7e2 (git)</option>
159 <option value="5530bd87f7e2e124a64d07cb2654c997682128be">r101:5530bd87f7e2 (git)</option>
156 <option value="e516008b1c93f142263dc4b7961787cbad654ce1">r99:e516008b1c93 (default)</option>
160 <option value="e516008b1c93f142263dc4b7961787cbad654ce1">r99:e516008b1c93 (default)</option>
157 <option value="41f43fc74b8b285984554532eb105ac3be5c434f">r93:41f43fc74b8b (default)</option>
161 <option value="41f43fc74b8b285984554532eb105ac3be5c434f">r93:41f43fc74b8b (default)</option>
158 <option value="cc66b61b8455b264a7a8a2d8ddc80fcfc58c221e">r92:cc66b61b8455 (default)</option>
162 <option value="cc66b61b8455b264a7a8a2d8ddc80fcfc58c221e">r92:cc66b61b8455 (default)</option>
159 <option value="73ab5b616b3271b0518682fb4988ce421de8099f">r91:73ab5b616b32 (default)</option>
163 <option value="73ab5b616b3271b0518682fb4988ce421de8099f">r91:73ab5b616b32 (default)</option>
160 <option value="e0da75f308c0f18f98e9ce6257626009fdda2b39">r82:e0da75f308c0 (default)</option>
164 <option value="e0da75f308c0f18f98e9ce6257626009fdda2b39">r82:e0da75f308c0 (default)</option>
161 <option value="fb2e41e0f0810be4d7103bc2a4c7be16ee3ec611">r81:fb2e41e0f081 (default)</option>
165 <option value="fb2e41e0f0810be4d7103bc2a4c7be16ee3ec611">r81:fb2e41e0f081 (default)</option>
162 <option value="602ae2f5e7ade70b3b66a58cdd9e3e613dc8a028">r76:602ae2f5e7ad (default)</option>
166 <option value="602ae2f5e7ade70b3b66a58cdd9e3e613dc8a028">r76:602ae2f5e7ad (default)</option>
163 <option value="a066b25d5df7016b45a41b7e2a78c33b57adc235">r73:a066b25d5df7 (default)</option>
167 <option value="a066b25d5df7016b45a41b7e2a78c33b57adc235">r73:a066b25d5df7 (default)</option>
164 <option value="637a933c905958ce5151f154147c25c1c7b68832">r61:637a933c9059 (web)</option>
168 <option value="637a933c905958ce5151f154147c25c1c7b68832">r61:637a933c9059 (web)</option>
165 <option value="0c21004effeb8ce2d2d5b4a8baf6afa8394b6fbc">r60:0c21004effeb (web)</option>
169 <option value="0c21004effeb8ce2d2d5b4a8baf6afa8394b6fbc">r60:0c21004effeb (web)</option>
166 <option value="a1f39c56d3f1d52d5fb5920370a2a2716cd9a444">r59:a1f39c56d3f1 (web)</option>
170 <option value="a1f39c56d3f1d52d5fb5920370a2a2716cd9a444">r59:a1f39c56d3f1 (web)</option>
167 <option value="97d32df05c715a3bbf936bf3cc4e32fb77fe1a7f">r58:97d32df05c71 (web)</option>
171 <option value="97d32df05c715a3bbf936bf3cc4e32fb77fe1a7f">r58:97d32df05c71 (web)</option>
168 <option value="08eaf14517718dccea4b67755a93368341aca919">r57:08eaf1451771 (web)</option>
172 <option value="08eaf14517718dccea4b67755a93368341aca919">r57:08eaf1451771 (web)</option>
169 <option value="22f71ad265265a53238359c883aa976e725aa07d">r56:22f71ad26526 (web)</option>
173 <option value="22f71ad265265a53238359c883aa976e725aa07d">r56:22f71ad26526 (web)</option>
170 <option value="97501f02b7b4330924b647755663a2d90a5e638d">r49:97501f02b7b4 (web)</option>
174 <option value="97501f02b7b4330924b647755663a2d90a5e638d">r49:97501f02b7b4 (web)</option>
171 <option value="86ede6754f2b27309452bb11f997386ae01d0e5a">r47:86ede6754f2b (web)</option>
175 <option value="86ede6754f2b27309452bb11f997386ae01d0e5a">r47:86ede6754f2b (web)</option>
172 <option value="014c40c0203c423dc19ecf94644f7cac9d4cdce0">r45:014c40c0203c (web)</option>
176 <option value="014c40c0203c423dc19ecf94644f7cac9d4cdce0">r45:014c40c0203c (web)</option>
173 <option value="ee87846a61c12153b51543bf860e1026c6d3dcba">r30:ee87846a61c1 (default)</option>
177 <option value="ee87846a61c12153b51543bf860e1026c6d3dcba">r30:ee87846a61c1 (default)</option>
174 <option value="9bb326a04ae5d98d437dece54be04f830cf1edd9">r26:9bb326a04ae5 (default)</option>
178 <option value="9bb326a04ae5d98d437dece54be04f830cf1edd9">r26:9bb326a04ae5 (default)</option>
175 <option value="536c1a19428381cfea92ac44985304f6a8049569">r24:536c1a194283 (default)</option>
179 <option value="536c1a19428381cfea92ac44985304f6a8049569">r24:536c1a194283 (default)</option>
176 <option value="dc5d2c0661b61928834a785d3e64a3f80d3aad9c">r8:dc5d2c0661b6 (default)</option>
180 <option value="dc5d2c0661b61928834a785d3e64a3f80d3aad9c">r8:dc5d2c0661b6 (default)</option>
177 <option value="3803844fdbd3b711175fc3da9bdacfcd6d29a6fb">r7:3803844fdbd3 (default)</option>
181 <option value="3803844fdbd3b711175fc3da9bdacfcd6d29a6fb">r7:3803844fdbd3 (default)</option>
178 </optgroup>
182 </optgroup>
179 <optgroup label="Branches">
183 <optgroup label="Branches">
180 <option value="96507bd11ecc815ebc6270fdf6db110928c09c1e">default</option>
184 <option value="96507bd11ecc815ebc6270fdf6db110928c09c1e">default</option>
181 <option value="4f7e2131323e0749a740c0a56ab68ae9269c562a">stable</option>
185 <option value="4f7e2131323e0749a740c0a56ab68ae9269c562a">stable</option>
182 </optgroup>
186 </optgroup>
183 <optgroup label="Tags">
187 <optgroup label="Tags">
184 <option value="2c96c02def9a7c997f33047761a53943e6254396">v0.2.0</option>
188 <option value="2c96c02def9a7c997f33047761a53943e6254396">v0.2.0</option>
185 <option value="8680b1d1cee3aa3c1ab3734b76ee164bbedbc5c9">v0.1.9</option>
189 <option value="8680b1d1cee3aa3c1ab3734b76ee164bbedbc5c9">v0.1.9</option>
186 <option value="ecb25ba9c96faf1e65a0bc3fd914918420a2f116">v0.1.8</option>
190 <option value="ecb25ba9c96faf1e65a0bc3fd914918420a2f116">v0.1.8</option>
187 <option value="f67633a2894edaf28513706d558205fa93df9209">v0.1.7</option>
191 <option value="f67633a2894edaf28513706d558205fa93df9209">v0.1.7</option>
188 <option value="02b38c0eb6f982174750c0e309ff9faddc0c7e12">v0.1.6</option>
192 <option value="02b38c0eb6f982174750c0e309ff9faddc0c7e12">v0.1.6</option>
189 <option value="a6664e18181c6fc81b751a8d01474e7e1a3fe7fc">v0.1.5</option>
193 <option value="a6664e18181c6fc81b751a8d01474e7e1a3fe7fc">v0.1.5</option>
190 <option value="fd4bdb5e9b2a29b4393a4ac6caef48c17ee1a200">v0.1.4</option>
194 <option value="fd4bdb5e9b2a29b4393a4ac6caef48c17ee1a200">v0.1.4</option>
191 <option value="17544fbfcd33ffb439e2b728b5d526b1ef30bfcf">v0.1.3</option>
195 <option value="17544fbfcd33ffb439e2b728b5d526b1ef30bfcf">v0.1.3</option>
192 <option value="a7e60bff65d57ac3a1a1ce3b12a70f8a9e8a7720">v0.1.2</option>
196 <option value="a7e60bff65d57ac3a1a1ce3b12a70f8a9e8a7720">v0.1.2</option>
193 <option value="fef5bfe1dc17611d5fb59a7f6f95c55c3606f933">v0.1.11</option>
197 <option value="fef5bfe1dc17611d5fb59a7f6f95c55c3606f933">v0.1.11</option>
194 <option value="92831aebf2f8dd4879e897024b89d09af214df1c">v0.1.10</option>
198 <option value="92831aebf2f8dd4879e897024b89d09af214df1c">v0.1.10</option>
195 <option value="eb3a60fc964309c1a318b8dfe26aa2d1586c85ae">v0.1.1</option>
199 <option value="eb3a60fc964309c1a318b8dfe26aa2d1586c85ae">v0.1.1</option>
196 <option value="96507bd11ecc815ebc6270fdf6db110928c09c1e">tip</option>
200 <option value="96507bd11ecc815ebc6270fdf6db110928c09c1e">tip</option>
197 </optgroup>
201 </optgroup>
198 """)
202 """)
199
203
200 def test_file_annotation(self):
204 def test_file_annotation(self):
201 self.log_user()
205 self.log_user()
202 response = self.app.get(url(controller='files', action='index',
206 response = self.app.get(url(controller='files', action='index',
203 repo_name=HG_REPO,
207 repo_name=HG_REPO,
204 revision='27cd5cce30c96924232dffcd24178a07ffeb5dfc',
208 revision='27cd5cce30c96924232dffcd24178a07ffeb5dfc',
205 f_path='vcs/nodes.py',
209 f_path='vcs/nodes.py',
206 annotate=True))
210 annotate=True))
207
211
208 response.mustcontain("""<span style="text-transform: uppercase;"><a href="#">Branch: default</a></span>""")
212 response.mustcontain("""<span style="text-transform: uppercase;"><a href="#">Branch: default</a></span>""")
209
213
210 def test_file_annotation_history(self):
214 def test_file_annotation_history(self):
211 self.log_user()
215 self.log_user()
212 response = self.app.get(url(controller='files', action='history',
216 response = self.app.get(url(controller='files', action='history',
213 repo_name=HG_REPO,
217 repo_name=HG_REPO,
214 revision='27cd5cce30c96924232dffcd24178a07ffeb5dfc',
218 revision='27cd5cce30c96924232dffcd24178a07ffeb5dfc',
215 f_path='vcs/nodes.py',
219 f_path='vcs/nodes.py',
216 annotate=True),
220 annotate=True),
217 extra_environ={'HTTP_X_PARTIAL_XHR': '1'})
221 extra_environ={'HTTP_X_PARTIAL_XHR': '1'})
218
222
219 response.mustcontain("""
223 response.mustcontain("""
220 <option value="dbec37a0d5cab8ff39af4cfc4a4cd3996e4acfc6">r648:dbec37a0d5ca (default)</option>
224 <option value="dbec37a0d5cab8ff39af4cfc4a4cd3996e4acfc6">r648:dbec37a0d5ca (default)</option>
221 <option value="1d20ed9eda9482d46ff0a6af5812550218b3ff15">r639:1d20ed9eda94 (default)</option>
225 <option value="1d20ed9eda9482d46ff0a6af5812550218b3ff15">r639:1d20ed9eda94 (default)</option>
222 <option value="0173395e822797f098799ed95c1a81b6a547a9ad">r547:0173395e8227 (default)</option>
226 <option value="0173395e822797f098799ed95c1a81b6a547a9ad">r547:0173395e8227 (default)</option>
223 <option value="afbb45ade933a8182f1d8ec5d4d1bb2de2572043">r546:afbb45ade933 (default)</option>
227 <option value="afbb45ade933a8182f1d8ec5d4d1bb2de2572043">r546:afbb45ade933 (default)</option>
224 <option value="6f093e30cac34e6b4b11275a9f22f80c5d7ad1f7">r502:6f093e30cac3 (default)</option>
228 <option value="6f093e30cac34e6b4b11275a9f22f80c5d7ad1f7">r502:6f093e30cac3 (default)</option>
225 <option value="c7e2212dd2ae975d1d06534a3d7e317165c06960">r476:c7e2212dd2ae (default)</option>
229 <option value="c7e2212dd2ae975d1d06534a3d7e317165c06960">r476:c7e2212dd2ae (default)</option>
226 <option value="45477506df79f701bf69419aac3e1f0fed3c5bcf">r472:45477506df79 (default)</option>
230 <option value="45477506df79f701bf69419aac3e1f0fed3c5bcf">r472:45477506df79 (default)</option>
227 <option value="5fc76cb25d11e07c60de040f78b8cd265ff10d53">r469:5fc76cb25d11 (default)</option>
231 <option value="5fc76cb25d11e07c60de040f78b8cd265ff10d53">r469:5fc76cb25d11 (default)</option>
228 <option value="b073433cf8994969ee5cd7cce84cbe587bb880b2">r468:b073433cf899 (default)</option>
232 <option value="b073433cf8994969ee5cd7cce84cbe587bb880b2">r468:b073433cf899 (default)</option>
229 <option value="7a74dbfcacd1dbcb58bb9c860b2f29fbb22c4c96">r467:7a74dbfcacd1 (default)</option>
233 <option value="7a74dbfcacd1dbcb58bb9c860b2f29fbb22c4c96">r467:7a74dbfcacd1 (default)</option>
230 <option value="71ee52cc4d629096bdbee036325975dac2af4501">r465:71ee52cc4d62 (default)</option>
234 <option value="71ee52cc4d629096bdbee036325975dac2af4501">r465:71ee52cc4d62 (default)</option>
231 <option value="a5b217d26c5f111e72bae4de672b084ee0fbf75c">r452:a5b217d26c5f (default)</option>
235 <option value="a5b217d26c5f111e72bae4de672b084ee0fbf75c">r452:a5b217d26c5f (default)</option>
232 <option value="47aedd538bf616eedcb0e7d630ea476df0e159c7">r450:47aedd538bf6 (default)</option>
236 <option value="47aedd538bf616eedcb0e7d630ea476df0e159c7">r450:47aedd538bf6 (default)</option>
233 <option value="8e4915fa32d727dcbf09746f637a5f82e539511e">r432:8e4915fa32d7 (default)</option>
237 <option value="8e4915fa32d727dcbf09746f637a5f82e539511e">r432:8e4915fa32d7 (default)</option>
234 <option value="25213a5fbb048dff8ba65d21e466a835536e5b70">r356:25213a5fbb04 (default)</option>
238 <option value="25213a5fbb048dff8ba65d21e466a835536e5b70">r356:25213a5fbb04 (default)</option>
235 <option value="23debcedddc1c23c14be33e713e7786d4a9de471">r351:23debcedddc1 (default)</option>
239 <option value="23debcedddc1c23c14be33e713e7786d4a9de471">r351:23debcedddc1 (default)</option>
236 <option value="61e25b2a90a19e7fffd75dea1e4c7e20df526bbe">r342:61e25b2a90a1 (default)</option>
240 <option value="61e25b2a90a19e7fffd75dea1e4c7e20df526bbe">r342:61e25b2a90a1 (default)</option>
237 <option value="fb95b340e0d03fa51f33c56c991c08077c99303e">r318:fb95b340e0d0 (webvcs)</option>
241 <option value="fb95b340e0d03fa51f33c56c991c08077c99303e">r318:fb95b340e0d0 (webvcs)</option>
238 <option value="bda35e0e564fbbc5cd26fe0a37fb647a254c99fe">r303:bda35e0e564f (default)</option>
242 <option value="bda35e0e564fbbc5cd26fe0a37fb647a254c99fe">r303:bda35e0e564f (default)</option>
239 <option value="97ff74896d7dbf3115a337a421d44b55154acc89">r302:97ff74896d7d (default)</option>
243 <option value="97ff74896d7dbf3115a337a421d44b55154acc89">r302:97ff74896d7d (default)</option>
240 <option value="cec3473c3fdb9599c98067182a075b49bde570f9">r293:cec3473c3fdb (default)</option>
244 <option value="cec3473c3fdb9599c98067182a075b49bde570f9">r293:cec3473c3fdb (default)</option>
241 <option value="0e86c43eef866a013a587666a877c879899599bb">r289:0e86c43eef86 (default)</option>
245 <option value="0e86c43eef866a013a587666a877c879899599bb">r289:0e86c43eef86 (default)</option>
242 <option value="91a27c312808100cf20a602f78befbbff9d89bfd">r288:91a27c312808 (default)</option>
246 <option value="91a27c312808100cf20a602f78befbbff9d89bfd">r288:91a27c312808 (default)</option>
243 <option value="400e36a1670a57d11e3edcb5b07bf82c30006d0b">r287:400e36a1670a (default)</option>
247 <option value="400e36a1670a57d11e3edcb5b07bf82c30006d0b">r287:400e36a1670a (default)</option>
244 <option value="014fb17dfc95b0995e838c565376bf9a993e230a">r261:014fb17dfc95 (default)</option>
248 <option value="014fb17dfc95b0995e838c565376bf9a993e230a">r261:014fb17dfc95 (default)</option>
245 <option value="cca7aebbc4d6125798446b11e69dc8847834a982">r260:cca7aebbc4d6 (default)</option>
249 <option value="cca7aebbc4d6125798446b11e69dc8847834a982">r260:cca7aebbc4d6 (default)</option>
246 <option value="14cdb2957c011a5feba36f50d960d9832ba0f0c1">r258:14cdb2957c01 (workdir)</option>
250 <option value="14cdb2957c011a5feba36f50d960d9832ba0f0c1">r258:14cdb2957c01 (workdir)</option>
247 <option value="34df20118ed74b5987d22a579e8a60e903da5bf8">r245:34df20118ed7 (default)</option>
251 <option value="34df20118ed74b5987d22a579e8a60e903da5bf8">r245:34df20118ed7 (default)</option>
248 <option value="0375d9042a64a1ac1641528f0f0668f9a339e86d">r233:0375d9042a64 (workdir)</option>
252 <option value="0375d9042a64a1ac1641528f0f0668f9a339e86d">r233:0375d9042a64 (workdir)</option>
249 <option value="94aa45fc1806c04d4ba640933edf682c22478453">r222:94aa45fc1806 (workdir)</option>
253 <option value="94aa45fc1806c04d4ba640933edf682c22478453">r222:94aa45fc1806 (workdir)</option>
250 <option value="7ed99bc738818879941e3ce20243f8856a7cfc84">r188:7ed99bc73881 (default)</option>
254 <option value="7ed99bc738818879941e3ce20243f8856a7cfc84">r188:7ed99bc73881 (default)</option>
251 <option value="1e85975528bcebe853732a9e5fb8dbf4461f6bb2">r184:1e85975528bc (default)</option>
255 <option value="1e85975528bcebe853732a9e5fb8dbf4461f6bb2">r184:1e85975528bc (default)</option>
252 <option value="ed30beddde7bbddb26042625be19bcd11576c1dd">r183:ed30beddde7b (default)</option>
256 <option value="ed30beddde7bbddb26042625be19bcd11576c1dd">r183:ed30beddde7b (default)</option>
253 <option value="a6664e18181c6fc81b751a8d01474e7e1a3fe7fc">r177:a6664e18181c (default)</option>
257 <option value="a6664e18181c6fc81b751a8d01474e7e1a3fe7fc">r177:a6664e18181c (default)</option>
254 <option value="8911406ad776fdd3d0b9932a2e89677e57405a48">r167:8911406ad776 (default)</option>
258 <option value="8911406ad776fdd3d0b9932a2e89677e57405a48">r167:8911406ad776 (default)</option>
255 <option value="aa957ed78c35a1541f508d2ec90e501b0a9e3167">r165:aa957ed78c35 (default)</option>
259 <option value="aa957ed78c35a1541f508d2ec90e501b0a9e3167">r165:aa957ed78c35 (default)</option>
256 <option value="48e11b73e94c0db33e736eaeea692f990cb0b5f1">r140:48e11b73e94c (default)</option>
260 <option value="48e11b73e94c0db33e736eaeea692f990cb0b5f1">r140:48e11b73e94c (default)</option>
257 <option value="adf3cbf483298563b968a6c673cd5bde5f7d5eea">r126:adf3cbf48329 (default)</option>
261 <option value="adf3cbf483298563b968a6c673cd5bde5f7d5eea">r126:adf3cbf48329 (default)</option>
258 <option value="6249fd0fb2cfb1411e764129f598e2cf0de79a6f">r113:6249fd0fb2cf (git)</option>
262 <option value="6249fd0fb2cfb1411e764129f598e2cf0de79a6f">r113:6249fd0fb2cf (git)</option>
259 <option value="75feb4c33e81186c87eac740cee2447330288412">r109:75feb4c33e81 (default)</option>
263 <option value="75feb4c33e81186c87eac740cee2447330288412">r109:75feb4c33e81 (default)</option>
260 <option value="9a4dc232ecdc763ef2e98ae2238cfcbba4f6ad8d">r108:9a4dc232ecdc (default)</option>
264 <option value="9a4dc232ecdc763ef2e98ae2238cfcbba4f6ad8d">r108:9a4dc232ecdc (default)</option>
261 <option value="595cce4efa21fda2f2e4eeb4fe5f2a6befe6fa2d">r107:595cce4efa21 (default)</option>
265 <option value="595cce4efa21fda2f2e4eeb4fe5f2a6befe6fa2d">r107:595cce4efa21 (default)</option>
262 <option value="4a8bd421fbc2dfbfb70d85a3fe064075ab2c49da">r104:4a8bd421fbc2 (default)</option>
266 <option value="4a8bd421fbc2dfbfb70d85a3fe064075ab2c49da">r104:4a8bd421fbc2 (default)</option>
263 <option value="57be63fc8f85e65a0106a53187f7316f8c487ffa">r102:57be63fc8f85 (default)</option>
267 <option value="57be63fc8f85e65a0106a53187f7316f8c487ffa">r102:57be63fc8f85 (default)</option>
264 <option value="5530bd87f7e2e124a64d07cb2654c997682128be">r101:5530bd87f7e2 (git)</option>
268 <option value="5530bd87f7e2e124a64d07cb2654c997682128be">r101:5530bd87f7e2 (git)</option>
265 <option value="e516008b1c93f142263dc4b7961787cbad654ce1">r99:e516008b1c93 (default)</option>
269 <option value="e516008b1c93f142263dc4b7961787cbad654ce1">r99:e516008b1c93 (default)</option>
266 <option value="41f43fc74b8b285984554532eb105ac3be5c434f">r93:41f43fc74b8b (default)</option>
270 <option value="41f43fc74b8b285984554532eb105ac3be5c434f">r93:41f43fc74b8b (default)</option>
267 <option value="cc66b61b8455b264a7a8a2d8ddc80fcfc58c221e">r92:cc66b61b8455 (default)</option>
271 <option value="cc66b61b8455b264a7a8a2d8ddc80fcfc58c221e">r92:cc66b61b8455 (default)</option>
268 <option value="73ab5b616b3271b0518682fb4988ce421de8099f">r91:73ab5b616b32 (default)</option>
272 <option value="73ab5b616b3271b0518682fb4988ce421de8099f">r91:73ab5b616b32 (default)</option>
269 <option value="e0da75f308c0f18f98e9ce6257626009fdda2b39">r82:e0da75f308c0 (default)</option>
273 <option value="e0da75f308c0f18f98e9ce6257626009fdda2b39">r82:e0da75f308c0 (default)</option>
270 <option value="fb2e41e0f0810be4d7103bc2a4c7be16ee3ec611">r81:fb2e41e0f081 (default)</option>
274 <option value="fb2e41e0f0810be4d7103bc2a4c7be16ee3ec611">r81:fb2e41e0f081 (default)</option>
271 <option value="602ae2f5e7ade70b3b66a58cdd9e3e613dc8a028">r76:602ae2f5e7ad (default)</option>
275 <option value="602ae2f5e7ade70b3b66a58cdd9e3e613dc8a028">r76:602ae2f5e7ad (default)</option>
272 <option value="a066b25d5df7016b45a41b7e2a78c33b57adc235">r73:a066b25d5df7 (default)</option>
276 <option value="a066b25d5df7016b45a41b7e2a78c33b57adc235">r73:a066b25d5df7 (default)</option>
273 <option value="637a933c905958ce5151f154147c25c1c7b68832">r61:637a933c9059 (web)</option>
277 <option value="637a933c905958ce5151f154147c25c1c7b68832">r61:637a933c9059 (web)</option>
274 <option value="0c21004effeb8ce2d2d5b4a8baf6afa8394b6fbc">r60:0c21004effeb (web)</option>
278 <option value="0c21004effeb8ce2d2d5b4a8baf6afa8394b6fbc">r60:0c21004effeb (web)</option>
275 <option value="a1f39c56d3f1d52d5fb5920370a2a2716cd9a444">r59:a1f39c56d3f1 (web)</option>
279 <option value="a1f39c56d3f1d52d5fb5920370a2a2716cd9a444">r59:a1f39c56d3f1 (web)</option>
276 <option value="97d32df05c715a3bbf936bf3cc4e32fb77fe1a7f">r58:97d32df05c71 (web)</option>
280 <option value="97d32df05c715a3bbf936bf3cc4e32fb77fe1a7f">r58:97d32df05c71 (web)</option>
277 <option value="08eaf14517718dccea4b67755a93368341aca919">r57:08eaf1451771 (web)</option>
281 <option value="08eaf14517718dccea4b67755a93368341aca919">r57:08eaf1451771 (web)</option>
278 <option value="22f71ad265265a53238359c883aa976e725aa07d">r56:22f71ad26526 (web)</option>
282 <option value="22f71ad265265a53238359c883aa976e725aa07d">r56:22f71ad26526 (web)</option>
279 <option value="97501f02b7b4330924b647755663a2d90a5e638d">r49:97501f02b7b4 (web)</option>
283 <option value="97501f02b7b4330924b647755663a2d90a5e638d">r49:97501f02b7b4 (web)</option>
280 <option value="86ede6754f2b27309452bb11f997386ae01d0e5a">r47:86ede6754f2b (web)</option>
284 <option value="86ede6754f2b27309452bb11f997386ae01d0e5a">r47:86ede6754f2b (web)</option>
281 <option value="014c40c0203c423dc19ecf94644f7cac9d4cdce0">r45:014c40c0203c (web)</option>
285 <option value="014c40c0203c423dc19ecf94644f7cac9d4cdce0">r45:014c40c0203c (web)</option>
282 <option value="ee87846a61c12153b51543bf860e1026c6d3dcba">r30:ee87846a61c1 (default)</option>
286 <option value="ee87846a61c12153b51543bf860e1026c6d3dcba">r30:ee87846a61c1 (default)</option>
283 <option value="9bb326a04ae5d98d437dece54be04f830cf1edd9">r26:9bb326a04ae5 (default)</option>
287 <option value="9bb326a04ae5d98d437dece54be04f830cf1edd9">r26:9bb326a04ae5 (default)</option>
284 <option value="536c1a19428381cfea92ac44985304f6a8049569">r24:536c1a194283 (default)</option>
288 <option value="536c1a19428381cfea92ac44985304f6a8049569">r24:536c1a194283 (default)</option>
285 <option value="dc5d2c0661b61928834a785d3e64a3f80d3aad9c">r8:dc5d2c0661b6 (default)</option>
289 <option value="dc5d2c0661b61928834a785d3e64a3f80d3aad9c">r8:dc5d2c0661b6 (default)</option>
286 <option value="3803844fdbd3b711175fc3da9bdacfcd6d29a6fb">r7:3803844fdbd3 (default)</option>
290 <option value="3803844fdbd3b711175fc3da9bdacfcd6d29a6fb">r7:3803844fdbd3 (default)</option>
287 </optgroup>
291 </optgroup>
288 <optgroup label="Branches">
292 <optgroup label="Branches">
289 <option value="96507bd11ecc815ebc6270fdf6db110928c09c1e">default</option>
293 <option value="96507bd11ecc815ebc6270fdf6db110928c09c1e">default</option>
290 <option value="4f7e2131323e0749a740c0a56ab68ae9269c562a">stable</option>
294 <option value="4f7e2131323e0749a740c0a56ab68ae9269c562a">stable</option>
291 </optgroup>
295 </optgroup>
292 <optgroup label="Tags">
296 <optgroup label="Tags">
293 <option value="2c96c02def9a7c997f33047761a53943e6254396">v0.2.0</option>
297 <option value="2c96c02def9a7c997f33047761a53943e6254396">v0.2.0</option>
294 <option value="8680b1d1cee3aa3c1ab3734b76ee164bbedbc5c9">v0.1.9</option>
298 <option value="8680b1d1cee3aa3c1ab3734b76ee164bbedbc5c9">v0.1.9</option>
295 <option value="ecb25ba9c96faf1e65a0bc3fd914918420a2f116">v0.1.8</option>
299 <option value="ecb25ba9c96faf1e65a0bc3fd914918420a2f116">v0.1.8</option>
296 <option value="f67633a2894edaf28513706d558205fa93df9209">v0.1.7</option>
300 <option value="f67633a2894edaf28513706d558205fa93df9209">v0.1.7</option>
297 <option value="02b38c0eb6f982174750c0e309ff9faddc0c7e12">v0.1.6</option>
301 <option value="02b38c0eb6f982174750c0e309ff9faddc0c7e12">v0.1.6</option>
298 <option value="a6664e18181c6fc81b751a8d01474e7e1a3fe7fc">v0.1.5</option>
302 <option value="a6664e18181c6fc81b751a8d01474e7e1a3fe7fc">v0.1.5</option>
299 <option value="fd4bdb5e9b2a29b4393a4ac6caef48c17ee1a200">v0.1.4</option>
303 <option value="fd4bdb5e9b2a29b4393a4ac6caef48c17ee1a200">v0.1.4</option>
300 <option value="17544fbfcd33ffb439e2b728b5d526b1ef30bfcf">v0.1.3</option>
304 <option value="17544fbfcd33ffb439e2b728b5d526b1ef30bfcf">v0.1.3</option>
301 <option value="a7e60bff65d57ac3a1a1ce3b12a70f8a9e8a7720">v0.1.2</option>
305 <option value="a7e60bff65d57ac3a1a1ce3b12a70f8a9e8a7720">v0.1.2</option>
302 <option value="fef5bfe1dc17611d5fb59a7f6f95c55c3606f933">v0.1.11</option>
306 <option value="fef5bfe1dc17611d5fb59a7f6f95c55c3606f933">v0.1.11</option>
303 <option value="92831aebf2f8dd4879e897024b89d09af214df1c">v0.1.10</option>
307 <option value="92831aebf2f8dd4879e897024b89d09af214df1c">v0.1.10</option>
304 <option value="eb3a60fc964309c1a318b8dfe26aa2d1586c85ae">v0.1.1</option>
308 <option value="eb3a60fc964309c1a318b8dfe26aa2d1586c85ae">v0.1.1</option>
305 <option value="96507bd11ecc815ebc6270fdf6db110928c09c1e">tip</option>
309 <option value="96507bd11ecc815ebc6270fdf6db110928c09c1e">tip</option>
306 </optgroup>""")
310 </optgroup>""")
307
311
308 def test_file_annotation_git(self):
312 def test_file_annotation_git(self):
309 self.log_user()
313 self.log_user()
310 response = self.app.get(url(controller='files', action='index',
314 response = self.app.get(url(controller='files', action='index',
311 repo_name=GIT_REPO,
315 repo_name=GIT_REPO,
312 revision='master',
316 revision='master',
313 f_path='vcs/nodes.py',
317 f_path='vcs/nodes.py',
314 annotate=True))
318 annotate=True))
315
319
316 def test_archival(self):
320 def test_archival(self):
317 self.log_user()
321 self.log_user()
318 _set_downloads(HG_REPO, set_to=True)
322 _set_downloads(HG_REPO, set_to=True)
319 for arch_ext, info in ARCHIVE_SPECS.items():
323 for arch_ext, info in ARCHIVE_SPECS.items():
320 short = '27cd5cce30c9%s' % arch_ext
324 short = '27cd5cce30c9%s' % arch_ext
321 fname = '27cd5cce30c96924232dffcd24178a07ffeb5dfc%s' % arch_ext
325 fname = '27cd5cce30c96924232dffcd24178a07ffeb5dfc%s' % arch_ext
322 filename = '%s-%s' % (HG_REPO, short)
326 filename = '%s-%s' % (HG_REPO, short)
323 response = self.app.get(url(controller='files',
327 response = self.app.get(url(controller='files',
324 action='archivefile',
328 action='archivefile',
325 repo_name=HG_REPO,
329 repo_name=HG_REPO,
326 fname=fname))
330 fname=fname))
327
331
328 self.assertEqual(response.status, '200 OK')
332 self.assertEqual(response.status, '200 OK')
329 heads = [
333 heads = [
330 ('Pragma', 'no-cache'),
334 ('Pragma', 'no-cache'),
331 ('Cache-Control', 'no-cache'),
335 ('Cache-Control', 'no-cache'),
332 ('Content-Disposition', 'attachment; filename=%s' % filename),
336 ('Content-Disposition', 'attachment; filename=%s' % filename),
333 ('Content-Type', '%s; charset=utf-8' % info[0]),
337 ('Content-Type', '%s; charset=utf-8' % info[0]),
334 ]
338 ]
335 self.assertEqual(response.response._headers.items(), heads)
339 self.assertEqual(response.response._headers.items(), heads)
336
340
337 def test_archival_wrong_ext(self):
341 def test_archival_wrong_ext(self):
338 self.log_user()
342 self.log_user()
339 _set_downloads(HG_REPO, set_to=True)
343 _set_downloads(HG_REPO, set_to=True)
340 for arch_ext in ['tar', 'rar', 'x', '..ax', '.zipz']:
344 for arch_ext in ['tar', 'rar', 'x', '..ax', '.zipz']:
341 fname = '27cd5cce30c96924232dffcd24178a07ffeb5dfc%s' % arch_ext
345 fname = '27cd5cce30c96924232dffcd24178a07ffeb5dfc%s' % arch_ext
342
346
343 response = self.app.get(url(controller='files',
347 response = self.app.get(url(controller='files',
344 action='archivefile',
348 action='archivefile',
345 repo_name=HG_REPO,
349 repo_name=HG_REPO,
346 fname=fname))
350 fname=fname))
347 response.mustcontain('Unknown archive type')
351 response.mustcontain('Unknown archive type')
348
352
349 def test_archival_wrong_revision(self):
353 def test_archival_wrong_revision(self):
350 self.log_user()
354 self.log_user()
351 _set_downloads(HG_REPO, set_to=True)
355 _set_downloads(HG_REPO, set_to=True)
352 for rev in ['00x000000', 'tar', 'wrong', '@##$@$42413232', '232dffcd']:
356 for rev in ['00x000000', 'tar', 'wrong', '@##$@$42413232', '232dffcd']:
353 fname = '%s.zip' % rev
357 fname = '%s.zip' % rev
354
358
355 response = self.app.get(url(controller='files',
359 response = self.app.get(url(controller='files',
356 action='archivefile',
360 action='archivefile',
357 repo_name=HG_REPO,
361 repo_name=HG_REPO,
358 fname=fname))
362 fname=fname))
359 response.mustcontain('Unknown revision')
363 response.mustcontain('Unknown revision')
360
364
361 #==========================================================================
365 #==========================================================================
362 # RAW FILE
366 # RAW FILE
363 #==========================================================================
367 #==========================================================================
364 def test_raw_file_ok(self):
368 def test_raw_file_ok(self):
365 self.log_user()
369 self.log_user()
366 response = self.app.get(url(controller='files', action='rawfile',
370 response = self.app.get(url(controller='files', action='rawfile',
367 repo_name=HG_REPO,
371 repo_name=HG_REPO,
368 revision='27cd5cce30c96924232dffcd24178a07ffeb5dfc',
372 revision='27cd5cce30c96924232dffcd24178a07ffeb5dfc',
369 f_path='vcs/nodes.py'))
373 f_path='vcs/nodes.py'))
370
374
371 self.assertEqual(response.content_disposition, "attachment; filename=nodes.py")
375 self.assertEqual(response.content_disposition, "attachment; filename=nodes.py")
372 self.assertEqual(response.content_type, "text/x-python")
376 self.assertEqual(response.content_type, "text/x-python")
373
377
374 def test_raw_file_wrong_cs(self):
378 def test_raw_file_wrong_cs(self):
375 self.log_user()
379 self.log_user()
376 rev = u'ERRORce30c96924232dffcd24178a07ffeb5dfc'
380 rev = u'ERRORce30c96924232dffcd24178a07ffeb5dfc'
377 f_path = 'vcs/nodes.py'
381 f_path = 'vcs/nodes.py'
378
382
379 response = self.app.get(url(controller='files', action='rawfile',
383 response = self.app.get(url(controller='files', action='rawfile',
380 repo_name=HG_REPO,
384 repo_name=HG_REPO,
381 revision=rev,
385 revision=rev,
382 f_path=f_path), status=404)
386 f_path=f_path), status=404)
383
387
384 msg = """Revision %s does not exist for this repository""" % (rev)
388 msg = """Revision %s does not exist for this repository""" % (rev)
385 response.mustcontain(msg)
389 response.mustcontain(msg)
386
390
387 def test_raw_file_wrong_f_path(self):
391 def test_raw_file_wrong_f_path(self):
388 self.log_user()
392 self.log_user()
389 rev = '27cd5cce30c96924232dffcd24178a07ffeb5dfc'
393 rev = '27cd5cce30c96924232dffcd24178a07ffeb5dfc'
390 f_path = 'vcs/ERRORnodes.py'
394 f_path = 'vcs/ERRORnodes.py'
391 response = self.app.get(url(controller='files', action='rawfile',
395 response = self.app.get(url(controller='files', action='rawfile',
392 repo_name=HG_REPO,
396 repo_name=HG_REPO,
393 revision=rev,
397 revision=rev,
394 f_path=f_path), status=404)
398 f_path=f_path), status=404)
395
399
396 msg = "There is no file nor directory at the given path: &#39;%s&#39; at revision %s" % (f_path, rev[:12])
400 msg = "There is no file nor directory at the given path: &#39;%s&#39; at revision %s" % (f_path, rev[:12])
397 response.mustcontain(msg)
401 response.mustcontain(msg)
398
402
399 #==========================================================================
403 #==========================================================================
400 # RAW RESPONSE - PLAIN
404 # RAW RESPONSE - PLAIN
401 #==========================================================================
405 #==========================================================================
402 def test_raw_ok(self):
406 def test_raw_ok(self):
403 self.log_user()
407 self.log_user()
404 response = self.app.get(url(controller='files', action='raw',
408 response = self.app.get(url(controller='files', action='raw',
405 repo_name=HG_REPO,
409 repo_name=HG_REPO,
406 revision='27cd5cce30c96924232dffcd24178a07ffeb5dfc',
410 revision='27cd5cce30c96924232dffcd24178a07ffeb5dfc',
407 f_path='vcs/nodes.py'))
411 f_path='vcs/nodes.py'))
408
412
409 self.assertEqual(response.content_type, "text/plain")
413 self.assertEqual(response.content_type, "text/plain")
410
414
411 def test_raw_wrong_cs(self):
415 def test_raw_wrong_cs(self):
412 self.log_user()
416 self.log_user()
413 rev = u'ERRORcce30c96924232dffcd24178a07ffeb5dfc'
417 rev = u'ERRORcce30c96924232dffcd24178a07ffeb5dfc'
414 f_path = 'vcs/nodes.py'
418 f_path = 'vcs/nodes.py'
415
419
416 response = self.app.get(url(controller='files', action='raw',
420 response = self.app.get(url(controller='files', action='raw',
417 repo_name=HG_REPO,
421 repo_name=HG_REPO,
418 revision=rev,
422 revision=rev,
419 f_path=f_path), status=404)
423 f_path=f_path), status=404)
420
424
421 msg = """Revision %s does not exist for this repository""" % (rev)
425 msg = """Revision %s does not exist for this repository""" % (rev)
422 response.mustcontain(msg)
426 response.mustcontain(msg)
423
427
424 def test_raw_wrong_f_path(self):
428 def test_raw_wrong_f_path(self):
425 self.log_user()
429 self.log_user()
426 rev = '27cd5cce30c96924232dffcd24178a07ffeb5dfc'
430 rev = '27cd5cce30c96924232dffcd24178a07ffeb5dfc'
427 f_path = 'vcs/ERRORnodes.py'
431 f_path = 'vcs/ERRORnodes.py'
428 response = self.app.get(url(controller='files', action='raw',
432 response = self.app.get(url(controller='files', action='raw',
429 repo_name=HG_REPO,
433 repo_name=HG_REPO,
430 revision=rev,
434 revision=rev,
431 f_path=f_path), status=404)
435 f_path=f_path), status=404)
432 msg = "There is no file nor directory at the given path: &#39;%s&#39; at revision %s" % (f_path, rev[:12])
436 msg = "There is no file nor directory at the given path: &#39;%s&#39; at revision %s" % (f_path, rev[:12])
433 response.mustcontain(msg)
437 response.mustcontain(msg)
434
438
435 def test_ajaxed_files_list(self):
439 def test_ajaxed_files_list(self):
436 self.log_user()
440 self.log_user()
437 rev = '27cd5cce30c96924232dffcd24178a07ffeb5dfc'
441 rev = '27cd5cce30c96924232dffcd24178a07ffeb5dfc'
438 response = self.app.get(
442 response = self.app.get(
439 url('files_nodelist_home', repo_name=HG_REPO, f_path='/',
443 url('files_nodelist_home', repo_name=HG_REPO, f_path='/',
440 revision=rev),
444 revision=rev),
441 extra_environ={'HTTP_X_PARTIAL_XHR': '1'},
445 extra_environ={'HTTP_X_PARTIAL_XHR': '1'},
442 )
446 )
443 response.mustcontain("vcs/web/simplevcs/views/repository.py")
447 response.mustcontain("vcs/web/simplevcs/views/repository.py")
444
448
449 #HG - ADD FILE
445 def test_add_file_view_hg(self):
450 def test_add_file_view_hg(self):
446 self.log_user()
451 self.log_user()
447 response = self.app.get(url('files_add_home',
452 response = self.app.get(url('files_add_home',
448 repo_name=HG_REPO,
453 repo_name=HG_REPO,
449 revision='tip', f_path='/'))
454 revision='tip', f_path='/'))
450
455
456 def test_add_file_into_hg_missing_content(self):
457 self.log_user()
458 response = self.app.post(url('files_add_home',
459 repo_name=HG_REPO,
460 revision='tip', f_path='/'),
461 params={
462 ''
463 },
464 status=302)
465
466 self.checkSessionFlash(response, 'No content')
467
468 def test_add_file_into_hg_missing_filename(self):
469 self.log_user()
470 response = self.app.post(url('files_add_home',
471 repo_name=HG_REPO,
472 revision='tip', f_path='/'),
473 params={
474 'content': "foo"
475 },
476 status=302)
477
478 self.checkSessionFlash(response, 'No filename')
479
480 @parameterized.expand([
481 ('/abs', 'foo'),
482 ('../rel', 'foo'),
483 ('file/../foo', 'foo'),
484 ])
485 def test_add_file_into_hg_bad_filenames(self, location, filename):
486 self.log_user()
487 response = self.app.post(url('files_add_home',
488 repo_name=HG_REPO,
489 revision='tip', f_path='/'),
490 params={
491 'content': "foo",
492 'filename': filename,
493 'location': location
494 },
495 status=302)
496
497 self.checkSessionFlash(response, 'Location must be relative path and must not contain .. in path')
498
499 @parameterized.expand([
500 (1, '', 'foo.txt'),
501 (2, 'dir', 'foo.rst'),
502 (3, 'rel/dir', 'foo.bar'),
503 ])
504 def test_add_file_into_hg(self, cnt, location, filename):
505 self.log_user()
506 repo = fixture.create_repo('commit-test-%s' % cnt, repo_type='hg')
507 response = self.app.post(url('files_add_home',
508 repo_name=repo.repo_name,
509 revision='tip', f_path='/'),
510 params={
511 'content': "foo",
512 'filename': filename,
513 'location': location
514 },
515 status=302)
516 try:
517 self.checkSessionFlash(response, 'Successfully committed to %s'
518 % os.path.join(location, filename))
519 finally:
520 fixture.destroy_repo(repo.repo_name)
521
522 ##GIT - ADD FILE
451 def test_add_file_view_git(self):
523 def test_add_file_view_git(self):
452 self.log_user()
524 self.log_user()
453 response = self.app.get(url('files_add_home',
525 response = self.app.get(url('files_add_home',
454 repo_name=GIT_REPO,
526 repo_name=GIT_REPO,
455 revision='tip', f_path='/'))
527 revision='tip', f_path='/'))
456
528
529 def test_add_file_into_git_missing_content(self):
530 self.log_user()
531 response = self.app.post(url('files_add_home',
532 repo_name=GIT_REPO,
533 revision='tip', f_path='/'),
534 params={
535 ''
536 },
537 status=302)
538
539 self.checkSessionFlash(response, 'No content')
540
541 def test_add_file_into_git_missing_filename(self):
542 self.log_user()
543 response = self.app.post(url('files_add_home',
544 repo_name=GIT_REPO,
545 revision='tip', f_path='/'),
546 params={
547 'content': "foo"
548 },
549 status=302)
550
551 self.checkSessionFlash(response, 'No filename')
552
553 @parameterized.expand([
554 ('/abs', 'foo'),
555 ('../rel', 'foo'),
556 ('file/../foo', 'foo'),
557 ])
558 def test_add_file_into_git_bad_filenames(self, location, filename):
559 self.log_user()
560 response = self.app.post(url('files_add_home',
561 repo_name=GIT_REPO,
562 revision='tip', f_path='/'),
563 params={
564 'content': "foo",
565 'filename': filename,
566 'location': location
567 },
568 status=302)
569
570 self.checkSessionFlash(response, 'Location must be relative path and must not contain .. in path')
571
572 @parameterized.expand([
573 (1, '', 'foo.txt'),
574 (2, 'dir', 'foo.rst'),
575 (3, 'rel/dir', 'foo.bar'),
576 ])
577 def test_add_file_into_git(self, cnt, location, filename):
578 self.log_user()
579 repo = fixture.create_repo('commit-test-%s' % cnt, repo_type='git')
580 response = self.app.post(url('files_add_home',
581 repo_name=repo.repo_name,
582 revision='tip', f_path='/'),
583 params={
584 'content': "foo",
585 'filename': filename,
586 'location': location
587 },
588 status=302)
589 try:
590 self.checkSessionFlash(response, 'Successfully committed to %s'
591 % os.path.join(location, filename))
592 finally:
593 fixture.destroy_repo(repo.repo_name)
594
595 #HG - EDIT
457 def test_edit_file_view_hg(self):
596 def test_edit_file_view_hg(self):
458 self.log_user()
597 self.log_user()
459 response = self.app.get(url('files_edit_home',
598 response = self.app.get(url('files_edit_home',
460 repo_name=HG_REPO,
599 repo_name=HG_REPO,
461 revision='tip', f_path='vcs/nodes.py'))
600 revision='tip', f_path='vcs/nodes.py'))
462
601
602 def test_edit_file_view_not_on_branch_hg(self):
603 self.log_user()
604 repo = fixture.create_repo('test-edit-repo', repo_type='hg')
605
606 ## add file
607 location = 'vcs'
608 filename = 'nodes.py'
609 response = self.app.post(url('files_add_home',
610 repo_name=repo.repo_name,
611 revision='tip', f_path='/'),
612 params={
613 'content': "def py():\n print 'hello'\n",
614 'filename': filename,
615 'location': location
616 },
617 status=302)
618 response.follow()
619 try:
620 self.checkSessionFlash(response, 'Successfully committed to %s'
621 % os.path.join(location, filename))
622 response = self.app.get(url('files_edit_home',
623 repo_name=repo.repo_name,
624 revision='tip', f_path='vcs/nodes.py'),
625 status=302)
626 self.checkSessionFlash(response,
627 'You can only edit files with revision being a valid branch')
628 finally:
629 fixture.destroy_repo(repo.repo_name)
630
631 def test_edit_file_view_commit_changes_hg(self):
632 self.log_user()
633 repo = fixture.create_repo('test-edit-repo', repo_type='hg')
634
635 ## add file
636 location = 'vcs'
637 filename = 'nodes.py'
638 response = self.app.post(url('files_add_home',
639 repo_name=repo.repo_name,
640 revision='tip',
641 f_path='/'),
642 params={
643 'content': "def py():\n print 'hello'\n",
644 'filename': filename,
645 'location': location
646 },
647 status=302)
648 response.follow()
649 try:
650 self.checkSessionFlash(response, 'Successfully committed to %s'
651 % os.path.join(location, filename))
652 response = self.app.post(url('files_edit_home',
653 repo_name=repo.repo_name,
654 revision=repo.scm_instance.DEFAULT_BRANCH_NAME,
655 f_path='vcs/nodes.py'),
656 params={
657 'content': "def py():\n print 'hello world'\n",
658 'message': 'i commited',
659 },
660 status=302)
661 self.checkSessionFlash(response,
662 'Successfully committed to vcs/nodes.py')
663 finally:
664 fixture.destroy_repo(repo.repo_name)
665
666 #GIT - EDIT
463 def test_edit_file_view_git(self):
667 def test_edit_file_view_git(self):
464 self.log_user()
668 self.log_user()
465 response = self.app.get(url('files_edit_home',
669 response = self.app.get(url('files_edit_home',
466 repo_name=GIT_REPO,
670 repo_name=GIT_REPO,
467 revision='tip', f_path='vcs/nodes.py'))
671 revision='tip', f_path='vcs/nodes.py'))
672
673 def test_edit_file_view_not_on_branch_git(self):
674 self.log_user()
675 repo = fixture.create_repo('test-edit-repo', repo_type='git')
676
677 ## add file
678 location = 'vcs'
679 filename = 'nodes.py'
680 response = self.app.post(url('files_add_home',
681 repo_name=repo.repo_name,
682 revision='tip', f_path='/'),
683 params={
684 'content': "def py():\n print 'hello'\n",
685 'filename': filename,
686 'location': location
687 },
688 status=302)
689 response.follow()
690 try:
691 self.checkSessionFlash(response, 'Successfully committed to %s'
692 % os.path.join(location, filename))
693 response = self.app.get(url('files_edit_home',
694 repo_name=repo.repo_name,
695 revision='tip', f_path='vcs/nodes.py'),
696 status=302)
697 self.checkSessionFlash(response,
698 'You can only edit files with revision being a valid branch')
699 finally:
700 fixture.destroy_repo(repo.repo_name)
701
702 def test_edit_file_view_commit_changes_git(self):
703 self.log_user()
704 repo = fixture.create_repo('test-edit-repo', repo_type='git')
705
706 ## add file
707 location = 'vcs'
708 filename = 'nodes.py'
709 response = self.app.post(url('files_add_home',
710 repo_name=repo.repo_name,
711 revision='tip',
712 f_path='/'),
713 params={
714 'content': "def py():\n print 'hello'\n",
715 'filename': filename,
716 'location': location
717 },
718 status=302)
719 response.follow()
720 try:
721 self.checkSessionFlash(response, 'Successfully committed to %s'
722 % os.path.join(location, filename))
723 response = self.app.post(url('files_edit_home',
724 repo_name=repo.repo_name,
725 revision=repo.scm_instance.DEFAULT_BRANCH_NAME,
726 f_path='vcs/nodes.py'),
727 params={
728 'content': "def py():\n print 'hello world'\n",
729 'message': 'i commited',
730 },
731 status=302)
732 self.checkSessionFlash(response,
733 'Successfully committed to vcs/nodes.py')
734 finally:
735 fixture.destroy_repo(repo.repo_name)
General Comments 0
You need to be logged in to leave comments. Login now