##// END OF EJS Templates
Added initial support for creating new nodes in repos
marcink -
r1483:7b67b0dc beta
parent child Browse files
Show More
@@ -0,0 +1,85 b''
1 <%inherit file="/base/base.html"/>
2
3 <%def name="title()">
4 ${c.repo_name} ${_('Edit file')} - ${c.rhodecode_name}
5 </%def>
6
7 <%def name="js_extra()">
8 <script type="text/javascript" src="${h.url('/js/codemirror.js')}"></script>
9 </%def>
10 <%def name="css_extra()">
11 <link rel="stylesheet" type="text/css" href="${h.url('/css/codemirror.css')}"/>
12 </%def>
13
14 <%def name="breadcrumbs_links()">
15 ${h.link_to(u'Home',h.url('/'))}
16 &raquo;
17 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
18 &raquo;
19 ${_('add file')} @ R${c.cs.revision}:${h.short_id(c.cs.raw_id)}
20 </%def>
21
22 <%def name="page_nav()">
23 ${self.menu('files')}
24 </%def>
25 <%def name="main()">
26 <div class="box">
27 <!-- box / title -->
28 <div class="title">
29 ${self.breadcrumbs()}
30 <ul class="links">
31 <li>
32 <span style="text-transform: uppercase;">
33 <a href="#">${_('branch')}: ${c.cs.branch}</a></span>
34 </li>
35 </ul>
36 </div>
37 <div class="table">
38 <div id="files_data">
39 ${h.form(h.url.current(),method='post',id='eform')}
40 <h3>${_('Add new file')}</h3>
41 <div class="form">
42 <div class="fields">
43 <div class="field">
44 <div class="label">
45 <label for="location">${_('Location')}</label>
46 </div>
47 <div class="input">
48 <input type="text" value="${c.f_path}" size="30" name="location" id="location">
49 </div>
50 </div>
51
52 <div class="field">
53 <div class="label">
54 <label for="filename">${_('File Name')}:</label>
55 </div>
56 <div class="input">
57 <input type="text" value="" size="30" name="filename" id="filename">
58 </div>
59 </div>
60 </div>
61 </div>
62 <div id="body" class="codeblock">
63 <pre id="editor_pre"></pre>
64 <textarea id="editor" name="content" style="display:none"></textarea>
65 <div style="padding-top: 10px;">${_('commit message')}</div>
66 <textarea id="commit" name="message" style="height: 100px;width: 99%"></textarea>
67 </div>
68 <div style="text-align: right;padding-top: 5px">
69 <input id="reset" type="button" value="${_('Reset')}" class="ui-button-small" />
70 ${h.submit('commit',_('Commit changes'),class_="ui-button-small-blue")}
71 </div>
72 ${h.end_form()}
73 <script type="text/javascript">
74 var myCodeMirror = CodeMirror.fromTextArea(YUD.get('editor'),{
75 mode: "null",
76 lineNumbers:true
77 });
78 YUE.on('reset','click',function(){
79 window.location="${h.url('files_home',repo_name=c.repo_name,revision=c.cs.revision,f_path=c.f_path)}";
80 })
81 </script>
82 </div>
83 </div>
84 </div>
85 </%def> No newline at end of file
@@ -378,6 +378,11 b' def make_map(config):'
378 controller='files', action='edit', revision='tip',
378 controller='files', action='edit', revision='tip',
379 f_path='', conditions=dict(function=check_repo))
379 f_path='', conditions=dict(function=check_repo))
380
380
381 rmap.connect('files_add_home',
382 '/{repo_name:.*}/add/{revision}/{f_path:.*}',
383 controller='files', action='add', revision='tip',
384 f_path='', conditions=dict(function=check_repo))
385
381 rmap.connect('files_archive_home', '/{repo_name:.*}/archive/{fname}',
386 rmap.connect('files_archive_home', '/{repo_name:.*}/archive/{fname}',
382 controller='files', action='archivefile',
387 controller='files', action='archivefile',
383 conditions=dict(function=check_repo))
388 conditions=dict(function=check_repo))
@@ -57,7 +57,7 b' class FilesController(BaseRepoController'
57 super(FilesController, self).__before__()
57 super(FilesController, self).__before__()
58 c.cut_off_limit = self.cut_off_limit
58 c.cut_off_limit = self.cut_off_limit
59
59
60 def __get_cs_or_redirect(self, rev, repo_name):
60 def __get_cs_or_redirect(self, rev, repo_name, redirect_after=True):
61 """
61 """
62 Safe way to get changeset if error occur it redirects to tip with
62 Safe way to get changeset if error occur it redirects to tip with
63 proper message
63 proper message
@@ -69,7 +69,14 b' class FilesController(BaseRepoController'
69 try:
69 try:
70 return c.rhodecode_repo.get_changeset(rev)
70 return c.rhodecode_repo.get_changeset(rev)
71 except EmptyRepositoryError, e:
71 except EmptyRepositoryError, e:
72 h.flash(_('There are no files yet'), category='warning')
72 if not redirect_after:
73 return None
74 url_ = url('files_add_home',
75 repo_name=c.repo_name,
76 revision=0,f_path='')
77 add_new = '<a href="%s">[%s]</a>' % (url_,_('add new'))
78 h.flash(h.literal(_('There are no files yet %s' % add_new)),
79 category='warning')
73 redirect(h.url('summary_home', repo_name=repo_name))
80 redirect(h.url('summary_home', repo_name=repo_name))
74
81
75 except RepositoryError, e:
82 except RepositoryError, e:
@@ -247,7 +254,6 b' class FilesController(BaseRepoController'
247 return redirect(url('files_home', repo_name=c.repo_name,
254 return redirect(url('files_home', repo_name=c.repo_name,
248 revision=c.cs.raw_id, f_path=f_path))
255 revision=c.cs.raw_id, f_path=f_path))
249
256
250 c.file_history = self._get_node_history(c.cs, f_path)
251 c.f_path = f_path
257 c.f_path = f_path
252
258
253 if r_post:
259 if r_post:
@@ -286,6 +292,49 b' class FilesController(BaseRepoController'
286
292
287 return render('files/files_edit.html')
293 return render('files/files_edit.html')
288
294
295 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
296 def add(self, repo_name, revision, f_path):
297 r_post = request.POST
298 c.cs = self.__get_cs_or_redirect(revision, repo_name,
299 redirect_after=False)
300 if c.cs is None:
301 c.cs = EmptyChangeset(alias=c.rhodecode_repo.alias)
302
303 c.f_path = f_path
304
305 if r_post:
306 unix_mode = 0
307 content = convert_line_endings(r_post.get('content'), unix_mode)
308
309 message = r_post.get('message') or (_('Added %s via RhodeCode')
310 % (f_path))
311 location = r_post.get('location')
312 filename = r_post.get('filename')
313 node_path = os.path.join(location, filename)
314 author = self.rhodecode_user.full_contact
315
316 if not content:
317 h.flash(_('No content'), category='warning')
318 return redirect(url('changeset_home', repo_name=c.repo_name,
319 revision='tip'))
320
321 try:
322 self.scm_model.create_node(repo=c.rhodecode_repo,
323 repo_name=repo_name, cs=c.cs,
324 user=self.rhodecode_user,
325 author=author, message=message,
326 content=content, f_path=node_path)
327 h.flash(_('Successfully committed to %s' % node_path),
328 category='success')
329
330 except Exception:
331 log.error(traceback.format_exc())
332 h.flash(_('Error occurred during commit'), category='error')
333 return redirect(url('changeset_home',
334 repo_name=c.repo_name, revision='tip'))
335
336 return render('files/files_add.html')
337
289 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
338 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
290 'repository.admin')
339 'repository.admin')
291 def archivefile(self, repo_name, fname):
340 def archivefile(self, repo_name, fname):
@@ -42,6 +42,7 b' from webhelpers.text import collapse, re'
42
42
43 from vcs.backends.base import BaseChangeset
43 from vcs.backends.base import BaseChangeset
44 from vcs.utils.lazy import LazyProperty
44 from vcs.utils.lazy import LazyProperty
45 from vcs import get_backend
45
46
46 from rhodecode.model import meta
47 from rhodecode.model import meta
47 from rhodecode.model.caching_query import FromCache
48 from rhodecode.model.caching_query import FromCache
@@ -313,7 +314,7 b' class EmptyChangeset(BaseChangeset):'
313 an EmptyChangeset
314 an EmptyChangeset
314 """
315 """
315
316
316 def __init__(self, cs='0' * 40, repo=None,requested_revision=None):
317 def __init__(self, cs='0' * 40, repo=None, requested_revision=None, alias=None):
317 self._empty_cs = cs
318 self._empty_cs = cs
318 self.revision = -1
319 self.revision = -1
319 self.message = ''
320 self.message = ''
@@ -321,7 +322,8 b' class EmptyChangeset(BaseChangeset):'
321 self.date = ''
322 self.date = ''
322 self.repository = repo
323 self.repository = repo
323 self.requested_revision = requested_revision
324 self.requested_revision = requested_revision
324
325 self.alias = alias
326
325 @LazyProperty
327 @LazyProperty
326 def raw_id(self):
328 def raw_id(self):
327 """Returns raw string identifying this changeset, useful for web
329 """Returns raw string identifying this changeset, useful for web
@@ -331,6 +333,10 b' class EmptyChangeset(BaseChangeset):'
331 return self._empty_cs
333 return self._empty_cs
332
334
333 @LazyProperty
335 @LazyProperty
336 def branch(self):
337 return get_backend(self.alias).DEFAULT_BRANCH_NAME
338
339 @LazyProperty
334 def short_id(self):
340 def short_id(self):
335 return self.raw_id[:12]
341 return self.raw_id[:12]
336
342
@@ -602,3 +608,4 b' class BasePasterCommand(Command):'
602 path_to_ini_file = os.path.realpath(conf)
608 path_to_ini_file = os.path.realpath(conf)
603 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
609 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
604 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
610 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
611
@@ -39,7 +39,7 b' from rhodecode.lib import helpers as h'
39 from rhodecode.lib import safe_str
39 from rhodecode.lib import safe_str
40 from rhodecode.lib.auth import HasRepoPermissionAny
40 from rhodecode.lib.auth import HasRepoPermissionAny
41 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
41 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
42 action_logger
42 action_logger, EmptyChangeset
43 from rhodecode.model import BaseModel
43 from rhodecode.model import BaseModel
44 from rhodecode.model.user import UserModel
44 from rhodecode.model.user import UserModel
45 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
45 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
@@ -352,6 +352,37 b' class ScmModel(BaseModel):'
352
352
353 self.mark_for_invalidation(repo_name)
353 self.mark_for_invalidation(repo_name)
354
354
355 def create_node(self, repo, repo_name, cs, user, author, message, content,
356 f_path):
357 if repo.alias == 'hg':
358 from vcs.backends.hg import MercurialInMemoryChangeset as IMC
359 elif repo.alias == 'git':
360 from vcs.backends.git import GitInMemoryChangeset as IMC
361 # decoding here will force that we have proper encoded values
362 # in any other case this will throw exceptions and deny commit
363 content = safe_str(content)
364 message = safe_str(message)
365 path = safe_str(f_path)
366 author = safe_str(author)
367 m = IMC(repo)
368
369 if isinstance(cs, EmptyChangeset):
370 # Emptychangeset means we we're editing empty repository
371 parents = None
372 else:
373 parents = [cs]
374
375 m.add(FileNode(path, content=content))
376 tip = m.commit(message=message,
377 author=author,
378 parents=parents, branch=cs.branch)
379 new_cs = tip.short_id
380 action = 'push_local:%s' % new_cs
381
382 action_logger(user, action, repo_name)
383
384 self.mark_for_invalidation(repo_name)
385
355
386
356 def get_unread_journal(self):
387 def get_unread_journal(self):
357 return self.sa.query(UserLog).count()
388 return self.sa.query(UserLog).count()
@@ -368,3 +399,4 b' class ScmModel(BaseModel):'
368 .scalar()
399 .scalar()
369
400
370 return ret
401 return ret
402
@@ -1867,9 +1867,21 b' font-weight:700;'
1867 div.browserblock .browser-search{
1867 div.browserblock .browser-search{
1868 clear:both;
1868 clear:both;
1869 padding:8px 8px 0px 5px;
1869 padding:8px 8px 0px 5px;
1870 }
1870 height: 20px;
1871
1871 }
1872 div.browserblock .search_activate #filter_activate{
1872 div.browserblock #node_filter_box {
1873 }
1874
1875 div.browserblock .search_activate{
1876 float: left
1877 }
1878
1879 div.browserblock .add_node{
1880 float: left;
1881 padding-left: 5px;
1882 }
1883
1884 div.browserblock .search_activate #filter_activate,div.browserblock .add_node a{
1873 vertical-align: sub;
1885 vertical-align: sub;
1874 border: 1px solid;
1886 border: 1px solid;
1875 padding:2px;
1887 padding:2px;
@@ -1882,7 +1894,7 b' div.browserblock .search_activate #filte'
1882 color: #515151;
1894 color: #515151;
1883 }
1895 }
1884
1896
1885 div.browserblock .search_activate a:hover{
1897 div.browserblock .search_activate a:hover,div.browserblock .add_node a:hover{
1886 text-decoration: none !important;
1898 text-decoration: none !important;
1887 }
1899 }
1888
1900
@@ -23,11 +23,12 b''
23 <label>${_('follow current branch')}</label>
23 <label>${_('follow current branch')}</label>
24 </div>
24 </div>
25 <div class="browser-search">
25 <div class="browser-search">
26 <div class="search_activate">
26 <div id="search_activate_id" class="search_activate">
27 <a id="filter_activate" href="#">${_('search file list')}</a>
27 <a id="filter_activate" href="#">${_('search file list')}</a>
28 </div>
28 </div>
29
29 <div id="add_node_id" class="add_node">
30
30 <a href="${h.url('files_add_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path)}">${_('add new file')}</a>
31 </div>
31 <div>
32 <div>
32 <div id="node_filter_box_loading" style="display:none">${_('Loading file list...')}</div>
33 <div id="node_filter_box_loading" style="display:none">${_('Loading file list...')}</div>
33 <div id="node_filter_box" style="display:none">
34 <div id="node_filter_box" style="display:none">
@@ -67,7 +68,8 b''
67
68
68 F.initFilter = function(){
69 F.initFilter = function(){
69 YUD.setStyle('node_filter_box_loading','display','');
70 YUD.setStyle('node_filter_box_loading','display','');
70 YUD.setStyle('filter_activate','display','none');
71 YUD.setStyle('search_activate_id','display','none');
72 YUD.setStyle('add_node_id','display','none');
71 YUC.initHeader('X-PARTIAL-XHR',true);
73 YUC.initHeader('X-PARTIAL-XHR',true);
72 YUC.asyncRequest('GET',url,{
74 YUC.asyncRequest('GET',url,{
73 success:function(o){
75 success:function(o){
General Comments 0
You need to be logged in to leave comments. Login now