##// END OF EJS Templates
implemented admin page login
marcink -
r45:a886f5eb default
parent child Browse files
Show More
@@ -1,97 +1,128 b''
1 import logging
1 import logging
2
2
3 from pylons import request, response, session, tmpl_context as c, url, app_globals as g
3 from pylons import request, response, session, tmpl_context as c, url, app_globals as g
4 from pylons.controllers.util import abort, redirect
4 from pylons.controllers.util import abort, redirect
5
5
6 from pylons_app.lib.base import BaseController, render
6 from pylons_app.lib.base import BaseController, render
7 import os
7 import os
8 from mercurial import ui, hg
8 from mercurial import ui, hg
9 from mercurial.error import RepoError
9 from mercurial.error import RepoError
10 from ConfigParser import ConfigParser
10 from ConfigParser import ConfigParser
11 from pylons_app.lib import auth
11 from pylons_app.lib import auth
12 from pylons_app.model.forms import LoginForm
13 import formencode
14 import formencode.htmlfill as htmlfill
12 log = logging.getLogger(__name__)
15 log = logging.getLogger(__name__)
13
16
14 class AdminController(BaseController):
17 class AdminController(BaseController):
15
18
16
19
17 def __before__(self):
20 def __before__(self):
18 c.staticurl = g.statics
21 c.staticurl = g.statics
19 c.admin_user = True
22 c.admin_user = session.get('admin_user')
23 c.admin_username = session.get('admin_username')
20
24
21 def index(self):
25 def index(self):
22 # Return a rendered template
26 # Return a rendered template
27 if request.POST:
28 #import Login Form validator class
29 login_form = LoginForm()
30
31 try:
32 c.form_result = login_form.to_python(dict(request.params))
33 if auth.authfunc(None, c.form_result['username'], c.form_result['password']) and\
34 c.form_result['username'] == 'admin':
35 session['admin_user'] = True
36 session['admin_username'] = c.form_result['username']
37 session.save()
38 return redirect(url('admin_home'))
39 else:
40 raise formencode.Invalid('Login Error', None, None,
41 error_dict={'username':'invalid login',
42 'password':'invalid password'})
43
44 except formencode.Invalid, error:
45 c.form_result = error.value
46 c.form_errors = error.error_dict or {}
47 html = render('/admin.html')
48
49 return htmlfill.render(
50 html,
51 defaults=c.form_result,
52 encoding="UTF-8"
53 )
23 return render('/admin.html')
54 return render('/admin.html')
24
55
25 def repos_manage(self):
56 def repos_manage(self):
26 return render('/repos_manage.html')
57 return render('/repos_manage.html')
27
58
28 def users_manage(self):
59 def users_manage(self):
29 conn, cur = auth.get_sqlite_conn_cur()
60 conn, cur = auth.get_sqlite_conn_cur()
30 cur.execute('SELECT * FROM users')
61 cur.execute('SELECT * FROM users')
31 c.users_list = cur.fetchall()
62 c.users_list = cur.fetchall()
32 return render('/users_manage.html')
63 return render('/users_manage.html')
33
64
34 def manage_hgrc(self):
65 def manage_hgrc(self):
35 pass
66 pass
36
67
37 def hgrc(self, dirname):
68 def hgrc(self, dirname):
38 filename = os.path.join(dirname, '.hg', 'hgrc')
69 filename = os.path.join(dirname, '.hg', 'hgrc')
39 return filename
70 return filename
40
71
41 def add_repo(self, new_repo):
72 def add_repo(self, new_repo):
42
73
43
74
44 #extra check it can be add since it's the command
75 #extra check it can be add since it's the command
45 if new_repo == '_admin':
76 if new_repo == '_admin':
46 c.msg = 'DENIED'
77 c.msg = 'DENIED'
47 c.new_repo = ''
78 c.new_repo = ''
48 return render('add.html')
79 return render('add.html')
49
80
50 new_repo = new_repo.replace(" ", "_")
81 new_repo = new_repo.replace(" ", "_")
51 new_repo = new_repo.replace("-", "_")
82 new_repo = new_repo.replace("-", "_")
52
83
53 try:
84 try:
54 self._create_repo(new_repo)
85 self._create_repo(new_repo)
55 c.new_repo = new_repo
86 c.new_repo = new_repo
56 c.msg = 'added repo'
87 c.msg = 'added repo'
57 except Exception as e:
88 except Exception as e:
58 c.new_repo = 'Exception when adding: %s' % new_repo
89 c.new_repo = 'Exception when adding: %s' % new_repo
59 c.msg = str(e)
90 c.msg = str(e)
60
91
61 return render('add.html')
92 return render('add.html')
62
93
63 def _check_repo(self, repo_name):
94 def _check_repo(self, repo_name):
64 p = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
95 p = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
65 config_path = os.path.join(p, 'hgwebdir.config')
96 config_path = os.path.join(p, 'hgwebdir.config')
66
97
67 cp = ConfigParser()
98 cp = ConfigParser()
68
99
69 cp.read(config_path)
100 cp.read(config_path)
70 repos_path = cp.get('paths', '/').replace("**", '')
101 repos_path = cp.get('paths', '/').replace("**", '')
71
102
72 if not repos_path:
103 if not repos_path:
73 raise Exception('Could not read config !')
104 raise Exception('Could not read config !')
74
105
75 self.repo_path = os.path.join(repos_path, repo_name)
106 self.repo_path = os.path.join(repos_path, repo_name)
76
107
77 try:
108 try:
78 r = hg.repository(ui.ui(), self.repo_path)
109 r = hg.repository(ui.ui(), self.repo_path)
79 hg.verify(r)
110 hg.verify(r)
80 #here we hnow that repo exists it was verified
111 #here we hnow that repo exists it was verified
81 log.info('%s repo is already created', repo_name)
112 log.info('%s repo is already created', repo_name)
82 raise Exception('Repo exists')
113 raise Exception('Repo exists')
83 except RepoError:
114 except RepoError:
84 log.info('%s repo is free for creation', repo_name)
115 log.info('%s repo is free for creation', repo_name)
85 #it means that there is no valid repo there...
116 #it means that there is no valid repo there...
86 return True
117 return True
87
118
88
119
89 def _create_repo(self, repo_name):
120 def _create_repo(self, repo_name):
90 if repo_name in [None, '', 'add']:
121 if repo_name in [None, '', 'add']:
91 raise Exception('undefined repo_name of repo')
122 raise Exception('undefined repo_name of repo')
92
123
93 if self._check_repo(repo_name):
124 if self._check_repo(repo_name):
94 log.info('creating repo %s in %s', repo_name, self.repo_path)
125 log.info('creating repo %s in %s', repo_name, self.repo_path)
95 cmd = """mkdir %s && hg init %s""" \
126 cmd = """mkdir %s && hg init %s""" \
96 % (self.repo_path, self.repo_path)
127 % (self.repo_path, self.repo_path)
97 os.popen(cmd)
128 os.popen(cmd)
@@ -1,110 +1,108 b''
1 import sqlite3
1 import sqlite3
2 import os
2 import os
3 import logging
3 import logging
4 from os.path import dirname as dn
4 from os.path import dirname as dn
5 from datetime import datetime
5 from datetime import datetime
6 import crypt
6 import crypt
7
7
8 log = logging.getLogger(__name__)
8 log = logging.getLogger(__name__)
9 ROOT = dn(dn(dn(os.path.realpath(__file__))))
9 ROOT = dn(dn(dn(os.path.realpath(__file__))))
10
10
11 def get_sqlite_conn_cur():
11 def get_sqlite_conn_cur():
12 conn = sqlite3.connect(os.path.join(ROOT, 'auth.sqlite'))
12 conn = sqlite3.connect(os.path.join(ROOT, 'auth.sqlite'))
13 cur = conn.cursor()
13 cur = conn.cursor()
14 return conn, cur
14 return conn, cur
15
15
16 def authfunc(environ, username, password):
16 def authfunc(environ, username, password):
17 conn, cur = get_sqlite_conn_cur()
17 conn, cur = get_sqlite_conn_cur()
18 password_crypt = crypt.crypt(password, '6a')
18 password_crypt = crypt.crypt(password, '6a')
19
19
20 try:
20 try:
21 cur.execute("SELECT * FROM users WHERE username=?", (username,))
21 cur.execute("SELECT * FROM users WHERE username=?", (username,))
22 data = cur.fetchone()
22 data = cur.fetchone()
23 except sqlite3.OperationalError as e:
23 except sqlite3.OperationalError as e:
24 data = None
24 data = None
25 log.error(e)
25 log.error(e)
26
27 if data:
26 if data:
28 if data[3]:
27 if data[3]:
29 if data[1] == username and data[2] == password_crypt:
28 if data[1] == username and data[2] == password_crypt:
30 log.info('user %s authenticated correctly', username)
29 log.info('user %s authenticated correctly', username)
31
30 if environ:
32 http_accept = environ.get('HTTP_ACCEPT')
31 http_accept = environ.get('HTTP_ACCEPT')
33
32
34 if http_accept.startswith('application/mercurial') or \
33 if http_accept.startswith('application/mercurial') or \
35 environ['PATH_INFO'].find('raw-file') != -1:
34 environ['PATH_INFO'].find('raw-file') != -1:
36 cmd = environ['PATH_INFO']
35 cmd = environ['PATH_INFO']
37 for qry in environ['QUERY_STRING'].split('&'):
36 for qry in environ['QUERY_STRING'].split('&'):
38 if qry.startswith('cmd'):
37 if qry.startswith('cmd'):
39 cmd += "|" + qry
38 cmd += "|" + qry
40
41 try:
42 cur.execute('''INSERT INTO
43 user_logs
44 VALUES(?,?,?,?)''',
45 (None, data[0], cmd, datetime.now()))
46 conn.commit()
47 except Exception as e:
48 conn.rollback()
49 log.error(e)
50
51
39
40 try:
41 cur.execute('''INSERT INTO
42 user_logs
43 VALUES(?,?,?,?)''',
44 (None, data[0], cmd, datetime.now()))
45 conn.commit()
46 except Exception as e:
47 conn.rollback()
48 log.error(e)
49
52 return True
50 return True
53 else:
51 else:
54 log.error('user %s is disabled', username)
52 log.error('user %s is disabled', username)
55
53
56 return False
54 return False
57
55
58 def create_user_table():
56 def create_user_table():
59 '''
57 '''
60 Create a auth database
58 Create a auth database
61 '''
59 '''
62 conn, cur = get_sqlite_conn_cur()
60 conn, cur = get_sqlite_conn_cur()
63 try:
61 try:
64 log.info('creating table %s', 'users')
62 log.info('creating table %s', 'users')
65 cur.execute('''DROP TABLE IF EXISTS users ''')
63 cur.execute('''DROP TABLE IF EXISTS users ''')
66 cur.execute('''CREATE TABLE users
64 cur.execute('''CREATE TABLE users
67 (id INTEGER PRIMARY KEY AUTOINCREMENT,
65 (id INTEGER PRIMARY KEY AUTOINCREMENT,
68 username TEXT,
66 username TEXT,
69 password TEXT,
67 password TEXT,
70 active INTEGER)''')
68 active INTEGER)''')
71 log.info('creating table %s', 'user_logs')
69 log.info('creating table %s', 'user_logs')
72 cur.execute('''DROP TABLE IF EXISTS user_logs ''')
70 cur.execute('''DROP TABLE IF EXISTS user_logs ''')
73 cur.execute('''CREATE TABLE user_logs
71 cur.execute('''CREATE TABLE user_logs
74 (id INTEGER PRIMARY KEY AUTOINCREMENT,
72 (id INTEGER PRIMARY KEY AUTOINCREMENT,
75 user_id INTEGER,
73 user_id INTEGER,
76 last_action TEXT,
74 last_action TEXT,
77 last_action_date DATETIME)''')
75 last_action_date DATETIME)''')
78 conn.commit()
76 conn.commit()
79 except:
77 except:
80 conn.rollback()
78 conn.rollback()
81 raise
79 raise
82
80
83 cur.close()
81 cur.close()
84
82
85 def create_user(username, password):
83 def create_user(username, password):
86 conn, cur = get_sqlite_conn_cur()
84 conn, cur = get_sqlite_conn_cur()
87 password_crypt = crypt.crypt(password, '6a')
85 password_crypt = crypt.crypt(password, '6a')
88 cur_date = datetime.now()
86 cur_date = datetime.now()
89 log.info('creating user %s', username)
87 log.info('creating user %s', username)
90 try:
88 try:
91 cur.execute('''INSERT INTO users values (?,?,?,?) ''',
89 cur.execute('''INSERT INTO users values (?,?,?,?) ''',
92 (None, username, password_crypt, 1,))
90 (None, username, password_crypt, 1,))
93 conn.commit()
91 conn.commit()
94 except:
92 except:
95 conn.rollback()
93 conn.rollback()
96 raise
94 raise
97
95
98 if __name__ == "__main__":
96 if __name__ == "__main__":
99 create_user_table()
97 create_user_table()
100 create_user('marcink', 'qweqwe')
98 create_user('marcink', 'qweqwe')
101 create_user('lukaszd', 'qweqwe')
99 create_user('lukaszd', 'qweqwe')
102 create_user('adriand', 'qweqwe')
100 create_user('adriand', 'qweqwe')
103 create_user('radek', 'qweqwe')
101 create_user('radek', 'qweqwe')
104 create_user('skrzeka', 'qweqwe')
102 create_user('skrzeka', 'qweqwe')
105 create_user('bart', 'qweqwe')
103 create_user('bart', 'qweqwe')
106 create_user('maho', 'qweqwe')
104 create_user('maho', 'qweqwe')
107 create_user('michalg', 'qweqwe')
105 create_user('michalg', 'qweqwe')
108 create_user('admin', 'qwe123qwe')
106 create_user('admin', 'qwe123qwe')
109
107
110 #authfunc('', 'marcink', 'qweqwe')
108 #authfunc('', 'marcink', 'qweqwe')
@@ -1,48 +1,48 b''
1 """Helper functions
1 """Helper functions
2
2
3 Consists of functions to typically be used within templates, but also
3 Consists of functions to typically be used within templates, but also
4 available to Controllers. This module is available to both as 'h'.
4 available to Controllers. This module is available to both as 'h'.
5 """
5 """
6 from pylons import url
6 from pylons import url
7 from webhelpers.html import (literal, HTML, escape)
7 from webhelpers.html import (literal, HTML, escape)
8 from webhelpers.html.tools import (auto_link, button_to, highlight, js_obfuscate
8 from webhelpers.html.tools import (auto_link, button_to, highlight, js_obfuscate
9 , mail_to, strip_links, strip_tags, tag_re)
9 , mail_to, strip_links, strip_tags, tag_re)
10 from webhelpers.html.tags import (auto_discovery_link, checkbox, css_classes,
10 from webhelpers.html.tags import (auto_discovery_link, checkbox, css_classes,
11 end_form, file, form, hidden, image,
11 end_form, file, form, hidden, image,
12 javascript_link, link_to, link_to_if,
12 javascript_link, link_to, link_to_if,
13 link_to_unless, ol, required_legend,
13 link_to_unless, ol, required_legend,
14 select, stylesheet_link,
14 select, stylesheet_link,
15 submit, text, textarea, title, ul, xml_declaration)
15 submit, text, password, textarea, title, ul, xml_declaration)
16 from webhelpers.text import (chop_at, collapse, convert_accented_entities,
16 from webhelpers.text import (chop_at, collapse, convert_accented_entities,
17 convert_misc_characters, convert_misc_entities,
17 convert_misc_characters, convert_misc_entities,
18 lchop, plural, rchop, remove_formatting, replace_whitespace,
18 lchop, plural, rchop, remove_formatting, replace_whitespace,
19 urlify)
19 urlify)
20
20
21 from webhelpers.pylonslib import Flash as _Flash
21 from webhelpers.pylonslib import Flash as _Flash
22 from webhelpers.pylonslib.secure_form import secure_form
22 from webhelpers.pylonslib.secure_form import secure_form
23
23
24 #Custom helper here :)
24 #Custom helper here :)
25 class _Link(object):
25 class _Link(object):
26 '''
26 '''
27 Make a url based on label and url with help of url_for
27 Make a url based on label and url with help of url_for
28 @param label:name of link if not defined url is used
28 @param label:name of link if not defined url is used
29 @param url: the url for link
29 @param url: the url for link
30 '''
30 '''
31
31
32 def __call__(self, label='', *url_, **urlargs):
32 def __call__(self, label='', *url_, **urlargs):
33 if label is None or '':
33 if label is None or '':
34 label = url
34 label = url
35 link_fn = link_to(label, url(*url_, **urlargs))
35 link_fn = link_to(label, url(*url_, **urlargs))
36 return link_fn
36 return link_fn
37
37
38
38
39 class _GetError(object):
39 class _GetError(object):
40
40
41 def __call__(self, field_name, form_errors):
41 def __call__(self, field_name, form_errors):
42 tmpl = """<span class="error_msg">%s</span>"""
42 tmpl = """<span class="error_msg">%s</span>"""
43 if form_errors and form_errors.has_key(field_name):
43 if form_errors and form_errors.has_key(field_name):
44 return literal(tmpl % form_errors.get(field_name))
44 return literal(tmpl % form_errors.get(field_name))
45
45
46 link = _Link()
46 link = _Link()
47 flash = _Flash()
47 flash = _Flash()
48 get_error = _GetError()
48 get_error = _GetError()
@@ -1,63 +1,58 b''
1 """ this is forms validation classes
1 """ this is forms validation classes
2 http://formencode.org/module-formencode.validators.html
2 http://formencode.org/module-formencode.validators.html
3 for list off all availible validators
3 for list off all availible validators
4
4
5 we can create our own validators
5 we can create our own validators
6
6
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 pre_validators [] These validators will be applied before the schema
8 pre_validators [] These validators will be applied before the schema
9 chained_validators [] These validators will be applied after the schema
9 chained_validators [] These validators will be applied after the schema
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
14
14
15
15
16 <name> = formencode.validators.<name of validator>
16 <name> = formencode.validators.<name of validator>
17 <name> must equal form name
17 <name> must equal form name
18 list=[1,2,3,4,5]
18 list=[1,2,3,4,5]
19 for select use formencode.All(OneOf(list), Int())
19 for select use formencode.All(OneOf(list), Int())
20
20
21 """
21 """
22
22
23 import formencode
23 import formencode
24 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex
24 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex
25 from pylons.i18n.translation import _
25 from pylons.i18n.translation import _
26 from webhelpers.pylonslib.secure_form import authentication_token
26 from webhelpers.pylonslib.secure_form import authentication_token
27
27
28 class ValidAuthToken(formencode.validators.FancyValidator):
28 class ValidAuthToken(formencode.validators.FancyValidator):
29 messages = {'invalid_token':_('Token mismatch')}
29 messages = {'invalid_token':_('Token mismatch')}
30
30
31 def validate_python(self, value, state):
31 def validate_python(self, value, state):
32
32
33 if value != authentication_token():
33 if value != authentication_token():
34 raise formencode.Invalid(self.message('invalid_token', state, search_number = value), value, state)
34 raise formencode.Invalid(self.message('invalid_token', state, search_number=value), value, state)
35
35
36
36
37 class WireTransferForm(object):
37 class LoginForm(formencode.Schema):
38 '''
38 allow_extra_fields = True
39 A factory wrapper class. It might return the instance of class for a validation, but also it can
39 filter_extra_fields = True
40 return the list for select fields values.
40 username = UnicodeString(
41 @param ret_type: type to return defaut: 'class'
41 strip=True,
42 '''
42 min=3,
43 #class attributes here
43 not_empty=True,
44 #it might be fetched from db,from models and so on
44 messages={
45 recipients_list = [
45 'empty':_('Please enter a login'),
46 (1, 'a'),
46 'tooShort':_('Enter a value %(min)i characters long or more')}
47 (2, 'b')
47 )
48 ]
49
48
50 def _form(self):
49 password = UnicodeString(
51 class _WireTransferForm(formencode.Schema):
50 strip=True,
52 allow_extra_fields = True
51 min=3,
53 _authentication_token = ValidAuthToken()
52 not_empty=True,
54 account_number = Regex(r'[0-9]{26}', not_empty = True, messages = {
53 messages={
55 'invalid': _("Account number is invalid, it must be 26 digits")})
54 'empty':_('Please enter a password'),
56 title = UnicodeString(not_empty = True, min = 3, strip = True)
55 'tooShort':_('Enter a value %(min)i characters long or more')}
57 recipient = formencode.All(OneOf([i[0] for i in WireTransferForm.recipients_list],
56 )
58 testValueList = True, hideList = True), Int())
59 recipient_address = UnicodeString(not_empty = True, strip = True)
60 amount = Number(not_empty = True, min = 1)
61
57
62 return _WireTransferForm()
63
58
@@ -1,53 +1,64 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="base/base.html"/>
2 <%inherit file="base/base.html"/>
3 <%def name="get_form_error(element)">
4 %if type(c.form_errors) == dict:
5 %if c.form_errors.get(element,False):
6 <span class="error-message">
7 ${c.form_errors.get(element,'')}
8 </span>
9 %endif
10 %endif
11 </%def>
3 <%def name="title()">
12 <%def name="title()">
4 ${_('Repository managment')}
13 ${_('Repository managment')}
5 </%def>
14 </%def>
6 <%def name="breadcrumbs()">
15 <%def name="breadcrumbs()">
7 ${h.link_to(u'Home',h.url('/'))}
16 ${h.link_to(u'Home',h.url('/'))}
8 /
17 /
9 ${h.link_to(u'Admin',h.url('admin_home'))}
18 ${h.link_to(u'Admin',h.url('admin_home'))}
10 </%def>
19 </%def>
11 <%def name="page_nav()">
20 <%def name="page_nav()">
12 <li>${h.link_to(u'Home',h.url('/'))}</li>
21 <li>${h.link_to(u'Home',h.url('/'))}</li>
13 <li class="current">${_('Admin')}</li>
22 <li class="current">${_('Admin')}</li>
14 </%def>
23 </%def>
15 <%def name="main()">
24 <%def name="main()">
16 %if c.admin_user:
25 %if c.admin_user:
17 <ul class="submenu">
26 <ul class="submenu">
18 <li>
27 <li>
19 ${h.link_to(u'Repos managment',h.url('admin_repos_manage'))}
28 ${h.link_to(u'Repos managment',h.url('admin_repos_manage'))}
20 </li>
29 </li>
21 <li>
30 <li>
22 ${h.link_to(u'Users managment',h.url('admin_users_manage'))}
31 ${h.link_to(u'Users managment',h.url('admin_users_manage'))}
23 </li>
32 </li>
24 </ul>
33 </ul>
25 <br/>
34 <br/>
26 <div>
35 <div>
27
36
28 <h2>Hi !</h2>
37 <h2>Hi !</h2>
29 </div>
38 </div>
30 %else:
39 %else:
31 <div>
40 <div>
32 <br />
41 <br />
33 <h2>${_('Login')}</h2>
42 <h2>${_('Login')}</h2>
34 ${h.form(h.url.current())}
43 ${h.form(h.url.current())}
35 <table>
44 <table>
36 <tr>
45 <tr>
37 <td>${_('Username')}</td>
46 <td>${_('Username')}</td>
38 <td>${h.text('username')}</td>
47 <td>${h.text('username')}</td>
48 <td>${get_form_error('username')} </td>
39 </tr>
49 </tr>
40 <tr>
50 <tr>
41 <td>${_('Password')}</td>
51 <td>${_('Password')}</td>
42 <td>${h.text('password')}</td>
52 <td>${h.password('password')}</td>
53 <td>${get_form_error('password')}</td>
43 </tr>
54 </tr>
44 <tr>
55 <tr>
45 <td></td>
56 <td></td>
46 <td>${h.submit('login','login')}</td>
57 <td>${h.submit('login','login')}</td>
47 </tr>
58 </tr>
48 </table>
59 </table>
49 ${h.end_form()}
60 ${h.end_form()}
50 </div>
61 </div>
51 %endif
62 %endif
52
63
53 </%def> No newline at end of file
64 </%def>
@@ -1,42 +1,42 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 {header}
2 {header}
3 <title>{repo|escape}: Mercurial repositories index</title>
3 <title>{repo|escape}: Mercurial repositories index</title>
4 </head>
4 </head>
5
5
6 <body>
6 <body>
7 <div id="container">
7 <div id="container">
8 <div class="page-header">
8 <div class="page-header">
9 <h1>${c.repos_prefix} Mercurial Repositories</h1>
9 <h1>${c.repos_prefix} Mercurial Repositories</h1>
10 <ul class="page-nav">
10 <ul class="page-nav">
11 <li class="current">Home</li>
11 <li class="current">Home</li>
12 <li>${h.link_to(u'Admin',h.url('admin_home'))}</li>
12 <li><a href="/_admin/">Admin</a></li>
13 </ul>
13 </ul>
14 </div>
14 </div>
15
15
16 <table cellspacing="0">
16 <table cellspacing="0">
17 <tr>
17 <tr>
18 <td><a href="?sort={sort_name}">Name</a></td>
18 <td><a href="?sort={sort_name}">Name</a></td>
19 <td><a href="?sort={sort_description}">Description</a></td>
19 <td><a href="?sort={sort_description}">Description</a></td>
20 <td><a href="?sort={sort_contact}">Contact</a></td>
20 <td><a href="?sort={sort_contact}">Contact</a></td>
21 <td><a href="?sort={sort_lastchange}">Last change</a></td>
21 <td><a href="?sort={sort_lastchange}">Last change</a></td>
22 <td>&nbsp;</td>
22 <td>&nbsp;</td>
23 <td>&nbsp;</td>
23 <td>&nbsp;</td>
24 </tr>
24 </tr>
25 {entries%indexentry}
25 {entries%indexentry}
26 </table>
26 </table>
27 <div class="page-footer">
27 <div class="page-footer">
28 {motd}
28 {motd}
29 </div>
29 </div>
30
30
31 <div id="powered-by">
31 <div id="powered-by">
32 <p><a href="http://mercurial.selenic.com/" title="Mercurial"><img src="{staticurl}hglogo.png" width="75" height="90" alt="mercurial"/></a></p>
32 <p><a href="http://mercurial.selenic.com/" title="Mercurial"><img src="{staticurl}hglogo.png" width="75" height="90" alt="mercurial"/></a></p>
33 </div>
33 </div>
34
34
35 <div id="corner-top-left"></div>
35 <div id="corner-top-left"></div>
36 <div id="corner-top-right"></div>
36 <div id="corner-top-right"></div>
37 <div id="corner-bottom-left"></div>
37 <div id="corner-bottom-left"></div>
38 <div id="corner-bottom-right"></div>
38 <div id="corner-bottom-right"></div>
39
39
40 </div>
40 </div>
41 </body>
41 </body>
42 </html>
42 </html>
General Comments 0
You need to be logged in to leave comments. Login now