changeset 1483:7b67b0dcad6d beta

Added initial support for creating new nodes in repos
author Marcin Kuzminski <marcin@python-works.com>
date Thu, 22 Sep 2011 04:33:29 +0300
parents a39c0e5fca89
children 1db451a44504
files rhodecode/config/routing.py rhodecode/controllers/files.py rhodecode/lib/utils.py rhodecode/model/scm.py rhodecode/public/css/style.css rhodecode/templates/files/files_add.html rhodecode/templates/files/files_browser.html
diffstat 7 files changed, 208 insertions(+), 16 deletions(-) [+]
line wrap: on
line diff
--- a/rhodecode/config/routing.py	Thu Sep 22 03:08:02 2011 +0300
+++ b/rhodecode/config/routing.py	Thu Sep 22 04:33:29 2011 +0300
@@ -378,6 +378,11 @@
                  controller='files', action='edit', revision='tip',
                  f_path='', conditions=dict(function=check_repo))
 
+    rmap.connect('files_add_home',
+                 '/{repo_name:.*}/add/{revision}/{f_path:.*}',
+                 controller='files', action='add', revision='tip',
+                 f_path='', conditions=dict(function=check_repo))
+
     rmap.connect('files_archive_home', '/{repo_name:.*}/archive/{fname}',
                 controller='files', action='archivefile',
                 conditions=dict(function=check_repo))
--- a/rhodecode/controllers/files.py	Thu Sep 22 03:08:02 2011 +0300
+++ b/rhodecode/controllers/files.py	Thu Sep 22 04:33:29 2011 +0300
@@ -57,7 +57,7 @@
         super(FilesController, self).__before__()
         c.cut_off_limit = self.cut_off_limit
 
-    def __get_cs_or_redirect(self, rev, repo_name):
+    def __get_cs_or_redirect(self, rev, repo_name, redirect_after=True):
         """
         Safe way to get changeset if error occur it redirects to tip with
         proper message
@@ -69,7 +69,14 @@
         try:
             return c.rhodecode_repo.get_changeset(rev)
         except EmptyRepositoryError, e:
-            h.flash(_('There are no files yet'), category='warning')
+            if not redirect_after:
+                return None
+            url_ = url('files_add_home',
+                       repo_name=c.repo_name,
+                       revision=0,f_path='')
+            add_new = '<a href="%s">[%s]</a>' % (url_,_('add new'))
+            h.flash(h.literal(_('There are no files yet %s' % add_new)), 
+                    category='warning')
             redirect(h.url('summary_home', repo_name=repo_name))
 
         except RepositoryError, e:
@@ -247,7 +254,6 @@
             return redirect(url('files_home', repo_name=c.repo_name,
                          revision=c.cs.raw_id, f_path=f_path))
 
-        c.file_history = self._get_node_history(c.cs, f_path)
         c.f_path = f_path
 
         if r_post:
@@ -286,6 +292,49 @@
 
         return render('files/files_edit.html')
 
+    @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
+    def add(self, repo_name, revision, f_path):
+        r_post = request.POST
+        c.cs = self.__get_cs_or_redirect(revision, repo_name, 
+                                         redirect_after=False)
+        if c.cs is None:
+            c.cs = EmptyChangeset(alias=c.rhodecode_repo.alias)
+
+        c.f_path = f_path
+
+        if r_post:
+            unix_mode = 0
+            content = convert_line_endings(r_post.get('content'), unix_mode)
+
+            message = r_post.get('message') or (_('Added %s via RhodeCode')
+                                                % (f_path))
+            location = r_post.get('location')
+            filename = r_post.get('filename')
+            node_path = os.path.join(location, filename)
+            author = self.rhodecode_user.full_contact
+
+            if not content:
+                h.flash(_('No content'), category='warning')
+                return redirect(url('changeset_home', repo_name=c.repo_name,
+                                    revision='tip'))
+
+            try:
+                self.scm_model.create_node(repo=c.rhodecode_repo,
+                                             repo_name=repo_name, cs=c.cs,
+                                             user=self.rhodecode_user,
+                                             author=author, message=message,
+                                             content=content, f_path=node_path)
+                h.flash(_('Successfully committed to %s' % node_path),
+                        category='success')
+
+            except Exception:
+                log.error(traceback.format_exc())
+                h.flash(_('Error occurred during commit'), category='error')
+            return redirect(url('changeset_home',
+                                repo_name=c.repo_name, revision='tip'))
+
+        return render('files/files_add.html')
+
     @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
                                    'repository.admin')
     def archivefile(self, repo_name, fname):
--- a/rhodecode/lib/utils.py	Thu Sep 22 03:08:02 2011 +0300
+++ b/rhodecode/lib/utils.py	Thu Sep 22 04:33:29 2011 +0300
@@ -42,6 +42,7 @@
 
 from vcs.backends.base import BaseChangeset
 from vcs.utils.lazy import LazyProperty
+from vcs import get_backend
 
 from rhodecode.model import meta
 from rhodecode.model.caching_query import FromCache
@@ -313,7 +314,7 @@
     an EmptyChangeset
     """
 
-    def __init__(self, cs='0' * 40, repo=None,requested_revision=None):
+    def __init__(self, cs='0' * 40, repo=None, requested_revision=None, alias=None):
         self._empty_cs = cs
         self.revision = -1
         self.message = ''
@@ -321,7 +322,8 @@
         self.date = ''
         self.repository = repo
         self.requested_revision = requested_revision
-        
+        self.alias = alias
+
     @LazyProperty
     def raw_id(self):
         """Returns raw string identifying this changeset, useful for web
@@ -331,6 +333,10 @@
         return self._empty_cs
 
     @LazyProperty
+    def branch(self):
+        return get_backend(self.alias).DEFAULT_BRANCH_NAME
+
+    @LazyProperty
     def short_id(self):
         return self.raw_id[:12]
 
@@ -602,3 +608,4 @@
         path_to_ini_file = os.path.realpath(conf)
         conf = paste.deploy.appconfig('config:' + path_to_ini_file)
         pylonsconfig.init_app(conf.global_conf, conf.local_conf)
+
--- a/rhodecode/model/scm.py	Thu Sep 22 03:08:02 2011 +0300
+++ b/rhodecode/model/scm.py	Thu Sep 22 04:33:29 2011 +0300
@@ -39,7 +39,7 @@
 from rhodecode.lib import safe_str
 from rhodecode.lib.auth import HasRepoPermissionAny
 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
-    action_logger
+    action_logger, EmptyChangeset
 from rhodecode.model import BaseModel
 from rhodecode.model.user import UserModel
 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
@@ -352,6 +352,37 @@
 
         self.mark_for_invalidation(repo_name)
 
+    def create_node(self, repo, repo_name, cs, user, author, message, content,
+                      f_path):
+        if repo.alias == 'hg':
+            from vcs.backends.hg import MercurialInMemoryChangeset as IMC
+        elif repo.alias == 'git':
+            from vcs.backends.git import GitInMemoryChangeset as IMC
+        # decoding here will force that we have proper encoded values
+        # in any other case this will throw exceptions and deny commit
+        content = safe_str(content)
+        message = safe_str(message)
+        path = safe_str(f_path)
+        author = safe_str(author)
+        m = IMC(repo)
+
+        if isinstance(cs, EmptyChangeset):
+            # Emptychangeset means we we're editing empty repository
+            parents = None
+        else:
+            parents = [cs]
+
+        m.add(FileNode(path, content=content))
+        tip = m.commit(message=message,
+                 author=author,
+                 parents=parents, branch=cs.branch)
+        new_cs = tip.short_id
+        action = 'push_local:%s' % new_cs
+
+        action_logger(user, action, repo_name)
+
+        self.mark_for_invalidation(repo_name)
+
 
     def get_unread_journal(self):
         return self.sa.query(UserLog).count()
@@ -368,3 +399,4 @@
             .scalar()
 
         return ret
+
--- a/rhodecode/public/css/style.css	Thu Sep 22 03:08:02 2011 +0300
+++ b/rhodecode/public/css/style.css	Thu Sep 22 04:33:29 2011 +0300
@@ -1867,9 +1867,21 @@
 div.browserblock .browser-search{
 	clear:both;
 	padding:8px 8px 0px 5px;
-}
-
-div.browserblock .search_activate #filter_activate{
+	height: 20px;
+}
+div.browserblock #node_filter_box {
+}
+
+div.browserblock .search_activate{
+    float: left
+}
+
+div.browserblock .add_node{
+    float: left;
+    padding-left: 5px;
+}
+
+div.browserblock .search_activate #filter_activate,div.browserblock .add_node a{
 	vertical-align: sub;
 	border: 1px solid;
 	padding:2px;
@@ -1882,7 +1894,7 @@
 	color: #515151;
 }
 
-div.browserblock .search_activate a:hover{
+div.browserblock .search_activate a:hover,div.browserblock .add_node a:hover{
     text-decoration: none !important;    
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rhodecode/templates/files/files_add.html	Thu Sep 22 04:33:29 2011 +0300
@@ -0,0 +1,85 @@
+<%inherit file="/base/base.html"/>
+
+<%def name="title()">
+    ${c.repo_name} ${_('Edit file')} - ${c.rhodecode_name}
+</%def>
+
+<%def name="js_extra()">
+<script type="text/javascript" src="${h.url('/js/codemirror.js')}"></script>
+</%def>
+<%def name="css_extra()">
+<link rel="stylesheet" type="text/css" href="${h.url('/css/codemirror.css')}"/>
+</%def>
+
+<%def name="breadcrumbs_links()">
+    ${h.link_to(u'Home',h.url('/'))}
+    &raquo;
+    ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
+    &raquo;
+    ${_('add file')} @ R${c.cs.revision}:${h.short_id(c.cs.raw_id)}
+</%def>
+
+<%def name="page_nav()">
+		${self.menu('files')}     
+</%def>
+<%def name="main()">
+<div class="box">
+    <!-- box / title -->
+    <div class="title">
+        ${self.breadcrumbs()}
+        <ul class="links">
+            <li>
+              <span style="text-transform: uppercase;">
+              <a href="#">${_('branch')}: ${c.cs.branch}</a></span>
+            </li>          
+        </ul>          
+    </div>
+    <div class="table">
+		<div id="files_data">
+		  ${h.form(h.url.current(),method='post',id='eform')}
+            <h3>${_('Add new file')}</h3>
+            <div class="form">
+                    <div class="fields">
+                         <div class="field">
+                            <div class="label">
+                                <label for="location">${_('Location')}</label>
+                            </div>
+                            <div class="input">
+                                <input type="text" value="${c.f_path}" size="30" name="location" id="location">
+                            </div>
+                         </div>
+                                      
+                        <div class="field">
+                            <div class="label">
+                                <label for="filename">${_('File Name')}:</label>
+                            </div>
+                            <div class="input">
+                                <input type="text" value="" size="30" name="filename" id="filename">
+                            </div>
+                        </div>                                                    
+                    </div>
+            </div>            
+			<div id="body" class="codeblock">
+			    <pre id="editor_pre"></pre>
+				<textarea id="editor" name="content" style="display:none"></textarea>
+				<div style="padding-top: 10px;">${_('commit message')}</div>
+				<textarea id="commit" name="message" style="height: 100px;width: 99%"></textarea>
+			</div>
+			<div style="text-align: right;padding-top: 5px">
+			<input id="reset" type="button" value="${_('Reset')}" class="ui-button-small" />
+			${h.submit('commit',_('Commit changes'),class_="ui-button-small-blue")}
+			</div>
+			${h.end_form()}
+			<script type="text/javascript">
+			 var myCodeMirror = CodeMirror.fromTextArea(YUD.get('editor'),{
+	                mode:  "null",
+	                lineNumbers:true
+	              });
+			 YUE.on('reset','click',function(){
+				 window.location="${h.url('files_home',repo_name=c.repo_name,revision=c.cs.revision,f_path=c.f_path)}";
+			 })
+			</script>
+		</div>    
+    </div>
+</div>    
+</%def>   
\ No newline at end of file
--- a/rhodecode/templates/files/files_browser.html	Thu Sep 22 03:08:02 2011 +0300
+++ b/rhodecode/templates/files/files_browser.html	Thu Sep 22 04:33:29 2011 +0300
@@ -23,11 +23,12 @@
 	       <label>${_('follow current branch')}</label>
 	    </div>
         <div class="browser-search">
-            <div class="search_activate">
-                <a id="filter_activate" href="#">${_('search file list')}</a>
-            </div>
-        
-            
+              <div id="search_activate_id" class="search_activate">
+                  <a id="filter_activate" href="#">${_('search file list')}</a>
+              </div>
+              <div  id="add_node_id" class="add_node">
+                  <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>
+              </div>        
         <div>
             <div id="node_filter_box_loading" style="display:none">${_('Loading file list...')}</div>
             <div id="node_filter_box" style="display:none">
@@ -67,7 +68,8 @@
             
             F.initFilter = function(){
               YUD.setStyle('node_filter_box_loading','display','');
-              YUD.setStyle('filter_activate','display','none');
+              YUD.setStyle('search_activate_id','display','none');
+              YUD.setStyle('add_node_id','display','none');
               YUC.initHeader('X-PARTIAL-XHR',true);
               YUC.asyncRequest('GET',url,{
                   success:function(o){