Skip to content

Commit

Permalink
support custom fields for timesheets, customers, projects and activit…
Browse files Browse the repository at this point in the history
…ies (kimai#871)
  • Loading branch information
kevinpapst authored Jun 25, 2019
1 parent 994c671 commit d8621f0
Show file tree
Hide file tree
Showing 103 changed files with 2,567 additions and 238 deletions.
13 changes: 7 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
# Contributing

Kimai is an open source project, contributions made by the community are welcome.
Send us your ideas, code reviews, pull requests and feature requests to help us improve this project.
Send your ideas, code reviews, pull requests and feature requests to help improving this project.

## Pull request rules

- We use PSR-2 with some additional code-style checks (see our [php-cs-fixer config](.php_cs.dist)). You can run `bin/console kimai:codestyle` to check and `bin/console kimai:codestyle --fix` to fix violations.
- Add PHPUnit tests for your changes, verify everything still works and execute our test-suites `bin/console kimai:test-unit` and `bin/console kimai:test-integration`.
- If you contribute new files, please add them with the file-header template from below (our chode-style fixer can do that for you).
- With sending in a PR, you accept that your contributions/code will be published under MIT license (see the [LICENSE](LICENSE) file as well).
- If one of the PR checks fails, please fix them before asking us for a review.
- Use the [pre-configured codesniffer](.php_cs.dist)) to check for `composer kimai:codestyle` and fix `composer kimai:codestyle-fix` violations
- Add PHPUnit tests for your changes!
- Verify everything still works with `composer kimai:tests-unit` and `composer kimai:tests-integration`
- If you contribute new files, add them with the file-header template from below (the code-style fixer can do that for you)
- When sending in a PR, you must accept that your contributions/code will be published under MIT license (see the [LICENSE](LICENSE) file as well), otherwise your PR will be closed
- If one of the PR checks/builds fails, fix it before asking for a review

Further documentation can be found in the [developer documentation](https://www.kimai.org/documentation/developers.html).

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
[![Gitter](https://badges.gitter.im/kimai2/support.svg)](https://gitter.im/kimai2/support)

Kimai is a free, open source and online time-tracking software designed for small businesses and freelancers.
It is built with modern technologies such as Symfony, Bootstrap, RESTful API, responsive and mobile-ready etc.
It is built with modern technologies such as Symfony, Bootstrap, RESTful API, Doctrine, AdminLTE, Webpack, ES6 etc.

## Introduction

Expand Down
2 changes: 1 addition & 1 deletion config/packages/kimai.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ kimai:

# The time-tracking mode that should be used.
# See https://www.kimai.org/documentation/timesheet.html#tracking-modes
mode: default
# mode: default

# The default time to pre-fill the "create timesheet" form (in some cases).
# This setting is only respected by some timetracking modes and not in all situations.
Expand Down
12 changes: 8 additions & 4 deletions config/packages/nelmio_api_doc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,21 @@ nelmio_api_doc:
models:
use_jms: true
names:
- { alias: CustomerEditForm, type: App\Form\CustomerEditForm, groups: [Default, Entity, Customer] }
- { alias: CustomerEditForm, type: App\Form\API\CustomerApiEditForm, groups: [Default, Entity, Customer] }
- { alias: CustomerEntity, type: App\Entity\Customer, groups: [Default, Entity, Customer] }
- { alias: CustomerMetaField, type: App\Entity\CustomerMeta, groups: [Default, Customer] }
- { alias: CustomerCollection, type: App\Entity\Customer, groups: [Default, Collection, Customer] }
- { alias: ProjectEditForm, type: App\Form\ProjectEditForm, groups: [Default, Entity, Project] }
- { alias: ProjectEditForm, type: App\Form\API\ProjectApiEditForm, groups: [Default, Entity, Project] }
- { alias: ProjectEntity, type: App\Entity\Project, groups: [Default, Entity, Project] }
- { alias: ProjectMetaField, type: App\Entity\ProjectMeta, groups: [Default, Project] }
- { alias: ProjectCollection, type: App\Entity\Project, groups: [Default, Collection, Project] }
- { alias: ActivityEditForm, type: App\Form\ActivityEditForm, groups: [Default, Entity, Activity] }
- { alias: ActivityEditForm, type: App\Form\API\ActivityApiEditForm, groups: [Default, Entity, Activity] }
- { alias: ActivityEntity, type: App\Entity\Activity, groups: [Default, Entity, Activity] }
- { alias: ActivityMetaField, type: App\Entity\ActivityMeta, groups: [Default, Activity] }
- { alias: ActivityCollection, type: App\Entity\Activity, groups: [Default, Collection, Activity] }
- { alias: TimesheetEditForm, type: App\Form\TimesheetEditForm, groups: [Default, Entity, Timesheet] }
- { alias: TimesheetEditForm, type: App\Form\API\TimesheetApiEditForm, groups: [Default, Entity, Timesheet] }
- { alias: TimesheetEntity, type: App\Entity\Timesheet, groups: [Default, Entity, Timesheet] }
- { alias: TimesheetMeta, type: App\Entity\TimesheetMeta, groups: [Default, Timesheet] }
- { alias: TimesheetCollection, type: App\Entity\Timesheet, groups: [Default, Collection, Timesheet] }
- { alias: TimesheetSubCollection, type: App\Entity\Timesheet, groups: [Default, Subresource, Timesheet] }
- { alias: UserEntity, type: App\Entity\User, groups: [Default, Entity, User] }
Expand Down
9 changes: 8 additions & 1 deletion config/serializer/App/Entity.Activity.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
App\Entity\Activity:
exclusion_policy: All
custom_accessor_order: [id, name, comment, visible, project, fixedRate, hourlyRate, color, budget, timeBudget]
custom_accessor_order: [id, name, comment, visible, project, fixedRate, hourlyRate, color, budget, timeBudget, metaFields]
properties:
id:
include: true
Expand Down Expand Up @@ -32,9 +32,16 @@ App\Entity\Activity:
groups: [Default]
color:
include: true
metaFields:
exclude: true
virtual_properties:
getProject:
serialized_name: project
exp: "object.getProject() === null ? null : object.getProject().getId()"
type: integer
groups: [Default]
getMetaFields:
serialized_name: metaFields
exp: "object.getVisibleMetaFields()"
type: array<App\Entity\ActivityMeta>
groups: [Default]
15 changes: 15 additions & 0 deletions config/serializer/App/Entity.ActivityMeta.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
App\Entity\ActivityMeta:
exclusion_policy: All
custom_accessor_order: [name, value]
properties:
name:
include: false
value:
include: false
virtual_properties:
getName:
serialized_name: name
exp: "object.isVisible() ? object.getName() : null"
getValue:
serialized_name: value
exp: "object.isVisible() ? object.getValue() : null"
10 changes: 9 additions & 1 deletion config/serializer/App/Entity.Customer.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
App\Entity\Customer:
exclusion_policy: All
custom_accessor_order: [id, name, number, comment, visible, company, contact, address, country, currency, phone, fax, mobile, email, homepage, timezone, fixedRate, hourlyRate, color, budget, timeBudget]
custom_accessor_order: [id, name, number, comment, visible, company, contact, address, country, currency, phone, fax, mobile, email, homepage, timezone, fixedRate, hourlyRate, color, budget, timeBudget, metaFields]
properties:
id:
include: true
Expand Down Expand Up @@ -61,3 +61,11 @@ App\Entity\Customer:
groups: [Customer]
color:
include: true
metaFields:
exclude: true
virtual_properties:
getMetaFields:
serialized_name: metaFields
exp: "object.getVisibleMetaFields()"
type: array<App\Entity\CustomerMeta>
groups: [Default]
15 changes: 15 additions & 0 deletions config/serializer/App/Entity.CustomerMeta.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
App\Entity\CustomerMeta:
exclusion_policy: All
custom_accessor_order: [name, value]
properties:
name:
include: false
value:
include: false
virtual_properties:
getName:
serialized_name: name
exp: "object.isVisible() ? object.getName() : null"
getValue:
serialized_name: value
exp: "object.isVisible() ? object.getValue() : null"
9 changes: 8 additions & 1 deletion config/serializer/App/Entity.Project.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
App\Entity\Project:
exclusion_policy: All
custom_accessor_order: [id, name, comment, visible, orderNumber, customer, fixedRate, hourlyRate, color, budget, timeBudget]
custom_accessor_order: [id, name, comment, visible, orderNumber, customer, fixedRate, hourlyRate, color, budget, timeBudget, metaFields]
properties:
id:
include: true
Expand Down Expand Up @@ -30,9 +30,16 @@ App\Entity\Project:
groups: [Subresource]
color:
include: true
metaFields:
exclude: true
virtual_properties:
getCustomer:
serialized_name: customer
exp: "object.getCustomer() === null ? null : object.getCustomer().getId()"
type: integer
groups: [Entity, Collection]
getMetaFields:
serialized_name: metaFields
exp: "object.getVisibleMetaFields()"
type: array<App\Entity\ProjectMeta>
groups: [Default]
15 changes: 15 additions & 0 deletions config/serializer/App/Entity.ProjectMeta.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
App\Entity\ProjectMeta:
exclusion_policy: All
custom_accessor_order: [name, value]
properties:
name:
include: false
value:
include: false
virtual_properties:
getName:
serialized_name: name
exp: "object.isVisible() ? object.getName() : null"
getValue:
serialized_name: value
exp: "object.isVisible() ? object.getValue() : null"
15 changes: 11 additions & 4 deletions config/serializer/App/Entity.Timesheet.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
App\Entity\Timesheet:
exclusion_policy: All
custom_accessor_order: [id, begin, end, duration, rate, activity, project, user, description, fixedRate, hourlyRate, tags, exported]
custom_accessor_order: [id, begin, end, duration, rate, activity, project, user, description, fixedRate, hourlyRate, tags, exported, metaFields]
properties:
id:
include: true
Expand Down Expand Up @@ -29,6 +29,8 @@ App\Entity\Timesheet:
groups: [Subresource]
user:
exclude: true
metaFields:
exclude: true
virtual_properties:
getBegin:
serialized_name: begin
Expand All @@ -53,6 +55,11 @@ App\Entity\Timesheet:
exp: "object.getUser().getId()"
type: integer
getTags:
serialized_name: tags
exp: "object.getTagsAsArray()"
type: array<string>
serialized_name: tags
exp: "object.getTagsAsArray()"
type: array<string>
getMetaFields:
serialized_name: metaFields
exp: "object.getVisibleMetaFields()"
type: array<App\Entity\TimesheetMeta>
groups: [Default]
15 changes: 15 additions & 0 deletions config/serializer/App/Entity.TimesheetMeta.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
App\Entity\TimesheetMeta:
exclusion_policy: All
custom_accessor_order: [name, value]
properties:
name:
include: false
value:
include: false
virtual_properties:
getName:
serialized_name: name
exp: "object.isVisible() ? object.getName() : null"
getValue:
serialized_name: value
exp: "object.isVisible() ? object.getValue() : null"
28 changes: 16 additions & 12 deletions src/API/ActivityController.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
namespace App\API;

use App\Entity\Activity;
use App\Form\ActivityEditForm;
use App\Event\ActivityMetaDefinitionEvent;
use App\Form\API\ActivityApiEditForm;
use App\Repository\ActivityRepository;
use App\Repository\Query\ActivityQuery;
use FOS\RestBundle\Controller\Annotations as Rest;
Expand All @@ -22,6 +23,7 @@
use FOS\RestBundle\View\ViewHandlerInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Swagger\Annotations as SWG;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
Expand All @@ -37,20 +39,20 @@ class ActivityController extends BaseApiController
* @var ActivityRepository
*/
protected $repository;

/**
* @var ViewHandlerInterface
*/
protected $viewHandler;

/**
* @param ViewHandlerInterface $viewHandler
* @param ActivityRepository $repository
* @var EventDispatcherInterface
*/
public function __construct(ViewHandlerInterface $viewHandler, ActivityRepository $repository)
protected $dispatcher;

public function __construct(ViewHandlerInterface $viewHandler, ActivityRepository $repository, EventDispatcherInterface $dispatcher)
{
$this->viewHandler = $viewHandler;
$this->repository = $repository;
$this->dispatcher = $dispatcher;
}

/**
Expand Down Expand Up @@ -133,12 +135,18 @@ public function cgetAction(ParamFetcherInterface $paramFetcher)
*/
public function getAction($id)
{
/** @var Activity $data */
$data = $this->repository->find($id);

if (null === $data) {
throw new NotFoundException();
}

// make sure the fields are properly setup and we know, which meta fields
// should be exposed and which not
$event = new ActivityMetaDefinitionEvent($data);
$this->dispatcher->dispatch(ActivityMetaDefinitionEvent::class, $event);

$view = new View($data, 200);
$view->getContext()->setGroups(['Default', 'Entity', 'Activity']);

Expand Down Expand Up @@ -177,9 +185,7 @@ public function postAction(Request $request)

$activity = new Activity();

$form = $this->createForm(ActivityEditForm::class, $activity, [
'csrf_protection' => false,
]);
$form = $this->createForm(ActivityApiEditForm::class, $activity);

$form->submit($request->request->all());

Expand Down Expand Up @@ -241,9 +247,7 @@ public function patchAction(Request $request, string $id)
throw new AccessDeniedHttpException('User cannot update activity');
}

$form = $this->createForm(ActivityEditForm::class, $activity, [
'csrf_protection' => false,
]);
$form = $this->createForm(ActivityApiEditForm::class, $activity);

$form->setData($activity);
$form->submit($request->request->all(), false);
Expand Down
Loading

0 comments on commit d8621f0

Please sign in to comment.