added team filter in timesheet search forms (#3590)
* added team filter in export and invoice search form * added teams select to timesheet filter * added team select to report filter * hide user and team filter if count < 2
This commit is contained in:
@@ -63,10 +63,6 @@ final class ReportUsersMonthController extends AbstractController
|
|||||||
$currentUser = $this->getUser();
|
$currentUser = $this->getUser();
|
||||||
$dateTimeFactory = $this->getDateTimeFactory();
|
$dateTimeFactory = $this->getDateTimeFactory();
|
||||||
|
|
||||||
$query = new UserQuery();
|
|
||||||
$query->setCurrentUser($currentUser);
|
|
||||||
$allUsers = $userRepository->getUsersForQuery($query);
|
|
||||||
|
|
||||||
$values = new MonthlyUserList();
|
$values = new MonthlyUserList();
|
||||||
$values->setDate($dateTimeFactory->getStartOfMonth());
|
$values->setDate($dateTimeFactory->getStartOfMonth());
|
||||||
|
|
||||||
@@ -77,9 +73,20 @@ final class ReportUsersMonthController extends AbstractController
|
|||||||
|
|
||||||
$form->submit($request->query->all(), false);
|
$form->submit($request->query->all(), false);
|
||||||
|
|
||||||
if ($form->isSubmitted() && !$form->isValid()) {
|
$query = new UserQuery();
|
||||||
|
$query->setCurrentUser($currentUser);
|
||||||
|
|
||||||
|
if ($form->isSubmitted()) {
|
||||||
|
if (!$form->isValid()) {
|
||||||
$values->setDate($dateTimeFactory->getStartOfMonth());
|
$values->setDate($dateTimeFactory->getStartOfMonth());
|
||||||
|
} else {
|
||||||
|
if ($values->getTeam() !== null) {
|
||||||
|
$query->setSearchTeams([$values->getTeam()]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$allUsers = $userRepository->getUsersForQuery($query);
|
||||||
|
|
||||||
if ($values->getDate() === null) {
|
if ($values->getDate() === null) {
|
||||||
$values->setDate($dateTimeFactory->getStartOfMonth());
|
$values->setDate($dateTimeFactory->getStartOfMonth());
|
||||||
|
|||||||
@@ -63,10 +63,6 @@ final class ReportUsersWeekController extends AbstractController
|
|||||||
$currentUser = $this->getUser();
|
$currentUser = $this->getUser();
|
||||||
$dateTimeFactory = $this->getDateTimeFactory();
|
$dateTimeFactory = $this->getDateTimeFactory();
|
||||||
|
|
||||||
$query = new UserQuery();
|
|
||||||
$query->setCurrentUser($currentUser);
|
|
||||||
$allUsers = $userRepository->getUsersForQuery($query);
|
|
||||||
|
|
||||||
$values = new WeeklyUserList();
|
$values = new WeeklyUserList();
|
||||||
$values->setDate($dateTimeFactory->getStartOfWeek());
|
$values->setDate($dateTimeFactory->getStartOfWeek());
|
||||||
|
|
||||||
@@ -77,9 +73,20 @@ final class ReportUsersWeekController extends AbstractController
|
|||||||
|
|
||||||
$form->submit($request->query->all(), false);
|
$form->submit($request->query->all(), false);
|
||||||
|
|
||||||
if ($form->isSubmitted() && !$form->isValid()) {
|
$query = new UserQuery();
|
||||||
|
$query->setCurrentUser($currentUser);
|
||||||
|
|
||||||
|
if ($form->isSubmitted()) {
|
||||||
|
if (!$form->isValid()) {
|
||||||
$values->setDate($dateTimeFactory->getStartOfWeek());
|
$values->setDate($dateTimeFactory->getStartOfWeek());
|
||||||
|
} else {
|
||||||
|
if ($values->getTeam() !== null) {
|
||||||
|
$query->setSearchTeams([$values->getTeam()]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$allUsers = $userRepository->getUsersForQuery($query);
|
||||||
|
|
||||||
if ($values->getDate() === null) {
|
if ($values->getDate() === null) {
|
||||||
$values->setDate($dateTimeFactory->getStartOfWeek());
|
$values->setDate($dateTimeFactory->getStartOfWeek());
|
||||||
|
|||||||
@@ -73,9 +73,6 @@ final class ReportUsersYearController extends AbstractController
|
|||||||
$currentUser = $this->getUser();
|
$currentUser = $this->getUser();
|
||||||
$dateTimeFactory = $this->getDateTimeFactory();
|
$dateTimeFactory = $this->getDateTimeFactory();
|
||||||
|
|
||||||
$query = new UserQuery();
|
|
||||||
$query->setCurrentUser($currentUser);
|
|
||||||
$allUsers = $userRepository->getUsersForQuery($query);
|
|
||||||
$defaultDate = $dateTimeFactory->createStartOfYear();
|
$defaultDate = $dateTimeFactory->createStartOfYear();
|
||||||
|
|
||||||
if (null !== ($financialYear = $systemConfiguration->getFinancialYearStart())) {
|
if (null !== ($financialYear = $systemConfiguration->getFinancialYearStart())) {
|
||||||
@@ -92,9 +89,20 @@ final class ReportUsersYearController extends AbstractController
|
|||||||
|
|
||||||
$form->submit($request->query->all(), false);
|
$form->submit($request->query->all(), false);
|
||||||
|
|
||||||
if ($form->isSubmitted() && !$form->isValid()) {
|
$query = new UserQuery();
|
||||||
|
$query->setCurrentUser($currentUser);
|
||||||
|
|
||||||
|
if ($form->isSubmitted()) {
|
||||||
|
if (!$form->isValid()) {
|
||||||
$values->setDate(clone $defaultDate);
|
$values->setDate(clone $defaultDate);
|
||||||
|
} else {
|
||||||
|
if ($values->getTeam() !== null) {
|
||||||
|
$query->setSearchTeams([$values->getTeam()]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$allUsers = $userRepository->getUsersForQuery($query);
|
||||||
|
|
||||||
if ($values->getDate() === null) {
|
if ($values->getDate() === null) {
|
||||||
$values->setDate(clone $defaultDate);
|
$values->setDate(clone $defaultDate);
|
||||||
|
|||||||
88
src/Form/Extension/ToolbarFormExtension.php
Normal file
88
src/Form/Extension/ToolbarFormExtension.php
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Kimai time-tracking app.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Form\Extension;
|
||||||
|
|
||||||
|
use App\Form\Toolbar\ExportToolbarForm;
|
||||||
|
use App\Form\Toolbar\InvoiceToolbarForm;
|
||||||
|
use App\Form\Toolbar\InvoiceToolbarSimpleForm;
|
||||||
|
use App\Form\Toolbar\TimesheetExportToolbarForm;
|
||||||
|
use App\Form\Toolbar\TimesheetToolbarForm;
|
||||||
|
use App\Form\Toolbar\UserToolbarForm;
|
||||||
|
use App\Reporting\MonthlyUserListForm;
|
||||||
|
use App\Reporting\WeeklyUserListForm;
|
||||||
|
use App\Reporting\YearlyUserListForm;
|
||||||
|
use App\User\TeamService;
|
||||||
|
use App\User\UserService;
|
||||||
|
use Symfony\Component\Form\AbstractTypeExtension;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
|
||||||
|
final class ToolbarFormExtension extends AbstractTypeExtension
|
||||||
|
{
|
||||||
|
private $userService;
|
||||||
|
private $teamService;
|
||||||
|
private $teamNames = ['team', 'teams', 'searchTeams'];
|
||||||
|
private $userNames = ['user', 'users'];
|
||||||
|
|
||||||
|
public function __construct(UserService $userService, TeamService $teamService)
|
||||||
|
{
|
||||||
|
$this->userService = $userService;
|
||||||
|
$this->teamService = $teamService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getExtendedTypes(): iterable
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
InvoiceToolbarForm::class,
|
||||||
|
InvoiceToolbarSimpleForm::class,
|
||||||
|
ExportToolbarForm::class,
|
||||||
|
TimesheetToolbarForm::class,
|
||||||
|
TimesheetExportToolbarForm::class,
|
||||||
|
UserToolbarForm::class,
|
||||||
|
WeeklyUserListForm::class,
|
||||||
|
MonthlyUserListForm::class,
|
||||||
|
YearlyUserListForm::class,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
|
{
|
||||||
|
$deleteUser = false;
|
||||||
|
foreach ($this->userNames as $name) {
|
||||||
|
if ($builder->has($name) && $this->userService->countUser(true) < 2) {
|
||||||
|
$deleteUser = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($deleteUser) {
|
||||||
|
foreach ($this->userNames as $name) {
|
||||||
|
if ($builder->has($name)) {
|
||||||
|
$builder->remove($name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$deleteTeams = false;
|
||||||
|
foreach ($this->teamNames as $name) {
|
||||||
|
if ($builder->has($name) && !$this->teamService->hasTeams()) {
|
||||||
|
$deleteTeams = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($deleteTeams) {
|
||||||
|
foreach ($this->teamNames as $name) {
|
||||||
|
if ($builder->has($name)) {
|
||||||
|
$builder->remove($name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,6 +31,7 @@ class ExportToolbarForm extends AbstractToolbarForm
|
|||||||
$this->addTimesheetStateChoice($builder);
|
$this->addTimesheetStateChoice($builder);
|
||||||
if ($options['include_user']) {
|
if ($options['include_user']) {
|
||||||
$this->addUsersChoice($builder);
|
$this->addUsersChoice($builder);
|
||||||
|
$this->addTeamsChoice($builder);
|
||||||
}
|
}
|
||||||
$this->addDateRange($builder, ['timezone' => $options['timezone']]);
|
$this->addDateRange($builder, ['timezone' => $options['timezone']]);
|
||||||
$this->addCustomerMultiChoice($builder, ['start_date_param' => null, 'end_date_param' => null, 'ignore_date' => true], true);
|
$this->addCustomerMultiChoice($builder, ['start_date_param' => null, 'end_date_param' => null, 'ignore_date' => true], true);
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ class InvoiceToolbarForm extends InvoiceToolbarSimpleForm
|
|||||||
$this->addSearchTermInputField($builder);
|
$this->addSearchTermInputField($builder);
|
||||||
if ($options['include_user']) {
|
if ($options['include_user']) {
|
||||||
$this->addUsersChoice($builder);
|
$this->addUsersChoice($builder);
|
||||||
|
$this->addTeamsChoice($builder);
|
||||||
}
|
}
|
||||||
$this->addActivityMultiChoice($builder, $options, true);
|
$this->addActivityMultiChoice($builder, $options, true);
|
||||||
$this->addTagInputField($builder);
|
$this->addTagInputField($builder);
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ class TimesheetToolbarForm extends AbstractToolbarForm
|
|||||||
$this->addTagInputField($builder);
|
$this->addTagInputField($builder);
|
||||||
if ($options['include_user']) {
|
if ($options['include_user']) {
|
||||||
$this->addUsersChoice($builder);
|
$this->addUsersChoice($builder);
|
||||||
|
$this->addTeamsChoice($builder);
|
||||||
}
|
}
|
||||||
$this->addTimesheetStateChoice($builder);
|
$this->addTimesheetStateChoice($builder);
|
||||||
$this->addBillableChoice($builder);
|
$this->addBillableChoice($builder);
|
||||||
|
|||||||
@@ -9,11 +9,17 @@
|
|||||||
|
|
||||||
namespace App\Reporting;
|
namespace App\Reporting;
|
||||||
|
|
||||||
|
use App\Entity\Team;
|
||||||
|
|
||||||
abstract class AbstractUserList
|
abstract class AbstractUserList
|
||||||
{
|
{
|
||||||
private $date;
|
private $date;
|
||||||
private $decimal = false;
|
private $decimal = false;
|
||||||
private $sumType = 'duration';
|
private $sumType = 'duration';
|
||||||
|
/**
|
||||||
|
* @var Team|null
|
||||||
|
*/
|
||||||
|
private $team = null;
|
||||||
|
|
||||||
public function getDate(): ?\DateTime
|
public function getDate(): ?\DateTime
|
||||||
{
|
{
|
||||||
@@ -48,4 +54,14 @@ abstract class AbstractUserList
|
|||||||
|
|
||||||
$this->sumType = $sumType;
|
$this->sumType = $sumType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getTeam(): ?Team
|
||||||
|
{
|
||||||
|
return $this->team;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTeam(?Team $team = null): void
|
||||||
|
{
|
||||||
|
$this->team = $team;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ namespace App\Reporting;
|
|||||||
|
|
||||||
use App\Form\Type\MonthPickerType;
|
use App\Form\Type\MonthPickerType;
|
||||||
use App\Form\Type\ReportSumType;
|
use App\Form\Type\ReportSumType;
|
||||||
|
use App\Form\Type\TeamType;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
@@ -37,6 +38,11 @@ class MonthlyUserListForm extends AbstractType
|
|||||||
'view_timezone' => $options['timezone'],
|
'view_timezone' => $options['timezone'],
|
||||||
'start_date' => $options['start_date'],
|
'start_date' => $options['start_date'],
|
||||||
]);
|
]);
|
||||||
|
$builder->add('team', TeamType::class, [
|
||||||
|
'multiple' => false,
|
||||||
|
'required' => false,
|
||||||
|
'width' => false,
|
||||||
|
]);
|
||||||
$builder->add('sumType', ReportSumType::class);
|
$builder->add('sumType', ReportSumType::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
namespace App\Reporting;
|
namespace App\Reporting;
|
||||||
|
|
||||||
use App\Form\Type\ReportSumType;
|
use App\Form\Type\ReportSumType;
|
||||||
|
use App\Form\Type\TeamType;
|
||||||
use App\Form\Type\WeekPickerType;
|
use App\Form\Type\WeekPickerType;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
@@ -37,6 +38,11 @@ class WeeklyUserListForm extends AbstractType
|
|||||||
'view_timezone' => $options['timezone'],
|
'view_timezone' => $options['timezone'],
|
||||||
'start_date' => $options['start_date'],
|
'start_date' => $options['start_date'],
|
||||||
]);
|
]);
|
||||||
|
$builder->add('team', TeamType::class, [
|
||||||
|
'multiple' => false,
|
||||||
|
'required' => false,
|
||||||
|
'width' => false,
|
||||||
|
]);
|
||||||
$builder->add('sumType', ReportSumType::class);
|
$builder->add('sumType', ReportSumType::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
namespace App\Reporting;
|
namespace App\Reporting;
|
||||||
|
|
||||||
use App\Form\Type\ReportSumType;
|
use App\Form\Type\ReportSumType;
|
||||||
|
use App\Form\Type\TeamType;
|
||||||
use App\Form\Type\YearPickerType;
|
use App\Form\Type\YearPickerType;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
@@ -38,6 +39,11 @@ class YearlyUserListForm extends AbstractType
|
|||||||
'start_date' => $options['start_date'],
|
'start_date' => $options['start_date'],
|
||||||
'show_range' => true,
|
'show_range' => true,
|
||||||
]);
|
]);
|
||||||
|
$builder->add('team', TeamType::class, [
|
||||||
|
'multiple' => false,
|
||||||
|
'required' => false,
|
||||||
|
'width' => false,
|
||||||
|
]);
|
||||||
$builder->add('sumType', ReportSumType::class);
|
$builder->add('sumType', ReportSumType::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
43
src/User/TeamService.php
Normal file
43
src/User/TeamService.php
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Kimai time-tracking app.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\User;
|
||||||
|
|
||||||
|
use App\Repository\TeamRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @final
|
||||||
|
*/
|
||||||
|
class TeamService
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array<string, int>
|
||||||
|
*/
|
||||||
|
private $cache = [];
|
||||||
|
private $repository;
|
||||||
|
|
||||||
|
public function __construct(TeamRepository $repository)
|
||||||
|
{
|
||||||
|
$this->repository = $repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function countTeams(): int
|
||||||
|
{
|
||||||
|
if (!\array_key_exists('count', $this->cache)) {
|
||||||
|
$this->cache['count'] = $this->repository->count([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->cache['count'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasTeams(): bool
|
||||||
|
{
|
||||||
|
return $this->countTeams() > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,6 +29,11 @@ use Symfony\Component\Validator\Validator\ValidatorInterface;
|
|||||||
*/
|
*/
|
||||||
class UserService
|
class UserService
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var array<string, int>
|
||||||
|
*/
|
||||||
|
private $cache = [];
|
||||||
|
|
||||||
private $repository;
|
private $repository;
|
||||||
private $dispatcher;
|
private $dispatcher;
|
||||||
private $validator;
|
private $validator;
|
||||||
@@ -44,6 +49,17 @@ class UserService
|
|||||||
$this->encoderFactory = $encoderFactory;
|
$this->encoderFactory = $encoderFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function countUser(?bool $enabled = null): int
|
||||||
|
{
|
||||||
|
$key = 'count' . ($enabled === null ? '_all' : ($enabled ? '_visible' : '_invisible'));
|
||||||
|
|
||||||
|
if (!\array_key_exists($key, $this->cache)) {
|
||||||
|
$this->cache[$key] = $this->repository->countUser($enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->cache[$key];
|
||||||
|
}
|
||||||
|
|
||||||
public function createNewUser(): User
|
public function createNewUser(): User
|
||||||
{
|
{
|
||||||
$user = new User();
|
$user = new User();
|
||||||
|
|||||||
@@ -47,6 +47,9 @@
|
|||||||
{% if form.users is defined %}
|
{% if form.users is defined %}
|
||||||
{{ form_row(form.users) }}
|
{{ form_row(form.users) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if form.teams is defined %}
|
||||||
|
{{ form_row(form.teams) }}
|
||||||
|
{% endif %}
|
||||||
{{ form_row(form.billable) }}
|
{{ form_row(form.billable) }}
|
||||||
{{ form_row(form.exported) }}
|
{{ form_row(form.exported) }}
|
||||||
{{ form_row(form.state) }}
|
{{ form_row(form.state) }}
|
||||||
|
|||||||
@@ -49,6 +49,9 @@
|
|||||||
{% if form.users is defined %}
|
{% if form.users is defined %}
|
||||||
{{ form_row(form.users, {'row_attr': {'class': 'invoice_search_form_row_users'}}) }}
|
{{ form_row(form.users, {'row_attr': {'class': 'invoice_search_form_row_users'}}) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if form.teams is defined %}
|
||||||
|
{{ form_row(form.teams, {'row_attr': {'class': 'invoice_search_form_row_teams'}}) }}
|
||||||
|
{% endif %}
|
||||||
{% if form.exported is defined %}
|
{% if form.exported is defined %}
|
||||||
{{ form_row(form.exported, {'row_attr': {'class': 'invoice_search_form_row_exported'}}) }}
|
{{ form_row(form.exported, {'row_attr': {'class': 'invoice_search_form_row_exported'}}) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -14,6 +14,9 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block box_title %}
|
{% block box_title %}
|
||||||
{{ form_widget(form.date) }}
|
{{ form_widget(form.date) }}
|
||||||
|
{% if form.team is defined %}
|
||||||
|
{{ form_widget(form.team) }}
|
||||||
|
{% endif %}
|
||||||
{% if form.sumType.vars.choices|length > 1 %}
|
{% if form.sumType.vars.choices|length > 1 %}
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
|
|||||||
@@ -14,6 +14,9 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block box_title %}
|
{% block box_title %}
|
||||||
{{ form_widget(form.date) }}
|
{{ form_widget(form.date) }}
|
||||||
|
{% if form.team is defined %}
|
||||||
|
{{ form_widget(form.team) }}
|
||||||
|
{% endif %}
|
||||||
{% if form.sumType.vars.choices|length > 1 %}
|
{% if form.sumType.vars.choices|length > 1 %}
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
|
|||||||
Reference in New Issue
Block a user