Changeset - 3782a6d698af
COPYING
Show inline comments
 
@@ -75,266 +75,266 @@ running the Program is not restricted, a
 
is covered only if its contents constitute a work based on the
 
Program (independent of having been made by running the Program).
 
Whether that is true depends on what the Program does.
 

	
 
  1. You may copy and distribute verbatim copies of the Program's
 
source code as you receive it, in any medium, provided that you
 
conspicuously and appropriately publish on each copy an appropriate
 
copyright notice and disclaimer of warranty; keep intact all the
 
notices that refer to this License and to the absence of any warranty;
 
and give any other recipients of the Program a copy of this License
 
along with the Program.
 

	
 
You may charge a fee for the physical act of transferring a copy, and
 
you may at your option offer warranty protection in exchange for a fee.
 

	
 
  2. You may modify your copy or copies of the Program or any portion
 
of it, thus forming a work based on the Program, and copy and
 
distribute such modifications or work under the terms of Section 1
 
above, provided that you also meet all of these conditions:
 

	
 
    a) You must cause the modified files to carry prominent notices
 
    stating that you changed the files and the date of any change.
 

	
 
    b) You must cause any work that you distribute or publish, that in
 
    whole or in part contains or is derived from the Program or any
 
    part thereof, to be licensed as a whole at no charge to all third
 
    parties under the terms of this License.
 

	
 
    c) If the modified program normally reads commands interactively
 
    when run, you must cause it, when started running for such
 
    interactive use in the most ordinary way, to print or display an
 
    announcement including an appropriate copyright notice and a
 
    notice that there is no warranty (or else, saying that you provide
 
    a warranty) and that users may redistribute the program under
 
    these conditions, and telling the user how to view a copy of this
 
    License.  (Exception: if the Program itself is interactive but
 
    does not normally print such an announcement, your work based on
 
    the Program is not required to print an announcement.)
 

	
 
These requirements apply to the modified work as a whole.  If
 
identifiable sections of that work are not derived from the Program,
 
and can be reasonably considered independent and separate works in
 
themselves, then this License, and its terms, do not apply to those
 
sections when you distribute them as separate works.  But when you
 
distribute the same sections as part of a whole which is a work based
 
on the Program, the distribution of the whole must be on the terms of
 
this License, whose permissions for other licensees extend to the
 
entire whole, and thus to each and every part regardless of who wrote it.
 

	
 
Thus, it is not the intent of this section to claim rights or contest
 
your rights to work written entirely by you; rather, the intent is to
 
exercise the right to control the distribution of derivative or
 
collective works based on the Program.
 

	
 
In addition, mere aggregation of another work not based on the Program
 
with the Program (or with a work based on the Program) on a volume of
 
a storage or distribution medium does not bring the other work under
 
the scope of this License.
 

	
 
  3. You may copy and distribute the Program (or a work based on it,
 
under Section 2) in object code or executable form under the terms of
 
Sections 1 and 2 above provided that you also do one of the following:
 

	
 
    a) Accompany it with the complete corresponding machine-readable
 
    source code, which must be distributed under the terms of Sections
 
    1 and 2 above on a medium customarily used for software interchange; or,
 

	
 
    b) Accompany it with a written offer, valid for at least three
 
    years, to give any third party, for a charge no more than your
 
    cost of physically performing source distribution, a complete
 
    machine-readable copy of the corresponding source code, to be
 
    distributed under the terms of Sections 1 and 2 above on a medium
 
    customarily used for software interchange; or,
 

	
 
    c) Accompany it with the information you received as to the offer
 
    to distribute corresponding source code.  (This alternative is
 
    allowed only for noncommercial distribution and only if you
 
    received the program in object code or executable form with such
 
    an offer, in accord with Subsection b above.)
 

	
 
The source code for a work means the preferred form of the work for
 
making modifications to it.  For an executable work, complete source
 
code means all the source code for all modules it contains, plus any
 
associated interface definition files, plus the scripts used to
 
control compilation and installation of the executable.  However, as a
 
special exception, the source code distributed need not include
 
anything that is normally distributed (in either source or binary
 
form) with the major components (compiler, kernel, and so on) of the
 
operating system on which the executable runs, unless that component
 
itself accompanies the executable.
 

	
 
If distribution of executable or object code is made by offering
 
access to copy from a designated place, then offering equivalent
 
access to copy the source code from the same place counts as
 
distribution of the source code, even though third parties are not
 
compelled to copy the source along with the object code.
 

 

	
 
  4. You may not copy, modify, sublicense, or distribute the Program
 
except as expressly provided under this License.  Any attempt
 
otherwise to copy, modify, sublicense or distribute the Program is
 
void, and will automatically terminate your rights under this License.
 
However, parties who have received copies, or rights, from you under
 
this License will not have their licenses terminated so long as such
 
parties remain in full compliance.
 

	
 
  5. You are not required to accept this License, since you have not
 
signed it.  However, nothing else grants you permission to modify or
 
distribute the Program or its derivative works.  These actions are
 
prohibited by law if you do not accept this License.  Therefore, by
 
modifying or distributing the Program (or any work based on the
 
Program), you indicate your acceptance of this License to do so, and
 
all its terms and conditions for copying, distributing or modifying
 
the Program or works based on it.
 

	
 
  6. Each time you redistribute the Program (or any work based on the
 
Program), the recipient automatically receives a license from the
 
original licensor to copy, distribute or modify the Program subject to
 
these terms and conditions.  You may not impose any further
 
restrictions on the recipients' exercise of the rights granted herein.
 
You are not responsible for enforcing compliance by third parties to
 
this License.
 

	
 
  7. If, as a consequence of a court judgment or allegation of patent
 
infringement or for any other reason (not limited to patent issues),
 
conditions are imposed on you (whether by court order, agreement or
 
otherwise) that contradict the conditions of this License, they do not
 
excuse you from the conditions of this License.  If you cannot
 
distribute so as to satisfy simultaneously your obligations under this
 
License and any other pertinent obligations, then as a consequence you
 
may not distribute the Program at all.  For example, if a patent
 
license would not permit royalty-free redistribution of the Program by
 
all those who receive copies directly or indirectly through you, then
 
the only way you could satisfy both it and this License would be to
 
refrain entirely from distribution of the Program.
 

	
 
If any portion of this section is held invalid or unenforceable under
 
any particular circumstance, the balance of the section is intended to
 
apply and the section as a whole is intended to apply in other
 
circumstances.
 

	
 
It is not the purpose of this section to induce you to infringe any
 
patents or other property right claims or to contest validity of any
 
such claims; this section has the sole purpose of protecting the
 
integrity of the free software distribution system, which is
 
implemented by public license practices.  Many people have made
 
generous contributions to the wide range of software distributed
 
through that system in reliance on consistent application of that
 
system; it is up to the author/donor to decide if he or she is willing
 
to distribute software through any other system and a licensee cannot
 
impose that choice.
 

	
 
This section is intended to make thoroughly clear what is believed to
 
be a consequence of the rest of this License.
 

	
 
  8. If the distribution and/or use of the Program is restricted in
 
certain countries either by patents or by copyrighted interfaces, the
 
original copyright holder who places the Program under this License
 
may add an explicit geographical distribution limitation excluding
 
those countries, so that distribution is permitted only in or among
 
countries not thus excluded.  In such case, this License incorporates
 
the limitation as if written in the body of this License.
 

	
 
  9. The Free Software Foundation may publish revised and/or new versions
 
of the General Public License from time to time.  Such new versions will
 
be similar in spirit to the present version, but may differ in detail to
 
address new problems or concerns.
 

	
 
Each version is given a distinguishing version number.  If the Program
 
specifies a version number of this License which applies to it and "any
 
later version", you have the option of following the terms and conditions
 
either of that version or of any later version published by the Free
 
Software Foundation.  If the Program does not specify a version number of
 
this License, you may choose any version ever published by the Free Software
 
Foundation.
 

	
 
  10. If you wish to incorporate parts of the Program into other free
 
programs whose distribution conditions are different, write to the author
 
to ask for permission.  For software which is copyrighted by the Free
 
Software Foundation, write to the Free Software Foundation; we sometimes
 
make exceptions for this.  Our decision will be guided by the two goals
 
of preserving the free status of all derivatives of our free software and
 
of promoting the sharing and reuse of software generally.
 

	
 
			    NO WARRANTY
 

	
 
  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
 
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
 
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
 
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
 
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
 
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
 
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
 
REPAIR OR CORRECTION.
 

	
 
  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
 
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
 
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
 
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
 
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
 
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
 
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
 
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
 
POSSIBILITY OF SUCH DAMAGES.
 

	
 
		     END OF TERMS AND CONDITIONS
 

 

	
 
	    How to Apply These Terms to Your New Programs
 

	
 
  If you develop a new program, and you want it to be of the greatest
 
possible use to the public, the best way to achieve this is to make it
 
free software which everyone can redistribute and change under these terms.
 

	
 
  To do so, attach the following notices to the program.  It is safest
 
to attach them to the start of each source file to most effectively
 
convey the exclusion of warranty; and each file should have at least
 
the "copyright" line and a pointer to where the full notice is found.
 

	
 
    <one line to give the program's name and a brief idea of what it does.>
 
    Copyright (C) <year>  <name of author>
 

	
 
    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; either version 2 of the License, or
 
    (at your option) any later version.
 

	
 
    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 

	
 

	
 
Also add information on how to contact you by electronic and paper mail.
 

	
 
If the program is interactive, make it output a short notice like this
 
when it starts in an interactive mode:
 

	
 
    Gnomovision version 69, Copyright (C) year  name of author
 
    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
 
    This is free software, and you are welcome to redistribute it
 
    under certain conditions; type `show c' for details.
 

	
 
The hypothetical commands `show w' and `show c' should show the appropriate
 
parts of the General Public License.  Of course, the commands you use may
 
be called something other than `show w' and `show c'; they could even be
 
mouse-clicks or menu items--whatever suits your program.
 

	
 
You should also get your employer (if you work as a programmer) or your
 
school, if any, to sign a "copyright disclaimer" for the program, if
 
necessary.  Here is a sample; alter the names:
 

	
 
  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
 
  `Gnomovision' (which makes passes at compilers) written by James Hacker.
 

	
 
  <signature of Ty Coon>, 1 April 1989
 
  Ty Coon, President of Vice
 

	
 
This General Public License does not permit incorporating your program into
 
proprietary programs.  If your program is a subroutine library, you may
 
consider it more useful to permit linking proprietary applications with the
 
library.  If this is what you want to do, use the GNU Library General
 
Public License instead of this License.
pylons_app/__init__.py
Show inline comments
 
#!/usr/bin/env python
 
# encoding: utf-8
 
# Hg app, a web based mercurial repository managment based on pylons
 
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
 
 
 
# 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 April 9, 2010
 
Hg app, a web based mercurial repository managment based on pylons
 
@author: marcink
 
"""
 

	
 
VERSION = (0, 7, 6, 'beta')
 

	
 
__version__ = '.'.join((str(each) for each in VERSION[:4]))
 

	
 
def get_version():
 
    """
 
    Returns shorter version (digit parts only) as string.
 
    """
 
    return '.'.join((str(each) for each in VERSION[:3]))
pylons_app/config/environment.py
Show inline comments
 
"""Pylons environment configuration"""
 
from mako.lookup import TemplateLookup
 
from pylons.configuration import PylonsConfig
 
from pylons.error import handle_mako_error
 
from pylons_app.config.routing import make_map
 
from pylons_app.lib.auth import set_available_permissions
 
from pylons_app.lib.utils import repo2db_mapper
 
from pylons_app.model import init_model
 
from sqlalchemy import engine_from_config
 
import logging
 
import os
 
import pylons_app.lib.app_globals as app_globals
 
import pylons_app.lib.helpers
 

	
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 
def load_environment(global_conf, app_conf):
 
    """Configure the Pylons environment via the ``pylons.config``
 
    object
 
    """
 
    config = PylonsConfig()
 
    
 
    # Pylons paths
 
    root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 
    paths = dict(root=root,
 
                 controllers=os.path.join(root, 'controllers'),
 
                 static_files=os.path.join(root, 'public'),
 
                 templates=[os.path.join(root, 'templates')])
 

	
 
    # Initialize config with the basic options
 
    config.init_app(global_conf, app_conf, package='pylons_app', paths=paths)
 

	
 
    config['routes.map'] = make_map(config)
 
    config['pylons.app_globals'] = app_globals.Globals(config)
 
    config['pylons.h'] = pylons_app.lib.helpers
 
    
 
    # Setup cache object as early as possible
 
    import pylons
 
    pylons.cache._push_object(config['pylons.app_globals'].cache)
 
    
 

	
 
    # Create the Mako TemplateLookup, with the default auto-escaping
 
    config['pylons.app_globals'].mako_lookup = TemplateLookup(
 
        directories=paths['templates'],
 
        error_handler=handle_mako_error,
 
        module_directory=os.path.join(app_conf['cache_dir'], 'templates'),
 
        input_encoding='utf-8', default_filters=['escape'],
 
        imports=['from webhelpers.html import escape'])
 

	
 
    #sets the c attribute access when don't existing attribute ar accessed
 
    #sets the c attribute access when don't existing attribute are accessed
 
    config['pylons.strict_tmpl_context'] = True
 
    
 
    #MULTIPLE DB configs
 
    # Setup the SQLAlchemy database engine
 
    if config['debug']:
 
        #use query time debugging.
 
        from pylons_app.lib.timerproxy import TimerProxy
 
        sa_engine_db1 = engine_from_config(config, 'sqlalchemy.db1.',
 
                                                            proxy=TimerProxy())
 
    else:
 
        sa_engine_db1 = engine_from_config(config, 'sqlalchemy.db1.')
 

	
 
    init_model(sa_engine_db1)
 

	
 
    repo2db_mapper()
 
    set_available_permissions(config)
 
    # CONFIGURATION OPTIONS HERE (note: all config options will override
 
    # any Pylons config options)
 
    
 
    return config
pylons_app/controllers/admin.py
Show inline comments
 
#!/usr/bin/env python
 
# encoding: utf-8
 
# admin controller for pylons 
 
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
 
 
 
# 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 April 7, 2010
 
admin controller for pylons
 
@author: marcink
 
"""
 
import logging
 
from pylons import request, response, session, tmpl_context as c, url, app_globals as g
 
from pylons.controllers.util import abort, redirect
 
from pylons import request, response, session, tmpl_context as c
 
from pylons_app.lib.base import BaseController, render
 
from pylons_app.model import meta
 
from pylons_app.model.db import UserLog
 
from webhelpers.paginate import Page
 
from pylons_app.lib.auth import LoginRequired
 

	
 
log = logging.getLogger(__name__)
 

	
 
class AdminController(BaseController):
 
    
 
    @LoginRequired()
 
    def __before__(self):
 
        user = session['hg_app_user']
 
        c.admin_user = user.is_admin
 
        c.admin_username = user.username
 
        super(AdminController, self).__before__()
 
        
 
    def index(self):
 
        sa = meta.Session
 
                         
 
        users_log = sa.query(UserLog).order_by(UserLog.action_date.desc())
 
        p = int(request.params.get('page', 1))
 
        c.users_log = Page(users_log, page=p, items_per_page=10)
 
        c.log_data = render('admin/admin_log.html')
 
        if request.params.get('partial'):
 
            return c.log_data
 
        return render('admin/admin.html')    
 
                
pylons_app/controllers/branches.py
Show inline comments
 
#!/usr/bin/env python
 
# encoding: utf-8
 
# branches controller for pylons
 
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
 
 
 
# 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 April 21, 2010
 
branches controller for pylons
 
@author: marcink
 
"""
 
from pylons import tmpl_context as c, app_globals as g
 
from pylons_app.lib.auth import LoginRequired
 
from pylons_app.lib.base import BaseController, render
 
from pylons_app.model.hg_model import HgModel
 
import logging
 

	
 
log = logging.getLogger(__name__)
 

	
 
class BranchesController(BaseController):
 
    
 
    @LoginRequired()
 
    def __before__(self):
 
        super(BranchesController, self).__before__()
 
    
 
    def index(self):
 
        hg_model = HgModel()
 
        c.repo_info = hg_model.get_repo(c.repo_name)
 
        c.repo_branches = c.repo_info.branches
 
                
 
        return render('branches/branches.html')
pylons_app/controllers/changelog.py
Show inline comments
 
from mercurial.graphmod import revisions as graph_rev, colored, CHANGESET
 
from mercurial.node import short
 
#!/usr/bin/env python
 
# encoding: utf-8
 
# changelog controller for pylons
 
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
 
 
 
# 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 April 21, 2010
 
changelog controller for pylons
 
@author: marcink
 
"""
 
from pylons import request, session, tmpl_context as c
 
from pylons_app.lib.auth import LoginRequired
 
from pylons_app.lib.base import BaseController, render
 
from pylons_app.lib.filters import age as _age, person
 
from pylons_app.model.hg_model import _full_changelog_cached
 
from simplejson import dumps
 
from webhelpers.paginate import Page
 
import logging
 
log = logging.getLogger(__name__)     
 

	
 
class ChangelogController(BaseController):
 
    
 
    @LoginRequired()
 
    def __before__(self):
 
        super(ChangelogController, self).__before__()
 
                
 
    def index(self):
 
        limit = 100
 
        default = 20
 
        if request.params.get('size'):
 
            try:
 
                int_size = int(request.params.get('size'))
 
            except ValueError:
 
                int_size = default
 
            int_size = int_size if int_size <= limit else limit 
 
            c.size = int_size
 
            session['changelog_size'] = c.size
 
            session.save()
 
        else:
 
            c.size = session.get('changelog_size', default)
 

	
 
        changesets = _full_changelog_cached(c.repo_name)
 
            
 
        p = int(request.params.get('page', 1))
 
        c.pagination = Page(changesets, page=p, item_count=len(changesets),
 
                            items_per_page=c.size)
 
            
 
        #self._graph(c.repo, c.size,p)
 
        
 
        return render('changelog/changelog.html')
 

	
 

	
 
    def _graph(self, repo, size, p):
 
        revcount = size
 
        if not repo.revisions:return dumps([]), 0
 
        
 
        max_rev = repo.revisions[-1]
 
        offset = 1 if p == 1 else  ((p - 1) * revcount)
 
        rev_start = repo.revisions[(-1 * offset)]
 
        c.bg_height = 120
 
        
 
        revcount = min(max_rev, revcount)
 
        rev_end = max(0, rev_start - revcount)
 
        dag = graph_rev(repo.repo, rev_start, rev_end)
 
        pass
 
#        revcount = size
 
#        if not repo.revisions:return dumps([]), 0
 
#        
 
#        max_rev = repo.revisions[-1]
 
#        offset = 1 if p == 1 else  ((p - 1) * revcount)
 
#        rev_start = repo.revisions[(-1 * offset)]
 
#        c.bg_height = 120
 
#        
 
#        revcount = min(max_rev, revcount)
 
#        rev_end = max(0, rev_start - revcount)
 
#        dag = graph_rev(repo.repo, rev_start, rev_end)
 
#        
 
#        c.dag = tree = list(colored(dag))
 
#        canvasheight = (len(tree) + 1) * c.bg_height - 27
 
#        data = []
 
#        for (id, type, ctx, vtx, edges) in tree:
 
#            if type != CHANGESET:
 
#                continue
 
#            node = short(ctx.node())
 
#            age = _age(ctx.date())
 
#            desc = ctx.description()
 
#            user = person(ctx.user())
 
#            branch = ctx.branch()
 
#            branch = branch, repo.repo.branchtags().get(branch) == ctx.node()
 
#            data.append((node, vtx, edges, desc, user, age, branch, ctx.tags()))
 
#    
 
#        c.jsdata = dumps(data) 
 
#        c.canvasheight = canvasheight 
 
        
 
        c.dag = tree = list(colored(dag))
 
        canvasheight = (len(tree) + 1) * c.bg_height - 27
 
        data = []
 
        for (id, type, ctx, vtx, edges) in tree:
 
            if type != CHANGESET:
 
                continue
 
            node = short(ctx.node())
 
            age = _age(ctx.date())
 
            desc = ctx.description()
 
            user = person(ctx.user())
 
            branch = ctx.branch()
 
            branch = branch, repo.repo.branchtags().get(branch) == ctx.node()
 
            data.append((node, vtx, edges, desc, user, age, branch, ctx.tags()))
 
    
 
        c.jsdata = dumps(data) 
 
        c.canvasheight = canvasheight 
 

	
pylons_app/controllers/changeset.py
Show inline comments
 
#!/usr/bin/env python
 
# encoding: utf-8
 
# changeset controller for pylons
 
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
 
 
 
# 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 April 25, 2010
 
changeset controller for pylons
 
@author: marcink
 
"""
 
from pylons import tmpl_context as c
 
from pylons_app.lib.auth import LoginRequired
 
from pylons_app.lib.base import BaseController, render
 
from pylons_app.model.hg_model import HgModel
 
from vcs.utils import diffs as differ
 
import logging
 
from vcs.nodes import FileNode
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 
class ChangesetController(BaseController):
 
    
 
    @LoginRequired()
 
    def __before__(self):
 
        super(ChangesetController, self).__before__()
 
        
 
    def index(self, revision):
 
        hg_model = HgModel()
 
        c.changeset = hg_model.get_repo(c.repo_name).get_changeset(revision)
 
        c.changeset_old = c.changeset.parents[0]
 
        c.changes = []
 
        
 
                
 
        for node in c.changeset.added:
 
            filenode_old = FileNode(node.path, '')
 
            f_udiff = differ.get_udiff(filenode_old, node)
 
            diff = differ.DiffProcessor(f_udiff).as_html()
 
            c.changes.append(('added', node, diff))
 
            
 
        for node in c.changeset.changed:
 
            filenode_old = c.changeset_old.get_node(node.path)
 
            f_udiff = differ.get_udiff(filenode_old, node)
 
            diff = differ.DiffProcessor(f_udiff).as_html()
 
            c.changes.append(('changed', node, diff))
 
            
 
        for node in c.changeset.removed:
 
            c.changes.append(('removed', node, None))            
 
            
 
        return render('changeset/changeset.html')
pylons_app/controllers/feed.py
Show inline comments
 
#!/usr/bin/python
 
# -*- coding: utf-8 -*-
 
#!/usr/bin/env python
 
# encoding: utf-8
 
# feed controller for pylons
 
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
 
 
 
# 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 April 23, 2010
 
feed controller for pylons
 
@author: marcink
 
"""
 
from pylons import tmpl_context as c, url, response
 
from pylons_app.lib.base import BaseController, render
 
from pylons_app.model.hg_model import _full_changelog_cached
 
from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
 
import logging
 
log = logging.getLogger(__name__)
 

	
 
class FeedController(BaseController):
 
    
 
    #secure it or not ?
 
    def __before__(self):
 
        super(FeedController, self).__before__()
 
        #common values for feeds
 
        self.description = 'Changes on %s repository'
 
        self.title = "%s feed"
 
        self.language = 'en-us'
 
        self.ttl = "5"
 
        self.feed_nr = 10
 

	
 
    def atom(self, repo_name):
 
        """Produce an atom-1.0 feed via feedgenerator module"""
 
        feed = Atom1Feed(title=self.title % repo_name,
 
                         link=url('summary_home', repo_name=repo_name, qualified=True),
 
                         description=self.description % repo_name,
 
                         language=self.language,
 
                         ttl=self.ttl)
 
        
 
        
 
        for cnt, cs in enumerate(_full_changelog_cached(repo_name)):
 
            if cnt > self.feed_nr:
 
                break
 
            feed.add_item(title=cs.message,
 
                          link=url('changeset_home', repo_name=repo_name,
 
                                   revision=cs.raw_id, qualified=True),
 
                                   description=str(cs.date))
 
        
 
        response.content_type = feed.mime_type
 
        return feed.writeString('utf-8')
 

	
 
    
 
    def rss(self, repo_name):
 
        """Produce an rss2 feed via feedgenerator module"""
 
        feed = Rss201rev2Feed(title=self.title % repo_name,
 
                         link=url('summary_home', repo_name=repo_name, qualified=True),
 
                         description=self.description % repo_name,
 
                         language=self.language,
 
                         ttl=self.ttl)
 
        
 
        for cnt, cs in enumerate(_full_changelog_cached(repo_name)):
 
            if cnt > self.feed_nr:
 
                break
 
            feed.add_item(title=cs.message,
 
                          link=url('changeset_home', repo_name=repo_name, revision=cs.raw_id, qualified=True),
 
                          description=str(cs.date))
 
            
 
        response.content_type = feed.mime_type
 
        return feed.writeString('utf-8')
pylons_app/controllers/files.py
Show inline comments
 
#!/usr/bin/env python
 
# encoding: utf-8
 
# files controller for pylons
 
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
 
 
 
# 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 April 21, 2010
 
files controller for pylons
 
@author: marcink
 
"""
 
from mercurial import archival
 
from pylons import request, response, session, tmpl_context as c, url
 
from pylons_app.lib.auth import LoginRequired
 
from pylons_app.lib.base import BaseController, render
 
from pylons_app.model.hg_model import HgModel
 
from vcs.exceptions import RepositoryError, ChangesetError
 
from vcs.utils import diffs as differ
 
import logging
 
import tempfile
 

	
 
        
 
log = logging.getLogger(__name__)
 

	
 
class FilesController(BaseController):
 
    
 
    @LoginRequired()
 
    def __before__(self):
 
        super(FilesController, self).__before__()
 

	
 
    def index(self, repo_name, revision, f_path):
 
        hg_model = HgModel()
 
        c.repo = repo = hg_model.get_repo(c.repo_name)
 
        revision = request.POST.get('at_rev', None) or revision
 
        
 
        def get_next_rev(cur):
 
            max_rev = len(c.repo.revisions) - 1
 
            r = cur + 1
 
            if r > max_rev:
 
                r = max_rev
 
            return r
 
            
 
        def get_prev_rev(cur):
 
            r = cur - 1
 
            return r
 

	
 
        c.f_path = f_path
 
     
 
        
 
        try:
 
            cur_rev = repo.get_changeset(revision).revision
 
            prev_rev = repo.get_changeset(get_prev_rev(cur_rev)).raw_id
 
            next_rev = repo.get_changeset(get_next_rev(cur_rev)).raw_id
 
                    
 
            c.url_prev = url('files_home', repo_name=c.repo_name,
 
                             revision=prev_rev, f_path=f_path) 
 
            c.url_next = url('files_home', repo_name=c.repo_name,
 
                             revision=next_rev, f_path=f_path)   
 
                    
 
            c.changeset = repo.get_changeset(revision)
 
            try:
 
                c.file_msg = c.changeset.get_file_message(f_path)
 
            except:
 
                c.file_msg = None
 
                        
 
            c.cur_rev = c.changeset.raw_id
 
            c.rev_nr = c.changeset.revision
 
            c.files_list = c.changeset.get_node(f_path)
 
            c.file_history = self._get_history(repo, c.files_list, f_path)
 
            
 
        except (RepositoryError, ChangesetError):
 
            c.files_list = None
 
        
 
        return render('files/files.html')
 

	
 
    def rawfile(self, repo_name, revision, f_path):
 
        hg_model = HgModel()
 
        c.repo = hg_model.get_repo(c.repo_name)
 
        file_node = c.repo.get_changeset(revision).get_node(f_path)
 
        response.content_type = file_node.mimetype
 
        response.content_disposition = 'attachment; filename=%s' \
 
                                                    % f_path.split('/')[-1] 
 
        return file_node.content
 
    
 
    def annotate(self, repo_name, revision, f_path):
 
        hg_model = HgModel()
 
        c.repo = hg_model.get_repo(c.repo_name)
 
        cs = c.repo.get_changeset(revision)
 
        c.file = cs.get_node(f_path)
 
        c.file_msg = cs.get_file_message(f_path)
 
        c.cur_rev = cs.raw_id
 
        c.f_path = f_path
 
        c.annotate = cs.get_file_annotate(f_path)
 
        return render('files/files_annotate.html')
 
      
 
    def archivefile(self, repo_name, revision, fileformat):
 
        archive_specs = {
 
          '.tar.bz2': ('application/x-tar', 'tbz2'),
 
          '.tar.gz': ('application/x-tar', 'tgz'),
 
          '.zip': ('application/zip', 'zip'),
 
        }
 
        if not archive_specs.has_key(fileformat):
 
            return 'Unknown archive type %s' % fileformat
 
                        
 
        def read_in_chunks(file_object, chunk_size=1024 * 40):
 
            """Lazy function (generator) to read a file piece by piece.
 
            Default chunk size: 40k."""
pylons_app/controllers/graph.py
Show inline comments
 
#!/usr/bin/env python
 
# encoding: utf-8
 
# graph controller for pylons
 
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
 
 
 
# 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 April 21, 2010
 
graph controller for pylons
 
@author: marcink
 
"""
 
from mercurial.graphmod import revisions as graph_rev, colored, CHANGESET
 
from mercurial.node import short
 
from pylons import request, response, session, tmpl_context as c, url, config, \
 
    app_globals as g
 
from pylons.controllers.util import abort, redirect
 
from pylons import request, tmpl_context as c
 
from pylons_app.lib.auth import LoginRequired
 
from pylons_app.lib.base import BaseController, render
 
from pylons_app.lib.filters import age as _age, person
 
from pylons_app.lib.utils import get_repo_slug
 
from pylons_app.model.hg_model import HgModel
 
from simplejson import dumps
 
from webhelpers.paginate import Page
 
import logging
 

	
 
        
 
        
 

	
 
log = logging.getLogger(__name__)
 

	
 
class GraphController(BaseController):
 
    
 
    @LoginRequired()
 
    def __before__(self):
 
        super(GraphController, self).__before__()
 
        
 
    def index(self):
 
        # Return a rendered template
 
        hg_model = HgModel()
 
        if request.POST.get('size'):
 
            c.size = int(request.params.get('size', 20))
 
        else:
 
            c.size = int(request.params.get('size', 20))
 
        c.jsdata, c.canvasheight = self.graph(hg_model.get_repo(c.repo_name), c.size)
 
        
 
        return render('/graph.html')
 

	
 

	
 
    def graph(self, repo, size):
 
        revcount = size
 
        p = int(request.params.get('page', 1))
 
        c.pagination = Page(repo.revisions, page=p, item_count=len(repo.revisions), items_per_page=revcount)
 
        if not repo.revisions:return dumps([]), 0
 
        
 
        max_rev = repo.revisions[-1]
 
        offset = 1 if p == 1 else  ((p - 1) * revcount)
 
        rev_start = repo.revisions[(-1 * offset)]
 
        bg_height = 39
 
        
 
        revcount = min(max_rev, revcount)
 
        rev_end = max(0, rev_start - revcount)
 
        dag = graph_rev(repo.repo, rev_start, rev_end)
 
        tree = list(colored(dag))
 
        canvasheight = (len(tree) + 1) * bg_height - 27
 
        data = []
 
        for (id, type, ctx, vtx, edges) in tree:
 
            if type != CHANGESET:
 
                continue
 
            node = short(ctx.node())
 
            age = _age(ctx.date())
 
            desc = ctx.description()
 
            user = person(ctx.user())
 
            branch = ctx.branch()
 
            branch = branch, repo.repo.branchtags().get(branch) == ctx.node()
 
            data.append((node, vtx, edges, desc, user, age, branch, ctx.tags()))
 
    
 
        return dumps(data), canvasheight
pylons_app/controllers/hg.py
Show inline comments
 
#!/usr/bin/python
 
# -*- coding: utf-8 -*-
 
#!/usr/bin/env python
 
# encoding: utf-8
 
# hg controller for pylons
 
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
 
 
 
# 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 February 18, 2010
 
hg controller for pylons
 
@author: marcink
 
"""
 
from operator import itemgetter
 
from pylons import tmpl_context as c, request, config
 
from pylons import tmpl_context as c, request
 
from pylons_app.lib.auth import LoginRequired
 
from pylons_app.lib.base import BaseController, render
 
from pylons_app.model.hg_model import HgModel
 
import logging
 
log = logging.getLogger(__name__)
 

	
 
class HgController(BaseController):
 

	
 
    @LoginRequired()
 
    def __before__(self):
 
        super(HgController, self).__before__()
 
        
 
    def index(self):
 
        c.current_sort = request.GET.get('sort', 'name')
 
        cs = c.current_sort
 
        c.cs_slug = cs.replace('-', '')
 
        sortables = ['name', 'description', 'last_change', 'tip', 'contact']
 
        cached_repo_list = HgModel().get_repos()
 
        if cs and c.cs_slug in sortables:
 
            sort_key = c.cs_slug + '_sort'
 
            if cs.startswith('-'):
 
                c.repos_list = sorted(cached_repo_list, key=itemgetter(sort_key), reverse=True)
 
            else:
 
                c.repos_list = sorted(cached_repo_list, key=itemgetter(sort_key), reverse=False)
 
            
 
        return render('/index.html')
pylons_app/controllers/login.py
Show inline comments
 
#!/usr/bin/env python
 
# encoding: utf-8
 
# login controller for pylons
 
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
 
 
 
# 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 April 22, 2010
 
login controller for pylons
 
@author: marcink
 
"""
 
import logging
 
from formencode import htmlfill
 
from pylons import request, response, session, tmpl_context as c, url
 
from pylons.controllers.util import abort, redirect
 
from pylons_app.lib.base import BaseController, render
 
import formencode
 
from pylons_app.model.forms import LoginForm
 
from pylons_app.lib.auth import AuthUser
 

	
 
log = logging.getLogger(__name__)
 

	
 
class LoginController(BaseController):
 

	
 
    def __before__(self):
 
        super(LoginController, self).__before__()
 

	
 
    def index(self):
 
        #redirect if already logged in
 
        if c.hg_app_user.is_authenticated:
 
            return redirect(url('hg_home'))
 
        
 
        if request.POST:
 
            #import Login Form validator class
 
            login_form = LoginForm()
 
            try:
 
                c.form_result = login_form.to_python(dict(request.POST))
 
                return redirect(url('hg_home'))
 
                               
 
            except formencode.Invalid as errors:
 
                c.form_errors = errors.error_dict
 
                return htmlfill.render(
 
                    render('/login.html'),
 
                    defaults=errors.value,
 
                    encoding="UTF-8")
 
                        
 
        return render('/login.html')
 
    
 
    def logout(self):
 
        session['hg_app_user'] = AuthUser()
 
        session.save()
 
        log.info('Logging out and setting user as Empty')
 
        redirect(url('hg_home'))
pylons_app/controllers/permissions.py
Show inline comments
 
#!/usr/bin/env python
 
# encoding: utf-8
 
# permissions controller for pylons
 
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
 
 
 
# 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 April 27, 2010
 
permissions controller for pylons
 
@author: marcink
 
"""
 
import logging
 

	
 
from pylons import request, response, session, tmpl_context as c, url
 
from pylons.controllers.util import abort, redirect
 

	
 
from pylons_app.lib.base import BaseController, render
 

	
 
log = logging.getLogger(__name__)
 

	
 
class PermissionsController(BaseController):
 
    """REST Controller styled on the Atom Publishing Protocol"""
 
    # To properly map this controller, ensure your config/routing.py
 
    # file has a resource setup:
 
    #     map.resource('permission', 'permissions')
 

	
 
    def index(self, format='html'):
 
        """GET /permissions: All items in the collection"""
 
        # url('permissions')
 
        return render('admin/permissions/permissions.html')
 

	
 
    def create(self):
 
        """POST /permissions: Create a new item"""
 
        # url('permissions')
 

	
 
    def new(self, format='html'):
 
        """GET /permissions/new: Form to create a new item"""
 
        # url('new_permission')
 

	
 
    def update(self, id):
 
        """PUT /permissions/id: Update an existing item"""
 
        # Forms posted to this method should contain a hidden field:
 
        #    <input type="hidden" name="_method" value="PUT" />
 
        # Or using helpers:
 
        #    h.form(url('permission', id=ID),
 
        #           method='put')
 
        # url('permission', id=ID)
 

	
 
    def delete(self, id):
 
        """DELETE /permissions/id: Delete an existing item"""
 
        # Forms posted to this method should contain a hidden field:
 
        #    <input type="hidden" name="_method" value="DELETE" />
 
        # Or using helpers:
 
        #    h.form(url('permission', id=ID),
 
        #           method='delete')
 
        # url('permission', id=ID)
 

	
 
    def show(self, id, format='html'):
 
        """GET /permissions/id: Show a specific item"""
 
        # url('permission', id=ID)
 

	
 
    def edit(self, id, format='html'):
 
        """GET /permissions/id/edit: Form to edit an existing item"""
 
        # url('edit_permission', id=ID)
pylons_app/controllers/repos.py
Show inline comments
 
#!/usr/bin/env python
 
# encoding: utf-8
 
# repos controller for pylons
 
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
 
 
 
# 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 April 7, 2010
 
admin controller for pylons
 
@author: marcink
 
"""
 
import logging
 
from pylons import request, response, session, tmpl_context as c, url, \
 
    app_globals as g
 
from pylons.controllers.util import abort, redirect
 
from pylons_app.lib.auth import LoginRequired
 
from pylons.i18n.translation import _
 
from pylons_app.lib import helpers as h
 
from pylons_app.lib.base import BaseController, render
 
from pylons_app.lib.filters import clean_repo
 
from pylons_app.lib.utils import check_repo, invalidate_cache
 
from pylons_app.model.hg_model import HgModel
 
import logging
 
import os
 
import shutil
 
from operator import itemgetter
 
log = logging.getLogger(__name__)
 

	
 
class ReposController(BaseController):
 
    """REST Controller styled on the Atom Publishing Protocol"""
 
    # To properly map this controller, ensure your config/routing.py
 
    # file has a resource setup:
 
    #     map.resource('repo', 'repos')
 
    @LoginRequired()
 
    def __before__(self):
 
        c.admin_user = session.get('admin_user')
 
        c.admin_username = session.get('admin_username')
 
        super(ReposController, self).__before__()
 
                
 
    def index(self, format='html'):
 
        """GET /repos: All items in the collection"""
 
        # url('repos')
 
        cached_repo_list = HgModel().get_repos()
 
        c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
 
        return render('admin/repos/repos.html')
 
    
 
    def create(self):
 
        """POST /repos: Create a new item"""
 
        # url('repos')
 
        name = request.POST.get('name')
 

	
 
        try:
 
            self._create_repo(name)
 
            #clear our cached list for refresh with new repo
 
            invalidate_cache('cached_repo_list')
 
            h.flash(_('created repository %s') % name, category='success')
 
        except Exception as e:
 
            log.error(e)
 
        
 
        return redirect('repos')
 
        
 
    def _create_repo(self, repo_name):        
 
        repo_path = os.path.join(g.base_path, repo_name)
 
        if check_repo(repo_name, g.base_path):
 
            log.info('creating repo %s in %s', repo_name, repo_path)
 
            from vcs.backends.hg import MercurialRepository
 
            MercurialRepository(repo_path, create=True)
 
                        
 

	
 
    def new(self, format='html'):
 
        """GET /repos/new: Form to create a new item"""
 
        new_repo = request.GET.get('repo', '')
 
        c.new_repo = clean_repo(new_repo)
 

	
 
        return render('admin/repos/repo_add.html')
 

	
 
    def update(self, id):
 
        """PUT /repos/id: Update an existing item"""
 
        # Forms posted to this method should contain a hidden field:
 
        #    <input type="hidden" name="_method" value="PUT" />
 
        # Or using helpers:
 
        #    h.form(url('repo', id=ID),
 
        #           method='put')
 
        # url('repo', id=ID)
 

	
 
    def delete(self, id):
 
        """DELETE /repos/id: Delete an existing item"""
 
        # Forms posted to this method should contain a hidden field:
 
        #    <input type="hidden" name="_method" value="DELETE" />
 
        # Or using helpers:
 
        #    h.form(url('repo', id=ID),
 
        #           method='delete')
 
        # url('repo', id=ID)
 
        from datetime import datetime
 
        path = g.paths[0][1].replace('*', '')
 
        rm_path = os.path.join(path, id)
 
        log.info("Removing %s", rm_path)
 
        shutil.move(os.path.join(rm_path, '.hg'), os.path.join(rm_path, 'rm__.hg'))
 
        shutil.move(rm_path, os.path.join(path, 'rm__%s-%s' % (datetime.today(), id)))
 
        
 
        #clear our cached list for refresh with new repo
 
        invalidate_cache('cached_repo_list')
 
        h.flash(_('deleted repository %s') % rm_path, category='success')            
 
        return redirect(url('repos'))
 
        
 

	
 
    def show(self, id, format='html'):
 
        """GET /repos/id: Show a specific item"""
 
        # url('repo', id=ID)
 
        
 
    def edit(self, id, format='html'):
 
        """GET /repos/id/edit: Form to edit an existing item"""
 
        # url('edit_repo', id=ID)
 
        c.new_repo = id
 
        return render('admin/repos/repo_edit.html')
pylons_app/controllers/shortlog.py
Show inline comments
 
#!/usr/bin/env python
 
# encoding: utf-8
 
# shortlog controller for pylons
 
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
 
 
 
# 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 April 18, 2010
 
shortlog controller for pylons
 
@author: marcink
 
"""
 
from pylons import tmpl_context as c, request
 
from pylons_app.lib.auth import LoginRequired
 
from pylons_app.lib.base import BaseController, render
 
from pylons_app.model.hg_model import HgModel
 
from webhelpers.paginate import Page
 
import logging
 

	
 
log = logging.getLogger(__name__)
 

	
 
class ShortlogController(BaseController):
 
    
 
    @LoginRequired()
 
    def __before__(self):
 
        super(ShortlogController, self).__before__()
 
        
 
    def index(self):
 
        hg_model = HgModel()
 
        p = int(request.params.get('page', 1))
 
        repo = hg_model.get_repo(c.repo_name)
 
        c.repo_changesets = Page(repo, page=p, items_per_page=20)
 
        c.shortlog_data = render('shortlog/shortlog_data.html')
 
        if request.params.get('partial'):
 
            return c.shortlog_data
 
        r = render('shortlog/shortlog.html')
 
        return r
pylons_app/controllers/summary.py
Show inline comments
 
#!/usr/bin/env python
 
# encoding: utf-8
 
# summary controller for pylons
 
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
 
 
 
# 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 April 18, 2010
 
summary controller for pylons
 
@author: marcink
 
"""
 
from pylons import tmpl_context as c, request
 
from pylons_app.lib.auth import LoginRequired
 
from pylons_app.lib.base import BaseController, render
 
from pylons_app.model.hg_model import HgModel, _full_changelog_cached
 
import logging
 

	
 
log = logging.getLogger(__name__)
 

	
 
class SummaryController(BaseController):
 
    
 
    @LoginRequired()
 
    def __before__(self):
 
        super(SummaryController, self).__before__()
 
        
 
    def index(self):
 
        hg_model = HgModel()
 
        c.repo_info = hg_model.get_repo(c.repo_name)
 
        c.repo_changesets = _full_changelog_cached(c.repo_name)[:10]
 
        e = request.environ
 
        uri = u'%(protocol)s://%(user)s@%(host)s/%(repo_name)s' % {
 
                                        'protocol': e.get('wsgi.url_scheme'),
 
                                        'user':str(c.hg_app_user.username),
 
                                        'host':e.get('HTTP_HOST'),
 
                                        'repo_name':c.repo_name, }
 
        c.clone_repo_url = uri
 
        c.repo_tags = c.repo_info.tags[:10]
 
        c.repo_branches = c.repo_info.branches[:10]
 
        return render('summary/summary.html')
pylons_app/controllers/tags.py
Show inline comments
 
#!/usr/bin/env python
 
# encoding: utf-8
 
# tags controller for pylons
 
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
 
 
 
# 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 April 21, 2010
 
tags controller for pylons
 
@author: marcink
 
"""
 
from pylons import tmpl_context as c
 
from pylons_app.lib.auth import LoginRequired
 
from pylons_app.lib.base import BaseController, render
 
from pylons_app.model.hg_model import HgModel
 
import logging
 

	
 
log = logging.getLogger(__name__)
 

	
 
class TagsController(BaseController):
 
    
 
    @LoginRequired()
 
    def __before__(self):
 
        super(TagsController, self).__before__()
 
        
 
    def index(self):
 
        hg_model = HgModel()
 
        c.repo_info = hg_model.get_repo(c.repo_name)
 
        c.repo_tags = c.repo_info.tags
 
        
 
        return render('tags/tags.html')
pylons_app/controllers/users.py
Show inline comments
 
#!/usr/bin/env python
 
# encoding: utf-8
 
# users controller for pylons
 
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
 
 
 
# 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 April 4, 2010
 
users controller for pylons
 
@author: marcink
 
"""
 
import logging
 
from formencode import htmlfill
 
from pylons import request, session, tmpl_context as c, url
 
from pylons.controllers.util import abort, redirect
 
from pylons.i18n.translation import _
 
from pylons_app.lib import helpers as h
 
from pylons_app.lib.auth import LoginRequired, CheckPermissionAll
 
from pylons_app.lib.base import BaseController, render
 
from pylons_app.model.db import User, UserLog
 
from pylons_app.model.forms import UserForm
 
from pylons_app.model.user_model import UserModel
 
import formencode
 
import logging
 

	
 
log = logging.getLogger(__name__)
 

	
 
class UsersController(BaseController):
 
    """REST Controller styled on the Atom Publishing Protocol"""
 
    # To properly map this controller, ensure your config/routing.py
 
    # file has a resource setup:
 
    #     map.resource('user', 'users')
 
    @LoginRequired()
 
    def __before__(self):
 
        c.admin_user = session.get('admin_user')
 
        c.admin_username = session.get('admin_username')
 
        super(UsersController, self).__before__()
 
    
 

	
 
    def index(self, format='html'):
 
        """GET /users: All items in the collection"""
 
        # url('users')
 
        
 
        c.users_list = self.sa.query(User).all()     
 
        return render('admin/users/users.html')
 
    
 
    def create(self):
 
        """POST /users: Create a new item"""
 
        # url('users')
 
        
 
        user_model = UserModel()
 
        login_form = UserForm()()
 
        try:
 
            form_result = login_form.to_python(dict(request.POST))
 
            user_model.create(form_result)
 
            h.flash(_('created user %s') % form_result['username'], category='success')
 
            return redirect(url('users'))
 
                           
 
        except formencode.Invalid as errors:
 
            c.form_errors = errors.error_dict
 
            return htmlfill.render(
 
                 render('admin/users/user_add.html'),
 
                defaults=errors.value,
 
                encoding="UTF-8")
 
    
 
    def new(self, format='html'):
 
        """GET /users/new: Form to create a new item"""
 
        # url('new_user')
 
        return render('admin/users/user_add.html')
 

	
 
    def update(self, id):
 
        """PUT /users/id: Update an existing item"""
 
        # Forms posted to this method should contain a hidden field:
 
        #    <input type="hidden" name="_method" value="PUT" />
 
        # Or using helpers:
 
        #    h.form(url('user', id=ID),
 
        #           method='put')
 
        # url('user', id=ID)
 
        user_model = UserModel()
 
        login_form = UserForm(edit=True)()
 
        try:
 
            form_result = login_form.to_python(dict(request.POST))
 
            user_model.update(id, form_result)
 
            h.flash(_('User updated succesfully'), category='success')
 
            return redirect(url('users'))
 
                           
 
        except formencode.Invalid as errors:
 
            c.user = user_model.get_user(id)
 
            c.form_errors = errors.error_dict
 
            return htmlfill.render(
 
                 render('admin/users/user_edit.html'),
 
                defaults=errors.value,
 
                encoding="UTF-8")
 
    
 
    def delete(self, id):
 
        """DELETE /users/id: Delete an existing item"""
 
        # Forms posted to this method should contain a hidden field:
 
        #    <input type="hidden" name="_method" value="DELETE" />
 
        # Or using helpers:
 
        #    h.form(url('user', id=ID),
 
        #           method='delete')
 
        # url('user', id=ID)
 
        try:
 
            self.sa.delete(self.sa.query(User).get(id))
 
            self.sa.commit()
 
            h.flash(_('sucessfully deleted user'), category='success')
 
        except:
 
            self.sa.rollback()
 
            raise
 
        return redirect(url('users'))
 
        
 
    def show(self, id, format='html'):
 
        """GET /users/id: Show a specific item"""
 
        # url('user', id=ID)
 
    
 
    
 
    def edit(self, id, format='html'):
 
        """GET /users/id/edit: Form to edit an existing item"""
 
        # url('edit_user', id=ID)
 
        c.user = self.sa.query(User).get(id)
pylons_app/lib/auth.py
Show inline comments
 
#!/usr/bin/env python
 
# encoding: utf-8
 
# authentication and permission libraries
 
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
 
 
 
# 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 April 4, 2010
 

	
 
@author: marcink
 
"""
 

	
 
from functools import wraps
 
from pylons import session, url, app_globals as g
 
from pylons.controllers.util import abort, redirect
 
from pylons_app.model import meta
 
from pylons_app.model.db import User
 
from sqlalchemy.exc import OperationalError
 
from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
 
import crypt
 
import logging
 
log = logging.getLogger(__name__)
 

	
 
def get_crypt_password(password):
 
    """
 
    Cryptographic function used for password hashing
 
    @param password: password to hash
 
    """
 
    return crypt.crypt(password, '6a')
 

	
 
def authfunc(environ, username, password):
 
    sa = meta.Session
 
    password_crypt = get_crypt_password(password)
 
    try:
 
        user = sa.query(User).filter(User.username == username).one()
 
    except (NoResultFound, MultipleResultsFound, OperationalError) as e:
 
        log.error(e)
 
        user = None
 
        
 
    if user:
 
        if user.active:
 
            if user.username == username and user.password == password_crypt:
 
                log.info('user %s authenticated correctly', username)
 
                return True
 
        else:
 
            log.error('user %s is disabled', username)
 
            
 
    return False
 

	
 
class  AuthUser(object):
 
    """
 
    A simple object that handles a mercurial username for authentication
 
    """
 
    username = 'None'
 
    is_authenticated = False
 
    is_admin = False
 
    permissions = set()
 
    group = set()
 
    
 
    def __init__(self):
 
        pass
 

	
 

	
 

	
 
def set_available_permissions(config):
 
    """
 
    This function will propagate pylons globals with all available defined
 
    permission given in db. We don't wannt to check each time from db for new 
 
    permissions since adding a new permission also requires application restart
 
    ie. to decorate new views with the newly created permission
 
    @param config:
 
    """
 
    from pylons_app.model.meta import Session
 
    from pylons_app.model.db import Permission
 
    logging.info('getting information about all available permissions')
 
    sa = Session()
 
    all_perms = sa.query(Permission).all()
 
    config['pylons.app_globals'].available_permissions = [x.permission_name for x in all_perms]
 

	
 

	
 
        
 
#===============================================================================
 
# DECORATORS
 
#===============================================================================
 
class LoginRequired(object):
 
    """
 
    Must be logged in to execute this function else redirect to login page
 
    """
 
    def __init__(self):
 
        pass
 
    
 
    def __call__(self, func):
 
        
 
        @wraps(func)
 
        def _wrapper(*fargs, **fkwargs):
 
            user = session.get('hg_app_user', AuthUser())
 
            log.info('Checking login required for user:%s', user.username)            
 
            if user.is_authenticated:
 
                    log.info('user %s is authenticated', user.username)
 
                    func(*fargs)
 
            else:
 
                logging.info('user %s not authenticated', user.username)
 
                logging.info('redirecting to login page')
 
                return redirect(url('login_home'))
 

	
 
        return _wrapper
 

	
 
class PermsDecorator(object):
pylons_app/lib/backup_manager.py
Show inline comments
 
'''BACKUP MANAGER'''
 
#!/usr/bin/env python
 
# encoding: utf-8
 
# mercurial repository backup manager
 
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
 
 
 
# 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 Feb 28, 2010
 
Mercurial repositories backup manager
 
@author: marcink
 
"""
 

	
 

	
 
import logging
 
from mercurial import config
 
import tarfile
 
import os
 
import datetime
 
import sys
 
import subprocess
 
logging.basicConfig(level=logging.DEBUG,
 
                    format="%(asctime)s %(levelname)-5.5s %(message)s")
 

	
 
class BackupManager(object):
 
    def __init__(self, id_rsa_path, repo_conf):
 
        self.repos_path = None
 
        self.backup_file_name = None
 
        self.id_rsa_path = id_rsa_path
 
        self.check_id_rsa()
 
        cur_dir = os.path.realpath(__file__)
 
        dn = os.path.dirname
 
        self.backup_file_path = os.path.join(dn(dn(dn(cur_dir))), 'data')
 
        cfg = config.config()
 
        try:
 
            cfg.read(os.path.join(dn(dn(dn(cur_dir))), repo_conf))
 
        except IOError:
 
            logging.error('Could not read %s', repo_conf)
 
            sys.exit()
 
        self.set_repos_path(cfg.items('paths'))
 
        logging.info('starting backup for %s', self.repos_path)
 
        logging.info('backup target %s', self.backup_file_path)
 

	
 
        if not os.path.isdir(self.repos_path):
 
            raise Exception('Not a valid directory in %s' % self.repos_path)
 

	
 
    def check_id_rsa(self):
 
        if not os.path.isfile(self.id_rsa_path):
 
            logging.error('Could not load id_rsa key file in %s',
 
                          self.id_rsa_path)
 
            sys.exit()
 

	
 
    def set_repos_path(self, paths):
 
        repos_path = paths[0][1].split('/')
 
        if repos_path[-1] in ['*', '**']:
 
            repos_path = repos_path[:-1]
 
        if repos_path[0] != '/':
 
            repos_path[0] = '/'
 
        self.repos_path = os.path.join(*repos_path)
 

	
 
    def backup_repos(self):
 
        today = datetime.datetime.now().weekday() + 1
 
        self.backup_file_name = "mercurial_repos.%s.tar.gz" % today
 
        bckp_file = os.path.join(self.backup_file_path, self.backup_file_name)
 
        tar = tarfile.open(bckp_file, "w:gz")
 

	
 
        for dir_name in os.listdir(self.repos_path):
 
            logging.info('backing up %s', dir_name)
 
            tar.add(os.path.join(self.repos_path, dir_name), dir_name)
 
        tar.close()
 
        logging.info('finished backup of mercurial repositories')
 

	
 

	
 

	
 
    def transfer_files(self):
 
        params = {
 
                  'id_rsa_key': self.id_rsa_path,
 
                  'backup_file_path':self.backup_file_path,
 
                  'backup_file_name':self.backup_file_name,
 
                  }
 
        cmd = ['scp', '-l', '40000', '-i', '%(id_rsa_key)s' % params,
 
               '%(backup_file_path)s/%(backup_file_name)s' % params,
 
               'root@192.168.2.102:/backups/mercurial' % params]
 

	
 
        subprocess.call(cmd)
 
        logging.info('Transfered file %s to %s', self.backup_file_name, cmd[4])
 
        
 
    
 
    def rm_file(self):
 
        logging.info('Removing file %s', self.backup_file_name)
 
        os.remove(os.path.join(self.backup_file_path, self.backup_file_name))
 
    
 

	
 

	
 
if __name__ == "__main__":
 
    B_MANAGER = BackupManager('/home/pylons/id_rsa', 'repositories.config')
 
    B_MANAGER.backup_repos()
 
    B_MANAGER.transfer_files()
 
    B_MANAGER.rm_file()
 

	
 

	
pylons_app/lib/db_manage.py
Show inline comments
 
#!/usr/bin/env python
 
# encoding: utf-8
 
# database managment for hg app
 
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
 
 
 
# 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 April 10, 2010
 
database managment and creation for hg app
 
@author: marcink
 
"""
 

	
 
from os.path import dirname as dn, join as jn
 
import os
 
import sys
 
ROOT = dn(dn(dn(os.path.realpath(__file__))))
 
sys.path.append(ROOT)
 

	
 
from pylons_app.lib.auth import get_crypt_password
 
from pylons_app.model import init_model
 
from pylons_app.model.db import User, Permission
 
from pylons_app.model.meta import Session, Base
 
from sqlalchemy.engine import create_engine
 
import logging
 

	
 
log = logging.getLogger('db manage')
 
log.setLevel(logging.DEBUG)
 
console_handler = logging.StreamHandler()
 
console_handler.setFormatter(logging.Formatter("%(asctime)s.%(msecs)03d" 
 
                                    " %(levelname)-5.5s [%(name)s] %(message)s"))
 
log.addHandler(console_handler)
 

	
 
class DbManage(object):
 
    def __init__(self, log_sql):
 
        self.dbname = 'hg_app.db'
 
        dburi = 'sqlite:////%s' % jn(ROOT, self.dbname)
 
        engine = create_engine(dburi, echo=log_sql) 
 
        init_model(engine)
 
        self.sa = Session()
 
        self.db_exists = False
 
    
 
    def check_for_db(self, override):
 
        log.info('checking for exisiting db')
 
        if os.path.isfile(jn(ROOT, self.dbname)):
 
            self.db_exists = True
 
            log.info('database exisist')
 
            if not override:
 
                raise Exception('database already exists')
 

	
 
    def create_tables(self, override=False):
 
        """
 
        Create a auth database
 
        """
 
        self.check_for_db(override)
 
        if override:
 
            log.info("database exisist and it's going to be destroyed")
 
            if self.db_exists:
 
                os.remove(jn(ROOT, self.dbname))
 
        Base.metadata.create_all(checkfirst=override)
 
        log.info('Created tables for %s', self.dbname)
 
    
 
    def admin_prompt(self):
 
        import getpass
 
        username = raw_input('Specify admin username:')
 
        password = getpass.getpass('Specify admin password:')
 
        self.create_user(username, password, True)
 
        
 
    def create_user(self, username, password, admin=False):
 
        log.info('creating administrator user %s', username)
 
        
 
        new_user = User()
 
        new_user.username = username
 
        new_user.password = get_crypt_password(password)
 
        new_user.admin = admin
 
        new_user.active = True
 
        
 
        try:
 
            self.sa.add(new_user)
 
            self.sa.commit()
 
        except:
 
            self.sa.rollback()
 
            raise
 
    
 
    def create_permissions(self):
 
        #module.(access|create|change|delete)_[name]
 
        perms = [('admin.access_home', 'Access to admin user view'),
 
                 
 
                 ]
 
        
 
        for p in perms:
 
            new_perm = Permission()
 
            new_perm.permission_name = p[0]
 
            new_perm.permission_longname = p[1]
 
            try:
 
                self.sa.add(new_perm)
 
                self.sa.commit()
 
            except:
 
                self.sa.rollback()
 
                raise
 
        
 
        
 
        
 
if __name__ == '__main__':
 
    dbmanage = DbManage(log_sql=True)
 
    dbmanage.create_tables(override=True)
 
    dbmanage.admin_prompt()
 
    dbmanage.create_permissions()  
 

	
pylons_app/lib/filters.py
Show inline comments
 
#!/usr/bin/env python
 
# encoding: utf-8
 
# simple filters for hg apps html templates
 
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
 
 
 
# 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 April 12, 2010
 
simple filters for hg apps html templates
 
@author: marcink
 
"""
 

	
 
from mercurial import util
 
from mercurial.templatefilters import age as _age, person as _person
 
from string import punctuation
 

	
 
def clean_repo(repo_name):
 
    for x in punctuation:
 
        if x != '_':
 
            repo_name = repo_name.replace(x, '')
 
    repo_name = repo_name.lower().strip()
 
    return repo_name.replace(' ', '_')
 

	
 
age = lambda  x:_age(x)
 
capitalize = lambda x: x.capitalize()
 
date = lambda x: util.datestr(x)
 
email = util.email
 
person = lambda x: _person(x)
 
hgdate = lambda  x: "%d %d" % x
 
isodate = lambda  x: util.datestr(x, '%Y-%m-%d %H:%M %1%2')
 
isodatesec = lambda  x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2')
 
localdate = lambda  x: (x[0], util.makedate()[1])
 
rfc822date = lambda  x: util.datestr(x, "%a, %d %b %Y %H:%M:%S %1%2")
 
rfc3339date = lambda  x: util.datestr(x, "%Y-%m-%dT%H:%M:%S%1:%2")
 
time_ago = lambda x: util.datestr(_age(x), "%a, %d %b %Y %H:%M:%S %1%2")
pylons_app/lib/middleware/https_fixup.py
Show inline comments
 
#!/usr/bin/env python
 
# encoding: utf-8
 
# middleware to handle https correctly
 
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
 
 
 
# 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 May 23, 2010
 

	
 
@author: marcink
 
"""
 

	
 
class HttpsFixup(object):
 
    def __init__(self, app):
 
        self.application = app
 
    
 
    def __call__(self, environ, start_response):
 
        self.__fixup(environ)
 
        return self.application(environ, start_response)
 
    
 
    
 
    def __fixup(self, environ):
 
        """Function to fixup the environ as needed. In order to use this
 
        middleware you should set this header inside your 
 
        proxy ie. nginx, apache etc.
 
        """
 
        proto = environ.get('HTTP_X_URL_SCHEME')
 
            
 
        if proto == 'https':
 
            environ['wsgi.url_scheme'] = proto
 
        else:
 
            environ['wsgi.url_scheme'] = 'http'
 
        return None
pylons_app/lib/middleware/simplehg.py
Show inline comments
 
#!/usr/bin/env python
 
# encoding: utf-8
 
# middleware to handle mercurial api calls
 
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
 
 
 
# 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.
 
#
 
# Copyright (c) 2010 marcink.  All rights reserved.
 
# 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 2010-04-28
 

	
 
@author: marcink
 
SimpleHG middleware for handling mercurial protocol request (push/clone etc.)
 
It's implemented with basic auth function
 
"""
 
from datetime import datetime
 
from mercurial.hgweb import hgweb
 
from mercurial.hgweb.request import wsgiapplication
 
from paste.auth.basic import AuthBasicAuthenticator
 
from paste.httpheaders import REMOTE_USER, AUTH_TYPE
 
from pylons_app.lib.auth import authfunc
 
from pylons_app.lib.utils import is_mercurial, make_ui, invalidate_cache
 
from pylons_app.model import meta
 
from pylons_app.model.db import UserLog, User
 
from webob.exc import HTTPNotFound
 
import logging
 
import os
 
log = logging.getLogger(__name__)
 

	
 
class SimpleHg(object):
 

	
 
    def __init__(self, application, config):
 
        self.application = application
 
        self.config = config
 
        #authenticate this mercurial request using 
 
        realm = '%s %s' % (config['hg_app_name'], 'mercurial repository')
 
        self.authenticate = AuthBasicAuthenticator(realm, authfunc)
 
        
 
    def __call__(self, environ, start_response):
 
        if not is_mercurial(environ):
 
            return self.application(environ, start_response)
 
        else:
 
            #===================================================================
 
            # AUTHENTICATE THIS MERCURIAL REQUEST
 
            #===================================================================
 
            username = REMOTE_USER(environ)
 
            if not username:
 
                result = self.authenticate(environ)
 
                if isinstance(result, str):
 
                    AUTH_TYPE.update(environ, 'basic')
 
                    REMOTE_USER.update(environ, result)
 
                else:
 
                    return result.wsgi_application(environ, start_response)
 
            
 
            try:
 
                repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
 
            except Exception as e:
 
                log.error(e)
 
                return HTTPNotFound()(environ, start_response)
 
            
 
            #since we wrap into hgweb, just reset the path
 
            environ['PATH_INFO'] = '/'
 
            self.baseui = make_ui(self.config['hg_app_repo_conf'])
 
            self.basepath = self.baseui.configitems('paths')[0][1]\
 
                                                            .replace('*', '')
 
            self.repo_path = os.path.join(self.basepath, repo_name)
 
            try:
 
                app = wsgiapplication(self.__make_app)
 
            except Exception as e:
 
                log.error(e)
 
                return HTTPNotFound()(environ, start_response)
 
            action = self.__get_action(environ)            
 
            #invalidate cache on push
 
            if action == 'push':
 
                self.__invalidate_cache(repo_name)
 
            
 
            if action:
 
                username = self.__get_environ_user(environ)
 
                self.__log_user_action(username, action, repo_name)
 
                         
 
            return app(environ, start_response)            
 

	
 
    def __make_app(self):
 
        hgserve = hgweb(self.repo_path)
 
        return  self.__load_web_settings(hgserve)
 
    
 
    def __get_environ_user(self, environ):
 
        return environ.get('REMOTE_USER')
 
        
 
    def __get_action(self, environ):
 
        """
 
        Maps mercurial request commands into a pull or push command.
 
        @param environ:
 
        """
 
        mapping = {
 
            'changegroup': 'pull',
 
            'changegroupsubset': 'pull',
 
            'unbundle': 'push',
 
            'stream_out': 'pull',
 
        }                    
 
        for qry in environ['QUERY_STRING'].split('&'):
 
            if qry.startswith('cmd'):
 
                cmd = qry.split('=')[-1]
 
                if mapping.has_key(cmd):
pylons_app/lib/utils.py
Show inline comments
 
#!/usr/bin/env python
 
# encoding: utf-8
 
# Utilities for hg app
 
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
 
 
 
# 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 April 18, 2010
 
Utilities for hg app
 
@author: marcink
 
"""
 

	
 
import os
 
import logging
 
from mercurial import ui, config, hg
 
from mercurial.error import RepoError
 
log = logging.getLogger(__name__)
 

	
 

	
 
def get_repo_slug(request):
 
    return request.environ['pylons.routes_dict'].get('repo_name')
 

	
 
def is_mercurial(environ):
 
    """
 
    Returns True if request's target is mercurial server - header
 
    ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
 
    """
 
    http_accept = environ.get('HTTP_ACCEPT')
 
    if http_accept and http_accept.startswith('application/mercurial'):
 
        return True
 
    return False
 

	
 
def check_repo_dir(paths):
 
    repos_path = paths[0][1].split('/')
 
    if repos_path[-1] in ['*', '**']:
 
        repos_path = repos_path[:-1]
 
    if repos_path[0] != '/':
 
        repos_path[0] = '/'
 
    if not os.path.isdir(os.path.join(*repos_path)):
 
        raise Exception('Not a valid repository in %s' % paths[0][1])
 

	
 
def check_repo(repo_name, base_path):
 

	
 
    repo_path = os.path.join(base_path, repo_name)
 

	
 
    try:
 
        r = hg.repository(ui.ui(), repo_path)
 
        hg.verify(r)
 
        #here we hnow that repo exists it was verified
 
        log.info('%s repo is already created', repo_name)
 
        return False
 
        #raise Exception('Repo exists')
 
    except RepoError:
 
        log.info('%s repo is free for creation', repo_name)
 
        #it means that there is no valid repo there...
 
        return True
 
                
 
def make_ui(path=None, checkpaths=True):        
 
    """
 
    A funcion that will read python rc files and make an ui from read options
 
    
 
    @param path: path to mercurial config file
 
    """
 
    if not path:
 
        log.error('repos config path is empty !')
 
    
 
    if not os.path.isfile(path):
 
        log.warning('Unable to read config file %s' % path)
 
        return False
 
    #propagated from mercurial documentation
 
    sections = [
 
                'alias',
 
                'auth',
 
                'decode/encode',
 
                'defaults',
 
                'diff',
 
                'email',
 
                'extensions',
 
                'format',
 
                'merge-patterns',
 
                'merge-tools',
 
                'hooks',
 
                'http_proxy',
 
                'smtp',
 
                'patch',
 
                'paths',
 
                'profiling',
 
                'server',
 
                'trusted',
 
                'ui',
 
                'web',
 
                ]
 

	
 
    baseui = ui.ui()
 
    cfg = config.config()
 
    cfg.read(path)
 
    if checkpaths:check_repo_dir(cfg.items('paths'))
 

	
 
    for section in sections:
 
        for k, v in cfg.items(section):
 
            baseui.setconfig(section, k, v)
 
    
 
    return baseui
 

	
 
def invalidate_cache(name, *args):
 
    """Invalidates given name cache"""
 
    
 
    from beaker.cache import region_invalidate
 
    log.info('INVALIDATING CACHE FOR %s', name)
 
    
 
    """propagate our arguments to make sure invalidation works. First
 
    argument has to be the name of cached func name give to cache decorator
 
    without that the invalidation would not work"""
 
    tmp = [name]
 
    tmp.extend(args)
 
    args = tuple(tmp)
 
    
 
    if name == 'cached_repo_list':
 
        from pylons_app.model.hg_model import _get_repos_cached
 
        region_invalidate(_get_repos_cached, None, *args)
 
        
 
    if name == 'full_changelog':
 
        from pylons_app.model.hg_model import _full_changelog_cached
 
        region_invalidate(_full_changelog_cached, None, *args)
 
        
 
from vcs.backends.base import BaseChangeset
 
from vcs.utils.lazy import LazyProperty
 
class EmptyChangeset(BaseChangeset):
 
    
 
    revision = -1
 

	
 
    @LazyProperty
 
    def raw_id(self):
 
        """
 
        Returns raw string identifing this changeset, useful for web
 
        representation.
 
        """
 
        return '0' * 12
 

	
 

	
 
def repo2db_mapper():
 
    """
 
    scann all dirs for .hgdbid
 
    if some dir doesn't have one generate one.
 
    """
 
    pass
 
\ No newline at end of file
 
    pass
pylons_app/model/hg_model.py
Show inline comments
 
#!/usr/bin/env python
 
# encoding: utf-8
 
#
 
# Copyright (c) 2010 marcink.  All rights reserved.
 
# Model for hg app
 
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
 
 
 
# 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.
 
#
 
'''
 
Created on Apr 9, 2010
 
# 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 April 9, 2010
 
Model for hg app
 
@author: marcink
 
'''
 
"""
 

	
 
from beaker.cache import cache_region
 
from mercurial import ui
 
from mercurial.hgweb.hgwebdir_mod import findrepos
 
from pylons import app_globals as g
 
from vcs.exceptions import RepositoryError, VCSError
 
import logging
 
import os
 
import sys
 
log = logging.getLogger(__name__)
 

	
 
try:
 
    from vcs.backends.hg import MercurialRepository
 
except ImportError:
 
    sys.stderr.write('You have to import vcs module')
 
    raise Exception('Unable to import vcs')
 

	
 

	
 
@cache_region('long_term', 'cached_repo_list')
 
def _get_repos_cached():
 
    """
 
    return cached dict with repos
 
    """
 
    return HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui)
 

	
 
@cache_region('long_term', 'full_changelog')
 
def _full_changelog_cached(repo_name):
 
    log.info('getting full changelog for %s', repo_name)
 
    return list(reversed(list(HgModel().get_repo(repo_name))))
 

	
 
class HgModel(object):
 
    """
 
    Mercurial Model
 
    """
 

	
 
    def __init__(self):
 
        """
 
        Constructor
 
        """
 
        pass
 
    
 
    @staticmethod
 
    def repo_scan(repos_prefix, repos_path, baseui):
 
        """
 
        Listing of repositories in given path. This path should not be a 
 
        repository itself. Return a dictionary of repository objects
 
        :param repos_path: path to directory it could take syntax with 
 
        * or ** for deep recursive displaying repositories
 
        """
 
        def check_repo_dir(path):
 
            """
 
            Checks the repository
 
            :param path:
 
            """
 
            repos_path = path.split('/')
 
            if repos_path[-1] in ['*', '**']:
 
                repos_path = repos_path[:-1]
 
            if repos_path[0] != '/':
 
                repos_path[0] = '/'
 
            if not os.path.isdir(os.path.join(*repos_path)):
 
                raise RepositoryError('Not a valid repository in %s' % path[0][1])        
 
        if not repos_path.endswith('*'):
 
            raise VCSError('You need to specify * or ** at the end of path '
 
                            'for recursive scanning')
 
            
 
        check_repo_dir(repos_path)
 
        log.info('scanning for repositories in %s', repos_path)
 
        repos = findrepos([(repos_prefix, repos_path)])
 
        if not isinstance(baseui, ui.ui):
 
            baseui = ui.ui()
 
    
 
        repos_list = {}
 
        for name, path in repos:
 
            try:
 
                #name = name.split('/')[-1]
 
                if repos_list.has_key(name):
 
                    raise RepositoryError('Duplicate repository name %s found in'
 
                                    ' %s' % (name, path))
 
                else:
 
                    repos_list[name] = MercurialRepository(path, baseui=baseui)
 
                    repos_list[name].name = name
 
            except OSError:
 
                continue
 
        return repos_list
 
        
 
    def get_repos(self):
 
        for name, repo in _get_repos_cached().items():
 
            if repo._get_hidden():
 
                #skip hidden web repository
 
                continue
 
            
 
            last_change = repo.last_change
 
            try:
 
                tip = repo.get_changeset('tip')
 
            except RepositoryError:
 
                from pylons_app.lib.utils import EmptyChangeset
pylons_app/model/user_model.py
Show inline comments
 
#!/usr/bin/env python
 
# encoding: utf-8
 
# Model for users
 
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
 
 
 
# 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.
 
#
 
# Copyright (c) 2010 marcink.  All rights reserved.
 
# 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 April 9, 2010
 
Model for users
 
@author: marcink
 
"""
 

	
 
from pylons_app.model.db import User
 
from pylons_app.model.meta import Session
 
'''
 
Created on Apr 9, 2010
 

	
 
@author: marcink
 
'''
 

	
 
class UserModel(object):
 

	
 
    def __init__(self):
 
        self.sa = Session() 
 
    
 
    def get_user(self, id):
 
        return self.sa.query(User).get(id)
 
    
 
    def create(self, form_data):
 
        try:
 
            new_user = User()
 
            for k, v in form_data.items():
 
                setattr(new_user, k, v)
 
                
 
            self.sa.add(new_user)
 
            self.sa.commit()
 
        except:
 
            self.sa.rollback()
 
            raise      
 
    
 
    def update(self, id, form_data):
 
        try:
 
            new_user = self.sa.query(User).get(id)
 
            for k, v in form_data.items():
 
                if k == 'new_password' and v != '':
 
                    
 
                    new_user.password = v
 
                else:
 
                    setattr(new_user, k, v)
 
                
 
            self.sa.add(new_user)
 
            self.sa.commit()
 
        except:
 
            self.sa.rollback()
 
            raise      
pylons_app/templates/admin/permissions/permissions.html
Show inline comments
 
## -*- coding: utf-8 -*-
 
<%inherit file="/base/base.html"/>
 

	
 
<%def name="title()">
 
    ${_('Permissions administration')}
 
</%def>
 
<%def name="breadcrumbs()">
 
	${h.link_to(u'Admin',h.url('admin_home'))}
 
	 /  
 
	 ${_('Permissions')}
 
</%def>
 
<%def name="page_nav()">
 
	${self.menu('admin')}
 
	${self.submenu('permissions')}
 
</%def>
 
<%def name="main()">
 
	<div>
 
	<h2>${_('Permissions')}</h2>
 
	todo :) 
 
	</i>
 
    </div>
 
</%def>    
0 comments (0 inline, 0 general)