@@ -249,96 +249,100 @@ def make_map(config):
action="show", conditions=dict(method=["GET"]))
m.connect("admin_settings_my_account", "/my_account",
action="my_account", conditions=dict(method=["GET"]))
m.connect("admin_settings_my_account_update", "/my_account_update",
action="my_account_update", conditions=dict(method=["PUT"]))
m.connect("admin_settings_create_repository", "/create_repository",
action="create_repository", conditions=dict(method=["GET"]))
#ADMIN MAIN PAGES
with rmap.submapper(path_prefix=ADMIN_PREFIX,
controller='admin/admin') as m:
m.connect('admin_home', '', action='index')
m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
action='add_repo')
#USER JOURNAL
rmap.connect('journal', '%s/journal' % ADMIN_PREFIX, controller='journal')
rmap.connect('public_journal', '%s/public_journal' % ADMIN_PREFIX,
controller='journal', action="public_journal")
rmap.connect('public_journal_rss', '%s/public_journal_rss' % ADMIN_PREFIX,
controller='journal', action="public_journal_rss")
rmap.connect('public_journal_atom',
'%s/public_journal_atom' % ADMIN_PREFIX, controller='journal',
action="public_journal_atom")
rmap.connect('toggle_following', '%s/toggle_following' % ADMIN_PREFIX,
controller='journal', action='toggle_following',
conditions=dict(method=["POST"]))
#SEARCH
rmap.connect('search', '%s/search' % ADMIN_PREFIX, controller='search',)
rmap.connect('search_repo', '%s/search/{search_repo:.*}' % ADMIN_PREFIX,
controller='search')
#LOGIN/LOGOUT/REGISTER/SIGN IN
rmap.connect('login_home', '%s/login' % ADMIN_PREFIX, controller='login')
rmap.connect('logout_home', '%s/logout' % ADMIN_PREFIX, controller='login',
action='logout')
rmap.connect('register', '%s/register' % ADMIN_PREFIX, controller='login',
action='register')
rmap.connect('reset_password', '%s/password_reset' % ADMIN_PREFIX,
controller='login', action='password_reset')
rmap.connect('reset_password_confirmation',
'%s/password_reset_confirmation' % ADMIN_PREFIX,
controller='login', action='password_reset_confirmation')
#FEEDS
rmap.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
controller='feed', action='rss',
conditions=dict(function=check_repo))
rmap.connect('atom_feed_home', '/{repo_name:.*}/feed/atom',
controller='feed', action='atom',
#==========================================================================
# REPOSITORY ROUTES
rmap.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
controller='changeset', revision='tip',
rmap.connect('raw_changeset_home',
'/{repo_name:.*}/raw-changeset/{revision}',
controller='changeset', action='raw_changeset',
revision='tip', conditions=dict(function=check_repo))
rmap.connect('summary_home', '/{repo_name:.*}',
controller='summary', conditions=dict(function=check_repo))
rmap.connect('summary_home', '/{repo_name:.*}/summary',
rmap.connect('shortlog_home', '/{repo_name:.*}/shortlog',
controller='shortlog', conditions=dict(function=check_repo))
rmap.connect('branches_home', '/{repo_name:.*}/branches',
controller='branches', conditions=dict(function=check_repo))
rmap.connect('tags_home', '/{repo_name:.*}/tags',
controller='tags', conditions=dict(function=check_repo))
rmap.connect('changelog_home', '/{repo_name:.*}/changelog',
controller='changelog', conditions=dict(function=check_repo))
rmap.connect('files_home', '/{repo_name:.*}/files/{revision}/{f_path:.*}',
controller='files', revision='tip', f_path='',
rmap.connect('files_diff_home', '/{repo_name:.*}/diff/{f_path:.*}',
controller='files', action='diff', revision='tip', f_path='',
rmap.connect('files_rawfile_home',
@@ -84,68 +84,85 @@ class LoginController(BaseController):
return htmlfill.render(
render('/login.html'),
defaults=errors.value,
errors=errors.error_dict or {},
prefix_error=False,
encoding="UTF-8")
return render('/login.html')
@HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
'hg.register.manual_activate')
def register(self):
user_model = UserModel()
c.auto_active = False
for perm in user_model.get_by_username('default',
cache=False).user_perms:
if perm.permission.permission_name == 'hg.register.auto_activate':
c.auto_active = True
break
if request.POST:
register_form = RegisterForm()()
try:
form_result = register_form.to_python(dict(request.POST))
form_result['active'] = c.auto_active
user_model.create_registration(form_result)
h.flash(_('You have successfully registered into rhodecode'),
category='success')
return redirect(url('login_home'))
except formencode.Invalid, errors:
render('/register.html'),
return render('/register.html')
def password_reset(self):
password_reset_form = PasswordResetForm()()
form_result = password_reset_form.to_python(dict(request.POST))
user_model.reset_password(form_result)
h.flash(_('Your new password was sent'),
user_model.reset_password_link(form_result)
h.flash(_('Your password reset link was sent'),
render('/password_reset.html'),
return render('/password_reset.html')
def password_reset_confirmation(self):
if request.GET and request.GET.get('key'):
user = User.get_by_api_key(request.GET.get('key'))
data = dict(email=user.email)
user_model.reset_password(data)
h.flash(_('Your password reset was successful, '
'new password has been sent to your email'),
except Exception, e:
log.error(e)
return redirect(url('reset_password'))
def logout(self):
del session['rhodecode_user']
session.save()
log.info('Logging out and setting user as Empty')
redirect(url('home'))
# -*- coding: utf-8 -*-
"""
rhodecode.lib.celerylib.tasks
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
RhodeCode task modules, containing all task that suppose to be run
by celery daemon
:created_on: Oct 6, 2010
:author: marcink
:copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
:license: GPLv3, see COPYING for more details.
# 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 3 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, see <http://www.gnu.org/licenses/>.
from celery.decorators import task
import os
import traceback
import logging
from os.path import dirname as dn, join as jn
from time import mktime
from operator import itemgetter
from string import lower
from pylons import config
from pylons import config, url
from pylons.i18n.translation import _
from rhodecode.lib import LANGUAGES_EXTENSIONS_MAP, safe_str
from rhodecode.lib.celerylib import run_task, locked_task, str2bool, \
__get_lockkey, LockHeld, DaemonLock
from rhodecode.lib.helpers import person
from rhodecode.lib.smtp_mailer import SmtpMailer
from rhodecode.lib.utils import add_cache
from rhodecode.lib.odict import OrderedDict
from rhodecode.model import init_model
from rhodecode.model import meta
from rhodecode.model.db import RhodeCodeUi, Statistics, Repository
from vcs.backends import get_repo
from sqlalchemy import engine_from_config
add_cache(config)
import json
except ImportError:
#python 2.5 compatibility
import simplejson as json
__all__ = ['whoosh_index', 'get_commits_stats',
'reset_user_password', 'send_email']
CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
def get_session():
if CELERY_ON:
engine = engine_from_config(config, 'sqlalchemy.db1.')
init_model(engine)
sa = meta.Session()
return sa
def get_repos_path():
sa = get_session()
q = sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
return q.ui_value
@task(ignore_result=True)
@locked_task
def whoosh_index(repo_location, full_index):
@@ -204,129 +204,168 @@ def get_commits_stats(repo_name, ts_min_
commits_by_day_aggregate[k] += 1
else:
commits_by_day_aggregate[k] = 1
overview_data = sorted(commits_by_day_aggregate.items(),
key=itemgetter(0))
if not co_day_auth_aggr:
co_day_auth_aggr[akc(repo.contact)] = {
"label": akc(repo.contact),
"data": [0, 1],
"schema": ["commits"],
}
stats = cur_stats if cur_stats else Statistics()
stats.commit_activity = json.dumps(co_day_auth_aggr)
stats.commit_activity_combined = json.dumps(overview_data)
log.debug('last revison %s', last_rev)
leftovers = len(repo.revisions[last_rev:])
log.debug('revisions to parse %s', leftovers)
if last_rev == 0 or leftovers < parse_limit:
log.debug('getting code trending stats')
stats.languages = json.dumps(__get_codes_stats(repo_name))
stats.repository = dbrepo
stats.stat_on_revision = last_cs.revision if last_cs else 0
sa.add(stats)
sa.commit()
except:
log.error(traceback.format_exc())
sa.rollback()
lock.release()
return False
#final release
#execute another task if celery is enabled
if len(repo.revisions) > 1 and CELERY_ON:
run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
return True
except LockHeld:
log.info('LockHeld')
return 'Task with key %s already running' % lockkey
def send_password_link(user_email):
log = reset_user_password.get_logger()
log = logging.getLogger(__name__)
from rhodecode.lib import auth
from rhodecode.model.db import User
user = sa.query(User).filter(User.email == user_email).scalar()
if user:
link = url('reset_password_confirmation', key=user.api_key,
qualified=True)
tmpl = """
Hello %s
We received a request to create a new password for your account.
You can generate it by clicking following URL:
%s
If you didn't request new password please ignore this email.
run_task(send_email, user_email,
"RhodeCode password reset link",
tmpl % (user.short_contact, link))
log.info('send new password mail to %s', user_email)
log.error('Failed to update user password')
def reset_user_password(user_email):
new_passwd = auth.PasswordGenerator().gen_password(8,
auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
user.password = auth.get_crypt_password(new_passwd)
user.api_key = auth.generate_api_key(user.username)
sa.add(user)
log.info('change password for %s', user_email)
if new_passwd is None:
raise Exception('unable to generate new password')
"Your new rhodecode password",
'Your new rhodecode password:%s' % (new_passwd))
"Your new RhodeCode password",
'Your new RhodeCode password:%s' % (new_passwd))
def send_email(recipients, subject, body):
Sends an email with defined parameters from the .ini files.
:param recipients: list of recipients, it this is empty the defined email
address from field 'email_to' is used instead
:param subject: subject of the mail
:param body: body of the mail
log = send_email.get_logger()
email_config = config
if not recipients:
recipients = [email_config.get('email_to')]
mail_from = email_config.get('app_email_from')
user = email_config.get('smtp_username')
passwd = email_config.get('smtp_password')
mail_server = email_config.get('smtp_server')
mail_port = email_config.get('smtp_port')
tls = str2bool(email_config.get('smtp_use_tls'))
ssl = str2bool(email_config.get('smtp_use_ssl'))
debug = str2bool(config.get('debug'))
m = SmtpMailer(mail_from, user, passwd, mail_server,
mail_port, ssl, tls, debug=debug)
m.send(recipients, subject, body)
log.error('Mail sending failed')
@@ -29,103 +29,109 @@ class SmtpMailer(object):
mailer = SmtpMailer(mail_from, user, passwd, mail_server,
mail_port, ssl, tls)
mailer.send(recipients, subject, body, attachment_files)
:param recipients might be a list of string or single string
:param attachment_files is a dict of {filename:location}
it tries to guess the mimetype and attach the file
def __init__(self, mail_from, user, passwd, mail_server,
mail_port=None, ssl=False, tls=False, debug=False):
self.mail_from = mail_from
self.mail_server = mail_server
self.mail_port = mail_port
self.user = user
self.passwd = passwd
self.ssl = ssl
self.tls = tls
self.debug = debug
def send(self, recipients=[], subject='', body='', attachment_files=None):
if isinstance(recipients, basestring):
recipients = [recipients]
if self.ssl:
smtp_serv = smtplib.SMTP_SSL(self.mail_server, self.mail_port)
smtp_serv = smtplib.SMTP(self.mail_server, self.mail_port)
if self.tls:
smtp_serv.ehlo()
smtp_serv.starttls()
if self.debug:
smtp_serv.set_debuglevel(1)
#if server requires authorization you must provide login and password
#but only if we have them
if self.user and self.passwd:
smtp_serv.login(self.user, self.passwd)
date_ = formatdate(localtime=True)
msg = MIMEMultipart()
msg.set_type('multipart/alternative')
msg.preamble = 'You will not see this in a MIME-aware mail reader.\n'
text_msg = MIMEText(body)
text_msg.set_type('text/plain')
text_msg.set_param('charset', 'UTF-8')
msg['From'] = self.mail_from
msg['To'] = ','.join(recipients)
msg['Date'] = date_
msg['Subject'] = subject
msg.attach(MIMEText(body))
msg.attach(text_msg)
if attachment_files:
self.__atach_files(msg, attachment_files)
smtp_serv.sendmail(self.mail_from, recipients, msg.as_string())
logging.info('MAIL SEND TO: %s' % recipients)
smtp_serv.quit()
except sslerror:
# sslerror is raised in tls connections on closing sometimes
pass
def __atach_files(self, msg, attachment_files):
if isinstance(attachment_files, dict):
for f_name, msg_file in attachment_files.items():
ctype, encoding = mimetypes.guess_type(f_name)
logging.info("guessing file %s type based on %s", ctype,
f_name)
if ctype is None or encoding is not None:
# No guess could be made, or the file is encoded
# (compressed), so use a generic bag-of-bits type.
ctype = 'application/octet-stream'
maintype, subtype = ctype.split('/', 1)
if maintype == 'text':
# Note: we should handle calculating the charset
file_part = MIMEText(self.get_content(msg_file),
_subtype=subtype)
elif maintype == 'image':
file_part = MIMEImage(self.get_content(msg_file),
elif maintype == 'audio':
file_part = MIMEAudio(self.get_content(msg_file),
file_part = MIMEBase(maintype, subtype)
file_part.set_payload(self.get_content(msg_file))
# Encode the payload using Base64
encoders.encode_base64(msg)
# Set the filename parameter
file_part.add_header('Content-Disposition', 'attachment',
filename=f_name)
file_part.add_header('Content-Type', ctype, name=f_name)
msg.attach(file_part)
raise Exception('Attachment files should be'
'a dict in format {"filename":"filepath"}')
@@ -198,96 +198,101 @@ class User(Base, BaseModel):
__table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
active = Column("active", Boolean(), nullable=True, unique=None, default=None)
admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
user_log = relationship('UserLog', cascade='all')
user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
repositories = relationship('Repository')
user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
repo_to_perm = relationship('RepoToPerm', primaryjoin='RepoToPerm.user_id==User.user_id', cascade='all')
group_member = relationship('UsersGroupMember', cascade='all')
@property
def full_contact(self):
return '%s %s <%s>' % (self.name, self.lastname, self.email)
def short_contact(self):
return '%s %s' % (self.name, self.lastname)
def is_admin(self):
return self.admin
def __repr__(self):
return "<%s('id:%s:%s')>" % (self.__class__.__name__,
self.user_id, self.username)
return self.__class__.__name__
@classmethod
def by_username(cls, username, case_insensitive=False):
if case_insensitive:
return Session.query(cls).filter(cls.username.like(username)).one()
return Session.query(cls).filter(cls.username == username).one()
def get_by_api_key(cls, api_key):
return Session.query(cls).filter(cls.api_key == api_key).one()
def update_lastlogin(self):
"""Update user lastlogin"""
self.last_login = datetime.datetime.now()
Session.add(self)
Session.commit()
log.debug('updated user %s lastlogin', self.username)
class UserLog(Base, BaseModel):
__tablename__ = 'user_logs'
__table_args__ = {'extend_existing':True}
user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
def action_as_day(self):
return date(*self.action_date.timetuple()[:3])
user = relationship('User')
repository = relationship('Repository')
class UsersGroup(Base, BaseModel):
__tablename__ = 'users_groups'
users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
gr = Session.query(cls)\
.filter(cls.users_group_name.ilike(group_name))
gr = Session.query(UsersGroup)\
.filter(UsersGroup.users_group_name == group_name)
if cache:
@@ -168,96 +168,100 @@ class UserModel(BaseModel):
self.sa.commit()
self.sa.rollback()
raise
def update_my_account(self, user_id, form_data):
user = self.get(user_id, cache=False)
if user.username == 'default':
raise DefaultUserException(
_("You can't Edit this user since it's"
" crucial for entire application"))
for k, v in form_data.items():
if k == 'new_password' and v != '':
user.password = v
user.api_key = generate_api_key(user.username)
if k not in ['admin', 'active']:
setattr(user, k, v)
self.sa.add(user)
def delete(self, user_id):
_("You can't remove this user since it's"
if user.repositories:
raise UserOwnsReposException(_('This user still owns %s '
'repositories and cannot be '
'removed. Switch owners or '
'remove those repositories') \
% user.repositories)
self.sa.delete(user)
def reset_password_link(self, data):
from rhodecode.lib.celerylib import tasks, run_task
run_task(tasks.send_password_link, data['email'])
def reset_password(self, data):
run_task(tasks.reset_user_password, data['email'])
def fill_data(self, auth_user, user_id=None, api_key=None):
Fetches auth_user by user_id,or api_key if present.
Fills auth_user attributes with those taken from database.
Additionally set's is_authenitated if lookup fails
present in database
:param auth_user: instance of user to set attributes
:param user_id: user id to fetch by
:param api_key: api key to fetch by
if user_id is None and api_key is None:
raise Exception('You need to pass user_id or api_key')
if api_key:
dbuser = self.get_by_api_key(api_key)
dbuser = self.get(user_id)
if dbuser is not None:
log.debug('filling %s data', dbuser)
for k, v in dbuser.get_dict().items():
setattr(auth_user, k, v)
auth_user.is_authenticated = False
return auth_user
def fill_perms(self, user):
Fills user permission attribute with permissions taken from database
works for permissions given for repositories, and for permissions that
are granted to groups
:param user: user instance to fill his perms
user.permissions['repositories'] = {}
user.permissions['global'] = set()
#======================================================================
@@ -153,97 +153,98 @@ height:1%;
display:block;
text-decoration:none;
margin:0;
padding:3px 8px;
.top-left-rounded-corner {
-webkit-border-top-left-radius: 8px;
-khtml-border-radius-topleft: 8px;
-moz-border-radius-topleft: 8px;
border-top-left-radius: 8px;
.top-right-rounded-corner {
-webkit-border-top-right-radius: 8px;
-khtml-border-radius-topright: 8px;
-moz-border-radius-topright: 8px;
border-top-right-radius: 8px;
.bottom-left-rounded-corner {
-webkit-border-bottom-left-radius: 8px;
-khtml-border-radius-bottomleft: 8px;
-moz-border-radius-bottomleft: 8px;
border-bottom-left-radius: 8px;
.bottom-right-rounded-corner {
-webkit-border-bottom-right-radius: 8px;
-khtml-border-radius-bottomright: 8px;
-moz-border-radius-bottomright: 8px;
border-bottom-right-radius: 8px;
#header {
padding:0 10px;
#header ul#logged-user{
margin-bottom:5px !important;
-webkit-border-radius: 0px 0px 8px 8px;
-khtml-border-radius: 0px 0px 8px 8px;
-moz-border-radius: 0px 0px 8px 8px;
border-radius: 0px 0px 8px 8px;
height:37px;
background:url("../images/header_inner.png") repeat-x scroll 0 0 #003367
background:url("../images/header_inner.png") repeat-x scroll 0 0 #003367;
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
#header ul#logged-user li {
list-style:none;
float:left;
margin:8px 0 0;
padding:4px 12px;
border-left: 1px solid #316293;
#header ul#logged-user li.first {
border-left:none;
margin:4px;
#header ul#logged-user li.first div.gravatar {
margin-top:-2px;
#header ul#logged-user li.first div.account {
padding-top:4px;
#header ul#logged-user li.last {
border-right:none;
#header ul#logged-user li a {
color:#fff;
font-weight:700;
#header ul#logged-user li a:hover {
text-decoration:underline;
#header ul#logged-user li.highlight a {
#header ul#logged-user li.highlight a:hover {
color:#FFF;
#header #header-inner {
height:40px;
@@ -1338,96 +1339,103 @@ padding:20px;
#login div.form div.fields div.field div.label {
width:173px;
text-align:right;
margin:2px 10px 0 0;
padding:5px 0 0 5px;
#login div.form div.fields div.field div.input input {
width:176px;
background:#FFF;
border-top:1px solid #b3b3b3;
border-left:1px solid #b3b3b3;
border-right:1px solid #eaeaea;
border-bottom:1px solid #eaeaea;
color:#000;
font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
font-size:11px;
padding:7px 7px 6px;
#login div.form div.fields div.buttons {
clear:both;
overflow:hidden;
border-top:1px solid #DDD;
padding:10px 0 0;
#login div.form div.links {
margin:10px 0 0;
padding:0 0 2px;
#quick_login{
top: 31px;
background-color: rgb(0, 51, 103);
z-index: 999;
height: 150px;
position: absolute;
margin-left: -16px;
width: 281px;
border-radius: 0 0 8px 8px;
#quick_login .password_forgoten{
padding-right:10px;
padding-top:10px;
#quick_login div.form div.fields{
padding-top: 2px;
padding-left:10px;
#quick_login div.form div.fields div.field{
padding: 5px;
#quick_login div.form div.fields div.field div.label label{
padding-bottom: 3px;
#quick_login div.form div.fields div.field div.input input {
width:236px;
padding:5px 7px 4px;
#quick_login div.form div.fields div.buttons {
padding:10px 14px 0;
#quick_login div.form div.fields div.buttons input.ui-button{
background:#e5e3e3 url("../images/button.png") repeat-x;
border-left:1px solid #c6c6c6;
border-right:1px solid #DDD;
border-bottom:1px solid #c6c6c6;
color:#515151;
padding:4px 10px;
## -*- coding: utf-8 -*-
<%inherit file="root.html"/>
<!-- HEADER -->
<div id="header">
<!-- user -->
<ul id="logged-user">
<li class="first">
<div id="quick_login" style="display:none">
${h.form(h.url('login_home',came_from=h.url.current()))}
<div class="form">
<div class="fields">
<div class="field">
<div class="label">
<label for="username">${_('Username')}:</label>
</div>
<div class="input">
${h.text('username',class_='focus',size=40)}
<label for="password">${_('Password')}:</label>
${h.password('password',class_='focus',size=40)}
<div class="buttons">
${h.submit('sign_in','Sign In',class_="ui-button")}
<div class="password_forgoten">${h.link_to(_('Forgot password ?'),h.url('reset_password'))}</div>${h.submit('sign_in','Sign In',class_="ui-button")}
${h.end_form()}
<script type="text/javascript">
YUE.on('quick_login_link','click',function(e){
if(YUD.hasClass('quick_login_link','enabled')){
YUD.setStyle('quick_login','display','none');
YUD.removeClass('quick_login_link','enabled');
else{
YUD.setStyle('quick_login','display','');
YUD.addClass('quick_login_link','enabled');
YUD.get('username').focus();
//make sure we don't redirect
YUE.preventDefault(e);
});
</script>
<div class="gravatar">
<img alt="gravatar" src="${h.gravatar_url(c.rhodecode_user.email,20)}" />
<div class="account">
%if c.rhodecode_user.username == 'default':
<a href="${h.url('public_journal')}">${_('Public journal')}</a>
%else:
${h.link_to(c.rhodecode_user.username,h.url('admin_settings_my_account'),title='%s %s'%(c.rhodecode_user.name,c.rhodecode_user.lastname))}
%endif
</li>
<li>
<a href="${h.url('home')}">${_('Home')}</a>
%if c.rhodecode_user.username != 'default':
<a href="${h.url('journal')}">${_('Journal')}</a>
##(${c.unread_journal}
<li class="last highlight">${h.link_to(u'Login',h.url('login_home'),id='quick_login_link')}</li>
<li class="last highlight">${h.link_to(u'Log Out',h.url('logout_home'))}</li>
<%inherit file="base/root.html"/>
<%def name="title()">
${_('Reset You password')} - ${c.rhodecode_name}
</%def>
<div id="register">
<div class="title top-left-rounded-corner top-right-rounded-corner">
<h5>${_('Reset You password to')} ${c.rhodecode_name}</h5>
<div class="inner">
${h.form(url('password_reset'))}
<!-- fields -->
<label for="email">${_('Email address')}:</label>
${h.text('email')}
<div class="nohighlight">
${h.submit('send','Reset my password',class_="ui-button")}
<div class="activation_msg">${_('Your new password will be send to matching email address')}</div>
<div class="activation_msg">${_('Password reset link will be send to matching email address')}</div>
YUE.onDOMReady(function(){
YUD.get('email').focus();
})
new file 100644
Status change: