Merge branch 'bugfix/broken-user-and-group-management-10367'

refs #10367
fixes #10137
fixes #10332
This commit is contained in:
Johannes Meyer 2015-10-29 08:52:31 +01:00
commit d32c3d2a52
13 changed files with 341 additions and 331 deletions

View File

@ -44,7 +44,7 @@ class GroupController extends AuthBackendController
'backend',
array(
'autosubmit' => true,
'label' => $this->translate('Usergroup Backend'),
'label' => $this->translate('User Group Backend'),
'multiOptions' => array_combine($backendNames, $backendNames),
'value' => $this->params->get('backend')
)
@ -66,7 +66,7 @@ class GroupController extends AuthBackendController
$this->setupLimitControl();
$this->setupSortControl(
array(
'group_name' => $this->translate('Group'),
'group_name' => $this->translate('User Group'),
'created_at' => $this->translate('Created at'),
'last_modified' => $this->translate('Last modified')
),
@ -134,7 +134,7 @@ class GroupController extends AuthBackendController
$removeForm->addElement('button', 'btn_submit', array(
'escape' => false,
'type' => 'submit',
'class' => 'link-like spinner',
'class' => 'link-button spinner',
'value' => 'btn_submit',
'decorators' => array('ViewHelper'),
'label' => $this->view->icon('trash'),

View File

@ -44,7 +44,7 @@ class UserController extends AuthBackendController
'backend',
array(
'autosubmit' => true,
'label' => $this->translate('Authentication Backend'),
'label' => $this->translate('User Backend'),
'multiOptions' => array_combine($backendNames, $backendNames),
'value' => $this->params->get('backend')
)
@ -140,7 +140,7 @@ class UserController extends AuthBackendController
$removeForm->addElement('button', 'btn_submit', array(
'escape' => false,
'type' => 'submit',
'class' => 'link-like spinner',
'class' => 'link-button spinner',
'value' => 'btn_submit',
'decorators' => array('ViewHelper'),
'label' => $this->view->icon('trash'),

View File

@ -4,30 +4,24 @@ use Icinga\Data\Extensible;
use Icinga\Data\Reducible;
if (! $this->compact): ?>
<div class="controls">
<?= $this->tabs; ?>
<div class="grid dont-print">
<div class="col-1-3 text-left">
<?= $this->limiter ?>
<div class="controls separated dont-print">
<?= $tabs; ?>
<div class="grid">
<?= $this->limiter ?>
<?= $this->paginator ?>
<?= $this->sortBox ?>
</div>
<div class="col-1-3">
<?= $this->paginator ?>
<div>
<?= $this->backendSelection; ?>
<?= $this->filterEditor; ?>
</div>
<div class="col-1-3 text-right">
<?= $this->sortBox ?>
</div>
</div>
<div>
<?= $this->backendSelection; ?>
<?= $this->filterEditor; ?>
</div>
</div>
<?php endif ?>
<div class="content groups">
<div class="content">
<?php
if (! isset($backend)) {
echo $this->translate('No backend found which is able to list groups') . '</div>';
echo $this->translate('No backend found which is able to list user groups') . '</div>';
return;
} else {
$extensible = $this->hasPermission('config/authentication/groups/add') && $backend instanceof Extensible;
@ -37,64 +31,68 @@ if (! isset($backend)) {
<?php if ($extensible): ?>
<?= $this->qlink(
$this->translate('Add a New User Group') ,
$this->translate('Add a New User Group'),
'group/add',
array('backend' => $backend->getName()),
array(
'class' => 'button-link',
'data-base-target' => '_next',
'icon' => 'plus',
'title' => $this->translate('Create a new user group')
'icon' => 'plus'
)
) ?>
<?php endif ?>
<?php
// @TODO(el): Remove $firstRow stuff
$firstRow = true;
foreach ($groups as $group): ?>
<?php if ($firstRow): ?>
<?php $firstRow = false; ?>
<table data-base-target="_next" class="action-table listing-table">
<thead>
<tr>
<th class="group-name"><?= $this->translate('Group'); ?></th>
<?php if ($reducible): ?>
<th class="group-remove"><?= $this->translate('Remove'); ?></th>
<?php endif ?>
</tr>
</thead>
<tbody>
<?php endif ?>
<tr>
<td class="group-name"><?= $this->qlink($group->group_name, 'group/show', array(
'backend' => $backend->getName(),
'group' => $group->group_name
), array(
'title' => sprintf($this->translate('Show detailed information for group %s'), $group->group_name)
)); ?></td>
<?php if ($reducible): ?>
<td class="group-remove">
<?= $this->qlink(
null,
'group/remove',
array(
'backend' => $backend->getName(),
'group' => $group->group_name
),
array(
'title' => sprintf($this->translate('Remove group %s'), $group->group_name),
'icon' => 'trash'
)
); ?>
</td>
<?php endif ?>
</tr>
<?php endforeach ?>
<?php if ($groups->hasResult()): ?>
</tbody>
</table>
<?php else: ?>
<p><?= $this->translate('No groups found matching the filter'); ?></p>
<?php endif ?>
<?php if (! $groups->hasResult()): ?>
<p><?= $this->translate('No user groups found matching the filter'); ?></p>
</div>
<?php endif ?>
<table data-base-target="_next" class="action-table listing-table">
<thead>
<tr>
<th><?= $this->translate('User Group'); ?></th>
<?php if ($reducible): ?>
<th><?= $this->translate('Remove'); ?></th>
<?php endif ?>
</tr>
</thead>
<tbody>
<?php foreach ($groups as $group): ?>
<tr>
<td>
<?= $this->qlink(
$group->group_name,
'group/show',
array(
'backend' => $backend->getName(),
'group' => $group->group_name
),
array(
'title' => sprintf(
$this->translate('Show detailed information for user group %s'),
$group->group_name
)
)
); ?>
</td>
<?php if ($reducible): ?>
<td class="icon-col">
<?= $this->qlink(
null,
'group/remove',
array(
'backend' => $backend->getName(),
'group' => $group->group_name
),
array(
'title' => sprintf($this->translate('Remove user group %s'), $group->group_name),
'icon' => 'trash'
)
); ?>
</td>
<?php endif ?>
</tr>
<?php endforeach ?>
</tbody>
</table>
</div>

View File

@ -2,7 +2,6 @@
use Icinga\Data\Extensible;
use Icinga\Data\Updatable;
use Icinga\Data\Selectable;
$extensible = $this->hasPermission('config/authentication/groups/add') && $backend instanceof Extensible;
@ -24,92 +23,86 @@ if ($this->hasPermission('config/authentication/groups/edit') && $backend instan
}
?>
<div class="controls">
<?php if (! $this->compact): ?>
<?= $tabs; ?>
<?php endif ?>
<h2 class="clearfix"><?= $this->escape($group->group_name) ?><span class="pull-right"><?= $editLink ?></span></h2>
<table class="name-value-table">
<tr>
<th><?= $this->translate('Created at'); ?></th>
<td><?= $group->created_at === null ? '-' : $this->formatDateTime($group->created_at); ?></td>
</tr>
<tr>
<th><?= $this->translate('Last modified'); ?></th>
<td><?= $group->last_modified === null ? '-' : $this->formatDateTime($group->last_modified); ?></td>
</tr>
</table>
<h2><?= $this->translate('Members'); ?></h2>
<div class="controls separated dont-print">
<?php if (! $this->compact): ?>
<?= $this->sortBox; ?>
<?= $tabs; ?>
<?php endif ?>
<?= $this->limiter; ?>
<?= $this->paginator; ?>
<h2 class="clearfix"><?= $this->escape($group->group_name) ?><span class="pull-right"><?= $editLink ?></span></h2>
<table class="name-value-table">
<tr>
<th><?= $this->translate('Created at'); ?></th>
<td><?= $group->created_at === null ? '-' : $this->formatDateTime($group->created_at); ?></td>
</tr>
<tr>
<th><?= $this->translate('Last modified'); ?></th>
<td><?= $group->last_modified === null ? '-' : $this->formatDateTime($group->last_modified); ?></td>
</tr>
</table>
<?php if (! $this->compact): ?>
<?= $this->filterEditor; ?>
<h2><?= $this->translate('Members'); ?></h2>
<div class="grid">
<?= $this->limiter; ?>
<?= $this->paginator; ?>
<?= $this->sortBox; ?>
</div>
<?= $this->filterEditor; ?>
<?php endif ?>
</div>
<div class="content members" data-base-target="_next">
<div class="content">
<?php if ($extensible): ?>
<?= $this->qlink(
$this->translate('Add User to Group') ,
$this->translate('Add New Member'),
'group/addmember',
null,
array(
'class' => 'button-link',
'data-base-target' => '_next',
'icon' => 'plus',
'title' => $this->translate('Add user to group')
'backend' => $backend->getName(),
'group' => $group->group_name
),
array(
'icon' => 'plus',
'class' => 'button-link'
)
) ?>
<?php endif ?>
<?php
$firstRow = true;
foreach ($members as $member): ?>
<?php if ($firstRow): ?>
<?php $firstRow = false; ?>
<table data-base-target="_next" class="action-table listing-table">
<thead>
<tr>
<th class="member-name"><?= $this->translate('Username'); ?></th>
<?php if (isset($removeForm)): ?>
<th class="member-remove"><?= $this->translate('Remove'); ?></th>
<?php endif ?>
</tr>
</thead>
<tbody>
<?php endif ?>
<tr>
<td class="member-name">
<?php if (
$this->hasPermission('config/authentication/users/show')
&& method_exists($backend, 'getUserBackend')
&& ($userBackend = $backend->getUserBackend()) !== null
&& $userBackend instanceof Selectable
): ?>
<?= $this->qlink($member->user_name, 'user/show', array(
'backend' => $userBackend->getName(),
'user' => $member->user_name
), array(
'title' => sprintf($this->translate('Show detailed information about %s'), $member->user_name)
)); ?>
<?php else: ?>
<?= $this->escape($member->user_name); ?>
<?php endif ?>
</td>
<?php if (isset($removeForm)): ?>
<td class="icon-col" data-base-target="_self">
<?php $removeForm->getElement('user_name')->setValue($member->user_name); echo $removeForm; ?>
</td>
<?php endif ?>
</tr>
<?php endforeach ?>
<?php if ($members->hasResult()): ?>
</tbody>
</table>
<?php else: ?>
<?php if (! $members->hasResult()): ?>
<p><?= $this->translate('No group member found matching the filter'); ?></p>
<?php endif ?>
</div>
<?php return; endif ?>
<table data-base-target="_next" class="action-table listing-table">
<thead>
<tr>
<th><?= $this->translate('Username'); ?></th>
<?php if (isset($removeForm)): ?>
<th><?= $this->translate('Remove'); ?></th>
<?php endif ?>
</tr>
</thead>
<tbody>
<?php foreach ($members as $member): ?>
<tr>
<td>
<?php if (
$this->hasPermission('config/authentication/users/show')
&& ($userBackend = $backend->getUserBackendName($member->user_name)) !== null
): ?>
<?= $this->qlink($member->user_name, 'user/show', array(
'backend' => $userBackend,
'user' => $member->user_name
), array(
'title' => sprintf($this->translate('Show detailed information about %s'), $member->user_name)
)); ?>
<?php else: ?>
<?= $this->escape($member->user_name); ?>
<?php endif ?>
</td>
<?php if (isset($removeForm)): ?>
<td class="icon-col" data-base-target="_self">
<?php $removeForm->getElement('user_name')->setValue($member->user_name); echo $removeForm; ?>
</td>
<?php endif ?>
</tr>
<?php endforeach ?>
</tbody>
</table>
</div>

View File

@ -1,20 +1,15 @@
<?php
use Icinga\Data\Extensible;
use Icinga\Data\Reducible;
if (! $this->compact): ?>
<div class="controls">
<?= $this->tabs ?>
<div class="grid dont-print">
<div class="col-1-3 text-left">
<?= $this->limiter ?>
</div>
<div class="col-1-3">
<?= $this->paginator ?>
</div>
<div class="col-1-3 text-right">
<?= $this->sortBox ?>
</div>
<div class="controls separated dont-print">
<?= $tabs ?>
<div class="grid">
<?= $this->limiter ?>
<?= $this->paginator ?>
<?= $this->sortBox ?>
</div>
<div>
<?= $this->backendSelection ?>
@ -22,20 +17,18 @@ if (! $this->compact): ?>
</div>
</div>
<?php endif ?>
<div class="content">
<?php if (! isset($backend)): ?>
<p><?= $this->translate('No backend found which is able to list users.') ?></p>
</div>
<?php return; endif ?>
<?php if (! $users->hasResult()): ?>
<p><?= $this->translate('No users found matching the filter.') ?></p>
</div>
<?php return; endif ?>
<?php
$extensible = $this->hasPermission('config/authentication/users/add') && $backend instanceof Extensible;
$reducible = $this->hasPermission('config/authentication/users/remove') && $backend instanceof Reducible;
if (! isset($backend)) {
echo $this->translate('No backend found which is able to list users') . '</div>';
return;
} else {
$extensible = $this->hasPermission('config/authentication/users/add') && $backend instanceof Extensible;
$reducible = $this->hasPermission('config/authentication/users/remove') && $backend instanceof Reducible;
}
?>
<?php if ($extensible): ?>
<?= $this->qlink(
$this->translate('Add a New User') ,
@ -44,12 +37,17 @@ $reducible = $this->hasPermission('config/authentication/users/remove') && $back
array(
'class' => 'button-link',
'data-base-target' => '_next',
'icon' => 'plus',
'title' => $this->translate('Create a new user')
'icon' => 'plus'
)
) ?>
<?php endif ?>
<table class="action-table listing-table" data-base-target="_next">
<?php if (! $users->hasResult()): ?>
<p><?= $this->translate('No users found matching the filter') ?></p>
</div>
<?php return; endif ?>
<table data-base-target="_next" class="action-table listing-table">
<thead>
<tr>
<th><?= $this->translate('Username') ?></th>
@ -91,4 +89,4 @@ $reducible = $this->hasPermission('config/authentication/users/remove') && $back
<?php endforeach ?>
</tbody>
</table>
</div>
</div>

View File

@ -22,93 +22,90 @@ if ($this->hasPermission('config/authentication/users/edit') && $backend instanc
}
?>
<div class="controls">
<div class="controls separated dont-print">
<?php if (! $this->compact): ?>
<?= $tabs; ?>
<?= $tabs; ?>
<?php endif ?>
<h2 class="clearfix"><?= $this->escape($user->user_name) ?><span class="pull-right"><?= $editLink ?></span></h2>
<table class="name-value-table">
<tr>
<th><?= $this->translate('State'); ?></th>
<td><?= $user->is_active === null ? '-' : ($user->is_active ? $this->translate('Active') : $this->translate('Inactive')); ?></td>
</tr>
<tr>
<th><?= $this->translate('Created at'); ?></th>
<td><?= $user->created_at === null ? '-' : $this->formatDateTime($user->created_at); ?></td>
</tr>
<tr>
<th><?= $this->translate('Last modified'); ?></th>
<td><?= $user->last_modified === null ? '-' : $this->formatDateTime($user->last_modified); ?></td>
</tr>
</table>
<h2><?= $this->translate('Group Memberships'); ?></h2>
<h2 class="clearfix"><?= $this->escape($user->user_name) ?><span class="pull-right"><?= $editLink ?></span></h2>
<table class="name-value-table">
<tr>
<th><?= $this->translate('State'); ?></th>
<td><?= $user->is_active === null ? '-' : ($user->is_active ? $this->translate('Active') : $this->translate('Inactive')); ?></td>
</tr>
<tr>
<th><?= $this->translate('Created at'); ?></th>
<td><?= $user->created_at === null ? '-' : $this->formatDateTime($user->created_at); ?></td>
</tr>
<tr>
<th><?= $this->translate('Last modified'); ?></th>
<td><?= $user->last_modified === null ? '-' : $this->formatDateTime($user->last_modified); ?></td>
</tr>
</table>
<?php if (! $this->compact): ?>
<?= $this->sortBox; ?>
<?php endif ?>
<?= $this->limiter; ?>
<?= $this->paginator; ?>
<?php if (! $this->compact): ?>
<?= $this->filterEditor; ?>
<h2><?= $this->translate('Group Memberships'); ?></h2>
<div class="grid">
<?= $this->limiter; ?>
<?= $this->paginator; ?>
<?= $this->sortBox; ?>
</div>
<?= $this->filterEditor; ?>
<?php endif ?>
</div>
<div class="content memberships">
<?php if ($showCreateMembershipLink): ?>
<div class="content">
<?php if ($showCreateMembershipLink): ?>
<?= $this->qlink(
$this->translate('Add User to Group') ,
$this->translate('Create New Membership'),
'user/createmembership',
array('backend' => $backend->getName()),
array(
'class' => 'button-link',
'data-base-target' => '_next',
'icon' => 'plus',
'title' => $this->translate('Add user to user group')
'backend' => $backend->getName(),
'user' => $user->user_name
),
array(
'icon' => 'plus',
'class' => 'button-link'
)
) ?>
<?php endif ?>
<?php
// @TODO(el): Remove that $firstRow thingy
$firstRow = true;
foreach ($memberships as $membership): ?>
<?php if ($firstRow): ?>
<?php $firstRow = false; ?>
<table data-base-target="_next" class="action-table listing-table">
<thead>
<tr>
<th class="membership-group"><?= $this->translate('Group'); ?></th>
<th class="membership-cancel"><?= $this->translate('Cancel', 'group.membership'); ?></th>
</tr>
</thead>
<tbody>
<?php endif ?>
<tr>
<td class="membership-group">
<?php if ($this->hasPermission('config/authentication/groups/show') && $membership->backend instanceof Selectable): ?>
<?= $this->qlink($membership->group_name, 'group/show', array(
'backend' => $membership->backend->getName(),
'group' => $membership->group_name
), array(
'title' => sprintf($this->translate('Show detailed information for group %s'), $membership->group_name)
)); ?>
<?php else: ?>
<?= $this->escape($membership->group_name); ?>
<?php endif ?>
</td>
<td class="membership-cancel" data-base-target="_self">
<?php if (isset($removeForm) && $membership->backend instanceof Reducible): ?>
<?= $removeForm->setAction($this->url('group/removemember', array(
'backend' => $membership->backend->getName(),
'group' => $membership->group_name
))); ?>
<?php else: ?>
-
<?php endif ?>
</td>
</tr>
<?php endforeach ?>
<?php if ($memberships->hasResult()): ?>
</tbody>
</table>
<?php else: ?>
<p><?= $this->translate('No memberships found matching the filter'); ?></p>
<?php endif ?>
<?php if (! $memberships->hasResult()): ?>
<p><?= $this->translate('No memberships found matching the filter'); ?></p>
</div>
<?php return; endif ?>
<table data-base-target="_next" class="action-table listing-table">
<thead>
<tr>
<th><?= $this->translate('Group'); ?></th>
<th><?= $this->translate('Cancel', 'group.membership'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($memberships as $membership): ?>
<tr>
<td>
<?php if ($this->hasPermission('config/authentication/groups/show') && $membership->backend instanceof Selectable): ?>
<?= $this->qlink($membership->group_name, 'group/show', array(
'backend' => $membership->backend->getName(),
'group' => $membership->group_name
), array(
'title' => sprintf($this->translate('Show detailed information for group %s'), $membership->group_name)
)); ?>
<?php else: ?>
<?= $this->escape($membership->group_name); ?>
<?php endif ?>
</td>
<td class="icon-col" data-base-target="_self">
<?php if (isset($removeForm) && $membership->backend instanceof Reducible): ?>
<?= $removeForm->setAction($this->url('group/removemember', array(
'backend' => $membership->backend->getName(),
'group' => $membership->group_name
))); ?>
<?php else: ?>
-
<?php endif ?>
</td>
</tr>
<?php endforeach ?>
</tbody>
</table>
</div>

View File

@ -130,6 +130,7 @@ class DbUserBackend extends DbRepository implements UserBackendInterface, Inspec
*/
public function insert($table, array $bind)
{
$this->requireTable($table);
$bind['created_at'] = date('Y-m-d H:i:s');
$this->ds->insert(
$this->prependTablePrefix($table),
@ -150,6 +151,7 @@ class DbUserBackend extends DbRepository implements UserBackendInterface, Inspec
*/
public function update($table, array $bind, Filter $filter = null)
{
$this->requireTable($table);
$bind['last_modified'] = date('Y-m-d H:i:s');
if ($filter) {
$filter = $this->requireFilter($table, $filter);

View File

@ -341,6 +341,27 @@ class LdapUserBackend extends LdapRepository implements UserBackendInterface, In
return $table;
}
/**
* Validate that the given column is a valid query target and return it or the actual name if it's an alias
*
* @param string $table The table where to look for the column or alias
* @param string $name The name or alias of the column to validate
* @param RepositoryQuery $query An optional query to pass as context
*
* @return string The given column's name
*
* @throws QueryException In case the given column is not a valid query column
*/
public function requireQueryColumn($table, $name, RepositoryQuery $query = null)
{
$column = parent::requireQueryColumn($table, $name, $query);
if ($name === 'user_name' && $query !== null) {
$query->getQuery()->setUnfoldAttribute('user_name');
}
return $column;
}
/**
* Authenticate the given user
*

View File

@ -217,6 +217,18 @@ class DbUserGroupBackend extends DbRepository implements UserGroupBackendInterfa
return $memberships;
}
/**
* Return the name of the backend that is providing the given user
*
* @param string $username Currently unused
*
* @return null|string The name of the backend or null in case this information is not available
*/
public function getUserBackendName($username)
{
return null; // TODO(10373): Store this to the database when inserting and fetch it here
}
/**
* Join group into group_membership
*
@ -238,7 +250,7 @@ class DbUserGroupBackend extends DbRepository implements UserGroupBackendInterfa
*/
protected function joinGroupMembership(RepositoryQuery $query)
{
$query->getQuery()->join(
$query->getQuery()->joinLeft(
$this->requireTable('group_membership'),
'g.id = gm.group_id',
array()

View File

@ -5,6 +5,7 @@ namespace Icinga\Authentication\UserGroup;
use Icinga\Authentication\User\UserBackend;
use Icinga\Authentication\User\LdapUserBackend;
use Icinga\Application\Logger;
use Icinga\Data\ConfigObject;
use Icinga\Exception\ConfigurationError;
use Icinga\Exception\ProgrammingError;
@ -454,12 +455,48 @@ class LdapUserGroupBackend extends LdapRepository implements UserGroupBackendInt
)
);
if (! $this->isAmbiguous($this->groupClass, $this->groupMemberAttribute)) {
$rules['group_membership']['user_name'] = 'user_name';
$rules['group_membership']['user'] = 'user_name';
$rules['group']['user_name'] = 'user_name';
$rules['group']['user'] = 'user_name';
}
return $rules;
}
/**
* Return the distinguished name for the given uid or gid
*
* @param string $name
*
* @return string
*/
protected function persistUserName($name)
{
$userDn = $this->ds
->select()
->from($this->userClass, array())
->where($this->userNameAttribute, $name)
->setUsePagedResults(false)
->fetchDn();
if ($userDn) {
return $userDn;
}
$groupDn = $this->ds
->select()
->from($this->groupClass, array())
->where($this->groupNameAttribute, $name)
->setUsePagedResults(false)
->fetchDn();
if ($groupDn) {
return $groupDn;
}
Logger::debug('Unable to persist uid or gid "%s" in repository "%s". No DN found.', $name, $this->getName());
return $name;
}
/**
* Return the uid for the given distinguished name
*
@ -564,6 +601,21 @@ class LdapUserGroupBackend extends LdapRepository implements UserGroupBackendInt
return $groups;
}
/**
* Return the name of the backend that is providing the given user
*
* @param string $username Unused
*
* @return null|string The name of the backend or null in case this information is not available
*/
public function getUserBackendName($username)
{
$userBackend = $this->getUserBackend();
if ($userBackend !== null) {
return $userBackend->getName();
}
}
/**
* Apply the given configuration on this backend
*

View File

@ -34,4 +34,13 @@ interface UserGroupBackendInterface
* @return array
*/
public function getMemberships(User $user);
/**
* Return the name of the backend that is providing the given user
*
* @param string $username
*
* @return null|string The name of the backend or null in case this information is not available
*/
public function getUserBackendName($username);
}

View File

@ -297,6 +297,7 @@ abstract class DbRepository extends Repository implements Extensible, Updatable,
*/
public function insert($table, array $bind)
{
$this->requireTable($table);
$this->ds->insert($this->prependTablePrefix($table), $this->requireStatementColumns($table, $bind));
}
@ -309,6 +310,8 @@ abstract class DbRepository extends Repository implements Extensible, Updatable,
*/
public function update($table, array $bind, Filter $filter = null)
{
$this->requireTable($table);
if ($filter) {
$filter = $this->requireFilter($table, $filter);
}
@ -324,6 +327,8 @@ abstract class DbRepository extends Repository implements Extensible, Updatable,
*/
public function delete($table, Filter $filter = null)
{
$this->requireTable($table);
if ($filter) {
$filter = $this->requireFilter($table, $filter);
}

View File

@ -233,83 +233,6 @@ div.content.users {
}
}
div.content.memberships {
table.membership-list {
th.membership-cancel {
width: 8em;
padding-right: 0.5em;
text-align: right;
}
td.membership-cancel {
text-align: right;
form button.link-like {
color: inherit;
}
}
}
p {
margin-top: 0;
}
a.membership-create {
display: block;
margin-top: 1em;
}
}
div.content.groups {
table.group-list {
th.group-remove {
width: 8em;
padding-right: 0.5em;
text-align: right;
}
td.group-remove {
text-align: right;
}
}
p {
margin-top: 0;
}
a.group-add {
display: block;
margin-top: 1em;
}
}
div.content.members {
table.member-list {
th.member-remove {
width: 8em;
padding-right: 0.5em;
text-align: right;
}
td.member-remove {
text-align: right;
form button.link-like {
color: inherit;
}
}
}
p {
margin-top: 0;
}
a.member-add {
display: block;
margin-top: 1em;
}
}
form.backend-selection {
float: right;