Skip to content

Commit

Permalink
paginate audit log
Browse files Browse the repository at this point in the history
  • Loading branch information
l3pp4rd committed Aug 11, 2015
1 parent 2d3cdd3 commit f44bdc3
Show file tree
Hide file tree
Showing 18 changed files with 239 additions and 95 deletions.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
"doctrine/doctrine-fixtures-bundle": "~2.2.0",
"sensio/framework-extra-bundle": "~3.0.9",
"knplabs/knp-menu-bundle": "~2.0",
"data-dog/pager-bundle": "~0.1",
"knplabs/knp-time-bundle": "~1.3",
"data-dog/pager-bundle": "~0.2",

"phpunit/phpunit": "~4.7.0"
},
Expand Down
1 change: 1 addition & 0 deletions example/app/AppKernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public function registerBundles()
new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle(),
new Knp\Bundle\MenuBundle\KnpMenuBundle(),
new Knp\Bundle\TimeBundle\KnpTimeBundle(),
new DataDog\AuditBundle\DataDogAuditBundle(),
new DataDog\PagerBundle\DataDogPagerBundle(),
new AppBundle\AppBundle(),
Expand Down
9 changes: 6 additions & 3 deletions example/app/Resources/views/layout.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Pager demo</title>
<link rel="icon" type="image/x-icon" href="{{ asset('favicon.ico') }}" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">

<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<script src="//oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="//oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
Expand All @@ -26,5 +26,8 @@

{% block body %}{% endblock %}
</div>
<script src="//code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
{% block js %}{% endblock %}
</body>
</html>
7 changes: 7 additions & 0 deletions example/app/config/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,15 @@ imports:
parameters:
locale: en

services:
twig.audit.extension:
class: AppBundle\Twig\AuditExtension
tags:
- { name: twig.extension }

framework:
secret: "%secret%"
translator: { fallback: %locale% }
router:
resource: "%kernel.root_dir%/config/routing.yml"
strict_requirements: true
Expand Down
40 changes: 37 additions & 3 deletions example/src/AppBundle/Controller/AuditController.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Doctrine\ORM\QueryBuilder;
Expand All @@ -23,11 +24,14 @@ public function filters(QueryBuilder $qb, $key, $val)
} else {
// this allows us to safely ignore empty values
// otherwise if $qb is not changed, it would add where the string is empty statement.
$qb->andWhere($qb->expr()->eq('b.pk', $val));
$qb->andWhere($qb->expr()->eq('b.fk', ':blame'));
$qb->setParameter('blame', $val);
}
break;
case 's.class':
return; // we allow this filter
case 'class':
$qb->orWhere($qb->expr()->eq('s.class', ':class'), $qb->expr()->eq('t.class', ':class'));
$qb->setParameter('class', $val);
break;
default:
// if user attemps to filter by other fields, we restrict it
throw new \Exception("filter not allowed");
Expand All @@ -41,6 +45,9 @@ public function filters(QueryBuilder $qb, $key, $val)
*/
public function indexAction(Request $request)
{
$this->someActions();
Pagination::$defaults = array_merge(Pagination::$defaults, ['limit' => 10]);

$qb = $this->repo("DataDogAuditBundle:AuditLog")
->createQueryBuilder('a')
->addSelect('s', 't', 'b')
Expand Down Expand Up @@ -72,4 +79,31 @@ public function indexAction(Request $request)
$logs = new Pagination($qb, $request, $options);
return compact('logs', 'sourceClasses', 'users');
}

private function someActions()
{
$tag = $this->repo('AppBundle:Tag')->findOneByName('New');
if (!$tag) {
return; // already performed
}

$user = $this->repo('AppBundle:User')->findOneByUsername('luke');
$old = $this->get('security.token_storage')->getToken();
$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$this->get('security.token_storage')->setToken($token);


$pager = $this->repo('AppBundle:Project')->findOneByCode('pg');
$pager->removeTag($tag);
$this->persist($pager);
$this->remove($tag);

$godog = $this->repo('AppBundle:Project')->findOneByCode('godog');
$godog->setName('Godog BDD framework');
$godog->setHoursSpent(85);
$this->persist($godog);

$this->flush();
$this->get('security.token_storage')->setToken($old);
}
}
7 changes: 7 additions & 0 deletions example/src/AppBundle/Controller/DoctrineControllerTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ private function flush($class = null)
$this->getDoctrine()->getManager()->flush($class);
}

private function remove(...$entities)
{
foreach ($entities as $entity) {
$this->getDoctrine()->getManager()->remove($entity);
}
}

private function repo($class)
{
return $this->getDoctrine()->getManager()->getRepository($class);
Expand Down
10 changes: 4 additions & 6 deletions example/src/AppBundle/Controller/ProjectController.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,6 @@ public function projectFilters(QueryBuilder $qb, $key, $val)
case 'p.name':
if ($val) {
$qb->andWhere($qb->expr()->like('p.name', "'%{$val}%'"));
} else {
// this allows us to safely ignore empty values
// otherwise if $qb is not changed, it would add where the string is empty statement.
$qb->andWhere('1 = 1');
}
break;
case 'p.hoursSpent':
Expand All @@ -44,7 +40,9 @@ public function projectFilters(QueryBuilder $qb, $key, $val)
}
break;
case 'l.code':
return; // we allow this filter
$qb->andWhere($qb->expr()->eq('l.code', ':code'));
$qb->setParameter('code', $val);
break;
default:
// if user attemps to filter by other fields, we restrict it
throw new \Exception("filter not allowed");
Expand Down Expand Up @@ -100,6 +98,6 @@ public function toggleAction(Project $project, Request $request)
$this->flush();

// redirect to the list with the same filters applied as before
return $this->redirect($this->generateUrl('homepage', $request->query->all()));
return $this->redirect($this->generateUrl('projects', $request->query->all()));
}
}
4 changes: 2 additions & 2 deletions example/src/AppBundle/Entity/Project.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class Project
private $language;

/**
* @ORM\ManyToMany(targetEntity="Tag")
* @ORM\ManyToMany(targetEntity="Tag", cascade={"persist", "remove"})
* @ORM\JoinTable(name="projects_tags")
*/
private $tags;
Expand Down Expand Up @@ -147,7 +147,7 @@ public function addTag(Tag $tag)
public function removeTag(Tag $tag)
{
if ($this->tags->contains($tag)) {
$this->tags->remove($tag);
$this->tags->removeElement($tag);
}
return $this;
}
Expand Down
11 changes: 11 additions & 0 deletions example/src/AppBundle/Resources/views/Audit/associate.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<div class="alert alert-success" role="alert">
<strong>{{ log.source.typLabel }}</strong>
<a class="alert-link" href="#">{{ log.source.label }}</a> was associated with

<strong>{{ log.target.typLabel }}</strong>
<a class="alert-link" href="#">{{ log.target.label }}</a>
{% if log.blame %}
by <a class="alert-link" href="#">{{ log.blame.label }}</a>
{% endif %}
<span class="badge">{{ log.loggedAt|ago }}</span>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<a href="#">{{ val.label }}</a>
11 changes: 11 additions & 0 deletions example/src/AppBundle/Resources/views/Audit/dissociate.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<div class="alert alert-danger" role="alert">
<strong>{{ log.source.typLabel }}</strong>
<a class="alert-link" href="#">{{ log.source.label }}</a> was dissociated from

<strong>{{ log.target.typLabel }}</strong>
<a class="alert-link" href="#">{{ log.target.label }}</a>
{% if log.blame %}
by <a class="alert-link" href="#">{{ log.blame.label }}</a>
{% endif %}
<span class="badge">{{ log.loggedAt|ago }}</span>
</div>
62 changes: 11 additions & 51 deletions example/src/AppBundle/Resources/views/Audit/index.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -5,57 +5,17 @@
<h1>Paginated audit log</h1>
</div>

<table class="table table-hover">
<thead>
<tr>
<th>#</th>
<th>{{ sorter_link(logs, "a.action", "Action") }}</th>
<th>{{ sorter_link(logs, "b.label", "User responsible") }}</th>
<th>{{ sorter_link(logs, "p.hoursSpent", "Hours Spent") }}</th>
<th>{{ sorter_link(logs, "l.code", "Language") }}</th>
<th></th>
</tr>

<tr role="row" class="filter">
<td></td>
<td></td>
<td>{{ filter_select(logs, "blamed", users) }}</td>
<td>{{ filter_select(logs, "s.class", sourceClasses) }}</td>
<td></td>
<td></td>
</tr>
</thead>
<tbody>
{% for log in logs %}
{% set act = 'success' %}
{% if log.action == 'dissociate' or log.action == 'remove' %}
{% set act = 'danger' %}
{% elseif log.action == 'update' %}
{% set act = 'info' %}
{% endif %}
<tr>
<td>
<i class="fa {% if act != 'danger' %}fa-check{% else %}fa-times{% endif %} text-{{ act }}"></i>
{{ log.id }}
</td>
<td>{{ log.action }}</td>
<td>{% if log.blame %}{{ log.blame.label }}{% else %}System{% endif %}</td>
<td>{{ log.source.typ }}</td>
{% if log.isOverDeadline %}
<td class="text-danger">{{ log.hoursSpent }}</td>
{% else %}
<td class="text-success">{{ log.hoursSpent }}</td>
{% endif %}
<td>{{ log.language.code }}</td>
<td>
<a href="{{ path("project_toggle", logs.query|merge({id: log.id})) }}" class="btn btn-xs btn-default">
{% if log.enabled %}Disable{% else %}Enable{% endif %}
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="row">
<div class="col-md-6">
{{ filter_select(logs, "blamed", users) }}
</div>
<div class="col-md-6">
{{ filter_select(logs, "class", sourceClasses) }}
</div>
</div>
{% for log in logs %}
{{ audit(log) }}
{% endfor %}

<div class="panel-footer">
{{ pagination(logs) }}
Expand Down
33 changes: 33 additions & 0 deletions example/src/AppBundle/Resources/views/Audit/insert.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<div class="alert alert-success" role="alert">
New <strong>{{ log.source.typLabel }}</strong>
<a class="alert-link" href="#">{{ log.source.label }}</a> was inserted,
{% if log.blame %}
by <a class="alert-link" href="#">{{ log.blame.label }}</a>,
{% endif %}
show <a class="alert-link" data-toggle="modal" data-target="#audit-{{ log.id }}" href="#">details</a>
<span class="badge">{{ log.loggedAt|ago }}</span>
</div>

<div class="modal fade" id="audit-{{ log.id }}" tabindex="-1" role="dialog" aria-labelledby="audit-label-{{ log.id }}">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="audit-label-{{ log.id }}">{{ log.source.label }}</h4>
</div>
<div class="modal-body">
<ul class="list-group">
{% for field, change in log.diff %}
<li class="list-group-item">
<span class="badge">{{ audit_value(change.new) }}</span>
{{ field }}
</li>
{% endfor %}
</ul>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
8 changes: 8 additions & 0 deletions example/src/AppBundle/Resources/views/Audit/remove.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<div class="alert alert-danger" role="alert">
<strong>{{ log.source.typLabel }}</strong> #{{ log.source.fk }}
labeled as <strong>{{ log.source.label }}</strong> was removed
{% if log.blame %}
by <a class="alert-link" href="#">{{ log.blame.label }}</a>
{% endif %}
<span class="badge">{{ log.loggedAt|ago }}</span>
</div>
39 changes: 39 additions & 0 deletions example/src/AppBundle/Resources/views/Audit/update.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<div class="alert alert-info" role="alert">
<strong>{{ log.source.typLabel }}</strong>
<a class="alert-link" href="#">{{ log.source.label }}</a> was updated,
{% if log.blame %}
by <a class="alert-link" href="#">{{ log.blame.label }}</a>,
{% endif %}
show <a class="alert-link" data-toggle="modal" data-target="#audit-{{ log.id }}" href="#">details</a>
<span class="badge">{{ log.loggedAt|ago }}</span>
</div>

<div class="modal fade" id="audit-{{ log.id }}" tabindex="-1" role="dialog" aria-labelledby="audit-label-{{ log.id }}">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="audit-label-{{ log.id }}">{{ log.source.label }}</h4>
</div>
<div class="modal-body">
<table class="table table-striped">
<tr>
<th>Field</th>
<th>Old</th>
<th>New</th>
</tr>
{% for field, change in log.diff %}
<tr>
<td>{{ field }}</td>
<td>{{ audit_value(change.old) }}</td>
<td>{{ audit_value(change.new) }}</td>
</tr>
{% endfor %}
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
Loading

0 comments on commit f44bdc3

Please sign in to comment.