@@ -30,24 +30,27 @@ news
- 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
- File view now displays small gravatars off all authors of given file
- Implemented landing revisions. Each repository will get landing_rev attribute
that defines 'default' revision/branch for generating readme files
- Implemented #509, RhodeCode enforces SSL for push/pulling if requested.
- Import remote svn repositories to mercurial using hgsubversion
- Fixed #508 RhodeCode now has a option to explicitly set forking permissions
- RhodeCode can use alternative server for generating avatar icons
- implemented repositories locking. Pull locks, push unlocks. Also can be done
via API calls
- #538 form for permissions can handle multiple users at once
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.
@@ -264,25 +264,24 @@ def engine_from_config(configuration, pr
COLOR_SQL = YELLOW
normal = '\x1b[0m'
return ''.join([COLOR_SEQ % COLOR_SQL, sql, normal])
if configuration['debug']:
#attach events only for debug configuration
def before_cursor_execute(conn, cursor, statement,
parameters, context, executemany):
context._query_start_time = time.time()
log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
def after_cursor_execute(conn, cursor, statement,
total = time.time() - context._query_start_time
log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total))
event.listen(engine, "before_cursor_execute",
before_cursor_execute)
event.listen(engine, "after_cursor_execute",
after_cursor_execute)
return engine
"""
Set of generic validators
import os
import re
import formencode
import logging
from collections import defaultdict
from pylons.i18n.translation import _
from webhelpers.pylonslib.secure_form import authentication_token
from formencode.validators import (
UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set,
NotEmpty
)
from rhodecode.lib.compat import OrderedSet
from rhodecode.lib.utils import repo_name_slug
from rhodecode.model.db import RepoGroup, Repository, UsersGroup, User,\
ChangesetStatus
from rhodecode.lib.exceptions import LdapImportError
from rhodecode.config.routing import ADMIN_PREFIX
# silence warnings and pylint
UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set, \
log = logging.getLogger(__name__)
@@ -468,51 +470,65 @@ def ValidPerms(type_='repo'):
if type_ == 'group':
EMPTY_PERM = 'group.none'
elif type_ == 'repo':
EMPTY_PERM = 'repository.none'
class _validator(formencode.validators.FancyValidator):
messages = {
'perm_new_member_name':
_(u'This username or users group name is not valid')
}
def to_python(self, value, state):
perms_update = []
perms_new = []
perms_update = OrderedSet()
perms_new = OrderedSet()
# build a list of permission to update and new permission to create
for k, v in value.items():
# means new added member to permissions
#CLEAN OUT ORG VALUE FROM NEW MEMBERS, and group them using
new_perms_group = defaultdict(dict)
for k, v in value.copy().iteritems():
if k.startswith('perm_new_member'):
new_perm = value.get('perm_new_member', False)
new_member = value.get('perm_new_member_name', False)
new_type = value.get('perm_new_member_type')
del value[k]
_type, part = k.split('perm_new_member_')
args = part.split('_')
if len(args) == 1:
new_perms_group[args[0]]['perm'] = v
elif len(args) == 2:
_key, pos = args
new_perms_group[pos][_key] = v
if new_member and new_perm:
if (new_member, new_perm, new_type) not in perms_new:
perms_new.append((new_member, new_perm, new_type))
elif k.startswith('u_perm_') or k.startswith('g_perm_'):
# fill new permissions in order of how they were added
for k in sorted(map(int, new_perms_group.keys())):
perm_dict = new_perms_group[str(k)]
new_member = perm_dict['name']
new_perm = perm_dict['perm']
new_type = perm_dict['type']
if new_member and new_perm and new_type:
perms_new.add((new_member, new_perm, new_type))
for k, v in value.iteritems():
if k.startswith('u_perm_') or k.startswith('g_perm_'):
member = k[7:]
t = {'u': 'user',
'g': 'users_group'
}[k[0]]
if member == 'default':
if value.get('private'):
# set none for default when updating to
# private repo
v = EMPTY_PERM
perms_update.append((member, v, t))
perms_update.add((member, v, t))
value['perms_updates'] = perms_update
value['perms_new'] = perms_new
value['perms_updates'] = list(perms_update)
value['perms_new'] = list(perms_new)
# update permissions
for k, v, t in perms_new:
try:
if t is 'user':
self.user_db = User.query()\
.filter(User.active == True)\
.filter(User.username == k).one()
if t is 'users_group':
self.user_db = UsersGroup.query()\
.filter(UsersGroup.users_group_active == True)\
.filter(UsersGroup.users_group_name == k).one()
@@ -899,25 +899,25 @@ var readNotification = function(url, not
failure:function(o){
alert("error");
},
};
var postData = '_method=put';
var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
var request = YAHOO.util.Connect.asyncRequest('POST', sUrl,
callback, postData);
/** MEMBERS AUTOCOMPLETE WIDGET **/
var MembersAutoComplete = function (users_list, groups_list) {
var MembersAutoComplete = function (divid, cont, users_list, groups_list) {
var myUsers = users_list;
var myGroups = groups_list;
// Define a custom search function for the DataSource of users
var matchUsers = function (sQuery) {
// Case insensitive matching
var query = sQuery.toLowerCase();
var i = 0;
var l = myUsers.length;
var matches = [];
// Match against each name of each contact
@@ -961,25 +961,25 @@ var MembersAutoComplete = function (user
var memberDS = new YAHOO.util.FunctionDataSource(matchAll);
memberDS.responseSchema = {
fields: ["id", "fname", "lname", "nname", "grname", "grmembers", "gravatar_lnk"]
// DataScheme for owner
var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
ownerDS.responseSchema = {
fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
// Instantiate AutoComplete for perms
var membersAC = new YAHOO.widget.AutoComplete("perm_new_member_name", "perm_container", memberDS);
var membersAC = new YAHOO.widget.AutoComplete(divid, cont, memberDS);
membersAC.useShadow = false;
membersAC.resultTypeList = false;
membersAC.animVert = false;
membersAC.animHoriz = false;
membersAC.animSpeed = 0.1;
// Instantiate AutoComplete for owner
var ownerAC = new YAHOO.widget.AutoComplete("user", "owner_container", ownerDS);
ownerAC.useShadow = false;
ownerAC.resultTypeList = false;
ownerAC.animVert = false;
ownerAC.animHoriz = false;
@@ -1046,37 +1046,37 @@ var MembersAutoComplete = function (user
displaynname = nname ? "(" + nname + ")" : "";
return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
} else {
return '';
membersAC.formatResult = custom_formatter;
ownerAC.formatResult = custom_formatter;
var myHandler = function (sType, aArgs) {
var nextId = divid.split('perm_new_member_name_')[1];
var myAC = aArgs[0]; // reference back to the AC instance
var elLI = aArgs[1]; // reference to the selected LI element
var oData = aArgs[2]; // object literal of selected item's result data
//fill the autocomplete with value
if (oData.nname != undefined) {
//users
myAC.getInputEl().value = oData.nname;
YUD.get('perm_new_member_type').value = 'user';
YUD.get('perm_new_member_type_'+nextId).value = 'user';
//groups
myAC.getInputEl().value = oData.grname;
YUD.get('perm_new_member_type').value = 'users_group';
YUD.get('perm_new_member_type_'+nextId).value = 'users_group';
membersAC.itemSelectEvent.subscribe(myHandler);
if(ownerAC.itemSelectEvent){
ownerAC.itemSelectEvent.subscribe(myHandler);
return {
memberDS: memberDS,
ownerDS: ownerDS,
membersAC: membersAC,
@@ -1593,24 +1593,43 @@ var linkSort = function(a, b, desc, fiel
var a_ = fromHTML(a.getData(field));
var b_ = fromHTML(a.getData(field));
// extract url text from string nodes
a_ = get_link(a_)
b_ = get_link(b_)
var comp = YAHOO.util.Sort.compare;
var compState = comp(a_, b_, desc);
return compState;
var addPermAction = function(_html, users_list, groups_list){
var elmts = YUD.getElementsByClassName('last_new_member');
var last_node = elmts[elmts.length-1];
if (last_node){
var next_id = (YUD.getElementsByClassName('new_members')).length;
_html = _html.format(next_id);
last_node.innerHTML = _html;
YUD.setStyle(last_node, 'display', '');
YUD.removeClass(last_node, 'last_new_member');
MembersAutoComplete("perm_new_member_name_"+next_id,
"perm_container_"+next_id, users_list, groups_list);
//create new last NODE
var el = document.createElement('tr');
el.id = 'add_perm_input';
YUD.addClass(el,'last_new_member');
YUD.addClass(el,'new_members');
YUD.insertAfter(el, last_node);
/* Multi selectors */
var MultiSelectWidget = function(selected_id, available_id, form_id){
//definition of containers ID's
var selected_container = selected_id;
var available_container = available_id;
//temp container for selected storage.
var cache = new Array();
@@ -51,38 +51,41 @@
<a href="${h.url('edit_users_group',id=g2p.users_group.users_group_id)}">${g2p.users_group.users_group_name}</a>
%else:
${g2p.users_group.users_group_name}
%endif
</td>
<td>
<span class="delete_icon action_button" onclick="ajaxActionUsersGroup(${g2p.users_group.users_group_id},'${'id%s'%id(g2p.users_group.users_group_name)}')">
${_('revoke')}
</span>
</tr>
%endfor
<tr id="add_perm_input">
<td>${h.radio('perm_new_member','repository.none')}</td>
<td>${h.radio('perm_new_member','repository.read')}</td>
<td>${h.radio('perm_new_member','repository.write')}</td>
<td>${h.radio('perm_new_member','repository.admin')}</td>
<td class='ac'>
<div class="perm_ac" id="perm_ac">
${h.text('perm_new_member_name',class_='yui-ac-input')}
${h.hidden('perm_new_member_type')}
<div id="perm_container"></div>
</div>
<td></td>
<%
_tmpl = h.literal("""' \
<td><input type="radio" value="repository.none" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
<td><input type="radio" value="repository.read" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
<td><input type="radio" value="repository.write" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
<td><input type="radio" value="repository.admin" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
<td class="ac"> \
<div class="perm_ac" id="perm_ac_{0}"> \
<input class="yui-ac-input" id="perm_new_member_name_{0}" name="perm_new_member_name_{0}" value="" type="text"> \
<input id="perm_new_member_type_{0}" name="perm_new_member_type_{0}" value="" type="hidden"> \
<div id="perm_container_{0}"></div> \
</div> \
</td> \
<td></td>'""")
%>
## ADD HERE DYNAMICALLY NEW INPUTS FROM THE '_tmpl'
<tr class="new_members last_new_member" id="add_perm_input"></tr>
<tr>
<td colspan="6">
<span id="add_perm" class="add_icon" style="cursor: pointer;">
${_('Add another member')}
</table>
<script type="text/javascript">
function ajaxActionUser(user_id, field_id) {
var sUrl = "${h.url('delete_repo_user',repo_name=c.repo_name)}";
var callback = {
@@ -109,20 +112,17 @@ function ajaxActionUsersGroup(users_grou
alert("${_('Failed to remove users group')}");
var postData = '_method=delete&users_group_id='+users_group_id;
var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
YUE.onDOMReady(function () {
if (!YUD.hasClass('perm_new_member_name', 'error')) {
YUD.setStyle('add_perm_input', 'display', 'none');
YAHOO.util.Event.addListener('add_perm', 'click', function () {
YUD.setStyle('add_perm_input', 'display', '');
YUD.setStyle('add_perm', 'opacity', '0.6');
YUD.setStyle('add_perm', 'cursor', 'default');
addPermAction(${_tmpl}, ${c.users_array|n}, ${c.users_groups_array|n});
});
MembersAutoComplete(${c.users_array|n}, ${c.users_groups_array|n});
</script>
@@ -35,38 +35,41 @@
<td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.write')}</td>
<td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.admin')}</td>
<td style="white-space: nowrap;">
<img class="perm-gravatar" src="${h.url('/images/icons/group.png')}"/>${g2p.users_group.users_group_name}
<td>${h.radio('perm_new_member','group.none')}</td>
<td>${h.radio('perm_new_member','group.read')}</td>
<td>${h.radio('perm_new_member','group.write')}</td>
<td>${h.radio('perm_new_member','group.admin')}</td>
<td><input type="radio" value="group.none" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
<td><input type="radio" value="group.read" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
<td><input type="radio" value="group.write" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
<td><input type="radio" value="group.admin" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
var sUrl = "${h.url('delete_repos_group_user_perm',group_name=c.repos_group.group_name)}";
@@ -93,20 +96,17 @@ function ajaxActionUsersGroup(users_grou
Status change: