Show More
@@ -0,0 +1,13 b'' | |||
|
1 | """ | |
|
2 | Hg app, a web based mercurial repository managment based on pylons | |
|
3 | """ | |
|
4 | ||
|
5 | VERSION = (0, 6, 0, 'beta') | |
|
6 | ||
|
7 | __version__ = '.'.join((str(each) for each in VERSION[:4])) | |
|
8 | ||
|
9 | def get_version(): | |
|
10 | """ | |
|
11 | Returns shorter version (digit parts only) as string. | |
|
12 | """ | |
|
13 | return '.'.join((str(each) for each in VERSION[:3])) |
@@ -1,28 +1,37 b'' | |||
|
1 | 1 | """The base Controller API |
|
2 | 2 | |
|
3 | 3 | Provides the BaseController class for subclassing. |
|
4 | 4 | """ |
|
5 | from beaker.cache import cache_region | |
|
6 | from pylons import config, tmpl_context as c, request, session | |
|
5 | 7 | from pylons.controllers import WSGIController |
|
6 | 8 | from pylons.templating import render_mako as render |
|
9 | from pylons_app.lib.auth import LoginRequired, AuthUser | |
|
10 | from pylons_app.lib.utils import get_repo_slug | |
|
7 | 11 | from pylons_app.model import meta |
|
8 | from beaker.cache import cache_region | |
|
9 | from pylons import tmpl_context as c | |
|
10 | 12 | from pylons_app.model.hg_model import HgModel |
|
13 | from pylons_app import get_version | |
|
11 | 14 | |
|
12 | 15 | @cache_region('long_term', 'cached_repo_list') |
|
13 | 16 | def _get_repos_cached(): |
|
14 | 17 | return [rep for rep in HgModel().get_repos()] |
|
15 | 18 | |
|
16 | 19 | class BaseController(WSGIController): |
|
17 |
|
|
|
20 | ||
|
21 | def __before__(self): | |
|
22 | c.hg_app_version = get_version() | |
|
23 | c.repos_prefix = config['hg_app_name'] | |
|
24 | c.repo_name = get_repo_slug(request) | |
|
25 | c.hg_app_user = session.get('hg_app_user', AuthUser()) | |
|
26 | c.cached_repo_list = _get_repos_cached() | |
|
27 | self.sa = meta.Session | |
|
28 | ||
|
18 | 29 | def __call__(self, environ, start_response): |
|
19 | 30 | """Invoke the Controller""" |
|
20 | 31 | # WSGIController.__call__ dispatches to the Controller method |
|
21 | 32 | # the request is routed to. This routing information is |
|
22 | 33 | # available in environ['pylons.routes_dict'] |
|
23 | c.cached_repo_list = _get_repos_cached() | |
|
24 | self.sa = meta.Session | |
|
25 | 34 | try: |
|
26 | 35 | return WSGIController.__call__(self, environ, start_response) |
|
27 | 36 | finally: |
|
28 | 37 | meta.Session.remove() |
@@ -1,86 +1,86 b'' | |||
|
1 | 1 | #!/usr/bin/env python |
|
2 | 2 | # encoding: utf-8 |
|
3 | 3 | # |
|
4 | 4 | # Copyright (c) 2010 marcink. All rights reserved. |
|
5 | 5 | # |
|
6 | 6 | """ |
|
7 | 7 | Created on 2010-04-28 |
|
8 | 8 | |
|
9 | 9 | @author: marcink |
|
10 | 10 | SimpleHG middleware for handling mercurial protocol request (push/clone etc.) |
|
11 | 11 | It's implemented with basic auth function |
|
12 | 12 | """ |
|
13 | 13 | |
|
14 | 14 | from mercurial.hgweb import hgweb |
|
15 | 15 | from mercurial.hgweb.request import wsgiapplication |
|
16 | 16 | from paste.auth.basic import AuthBasicAuthenticator |
|
17 | 17 | from paste.httpheaders import REMOTE_USER, AUTH_TYPE |
|
18 | 18 | from pylons_app.lib.utils import is_mercurial |
|
19 | 19 | from pylons_app.lib.auth import authfunc |
|
20 | 20 | from pylons_app.lib.utils import make_ui, invalidate_cache |
|
21 | 21 | from webob.exc import HTTPNotFound |
|
22 | 22 | import os |
|
23 | 23 | |
|
24 | 24 | class SimpleHg(object): |
|
25 | 25 | |
|
26 | 26 | def __init__(self, application, config): |
|
27 | 27 | self.application = application |
|
28 | 28 | self.config = config |
|
29 | 29 | #authenticate this mercurial request using |
|
30 |
realm = '%s %s' % (config[' |
|
|
30 | realm = '%s %s' % (config['hg_app_name'], 'mercurial repository') | |
|
31 | 31 | self.authenticate = AuthBasicAuthenticator(realm, authfunc) |
|
32 | 32 | |
|
33 | 33 | def __call__(self, environ, start_response): |
|
34 | 34 | if not is_mercurial(environ): |
|
35 | 35 | return self.application(environ, start_response) |
|
36 | 36 | else: |
|
37 | 37 | #=================================================================== |
|
38 | 38 | # AUTHENTICATE THIS MERCURIAL REQUEST |
|
39 | 39 | #=================================================================== |
|
40 | 40 | username = REMOTE_USER(environ) |
|
41 | 41 | if not username: |
|
42 | 42 | result = self.authenticate(environ) |
|
43 | 43 | if isinstance(result, str): |
|
44 | 44 | AUTH_TYPE.update(environ, 'basic') |
|
45 | 45 | REMOTE_USER.update(environ, result) |
|
46 | 46 | else: |
|
47 | 47 | return result.wsgi_application(environ, start_response) |
|
48 | 48 | |
|
49 | 49 | try: |
|
50 | 50 | repo_name = environ['PATH_INFO'].split('/')[1] |
|
51 | 51 | except: |
|
52 | 52 | return HTTPNotFound()(environ, start_response) |
|
53 | 53 | |
|
54 | 54 | #since we wrap into hgweb, just reset the path |
|
55 | 55 | environ['PATH_INFO'] = '/' |
|
56 | 56 | self.baseui = make_ui() |
|
57 | 57 | self.basepath = self.baseui.configitems('paths')[0][1]\ |
|
58 | 58 | .replace('*', '') |
|
59 | 59 | self.repo_path = os.path.join(self.basepath, repo_name) |
|
60 | 60 | try: |
|
61 | 61 | app = wsgiapplication(self._make_app) |
|
62 | 62 | except Exception as e: |
|
63 | 63 | return HTTPNotFound()(environ, start_response) |
|
64 | 64 | |
|
65 | 65 | """we know that some change was made to repositories and we should |
|
66 | 66 | invalidate the cache to see the changes right away""" |
|
67 | 67 | invalidate_cache('full_changelog', repo_name) |
|
68 | 68 | return app(environ, start_response) |
|
69 | 69 | |
|
70 | 70 | def _make_app(self): |
|
71 | 71 | hgserve = hgweb(self.repo_path) |
|
72 | 72 | return self.load_web_settings(hgserve) |
|
73 | 73 | |
|
74 | 74 | |
|
75 | 75 | def load_web_settings(self, hgserve): |
|
76 | 76 | repoui = make_ui(os.path.join(self.repo_path, '.hg', 'hgrc'), False) |
|
77 | 77 | #set the global ui for hgserve |
|
78 | 78 | hgserve.repo.ui = self.baseui |
|
79 | 79 | |
|
80 | 80 | if repoui: |
|
81 | 81 | #set the repository based config |
|
82 | 82 | hgserve.repo.ui = repoui |
|
83 | 83 | |
|
84 | 84 | return hgserve |
|
85 | 85 | |
|
86 | 86 |
@@ -1,121 +1,121 b'' | |||
|
1 | 1 | ## -*- coding: utf-8 -*- |
|
2 | 2 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> |
|
3 | 3 | <html xmlns="http://www.w3.org/1999/xhtml" id="mainhtml"> |
|
4 | 4 | <head> |
|
5 | 5 | <link rel="icon" href="/images/hgicon.png" type="image/png" /> |
|
6 | 6 | <meta name="robots" content="index, nofollow"/> |
|
7 | 7 | <title>${next.title()}</title> |
|
8 | 8 | ##<link rel="stylesheet" href="/js/yui/reset-fonts-grids/reset-fonts-grids.css" type="text/css" /> |
|
9 | 9 | ${self.css()} |
|
10 | 10 | ${self.js()} |
|
11 | 11 | </head> |
|
12 | 12 | |
|
13 | 13 | <body class="mainbody"> |
|
14 | 14 | <div id="container"> |
|
15 | 15 | <div class="page-header"> |
|
16 | 16 | <h1>${next.breadcrumbs()}</h1> |
|
17 | 17 | ${self.page_nav()} |
|
18 | 18 | </div> |
|
19 | 19 | <div id="main"> |
|
20 | 20 | ${next.main()} |
|
21 | 21 | </div> |
|
22 | 22 | <div class="page-footer"> |
|
23 |
|
|
|
23 | Hg App ${c.hg_app_version} © 2010 | |
|
24 | 24 | </div> |
|
25 | 25 | |
|
26 | 26 | <div id="powered-by"> |
|
27 | 27 | <p> |
|
28 | 28 | <a href="http://mercurial.selenic.com/" title="Mercurial"> |
|
29 | 29 | <img src="/images/hglogo.png" width="75" height="90" alt="mercurial"/></a> |
|
30 | 30 | </p> |
|
31 | 31 | </div> |
|
32 | 32 | |
|
33 | 33 | <div id="corner-top-left"></div> |
|
34 | 34 | <div id="corner-top-right"></div> |
|
35 | 35 | <div id="corner-bottom-left"></div> |
|
36 | 36 | <div id="corner-bottom-right"></div> |
|
37 | 37 | |
|
38 | 38 | </div> |
|
39 | 39 | </body> |
|
40 | 40 | </html> |
|
41 | 41 | |
|
42 | 42 | ### MAKO DEFS ### |
|
43 | 43 | |
|
44 | 44 | <%def name="page_nav()"> |
|
45 | 45 | ${self.menu()} |
|
46 | 46 | </%def> |
|
47 | 47 | |
|
48 | 48 | <%def name="menu(current)"> |
|
49 | 49 | <% |
|
50 | 50 | def is_current(selected): |
|
51 | 51 | if selected == current: |
|
52 | 52 | return 'class=current' |
|
53 | 53 | %> |
|
54 | 54 | %if current not in ['home','admin']: |
|
55 | 55 | <script type="text/javascript"> |
|
56 | 56 | YAHOO.util.Event.onDOMReady(function(){ |
|
57 | 57 | YAHOO.util.Event.addListener('repo_switcher','click',function(){ |
|
58 | 58 | if(YAHOO.util.Dom.hasClass('repo_switcher','selected')){ |
|
59 | 59 | YAHOO.util.Dom.setStyle('switch_repos','display','none'); |
|
60 | 60 | YAHOO.util.Dom.setStyle('repo_switcher','background',''); |
|
61 | 61 | YAHOO.util.Dom.removeClass('repo_switcher','selected'); |
|
62 | 62 | } |
|
63 | 63 | else{ |
|
64 | 64 | YAHOO.util.Dom.setStyle('switch_repos','display',''); |
|
65 | 65 | YAHOO.util.Dom.setStyle('repo_switcher','background','#FFFFFF'); |
|
66 | 66 | YAHOO.util.Dom.addClass('repo_switcher','selected'); |
|
67 | 67 | } |
|
68 | 68 | }); |
|
69 | 69 | YAHOO.util.Event.addListener('repos_list','change',function(e){ |
|
70 | 70 | var wa = YAHOO.util.Dom.get('repos_list').value; |
|
71 | 71 | |
|
72 | 72 | var url = "${h.url('summary_home',repo_name='__REPLACE__')}".replace('__REPLACE__',wa); |
|
73 | 73 | window.location = url; |
|
74 | 74 | }) |
|
75 | 75 | }); |
|
76 | 76 | </script> |
|
77 | 77 | <ul class="page-nav"> |
|
78 | 78 | <li> |
|
79 | 79 | <a id="repo_switcher" title="${_('Switch repository')}" href="#">↓</a> |
|
80 | 80 | <div id="switch_repos" style="display:none;position: absolute;width: 150px;height: 25px"> |
|
81 | 81 | <select id="repos_list" size="=10"> |
|
82 | 82 | %for repo in c.cached_repo_list: |
|
83 | 83 | <option value="${repo['name']}">${repo['name']}</option> |
|
84 | 84 | %endfor |
|
85 | 85 | </select> |
|
86 | 86 | </div> |
|
87 | 87 | </li> |
|
88 | 88 | <li ${is_current('summary')}>${h.link_to(_('summary'),h.url('summary_home',repo_name=c.repo_name))}</li> |
|
89 | 89 | <li ${is_current('shortlog')}>${h.link_to(_('shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}</li> |
|
90 | 90 | <li ${is_current('changelog')}>${h.link_to(_('changelog'),h.url('changelog_home',repo_name=c.repo_name))}</li> |
|
91 | 91 | <li ${is_current('branches')}>${h.link_to(_('branches'),h.url('branches_home',repo_name=c.repo_name))}</li> |
|
92 | 92 | <li ${is_current('tags')}>${h.link_to(_('tags'),h.url('tags_home',repo_name=c.repo_name))}</li> |
|
93 | 93 | <li ${is_current('files')}>${h.link_to(_('files'),h.url('files_home',repo_name=c.repo_name))}</li> |
|
94 | 94 | </ul> |
|
95 | 95 | %else: |
|
96 | 96 | <ul class="page-nav"> |
|
97 | 97 | <li ${is_current('home')}>${h.link_to(_('Home'),h.url('/'))}</li> |
|
98 | 98 | <li ${is_current('admin')}>${h.link_to(_('Admin'),h.url('admin_home'))}</li> |
|
99 | 99 | <li class="logout">${h.link_to(u'Logout',h.url('logout_home'))}</li> |
|
100 | 100 | </ul> |
|
101 | 101 | %endif |
|
102 | 102 | </%def> |
|
103 | 103 | |
|
104 | 104 | <%def name="css()"> |
|
105 | 105 | <link rel="stylesheet" href="/css/monoblue_custom.css" type="text/css" /> |
|
106 | 106 | </%def> |
|
107 | 107 | |
|
108 | 108 | <%def name="js()"> |
|
109 | 109 | <script type="text/javascript" src="/js/yui/utilities/utilities.js"></script> |
|
110 | 110 | </%def> |
|
111 | 111 | |
|
112 | 112 | <!-- DEFINITION OF FORM ERROR FETCHER --> |
|
113 | 113 | <%def name="get_form_error(element)"> |
|
114 | 114 | %if hasattr(c,'form_errors') and type(c.form_errors) == dict: |
|
115 | 115 | %if c.form_errors.get(element,False): |
|
116 | 116 | <span class="error-message"> |
|
117 | 117 | ${c.form_errors.get(element,'')} |
|
118 | 118 | </span> |
|
119 | 119 | %endif |
|
120 | 120 | %endif |
|
121 | 121 | </%def> No newline at end of file |
@@ -1,40 +1,41 b'' | |||
|
1 | from pylons_app import get_version | |
|
1 | 2 | try: |
|
2 | 3 | from setuptools import setup, find_packages |
|
3 | 4 | except ImportError: |
|
4 | 5 | from ez_setup import use_setuptools |
|
5 | 6 | use_setuptools() |
|
6 | 7 | from setuptools import setup, find_packages |
|
7 | 8 | |
|
8 | 9 | setup( |
|
9 | 10 | name='pylons_app', |
|
10 | version='1.0', | |
|
11 | version=get_version(), | |
|
11 | 12 | description='', |
|
12 | 13 | author='marcin kuzminski', |
|
13 | 14 | author_email='marcin@python-blog.com', |
|
14 | 15 | url='', |
|
15 | 16 | install_requires=[ |
|
16 | 17 | "Pylons>=1.0.0", |
|
17 | 18 | "SQLAlchemy>=0.6", |
|
18 | 19 | "Mako>=0.3.2", |
|
19 | 20 | "vcs>=0.1.1", |
|
20 | 21 | "pygments>=1.3.0" |
|
21 | 22 | ], |
|
22 | 23 | setup_requires=["PasteScript>=1.6.3"], |
|
23 | 24 | packages=find_packages(exclude=['ez_setup']), |
|
24 | 25 | include_package_data=True, |
|
25 | 26 | test_suite='nose.collector', |
|
26 | 27 | package_data={'pylons_app': ['i18n/*/LC_MESSAGES/*.mo']}, |
|
27 | 28 | message_extractors={'pylons_app': [ |
|
28 | 29 | ('**.py', 'python', None), |
|
29 | 30 | ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}), |
|
30 | 31 | ('public/**', 'ignore', None)]}, |
|
31 | 32 | zip_safe=False, |
|
32 | 33 | paster_plugins=['PasteScript', 'Pylons'], |
|
33 | 34 | entry_points=""" |
|
34 | 35 | [paste.app_factory] |
|
35 | 36 | main = pylons_app.config.middleware:make_app |
|
36 | 37 | |
|
37 | 38 | [paste.app_install] |
|
38 | 39 | main = pylons.util:PylonsInstaller |
|
39 | 40 | """, |
|
40 | 41 | ) |
General Comments 0
You need to be logged in to leave comments.
Login now