Changeset - 3d6d548ad3cc
[Not reviewed]
default
0 4 0
Marcin Kuzminski - 15 years ago 2010-09-24 18:13:29
marcin@python-works.com
Added user action mapper to map push to changeset.
made exception in simplehg python 2.5 ready
4 files changed with 43 insertions and 6 deletions:
0 comments (0 inline, 0 general)
pylons_app/lib/hooks.py
Show inline comments
 
@@ -5,44 +5,74 @@
 
#
 
# This program is free software; you can redistribute it and/or
 
# modify it under the terms of the GNU General Public License
 
# as published by the Free Software Foundation; version 2
 
# of the License or (at your opinion) any later version of the license.
 
# 
 
# This program is distributed in the hope that it will be useful,
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
# GNU General Public License for more details.
 
# 
 
# You should have received a copy of the GNU General Public License
 
# along with this program; if not, write to the Free Software
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 
# MA  02110-1301, USA.
 
"""
 
Created on Aug 6, 2010
 

	
 
@author: marcink
 
"""
 

	
 
import sys
 
import os
 
from pylons_app.lib import helpers as h
 
from pylons_app.model import meta
 
from pylons_app.model.db import UserLog, User
 

	
 
def repo_size(ui, repo, hooktype=None, **kwargs):
 

	
 
    if hooktype != 'changegroup':
 
        return False
 
    
 
    size_hg, size_root = 0, 0
 
    for path, dirs, files in os.walk(repo.root):
 
        if path.find('.hg') != -1:
 
            for f in files:
 
                size_hg += os.path.getsize(os.path.join(path, f))
 
        else:
 
            for f in files:
 
                size_root += os.path.getsize(os.path.join(path, f))
 
                
 
    size_hg_f = h.format_byte_size(size_hg)
 
    size_root_f = h.format_byte_size(size_root)
 
    size_total_f = h.format_byte_size(size_root + size_hg)                            
 
    size_total_f = h.format_byte_size(size_root + size_hg)
 
    sys.stdout.write('Repository size .hg:%s repo:%s total:%s\n' \
 
                     % (size_hg_f, size_root_f, size_total_f))
 
    
 
    user_action_mapper(ui, repo, hooktype, **kwargs)
 

	
 
def user_action_mapper(ui, repo, hooktype=None, **kwargs):
 
    """
 
    Maps user last push action to new changeset id, from mercurial
 
    @param ui:
 
    @param repo:
 
    @param hooktype:
 
    """
 
    
 
    try:
 
        sa = meta.Session
 
        username = kwargs['url'].split(':')[-1]
 
        user_log = sa.query(UserLog)\
 
            .filter(UserLog.user == sa.query(User)\
 
                                        .filter(User.username == username).one())\
 
            .order_by(UserLog.user_log_id.desc()).first()
 
            
 
        if not user_log.revision:
 
            user_log.revision = str(repo['tip'])
 
            sa.add(user_log)
 
            sa.commit()
 
        
 
    except Exception, e:
 
        sa.rollback()
 
        raise
 
    finally:
 
        meta.Session.remove()    
pylons_app/lib/middleware/simplehg.py
Show inline comments
 
@@ -100,49 +100,49 @@ class SimpleHg(object):
 
                if not HasPermissionAnyMiddleware('repository.write',
 
                                                  'repository.admin')\
 
                                                    (user, repo_name):
 
                    return HTTPForbidden()(environ, start_response)
 
            
 
            #log action    
 
            proxy_key = 'HTTP_X_REAL_IP'
 
            def_key = 'REMOTE_ADDR'
 
            ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
 
            self.__log_user_action(user, action, repo_name, ipaddr)            
 
        
 
        #===================================================================
 
        # MERCURIAL REQUEST HANDLING
 
        #===================================================================
 
        environ['PATH_INFO'] = '/'#since we wrap into hgweb, reset the path
 
        self.baseui = make_ui('db')
 
        self.basepath = self.config['base_path']
 
        self.repo_path = os.path.join(self.basepath, repo_name)
 

	
 
        #quick check if that dir exists...
 
        if check_repo_fast(repo_name, self.basepath):
 
            return HTTPNotFound()(environ, start_response)
 
        try:
 
            app = wsgiapplication(self.__make_app)
 
        except RepoError as e:
 
        except RepoError, e:
 
            if str(e).find('not found') != -1:
 
                return HTTPNotFound()(environ, start_response)
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            return HTTPInternalServerError()(environ, start_response)
 
        
 
        #invalidate cache on push
 
        if action == 'push':
 
            self.__invalidate_cache(repo_name)
 
            messages = []
 
            messages.append('thank you for using hg-app')
 
        
 
            return self.msg_wrapper(app, environ, start_response, messages)
 
        else:
 
            return app(environ, start_response)           
 

	
 

	
 
    def msg_wrapper(self, app, environ, start_response, messages=[]):
 
        """
 
        Wrapper for custom messages that come out of mercurial respond messages
 
        is a list of messages that the user will see at the end of response 
 
        from merurial protocol actions that involves remote answers
 
        @param app:
 
        @param environ:
 
@@ -174,49 +174,49 @@ class SimpleHg(object):
 
                   'stream_out': 'pull',
 
                   'listkeys': 'pull',
 
                   'unbundle': 'push',
 
                   'pushkey': 'push', }
 
        
 
        for qry in environ['QUERY_STRING'].split('&'):
 
            if qry.startswith('cmd'):
 
                cmd = qry.split('=')[-1]
 
                if mapping.has_key(cmd):
 
                    return mapping[cmd]
 
    
 
    def __log_user_action(self, user, action, repo, ipaddr):
 
        sa = meta.Session
 
        try:
 
            user_log = UserLog()
 
            user_log.user_id = user.user_id
 
            user_log.action = action
 
            user_log.repository = repo.replace('/', '')
 
            user_log.action_date = datetime.now()
 
            user_log.user_ip = ipaddr
 
            sa.add(user_log)
 
            sa.commit()
 
            log.info('Adding user %s, action %s on %s',
 
                                            user.username, action, repo)
 
        except Exception as e:
 
        except Exception, e:
 
            sa.rollback()
 
            log.error('could not log user action:%s', str(e))
 
        finally:
 
            meta.Session.remove()
 
        
 
    def __invalidate_cache(self, repo_name):
 
        """we know that some change was made to repositories and we should
 
        invalidate the cache to see the changes right away but only for
 
        push requests"""
 
        invalidate_cache('cached_repo_list')
 
        invalidate_cache('full_changelog', repo_name)
 
           
 
                   
 
    def __load_web_settings(self, hgserve):
 
        #set the global ui for hgserve
 
        hgserve.repo.ui = self.baseui
 
        
 
        hgrc = os.path.join(self.repo_path, '.hg', 'hgrc')
 
        repoui = make_ui('file', hgrc, False)
 
        
 
        
 
        if repoui:
 
            #overwrite our ui instance with the section from hgrc file
 
            for section in ui_sections:
pylons_app/model/db.py
Show inline comments
 
@@ -49,49 +49,49 @@ class User(Base):
 
    
 
    def update_lastlogin(self):
 
        """Update user lastlogin"""
 
        import datetime
 
        
 
        try:
 
            session = Session.object_session(self)
 
            self.last_login = datetime.datetime.now()
 
            session.add(self)
 
            session.commit()
 
            log.debug('updated user %s lastlogin', self.username)
 
        except Exception:
 
            session.rollback()        
 
    
 
      
 
class UserLog(Base): 
 
    __tablename__ = 'user_logs'
 
    __table_args__ = {'useexisting':True}
 
    user_log_id = Column("user_log_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
 
    user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
 
    user_ip = Column("user_ip", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 
 
    repository = Column("repository", TEXT(length=None, convert_unicode=False, assert_unicode=None), ForeignKey(u'repositories.repo_name'), nullable=False, unique=None, default=None)
 
    action = Column("action", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    action_date = Column("action_date", DATETIME(timezone=False), nullable=True, unique=None, default=None)
 
    
 
    revision = Column('revision', TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    user = relation('User')
 
    
 
class Repository(Base):
 
    __tablename__ = 'repositories'
 
    __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
 
    repo_id = Column("repo_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
 
    repo_name = Column("repo_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
 
    user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=False, default=None)
 
    private = Column("private", BOOLEAN(), nullable=True, unique=None, default=None)
 
    description = Column("description", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    
 
    user = relation('User')
 
    repo_to_perm = relation('RepoToPerm', cascade='all')
 
    
 
    def __repr__(self):
 
        return "<Repository('id:%s:%s')>" % (self.repo_id, self.repo_name)
 
        
 
class Permission(Base):
 
    __tablename__ = 'permissions'
 
    __table_args__ = {'useexisting':True}
 
    permission_id = Column("permission_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
 
    permission_name = Column("permission_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    permission_longname = Column("permission_longname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    
pylons_app/templates/admin/admin_log.html
Show inline comments
 
## -*- coding: utf-8 -*-
 
%if c.users_log:
 
<table>
 
	<tr>
 
		<th class="left">${_('Username')}</th>
 
		<th class="left">${_('Repository')}</th>
 
		<th class="left">${_('Action')}</th>
 
		<th class="left">${_('Date')}</th>
 
		<th class="left">${_('From IP')}</th>
 
	</tr>
 

	
 
	%for cnt,l in enumerate(c.users_log):
 
	<tr class="parity${cnt%2}">
 
		<td>${h.link_to(l.user.username,h.url('edit_user', id=l.user.user_id))}</td>
 
		<td>${h.link_to(l.repository,h.url('summary_home',repo_name=l.repository))}</td>
 
		<td>${l.action}</td>
 
		<td>
 
		% if l.action == 'push' and l.revision:
 
		  ${h.link_to('%s - %s' % (l.action,l.revision),
 
		  h.url('changeset_home',repo_name=l.repository,revision=l.revision))}
 
		%else:
 
		  ${l.action}
 
		%endif
 
		</td>
 
		<td>${l.action_date}</td>
 
		<td>${l.user_ip}</td>
 
	</tr>
 
	%endfor
 
</table>
 

	
 
<script type="text/javascript">
 
  var data_div = 'user_log';
 
  YAHOO.util.Event.onDOMReady(function(){
 
	YAHOO.util.Event.addListener(YAHOO.util.Dom.getElementsByClassName('pager_link'),"click",function(){
 
			YAHOO.util.Dom.setStyle('shortlog_data','opacity','0.3');});});
 
</script>
 
		
 
		
 
<div class="pagination-wh pagination-left">
 
${c.users_log.pager('$link_previous ~2~ $link_next',
 
onclick="""YAHOO.util.Connect.asyncRequest('GET','$partial_url',{
 
success:function(o){YAHOO.util.Dom.get(data_div).innerHTML=o.responseText;
 
YAHOO.util.Event.addListener(YAHOO.util.Dom.getElementsByClassName('pager_link'),"click",function(){
 
		YAHOO.util.Dom.setStyle(data_div,'opacity','0.3');});		
 
YAHOO.util.Dom.setStyle(data_div,'opacity','1');}},null); return false;""")}
 
</div>
 
%else: 
 
	${_('No actions yet')} 
0 comments (0 inline, 0 general)