several enhancements (#1808)
* show all fields in timesheet api docs * fix action dropdown links #1806 * apply same validation rules for passwords and for api-tokens #1753 * added "duplicate team" action #1136
This commit is contained in:
@@ -94,7 +94,7 @@ final class ConfigurationController extends BaseApiController
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the instance specific timesheet configuration
|
||||
* Returns the timesheet configuration
|
||||
*
|
||||
* @SWG\Response(
|
||||
* response=200,
|
||||
@@ -116,6 +116,7 @@ final class ConfigurationController extends BaseApiController
|
||||
->setActiveEntriesHardLimit($this->timesheetConfiguration->getActiveEntriesHardLimit())
|
||||
->setActiveEntriesSoftLimit($this->timesheetConfiguration->getActiveEntriesSoftLimit())
|
||||
->setIsAllowFutureTimes($this->timesheetConfiguration->isAllowFutureTimes())
|
||||
->setIsAllowOverlapping($this->timesheetConfiguration->isAllowOverlappingRecords())
|
||||
;
|
||||
|
||||
$view = new View($model, 200);
|
||||
|
||||
@@ -20,21 +20,35 @@ final class TimesheetConfig
|
||||
*/
|
||||
private $trackingMode = 'default';
|
||||
/**
|
||||
* Default begin datetime in PHP format
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $defaultBeginTime = 'now';
|
||||
/**
|
||||
* How many running timesheets a user is allowed to have at the same time
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $activeEntriesHardLimit = 1;
|
||||
/**
|
||||
* How many running timesheets a user is allowed before a warning is shown
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $activeEntriesSoftLimit = 1;
|
||||
/**
|
||||
* Whether entries for future times are allowed
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $isAllowFutureTimes = true;
|
||||
/**
|
||||
* Whether overlapping entries are allowed
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $isAllowOverlapping = true;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
@@ -98,4 +112,16 @@ final class TimesheetConfig
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isAllowOverlapping(): bool
|
||||
{
|
||||
return $this->isAllowOverlapping;
|
||||
}
|
||||
|
||||
public function setIsAllowOverlapping(bool $isAllowOverlapping): TimesheetConfig
|
||||
{
|
||||
$this->isAllowOverlapping = $isAllowOverlapping;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,22 +16,32 @@ use App\Constants;
|
||||
class Version
|
||||
{
|
||||
/**
|
||||
* Kimai Version, eg. "1.9"
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $version = Constants::VERSION;
|
||||
/**
|
||||
* Candidate: either "prod" or "dev"
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $candidate = Constants::STATUS;
|
||||
/**
|
||||
* Full version including status, eg: "1.9-prod"
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $semver = Constants::VERSION . '-' . Constants::STATUS;
|
||||
/**
|
||||
* The version name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = Constants::NAME;
|
||||
/**
|
||||
* A full copyright notice
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $copyright = Constants::SOFTWARE . ' - ' . Constants::VERSION . ' ' . Constants::STATUS . ' (' . Constants::NAME . ') by Kevin Papst and contributors.';
|
||||
|
||||
@@ -93,6 +93,7 @@ final class UserController extends BaseApiController
|
||||
public function cgetAction(ParamFetcherInterface $paramFetcher): Response
|
||||
{
|
||||
$query = new UserQuery();
|
||||
$query->setCurrentUser($this->getUser());
|
||||
|
||||
if (null !== ($visible = $paramFetcher->get('visible'))) {
|
||||
$query->setVisibility($visible);
|
||||
|
||||
@@ -376,7 +376,6 @@ class ProfileController extends AbstractController
|
||||
UserApiTokenType::class,
|
||||
$user,
|
||||
[
|
||||
'validation_groups' => ['apiTokenUpdate'],
|
||||
'action' => $this->generateUrl('user_profile_api_token', ['username' => $user->getUsername()]),
|
||||
'method' => 'POST'
|
||||
]
|
||||
|
||||
@@ -83,6 +83,42 @@ final class TeamController extends AbstractController
|
||||
return $this->renderEditScreen(new Team(), $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(path="/{id}/duplicate", name="team_duplicate", methods={"GET", "POST"})
|
||||
* @Security("is_granted('edit', team) and is_granted('create_team')")
|
||||
*/
|
||||
public function duplicateTeam(Team $team, Request $request)
|
||||
{
|
||||
$newTeam = new Team();
|
||||
$newTeam->setName($team->getName() . ' [COPY]');
|
||||
$newTeam->setTeamLead($team->getTeamLead());
|
||||
|
||||
foreach ($team->getUsers() as $user) {
|
||||
$newTeam->addUser($user);
|
||||
}
|
||||
foreach ($team->getCustomers() as $customer) {
|
||||
$newTeam->addCustomer($customer);
|
||||
}
|
||||
foreach ($team->getProjects() as $project) {
|
||||
$newTeam->addProject($project);
|
||||
}
|
||||
|
||||
try {
|
||||
// make sure that the teamlead is always part of the team, otherwise permission checks
|
||||
// and filtering might not work as expected!
|
||||
$team->addUser($team->getTeamLead());
|
||||
|
||||
$this->repository->saveTeam($newTeam);
|
||||
$this->flashSuccess('action.update.success');
|
||||
|
||||
return $this->redirectToRoute('admin_team_edit', ['id' => $newTeam->getId()]);
|
||||
} catch (\Exception $ex) {
|
||||
$this->flashError('action.update.error', ['%reason%' => $ex->getMessage()]);
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('admin_team');
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(path="/{id}/edit", name="admin_team_edit", methods={"GET", "POST"})
|
||||
* @Security("is_granted('edit', team)")
|
||||
|
||||
@@ -70,6 +70,7 @@ final class UserController extends AbstractController
|
||||
public function indexAction($page, Request $request): Response
|
||||
{
|
||||
$query = new UserQuery();
|
||||
$query->setCurrentUser($this->getUser());
|
||||
$query->setPage($page);
|
||||
|
||||
$form = $this->getToolbarForm($query);
|
||||
|
||||
@@ -50,6 +50,11 @@ class TimesheetApiEditForm extends TimesheetEditForm
|
||||
$resolver->setDefaults([
|
||||
'csrf_protection' => false,
|
||||
'allow_duration' => false,
|
||||
// overwritten and changed to default "true",
|
||||
// because the docs are cached without these fields otherwise
|
||||
'include_user' => true,
|
||||
'include_exported' => true,
|
||||
'include_rate' => true,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ class UserApiTokenType extends AbstractType
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'validation_groups' => ['ApiTokenUpdate'],
|
||||
'data_class' => User::class,
|
||||
'csrf_protection' => true,
|
||||
'csrf_field_name' => '_token',
|
||||
|
||||
@@ -30,4 +30,19 @@ trait VisibilityTrait
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isShowHidden(): bool
|
||||
{
|
||||
return $this->visibility === VisibilityInterface::SHOW_HIDDEN;
|
||||
}
|
||||
|
||||
public function isShowVisible(): bool
|
||||
{
|
||||
return $this->visibility === VisibilityInterface::SHOW_VISIBLE;
|
||||
}
|
||||
|
||||
public function isShowBoth(): bool
|
||||
{
|
||||
return $this->visibility === VisibilityInterface::SHOW_BOTH;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user