@@ -17,48 +17,49 @@
#
# 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/>.
import logging
import formencode
from formencode import htmlfill
from pylons.i18n.translation import _
from pylons.controllers.util import abort, redirect
from pylons import request, response, session, tmpl_context as c, url
import rhodecode.lib.helpers as h
from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
from rhodecode.lib.base import BaseController, render
from rhodecode.model.db import User
from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
from rhodecode.model.user import UserModel
from rhodecode.model.meta import Session
log = logging.getLogger(__name__)
class LoginController(BaseController):
def __before__(self):
super(LoginController, self).__before__()
def index(self):
# redirect if already logged in
c.came_from = request.GET.get('came_from', None)
if self.rhodecode_user.is_authenticated \
and self.rhodecode_user.username != 'default':
return redirect(url('home'))
if request.POST:
# import Login Form validator class
login_form = LoginForm()
try:
c.form_result = login_form.to_python(dict(request.POST))
@@ -88,48 +89,49 @@ class LoginController(BaseController):
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.get_by_username('default').user_perms:
if perm.permission.permission_name == 'hg.register.auto_activate':
c.auto_active = True
break
register_form = RegisterForm()()
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')
Session().commit()
return redirect(url('login_home'))
except formencode.Invalid, errors:
return htmlfill.render(
render('/register.html'),
defaults=errors.value,
errors=errors.error_dict or {},
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_link(form_result)
h.flash(_('Your password reset link was sent'),
@@ -262,48 +262,52 @@ class User(Base, BaseModel):
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('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
group_member = relationship('UsersGroupMember', cascade='all')
notifications = relationship('UserNotification',)
@property
def full_name(self):
return '%s %s' % (self.name, self.lastname)
def full_contact(self):
return '%s %s <%s>' % (self.name, self.lastname, self.email)
def short_contact(self):
def is_admin(self):
return self.admin
def __repr__(self):
return "<%s('id:%s:%s')>" % (self.__class__.__name__,
self.user_id, self.username)
@classmethod
def get_by_username(cls, username, case_insensitive=False, cache=False):
if case_insensitive:
q = cls.query().filter(cls.username.ilike(username))
else:
q = cls.query().filter(cls.username == username)
if cache:
@@ -1149,48 +1153,49 @@ class ChangesetComment(Base, BaseModel):
repo = relationship('Repository')
def get_users(cls, revision):
"""
Returns user associated with this changesetComment. ie those
who actually commented
:param cls:
:param revision:
return Session().query(User)\
.filter(cls.revision == revision)\
.join(ChangesetComment.author).all()
class Notification(Base, BaseModel):
__tablename__ = 'notifications'
__table_args__ = ({'extend_existing':True})
TYPE_CHANGESET_COMMENT = u'cs_comment'
TYPE_MESSAGE = u'message'
TYPE_MENTION = u'mention'
TYPE_REGISTRATION = u'registration'
notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
subject = Column('subject', Unicode(512), nullable=True)
body = Column('body', Unicode(50000), nullable=True)
created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
type_ = Column('type', Unicode(256))
created_by_user = relationship('User')
notifications_to_users = relationship('UserNotification', lazy='joined',
cascade="all, delete, delete-orphan")
def recipients(self):
return [x.user for x in UserNotification.query()\
.filter(UserNotification.notification == self).all()]
def create(cls, created_by, subject, body, recipients, type_=None):
if type_ is None:
type_ = Notification.TYPE_MESSAGE
notification = cls()
notification.created_by_user = created_by
@@ -36,87 +36,99 @@ from rhodecode.lib import helpers as h
from rhodecode.model import BaseModel
from rhodecode.model.db import Notification, User, UserNotification
class NotificationModel(BaseModel):
def __get_user(self, user):
if isinstance(user, basestring):
return User.get_by_username(username=user)
return self._get_instance(User, user)
def __get_notification(self, notification):
if isinstance(notification, Notification):
return notification
elif isinstance(notification, int):
return Notification.get(notification)
if notification:
raise Exception('notification must be int or Instance'
' of Notification got %s' % type(notification))
def create(self, created_by, subject, body, recipients,
type_=Notification.TYPE_MESSAGE):
def create(self, created_by, subject, body, recipients=None,
type_=Notification.TYPE_MESSAGE, with_email=True,
email_kwargs={}):
Creates notification of given type
:param created_by: int, str or User instance. User who created this
notification
:param subject:
:param body:
:param recipients: list of int, str or User objects
:param recipients: list of int, str or User objects, when None
is given send to all admins
:param type_: type of notification
:param with_email: send email with this notification
:param email_kwargs: additional dict to pass as args to email template
from rhodecode.lib.celerylib import tasks, run_task
if not getattr(recipients, '__iter__', False):
if recipients and not getattr(recipients, '__iter__', False):
raise Exception('recipients must be a list of iterable')
created_by_obj = self.__get_user(created_by)
recipients_objs = []
for u in recipients:
obj = self.__get_user(u)
if obj:
recipients_objs.append(obj)
recipients_objs = set(recipients_objs)
if recipients:
# empty recipients means to all admins
recipients_objs = User.query().filter(User.admin == True).all()
notif = Notification.create(created_by=created_by_obj, subject=subject,
body=body, recipients=recipients_objs,
type_=type_)
if with_email is False:
return notif
# send email with notification
for rec in recipients_objs:
email_subject = NotificationModel().make_description(notif, False)
type_ = EmailNotificationModel.TYPE_CHANGESET_COMMENT
type_ = type_
email_body = body
kwargs = {'subject':subject, 'body':h.rst(body)}
kwargs.update(email_kwargs)
email_body_html = EmailNotificationModel()\
.get_email_tmpl(type_, **{'subject':subject,
'body':h.rst(body)})
.get_email_tmpl(type_, **kwargs)
run_task(tasks.send_email, rec.email, email_subject, email_body,
email_body_html)
def delete(self, user, notification):
# we don't want to remove actual notification just the assignment
notification = self.__get_notification(notification)
user = self.__get_user(user)
if notification and user:
obj = UserNotification.query()\
.filter(UserNotification.user == user)\
.filter(UserNotification.notification
== notification)\
.one()
self.sa.delete(obj)
return True
except Exception:
log.error(traceback.format_exc())
raise
def get_for_user(self, user):
@@ -129,49 +141,51 @@ class NotificationModel(BaseModel):
.filter(UserNotification.user == user).count()
def get_unread_for_user(self, user):
return [x.notification for x in UserNotification.query()\
.filter(UserNotification.read == False)\
.filter(UserNotification.user == user).all()]
def get_user_notification(self, user, notification):
return UserNotification.query()\
.filter(UserNotification.notification == notification)\
.filter(UserNotification.user == user).scalar()
def make_description(self, notification, show_age=True):
Creates a human readable description based on properties
of notification object
_map = {notification.TYPE_CHANGESET_COMMENT:_('commented on commit'),
notification.TYPE_MESSAGE:_('sent message'),
notification.TYPE_MENTION:_('mentioned you')}
notification.TYPE_MENTION:_('mentioned you'),
notification.TYPE_REGISTRATION:_('registered in RhodeCode')}
DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S"
tmpl = "%(user)s %(action)s %(when)s"
if show_age:
when = h.age(notification.created_on)
DTF = lambda d: datetime.datetime.strftime(d, DATETIME_FORMAT)
when = DTF(notification.created_on)
data = dict(user=notification.created_by_user.username,
action=_map[notification.type_],
when=when)
return tmpl % data
class EmailNotificationModel(BaseModel):
TYPE_CHANGESET_COMMENT = 'changeset_comment'
TYPE_PASSWORD_RESET = 'passoword_link'
TYPE_REGISTRATION = 'registration'
TYPE_DEFAULT = 'default'
def __init__(self):
self._template_root = rhodecode.CONFIG['pylons.paths']['templates'][0]
self._tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
@@ -5,65 +5,68 @@
users model for RhodeCode
:created_on: Apr 9, 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.
import traceback
from pylons import url
from rhodecode.lib import safe_unicode
from rhodecode.lib.caching_query import FromCache
from rhodecode.model.db import User, UserRepoToPerm, Repository, Permission, \
UserToPerm, UsersGroupRepoToPerm, UsersGroupToPerm, UsersGroupMember
UserToPerm, UsersGroupRepoToPerm, UsersGroupToPerm, UsersGroupMember, \
Notification
from rhodecode.lib.exceptions import DefaultUserException, \
UserOwnsReposException
from sqlalchemy.exc import DatabaseError
from rhodecode.lib import generate_api_key
from sqlalchemy.orm import joinedload
PERM_WEIGHTS = {'repository.none': 0,
'repository.read': 1,
'repository.write': 3,
'repository.admin': 3}
class UserModel(BaseModel):
def get(self, user_id, cache=False):
user = self.sa.query(User)
user = user.options(FromCache("sql_cache_short",
"get_user_%s" % user_id))
return user.get(user_id)
def get_by_username(self, username, cache=False, case_insensitive=False):
user = self.sa.query(User).filter(User.username.ilike(username))
user = self.sa.query(User)\
.filter(User.username == username)
@@ -190,68 +193,77 @@ class UserModel(BaseModel):
new_user = User()
username = username.lower()
# add ldap account always lowercase
new_user.username = username
new_user.password = get_crypt_password(password)
new_user.api_key = generate_api_key(username)
new_user.email = attrs['email'] or generate_email(username)
new_user.active = attrs.get('active', True)
new_user.ldap_dn = safe_unicode(user_dn)
new_user.name = attrs['name']
new_user.lastname = attrs['lastname']
self.sa.add(new_user)
self.sa.commit()
return new_user
except (DatabaseError,):
self.sa.rollback()
log.debug('this %s user exists skipping creation of ldap account',
username)
return None
def create_registration(self, form_data):
from rhodecode.model.notification import NotificationModel
for k, v in form_data.items():
if k != 'admin':
setattr(new_user, k, v)
self.sa.flush()
# notification to admins
subject = _('new user registration')
body = ('New user registration\n'
'username: %s\n'
'email: %s\n')
body = body % (form_data['username'], form_data['email'])
'---------------------\n'
'- Username: %s\n'
'- Full Name: %s\n'
'- Email: %s\n')
body = body % (new_user.username, new_user.full_name,
new_user.email)
edit_url = url('edit_user', id=new_user.user_id, qualified=True)
kw = {'registered_user_url':edit_url}
NotificationModel().create(created_by=new_user, subject=subject,
body=body, recipients=None,
type_=Notification.TYPE_REGISTRATION,
email_kwargs=kw)
run_task(tasks.send_email, None,
_('[RhodeCode] New User registration'),
body)
except:
def update(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"))
if k == 'new_password' and v != '':
user.password = v
user.api_key = generate_api_key(user.username)
setattr(user, k, v)
self.sa.add(user)
new file 100644
## -*- coding: utf-8 -*-
<%inherit file="main.html"/>
A new user have registered in RhodeCode
${body}
View this user here :${registered_user_url}
\ No newline at end of file
Status change: