.. _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
- 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
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.
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
- fixed #390 cache invalidation problems on repos inside group
- fixed #385 clone by ID url was loosing proxy prefix in URL
- fixed some unicode problems with waitress
- fixed issue with escaping < and > in changeset commits
- fixed error occurring during recursive group creation in API
create_repo function
- fixed #393 py2.5 fixes for routes url generator
- fixed #397 Private repository groups shows up before login
- fixed #396 fixed problems with revoking users in nested groups
- fixed mysql unicode issues + specified InnoDB as default engine with
utf8 charset
- #406 trim long branch/tag names in changelog to not break UI
1.3.3 (**2012-03-02**)
- fixed some python2.5 compatibility issues
- fixed issues with removed repos was accidentally added as groups, after
full rescan of paths
- fixes #376 Cannot edit user (using container auth)
- fixes #378 Invalid image urls on changeset screen with proxy-prefix
configuration
- fixed initial sorting of repos inside repo group
- fixes issue when user tried to resubmit same permission into user/user_groups
- bumped beaker version that fixes #375 leap error bug
- fixed raw_changeset for git. It was generated with hg patch headers
- fixed vcs issue with last_changeset for filenodes
- fixed missing commit after hook delete
- fixed #372 issues with git operation detection that caused a security issue
for git repos
1.3.2 (**2012-02-28**)
- fixed git protocol issues with repos-groups
- fixed git remote repos validator that prevented from cloning remote git repos
- fixes #370 ending slashes fixes for repo and groups
- fixes #368 improved git-protocol detection to handle other clients
- fixes #366 When Setting Repository Group To Blank Repo Group Wont Be
Moved To Root
- fixes #371 fixed issues with beaker/sqlalchemy and non-ascii cache keys
- fixed #373 missing cascade drop on user_group_to_perm table
1.3.1 (**2012-02-27**)
- redirection loop occurs when remember-me wasn't checked during login
- fixes issues with git blob history generation
- don't fetch branch for git in file history dropdown. Causes unneeded slowness
1.3.0 (**2012-02-26**)
- code review, inspired by github code-comments
- #215 rst and markdown README files support
- #252 Container-based and proxy pass-through authentication support
- #44 branch browser. Filtering of changelog by branches
- mercurial bookmarks support
- new hover top menu, optimized to add maximum size for important views
- configurable clone url template with possibility to specify protocol like
ssh:// or http:// and also manually alter other parts of clone_url.
- enabled largefiles extension by default
- optimized summary file pages and saved a lot of unused space in them
- #239 option to manually mark repository as fork
- #320 mapping of commit authors to RhodeCode users
- #304 hashes are displayed using monospace font
- diff configuration, toggle white lines and context lines
- #307 configurable diffs, whitespace toggle, increasing context lines
- sorting on branches, tags and bookmarks using YUI datatable
- improved file filter on files page
- implements #330 api method for listing nodes ar particular revision
- #73 added linking issues in commit messages to chosen issue tracker url
@@ -24,385 +24,385 @@ def make_map(config):
def check_repo(environ, match_dict):
"""
check for valid repository for proper 404 handling
:param environ:
:param match_dict:
from rhodecode.model.db import Repository
repo_name = match_dict.get('repo_name')
try:
by_id = repo_name.split('_')
if len(by_id) == 2 and by_id[1].isdigit():
repo_name = Repository.get(by_id[1]).repo_name
match_dict['repo_name'] = repo_name
except:
pass
return is_valid_repo(repo_name, config['base_path'])
def check_group(environ, match_dict):
check for valid repositories group for proper 404 handling
repos_group_name = match_dict.get('group_name')
return is_valid_repos_group(repos_group_name, config['base_path'])
def check_int(environ, match_dict):
return match_dict.get('id').isdigit()
# The ErrorController route (handles 404/500 error pages); it should
# likely stay at the top, ensuring it can always be resolved
rmap.connect('/error/{action}', controller='error')
rmap.connect('/error/{action}/{id}', controller='error')
#==========================================================================
# CUSTOM ROUTES HERE
#MAIN PAGE
rmap.connect('home', '/', controller='home', action='index')
rmap.connect('repo_switcher', '/repos', controller='home',
action='repo_switcher')
rmap.connect('branch_tag_switcher', '/branches-tags/{repo_name:.*}',
controller='home', action='branch_tag_switcher')
rmap.connect('bugtracker',
"http://bitbucket.org/marcinkuzminski/rhodecode/issues",
_static=True)
rmap.connect('rst_help',
"http://docutils.sourceforge.net/docs/user/rst/quickref.html",
rmap.connect('rhodecode_official', "http://rhodecode.org", _static=True)
#ADMIN REPOSITORY REST ROUTES
with rmap.submapper(path_prefix=ADMIN_PREFIX,
controller='admin/repos') as m:
m.connect("repos", "/repos",
action="create", conditions=dict(method=["POST"]))
action="index", conditions=dict(method=["GET"]))
m.connect("formatted_repos", "/repos.{format}",
action="index",
conditions=dict(method=["GET"]))
m.connect("new_repo", "/repos/new",
action="new", conditions=dict(method=["GET"]))
m.connect("formatted_new_repo", "/repos/new.{format}",
m.connect("/repos/{repo_name:.*}",
action="update", conditions=dict(method=["PUT"],
function=check_repo))
action="delete", conditions=dict(method=["DELETE"],
m.connect("edit_repo", "/repos/{repo_name:.*}/edit",
action="edit", conditions=dict(method=["GET"],
m.connect("formatted_edit_repo", "/repos/{repo_name:.*}.{format}/edit",
m.connect("repo", "/repos/{repo_name:.*}",
action="show", conditions=dict(method=["GET"],
m.connect("formatted_repo", "/repos/{repo_name:.*}.{format}",
#ajax delete repo perm user
m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*}",
action="delete_perm_user",
conditions=dict(method=["DELETE"], function=check_repo))
#ajax delete repo perm users_group
m.connect('delete_repo_users_group',
"/repos_delete_users_group/{repo_name:.*}",
action="delete_perm_users_group",
#settings actions
m.connect('repo_stats', "/repos_stats/{repo_name:.*}",
action="repo_stats", conditions=dict(method=["DELETE"],
m.connect('repo_cache', "/repos_cache/{repo_name:.*}",
action="repo_cache", conditions=dict(method=["DELETE"],
m.connect('repo_public_journal', "/repos_public_journal/{repo_name:.*}",
action="repo_public_journal", conditions=dict(method=["PUT"],
m.connect('repo_pull', "/repo_pull/{repo_name:.*}",
action="repo_pull", conditions=dict(method=["PUT"],
m.connect('repo_as_fork', "/repo_as_fork/{repo_name:.*}",
action="repo_as_fork", conditions=dict(method=["PUT"],
controller='admin/repos_groups') as m:
m.connect("repos_groups", "/repos_groups",
m.connect("formatted_repos_groups", "/repos_groups.{format}",
m.connect("new_repos_group", "/repos_groups/new",
m.connect("formatted_new_repos_group", "/repos_groups/new.{format}",
m.connect("update_repos_group", "/repos_groups/{id}",
function=check_int))
m.connect("delete_repos_group", "/repos_groups/{id}",
m.connect("edit_repos_group", "/repos_groups/{id}/edit",
m.connect("formatted_edit_repos_group",
"/repos_groups/{id}.{format}/edit",
m.connect("repos_group", "/repos_groups/{id}",
m.connect("formatted_repos_group", "/repos_groups/{id}.{format}",
# ajax delete repos group perm user
m.connect('delete_repos_group_user_perm',
"/delete_repos_group_user_perm/{group_name:.*}",
action="delete_repos_group_user_perm",
conditions=dict(method=["DELETE"], function=check_group))
# ajax delete repos group perm users_group
m.connect('delete_repos_group_users_group_perm',
"/delete_repos_group_users_group_perm/{group_name:.*}",
action="delete_repos_group_users_group_perm",
#ADMIN USER REST ROUTES
controller='admin/users') as m:
m.connect("users", "/users",
m.connect("formatted_users", "/users.{format}",
m.connect("new_user", "/users/new",
m.connect("formatted_new_user", "/users/new.{format}",
m.connect("update_user", "/users/{id}",
action="update", conditions=dict(method=["PUT"]))
m.connect("delete_user", "/users/{id}",
action="delete", conditions=dict(method=["DELETE"]))
m.connect("edit_user", "/users/{id}/edit",
action="edit", conditions=dict(method=["GET"]))
m.connect("formatted_edit_user",
"/users/{id}.{format}/edit",
m.connect("user", "/users/{id}",
action="show", conditions=dict(method=["GET"]))
m.connect("formatted_user", "/users/{id}.{format}",
#EXTRAS USER ROUTES
m.connect("user_perm", "/users_perm/{id}",
action="update_perm", conditions=dict(method=["PUT"]))
#ADMIN USERS REST ROUTES
#ADMIN USERS GROUPS REST ROUTES
controller='admin/users_groups') as m:
m.connect("users_groups", "/users_groups",
m.connect("formatted_users_groups", "/users_groups.{format}",
m.connect("new_users_group", "/users_groups/new",
m.connect("formatted_new_users_group", "/users_groups/new.{format}",
m.connect("update_users_group", "/users_groups/{id}",
m.connect("delete_users_group", "/users_groups/{id}",
m.connect("edit_users_group", "/users_groups/{id}/edit",
m.connect("formatted_edit_users_group",
"/users_groups/{id}.{format}/edit",
m.connect("users_group", "/users_groups/{id}",
m.connect("formatted_users_group", "/users_groups/{id}.{format}",
m.connect("users_group_perm", "/users_groups_perm/{id}",
#ADMIN GROUP REST ROUTES
rmap.resource('group', 'groups',
controller='admin/groups', path_prefix=ADMIN_PREFIX)
#ADMIN PERMISSIONS REST ROUTES
rmap.resource('permission', 'permissions',
controller='admin/permissions', path_prefix=ADMIN_PREFIX)
##ADMIN LDAP SETTINGS
rmap.connect('ldap_settings', '%s/ldap' % ADMIN_PREFIX,
controller='admin/ldap_settings', action='ldap_settings',
conditions=dict(method=["POST"]))
rmap.connect('ldap_home', '%s/ldap' % ADMIN_PREFIX,
controller='admin/ldap_settings')
#ADMIN SETTINGS REST ROUTES
controller='admin/settings') as m:
m.connect("admin_settings", "/settings",
m.connect("formatted_admin_settings", "/settings.{format}",
m.connect("admin_new_setting", "/settings/new",
m.connect("formatted_admin_new_setting", "/settings/new.{format}",
m.connect("/settings/{setting_id}",
m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
m.connect("formatted_admin_edit_setting",
"/settings/{setting_id}.{format}/edit",
m.connect("admin_setting", "/settings/{setting_id}",
m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
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"]))
#NOTIFICATION REST ROUTES
controller='admin/notifications') as m:
m.connect("notifications", "/notifications",
m.connect("notifications_mark_all_read", "/notifications/mark_all_read",
action="mark_all_read", conditions=dict(method=["GET"]))
m.connect("formatted_notifications", "/notifications.{format}",
m.connect("new_notification", "/notifications/new",
m.connect("formatted_new_notification", "/notifications/new.{format}",
m.connect("/notification/{notification_id}",
m.connect("edit_notification", "/notification/{notification_id}/edit",
m.connect("formatted_edit_notification",
"/notification/{notification_id}.{format}/edit",
m.connect("notification", "/notification/{notification_id}",
m.connect("formatted_notification", "/notifications/{notification_id}.{format}",
#ADMIN MAIN PAGES
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')
# API V2
controller='api/api') as m:
m.connect('api', '/api')
#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',
#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('summary_home', '/{repo_name:.*}',
controller='summary',
rmap.connect('repos_group_home', '/{group_name:.*}',
controller='admin/repos_groups', action="show_by_name",
conditions=dict(function=check_group))
rmap.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
controller='changeset', revision='tip',
rmap.connect('changeset_comment',
'/{repo_name:.*}/changeset/{revision}/comment',
controller='changeset', revision='tip', action='comment',
rmap.connect('changeset_comment_delete',
'/{repo_name:.*}/changeset/comment/{comment_id}/delete',
controller='changeset', action='delete_comment',
conditions=dict(function=check_repo, method=["DELETE"]))
# -*- coding: utf-8 -*-
rhodecode.controllers.changelog
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
changelog controller for rhodecode
:created_on: Apr 21, 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
from mercurial import graphmod
from pylons import request, url, session, tmpl_context as c
from pylons.controllers.util import redirect
from pylons.i18n.translation import _
import rhodecode.lib.helpers as h
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
from rhodecode.lib.base import BaseRepoController, render
from rhodecode.lib.helpers import RepoPage
from rhodecode.lib.compat import json
from rhodecode.lib.vcs.exceptions import RepositoryError, ChangesetDoesNotExistError
log = logging.getLogger(__name__)
class ChangelogController(BaseRepoController):
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
'repository.admin')
def __before__(self):
super(ChangelogController, self).__before__()
c.affected_files_cut_off = 60
def index(self):
limit = 100
default = 20
if request.params.get('size'):
int_size = int(request.params.get('size'))
except ValueError:
int_size = default
int_size = int_size if int_size <= limit else limit
c.size = int_size
session['changelog_size'] = c.size
session.save()
else:
c.size = int(session.get('changelog_size', default))
p = int(request.params.get('page', 1))
branch_name = request.params.get('branch', None)
if branch_name:
collection = [z for z in
c.rhodecode_repo.get_changesets(start=0,
branch_name=branch_name)]
c.total_cs = len(collection)
collection = c.rhodecode_repo
c.total_cs = len(c.rhodecode_repo)
c.pagination = RepoPage(collection, page=p, item_count=c.total_cs,
items_per_page=c.size, branch=branch_name)
collection = list(c.pagination)
page_revisions = [x.raw_id for x in collection]
c.comments = c.rhodecode_db_repo.comments(page_revisions)
except (RepositoryError, ChangesetDoesNotExistError, Exception), e:
log.error(traceback.format_exc())
h.flash(str(e), category='warning')
return redirect(url('home'))
self._graph(c.rhodecode_repo, collection, c.total_cs, c.size, p)
c.branch_name = branch_name
c.branch_filters = [('', _('All Branches'))] + \
[(k, k) for k in c.rhodecode_repo.branches.keys()]
return render('changelog/changelog.html')
def changelog_details(self, cs):
if request.environ.get('HTTP_X_PARTIAL_XHR'):
c.cs = c.rhodecode_repo.get_changeset(cs)
return render('changelog/changelog_details.html')
def _graph(self, repo, collection, repo_size, size, p):
Generates a DAG graph for mercurial
:param repo: repo instance
:param size: number of commits to show
:param p: page number
if not collection:
c.jsdata = json.dumps([])
return
data = []
revs = [x.revision for x in collection]
if repo.alias == 'git':
for _ in revs:
vtx = [0, 1]
edges = [[0, 0, 1]]
data.append(['', vtx, edges])
elif repo.alias == 'hg':
dag = graphmod.dagwalker(repo._repo, revs)
c.dag = graphmod.colored(dag, repo._repo)
for (id, type, ctx, vtx, edges) in c.dag:
if type != graphmod.CHANGESET:
continue
c.jsdata = json.dumps(data)
<table id="permissions_manage" class="noborder">
<tr>
<td>${_('none')}</td>
<td>${_('read')}</td>
<td>${_('write')}</td>
<td>${_('admin')}</td>
<td>${_('member')}</td>
<td></td>
</tr>
## USERS
%for r2p in c.repo_info.repo_to_perm:
%if r2p.user.username =='default' and c.repo_info.private:
<td colspan="4">
<span class="private_repo_msg">
${_('private repository')}
</span>
</td>
<td class="private_repo_msg"><img style="vertical-align:bottom" src="${h.url('/images/icons/user.png')}"/>${r2p.user.username}</td>
%else:
<tr id="id${id(r2p.user.username)}">
<td>${h.radio('u_perm_%s' % r2p.user.username,'repository.none')}</td>
<td>${h.radio('u_perm_%s' % r2p.user.username,'repository.read')}</td>
<td>${h.radio('u_perm_%s' % r2p.user.username,'repository.write')}</td>
<td>${h.radio('u_perm_%s' % r2p.user.username,'repository.admin')}</td>
<td style="white-space: nowrap;">
<img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username}
<td>
%if r2p.user.username !='default':
<span class="delete_icon action_button" onclick="ajaxActionUser(${r2p.user.user_id},'${'id%s'%id(r2p.user.username)}')">
${_('revoke')}
%endif
%endfor
## USERS GROUPS
%for g2p in c.repo_info.users_group_to_perm:
<tr id="id${id(g2p.users_group.users_group_name)}">
<td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.none')}</td>
<td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.read')}</td>
<td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.write')}</td>
<td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.admin')}</td>
<img class="perm-gravatar" src="${h.url('/images/icons/group.png')}"/>${g2p.users_group.users_group_name}
<img class="perm-gravatar" src="${h.url('/images/icons/group.png')}"/>
%if h.HasPermissionAny('hg.admin')():
<a href="${h.url('edit_users_group',id=g2p.users_group.users_group_id)}">${g2p.users_group.users_group_name}</a>
${g2p.users_group.users_group_name}
<span class="delete_icon action_button" onclick="ajaxActionUsersGroup(${g2p.users_group.users_group_id},'${'id%s'%id(g2p.users_group.users_group_name)}')">
<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 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 = {
success: function (o) {
var tr = YUD.get(String(field_id));
tr.parentNode.removeChild(tr);
},
failure: function (o) {
alert("${_('Failed to remove user')}");
};
var postData = '_method=delete&user_id=' + user_id;
var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
function ajaxActionUsersGroup(users_group_id,field_id){
var sUrl = "${h.url('delete_repo_users_group',repo_name=c.repo_name)}";
success:function(o){
failure:function(o){
alert("${_('Failed to remove users group')}");
var postData = '_method=delete&users_group_id='+users_group_id;
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');
});
MembersAutoComplete(${c.users_array|n}, ${c.users_groups_array|n});
</script>
Status change: