.. _changelog:
=========
Changelog
1.4.0 (**2012-XX-XX**)
----------------------
:status: in-progress
:branch: beta
news
++++
- new codereview system
- email map, allowing users to have multiple email addresses mapped into
their accounts
- improved git-hook system. Now all actions for git are logged into journal
including pushed revisions, user and IP address
- changed setup-app into setup-rhodecode and added default options to it.
- new git repos are created as bare now by default
- #464 added links to groups in permission box
- #465 mentions autocomplete inside comments boxes
- #469 added --update-only option to whoosh to re-index only given list
of repos in index
- rhodecode-api CLI client
- new git http protocol replaced buggy dulwich implementation.
Now based on pygrack & gitweb
- Improved RSS/ATOM feeds. Discoverable by browsers using proper headers, and
reformated based on user suggestions. Additional rss/atom feeds for user
journal
- various i18n improvements
- #478 permissions overview for admin in user edit view
fixes
+++++
- improved translations
- fixes issue #455 Creating an archive generates an exception on Windows
- fixes #448 Download ZIP archive keeps file in /tmp open and results
in out of disk space
- fixes issue #454 Search results under Windows include proceeding
backslash
- fixed issue #450. Rhodecode no longer will crash when bad revision is
present in journal data.
- fix for issue #417, git execution was broken on windows for certain
commands.
- fixed #413. Don't disable .git directory for bare repos on deleting
- fixed issue #459. Changed the way of obtaining logger in reindex task.
- fixed #453 added ID field in whoosh SCHEMA that solves the issue of
reindexing modified files
1.3.6 (**2012-05-17**)
- chinese traditional translation
- changed setup-app into setup-rhodecode and added arguments for auto-setup
mode that doesn't need user interaction
- fixed no scm found warning
- fixed __future__ import error on rcextensions
- made simplejson required lib for speedup on JSON encoding
- fixes #449 bad regex could get more than revisions from parsing history
- don't clear DB session when CELERY_EAGER is turned ON
1.3.5 (**2012-05-10**)
- use ext_json for json module
- unified annotation view with file source view
- notification improvements, better inbox + css
- #419 don't strip passwords for login forms, make rhodecode
more compatible with LDAP servers
- Added HTTP_X_FORWARDED_FOR as another method of extracting
IP for pull/push logs. - moved all to base controller
- #415: Adding comment to changeset causes reload.
Comments are now added via ajax and doesn't reload the page
- #374 LDAP config is discarded when LDAP can't be activated
- limited push/pull operations are now logged for git in the journal
- bumped mercurial to 2.2.X series
- added support for displaying submodules in file-browser
- #421 added bookmarks in changelog view
- fixed dev-version marker for stable when served from source codes
- fixed missing permission checks on show forks page
- #418 cast to unicode fixes in notification objects
- #426 fixed mention extracting regex
- fixed remote-pulling for git remotes remopositories
- fixed #434: Error when accessing files or changesets of a git repository
with submodules
- fixed issue with empty APIKEYS for users after registration ref. #438
- fixed issue with getting README files from git repositories
1.3.4 (**2012-03-28**)
- Whoosh logging is now controlled by the .ini files logging setup
- added clone-url into edit form on /settings page
- added help text into repo add/edit forms
- created rcextensions module with additional mappings (ref #322) and
post push/pull/create repo hooks callbacks
- implemented #377 Users view for his own permissions on account page
- #399 added inheritance of permissions for users group on repos groups
- #401 repository group is automatically pre-selected when adding repos
inside a repository group
- added alternative HTTP 403 response when client failed to authenticate. Helps
solving issues with Mercurial and LDAP
- #402 removed group prefix from repository name when listing repositories
inside a group
- added gravatars into permission view and permissions autocomplete
- #347 when running multiple RhodeCode instances, properly invalidates cache
for all registered servers
# -*- coding: utf-8 -*-
"""
rhodecode.controllers.admin.users
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Users crud controller for pylons
:created_on: Apr 4, 2010
:author: marcink
:copyright: (C) 2010-2012 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/>.
import logging
import traceback
import formencode
from formencode import htmlfill
from pylons import request, session, tmpl_context as c, url, config
from pylons.controllers.util import redirect
from pylons.i18n.translation import _
from rhodecode.lib.exceptions import DefaultUserException, \
UserOwnsReposException
from rhodecode.lib import helpers as h
from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator,\
AuthUser
from rhodecode.lib.base import BaseController, render
from rhodecode.model.db import User, Permission
from rhodecode.model.forms import UserForm
from rhodecode.model.user import UserModel
from rhodecode.model.meta import Session
from rhodecode.lib.utils import action_logger
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()
@HasPermissionAllDecorator('hg.admin')
def __before__(self):
c.admin_user = session.get('admin_user')
c.admin_username = session.get('admin_username')
super(UsersController, self).__before__()
c.available_permissions = config['available_permissions']
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"""
user_model = UserModel()
user_form = UserForm()()
try:
form_result = user_form.to_python(dict(request.POST))
user_model.create(form_result)
usr = form_result['username']
action_logger(self.rhodecode_user, 'admin_created_user:%s' % usr,
None, self.ip_addr, self.sa)
h.flash(_('created user %s') % usr,
category='success')
Session.commit()
except formencode.Invalid, errors:
return htmlfill.render(
render('admin/users/user_add.html'),
defaults=errors.value,
errors=errors.error_dict or {},
prefix_error=False,
encoding="UTF-8")
except Exception:
log.error(traceback.format_exc())
h.flash(_('error occurred during creation of user %s') \
% request.POST.get('username'), category='error')
return redirect(url('users'))
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('update_user', id=ID),
# method='put')
# url('user', id=ID)
c.user = user_model.get(id)
c.perm_user = AuthUser(user_id=id)
_form = UserForm(edit=True, old_data={'user_id': id,
'email': c.user.email})()
form_result = {}
form_result = _form.to_python(dict(request.POST))
user_model.update(id, form_result)
action_logger(self.rhodecode_user, 'admin_updated_user:%s' % usr,
h.flash(_('User updated successfully'), category='success')
e = errors.error_dict or {}
perm = Permission.get_by_key('hg.create.repository')
e.update({'create_repo_perm': user_model.has_perm(id, perm)})
render('admin/users/user_edit.html'),
errors=e,
h.flash(_('error occurred during update of user %s') \
% form_result.get('username'), category='error')
def delete(self, id):
"""DELETE /users/id: Delete an existing item"""
# <input type="hidden" name="_method" value="DELETE" />
# h.form(url('delete_user', id=ID),
# method='delete')
user_model.delete(id)
h.flash(_('successfully deleted user'), category='success')
except (UserOwnsReposException, DefaultUserException), e:
h.flash(e, category='warning')
h.flash(_('An error occurred during deletion of user'),
category='error')
def show(self, id, format='html'):
"""GET /users/id: Show a specific item"""
def edit(self, id, format='html'):
"""GET /users/id/edit: Form to edit an existing item"""
# url('edit_user', id=ID)
c.user = User.get(id)
if not c.user:
if c.user.username == 'default':
h.flash(_("You can't edit this user"), category='warning')
c.user.permissions = {}
c.granted_permissions = UserModel().fill_perms(c.user)\
.permissions['global']
defaults = c.user.get_dict()
defaults.update({'create_repo_perm': UserModel().has_perm(id, perm)})
defaults=defaults,
encoding="UTF-8",
force_defaults=False
)
def update_perm(self, id):
"""PUT /users_perm/id: Update an existing item"""
# url('user_perm', id=ID, method='put')
grant_perm = request.POST.get('create_repo_perm', False)
if grant_perm:
perm = Permission.get_by_key('hg.create.none')
user_model.revoke_perm(id, perm)
user_model.grant_perm(id, perm)
h.flash(_("Granted 'repository create' permission to user"),
else:
h.flash(_("Revoked 'repository create' permission to user"),
return redirect(url('edit_user', id=id))
@@ -64,98 +64,144 @@
</div>
<div class="field">
<div class="label">
<label for="new_password">${_('New password')}:</label>
<div class="input">
${h.password('new_password',class_='medium',autocomplete="off")}
<label for="password_confirmation">${_('New password confirmation')}:</label>
${h.password('password_confirmation',class_="medium",autocomplete="off")}
<label for="name">${_('First Name')}:</label>
${h.text('name',class_='medium')}
<label for="lastname">${_('Last Name')}:</label>
${h.text('lastname',class_='medium')}
<label for="email">${_('Email')}:</label>
${h.text('email',class_='medium')}
<div class="label label-checkbox">
<label for="active">${_('Active')}:</label>
<div class="checkboxes">
${h.checkbox('active',value=True)}
<label for="admin">${_('Admin')}:</label>
${h.checkbox('admin',value=True)}
<div class="buttons">
${h.submit('save',_('Save'),class_="ui-button")}
${h.reset('reset',_('Reset'),class_="ui-button")}
${h.end_form()}
<div class="box box-right">
<!-- box / title -->
<div class="title">
<h5>${_('Permissions')}</h5>
${h.form(url('user_perm', id=c.user.user_id),method='put')}
<div class="form">
<!-- fields -->
<div class="fields">
<label for="create_repo_perm">${_('Create repositories')}:</label>
${h.checkbox('create_repo_perm',value=True)}
## permissions overview
<div id="perms" class="table">
%for section in sorted(c.perm_user.permissions.keys()):
<div class="perms_section_head">${section.replace("_"," ").capitalize()}</div>
<div id='tbl_list_wrap_${section}' class="yui-skin-sam">
<table id="tbl_list_${section}">
<thead>
<tr>
<th class="left">${_('Name')}</th>
<th class="left">${_('Permission')}</th>
</thead>
<tbody>
%for k in c.perm_user.permissions[section]:
<%
if section != 'global':
section_perm = c.perm_user.permissions[section].get(k)
_perm = section_perm.split('.')[-1]
_perm = section_perm = None
%>
<td>
%if section == 'repositories':
<a href="${h.url('summary_home',repo_name=k)}">${k}</a>
%elif section == 'repositories_groups':
<a href="${h.url('repos_group_home',group_name=k)}">${k}</a>
%else:
${k}
%endif
</td>
%if section == 'global':
${h.bool2icon(True)}
<span class="perm_tag ${_perm}">${section_perm}</span>
</tr>
%endfor
</tbody>
</table>
</%def>
Status change: