prepare for merging doc into mono-repository
BIN
docs/source/_static/access_control_model.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
docs/source/_static/bundles/docStore/doc_store_classes.png
Normal file
After Width: | Height: | Size: 30 KiB |
46
docs/source/_static/bundles/docStore/doc_store_classes.puml
Normal file
@@ -0,0 +1,46 @@
|
||||
# diagramme de classe du module "docStore"
|
||||
|
||||
@startuml
|
||||
|
||||
title Diagramme de classe du module "docStore"
|
||||
|
||||
|
||||
package "PersonBundle" {
|
||||
class Person
|
||||
class Center
|
||||
class PersonDocument
|
||||
}
|
||||
|
||||
package "DocStoreBundle" {
|
||||
Document "many" --- "1" DocumentCategory
|
||||
}
|
||||
|
||||
Document <|-- "herite" PersonDocument
|
||||
|
||||
class "Document" {
|
||||
- int id
|
||||
- varchar_150 title
|
||||
- text description
|
||||
- ArrayCollection_DocumentCategory categories
|
||||
- varchar_150 content #link to openstack
|
||||
- Center center
|
||||
- Cercle cercle
|
||||
- User user
|
||||
- DateTime date # Creation date
|
||||
}
|
||||
|
||||
|
||||
class "DocumentCategory" {
|
||||
.. Primary Key ..
|
||||
- varchar_150 bundle_id
|
||||
- int internal_bundle_id
|
||||
==
|
||||
- json_array name
|
||||
- ArrayCollection_Document documents
|
||||
}
|
||||
|
||||
class "PersonDocument" {
|
||||
- Person person
|
||||
}
|
||||
|
||||
@enduml
|
BIN
docs/source/_static/bundles/group/group_classes_uml.png
Normal file
After Width: | Height: | Size: 50 KiB |
49
docs/source/_static/bundles/group/group_classes_uml.pu
Normal file
@@ -0,0 +1,49 @@
|
||||
# diagramme de classe du module "groupe"
|
||||
|
||||
@startuml
|
||||
|
||||
title Diagramme de classe du module "groupe"
|
||||
|
||||
package "PersonBundle" {
|
||||
class Person
|
||||
class Center
|
||||
}
|
||||
|
||||
package "GroupBundle" {
|
||||
Person "1" <-- Membership
|
||||
Membership "0..*" <--> "Group"
|
||||
Type "1" --> "1..*" Role
|
||||
Membership -right-> "1" Role
|
||||
Group -right-> "1" Type
|
||||
Group -left-> "1" Center
|
||||
}
|
||||
|
||||
class Membership {
|
||||
- role
|
||||
- person
|
||||
- group
|
||||
}
|
||||
note left: <b>Membership</b> relie les groupes aux\npersonnes. Chaque membership a un\n <b>role</b>, le rôle est à choisir\nparmi ceux possibles pour le <b>type</b> de <b>groupe</b>
|
||||
|
||||
class Group {
|
||||
- type
|
||||
- memberships
|
||||
- name
|
||||
- center
|
||||
}
|
||||
note left: Un groupe a un type qui est défini à sa création.
|
||||
|
||||
class Type {
|
||||
- roles
|
||||
}
|
||||
note right: Les types de groupe qu'il est possible de créer \nsont définis dans l'interface d'administration.\nExemple de type: "famille", "groupe de parole", "stage", ...
|
||||
|
||||
class Role {
|
||||
- name
|
||||
}
|
||||
note right: Pour chaque <b>Type</b> on définit des rôles\npossibles pour ce type de groupe. Par exemple, pour le\ntype "famille" on peut avoir les rôles <i>parents</i> et <i>enfants</i>.
|
||||
|
||||
|
||||
|
||||
|
||||
@enduml
|
123
docs/source/_static/code/exports/BirthdateFilter.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\PersonBundle\Export\Filter;
|
||||
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Component\Validator\Constraints;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Doctrine\ORM\Query\Expr;
|
||||
use Chill\MainBundle\Form\Type\Export\FilterType;
|
||||
use Symfony\Component\Form\FormError;
|
||||
use Chill\MainBundle\Export\ExportElementValidatedInterface;
|
||||
|
||||
class BirthdateFilter implements FilterInterface, ExportElementValidatedInterface
|
||||
{
|
||||
// add specific role for this filter
|
||||
public function addRole()
|
||||
{
|
||||
// we do not need any new role for this filter, so we return null
|
||||
return null;
|
||||
}
|
||||
|
||||
// we give information on which type of export this filter applies
|
||||
public function applyOn()
|
||||
{
|
||||
return 'person';
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return 'Filter by person\'s birthdate';
|
||||
}
|
||||
|
||||
// we build a form to collect some parameters from the users
|
||||
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('date_from', DateType::class, array(
|
||||
'label' => "Born after this date",
|
||||
'data' => new \DateTime(),
|
||||
'attr' => array('class' => 'datepicker'),
|
||||
'widget'=> 'single_text',
|
||||
'format' => 'dd-MM-yyyy',
|
||||
));
|
||||
|
||||
$builder->add('date_to', DateType::class, array(
|
||||
'label' => "Born before this date",
|
||||
'data' => new \DateTime(),
|
||||
'attr' => array('class' => 'datepicker'),
|
||||
'widget'=> 'single_text',
|
||||
'format' => 'dd-MM-yyyy',
|
||||
));
|
||||
|
||||
}
|
||||
|
||||
// the form created above must be validated. The process of validation
|
||||
// is executed here. This function is added by the interface
|
||||
// `ExportElementValidatedInterface`, and can be ignore if there is
|
||||
// no need for a validation
|
||||
public function validateForm($data, ExecutionContextInterface $context)
|
||||
{
|
||||
$date_from = $data['date_from'];
|
||||
$date_to = $data['date_to'];
|
||||
|
||||
if ($date_from === null) {
|
||||
$context->buildViolation('The "date from" should not be empty')
|
||||
//->atPath('date_from')
|
||||
->addViolation();
|
||||
}
|
||||
|
||||
if ($date_to === null) {
|
||||
$context->buildViolation('The "date to" should not be empty')
|
||||
//->atPath('date_to')
|
||||
->addViolation();
|
||||
}
|
||||
|
||||
if (
|
||||
($date_from !== null && $date_to !== null)
|
||||
&&
|
||||
$date_from >= $date_to
|
||||
) {
|
||||
$context->buildViolation('The date "date to" should be after the '
|
||||
. 'date given in "date from" field')
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// here, we alter the query created by Export
|
||||
public function alterQuery(\Doctrine\ORM\QueryBuilder $qb, $data)
|
||||
{
|
||||
$where = $qb->getDQLPart('where');
|
||||
// we create the clause here
|
||||
$clause = $qb->expr()->between('person.birthdate', ':date_from',
|
||||
':date_to');
|
||||
|
||||
// we have to take care **not to** remove previous clauses...
|
||||
if ($where instanceof Expr\Andx) {
|
||||
$where->add($clause);
|
||||
} else {
|
||||
$where = $qb->expr()->andX($clause);
|
||||
}
|
||||
|
||||
$qb->add('where', $where);
|
||||
// we add parameters from $data. $data contains the parameters from the form
|
||||
$qb->setParameter('date_from', $data['date_from']);
|
||||
$qb->setParameter('date_to', $data['date_to']);
|
||||
}
|
||||
|
||||
// here, we create a simple string which will describe the action of
|
||||
// the filter in the Response
|
||||
public function describeAction($data, $format = 'string')
|
||||
{
|
||||
return array('Filtered by person\'s birtdate: '
|
||||
. 'between %date_from% and %date_to%', array(
|
||||
'%date_from%' => $data['date_from']->format('d-m-Y'),
|
||||
'%date_to%' => $data['date_to']->format('d-m-Y')
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
118
docs/source/_static/code/exports/CountPerson.php
Normal file
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Chill\PersonBundle\Export\Export;
|
||||
|
||||
use Chill\MainBundle\Export\ExportInterface;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Doctrine\ORM\Query;
|
||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||
use Symfony\Component\Security\Core\Role\Role;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Export\FormatterInterface;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*/
|
||||
class CountPerson implements ExportInterface
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @var EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
public function __construct(
|
||||
EntityManagerInterface $em
|
||||
)
|
||||
{
|
||||
$this->entityManager = $em;
|
||||
}
|
||||
|
||||
public function getType()
|
||||
{
|
||||
return Declarations::PERSON_TYPE;
|
||||
}
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
return "Count peoples by various parameters.";
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return "Count peoples";
|
||||
}
|
||||
|
||||
public function requiredRole()
|
||||
{
|
||||
return new Role(PersonVoter::STATS);
|
||||
}
|
||||
|
||||
public function initiateQuery(array $requiredModifiers, array $acl, array $data = array())
|
||||
{
|
||||
// we gather all center the user choose.
|
||||
$centers = array_map(function($el) { return $el['center']; }, $acl);
|
||||
|
||||
$qb = $this->entityManager->createQueryBuilder();
|
||||
|
||||
$qb->select('COUNT(person.id) AS export_result')
|
||||
->from('ChillPersonBundle:Person', 'person')
|
||||
->join('person.center', 'center')
|
||||
->andWhere('center IN (:authorized_centers)')
|
||||
->setParameter('authorized_centers', $centers);
|
||||
;
|
||||
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
public function getResult($qb, $data)
|
||||
{
|
||||
return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
|
||||
}
|
||||
|
||||
public function getQueryKeys($data)
|
||||
{
|
||||
// this array match the result keys in the query. We have only
|
||||
// one column.
|
||||
return array('export_result');
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, $data)
|
||||
{
|
||||
|
||||
// the Closure which will be executed by the formatter.
|
||||
return function($value) {
|
||||
switch($value) {
|
||||
case '_header':
|
||||
// we have to process specifically the '_header' string,
|
||||
// which will be used by the formatter to show a column title
|
||||
return $this->getTitle();
|
||||
default:
|
||||
// for all value, we do not process them and return them
|
||||
// immediatly
|
||||
return $value;
|
||||
};
|
||||
}
|
||||
|
||||
public function getAllowedFormattersTypes()
|
||||
{
|
||||
return array(FormatterInterface::TYPE_TABULAR);
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder) {
|
||||
// this export does not add any form
|
||||
}
|
||||
|
||||
public function supportsModifiers()
|
||||
{
|
||||
// explain the export manager which formatters and filters are allowed
|
||||
return array(Declarations::PERSON_TYPE, Declarations::PERSON_IMPLIED_IN);
|
||||
}
|
||||
|
||||
}
|
35
docs/source/_static/code/installation/docker-compose.yml
Normal file
@@ -0,0 +1,35 @@
|
||||
version: '2'
|
||||
services:
|
||||
fpm:
|
||||
image: dev-activity-person-report
|
||||
links:
|
||||
- db
|
||||
- redis
|
||||
- logstash:gelf
|
||||
environment:
|
||||
- ADMIN_PASSWORD=admin
|
||||
# add a link to custom.yml if needed
|
||||
# volumes:
|
||||
# - /tmp/custom.yml:/var/www/chill/app/config/custom.yml
|
||||
redis:
|
||||
image: redis
|
||||
db:
|
||||
image: chill/database
|
||||
logstash:
|
||||
image: logstash
|
||||
command: logstash -e 'input { gelf { } } output { stdout{ } }'
|
||||
expose:
|
||||
- "12201/udp"
|
||||
volumes:
|
||||
- /var/log/logstash
|
||||
nginx:
|
||||
image: nginx
|
||||
links:
|
||||
- fpm
|
||||
volumes:
|
||||
- ./nginx.conf:/etc/nginx/nginx.conf
|
||||
volumes_from:
|
||||
- fpm:ro
|
||||
ports:
|
||||
- 8080:80
|
||||
|
71
docs/source/_static/code/installation/nginx.conf
Normal file
@@ -0,0 +1,71 @@
|
||||
user nginx;
|
||||
worker_processes 1;
|
||||
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
error_log /var/log/nginx/error.log;
|
||||
|
||||
sendfile on;
|
||||
#tcp_nopush on;
|
||||
|
||||
keepalive_timeout 65;
|
||||
|
||||
gzip off;
|
||||
|
||||
#include /etc/nginx/conf.d/*.conf;
|
||||
|
||||
upstream phpfcgi {
|
||||
server fpm:9000;
|
||||
# server unix:/var/run/php5-fpm.sock; #for PHP-FPM running on UNIX socket
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
|
||||
#server_name symfony2;
|
||||
root /var/www/chill/web;
|
||||
|
||||
error_log /var/log/nginx/symfony2.error.log;
|
||||
access_log /var/log/nginx/symfony2.access.log;
|
||||
|
||||
# strip app.php/ prefix if it is present
|
||||
rewrite ^/app\.php/?(.*)$ /$1 permanent;
|
||||
|
||||
location / {
|
||||
index app.php;
|
||||
try_files $uri @rewriteapp;
|
||||
}
|
||||
|
||||
location @rewriteapp {
|
||||
rewrite ^(.*)$ /app.php/$1 last;
|
||||
}
|
||||
|
||||
# pass the PHP scripts to FastCGI server from upstream phpfcgi
|
||||
location ~ ^/(app|app_dev|config)\.php(/|$) {
|
||||
fastcgi_pass phpfcgi;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.*)$;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_param HTTPS off;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
BIN
docs/source/_static/puml/exports/form_steps.png
Normal file
After Width: | Height: | Size: 70 KiB |
82
docs/source/_static/puml/exports/form_steps.puml
Normal file
@@ -0,0 +1,82 @@
|
||||
@startuml
|
||||
|
||||
actor User
|
||||
participant Controller
|
||||
participant ExportFormType as eft
|
||||
participant ExportManager as em
|
||||
participant SelectedExport as se
|
||||
participant AggregatorFormType as aft
|
||||
collections aggregators as a
|
||||
participant FilterFormType as fft
|
||||
collections filters as f
|
||||
|
||||
|
||||
User -> Controller: request "/exports/new/<id of the export>?step=export"
|
||||
|
||||
activate Controller
|
||||
Controller -> eft: build the form (AbstractType::buildForm)
|
||||
|
||||
activate eft
|
||||
eft -> em: get the export
|
||||
|
||||
activate em
|
||||
em -> eft: return the SelectedExport
|
||||
deactivate em
|
||||
|
||||
eft -> se: get the Export Type
|
||||
|
||||
activate se
|
||||
se -> eft: return the export Type (a string)
|
||||
deactivate se
|
||||
|
||||
eft -> aft: build the subform 'aggregators'
|
||||
activate aft
|
||||
|
||||
aft -> em: get aggregators for this export type
|
||||
activate em
|
||||
em -> aft: return a collection of aggregators
|
||||
deactivate em
|
||||
|
||||
loop for each aggregator
|
||||
aft -> a: build eventual subform for the aggregator
|
||||
activate a
|
||||
a -> a: append enventually his form
|
||||
a -> aft
|
||||
deactivate a
|
||||
end
|
||||
|
||||
aft -> eft
|
||||
deactivate aft
|
||||
|
||||
eft -> fft: build the subform 'filters'
|
||||
activate fft
|
||||
|
||||
fft -> em: get filters for this export type
|
||||
activate em
|
||||
em -> fft: return a collection for filters
|
||||
deactivate em
|
||||
|
||||
loop for each filter
|
||||
fft -> f: build eventual subform for the filter
|
||||
activate f
|
||||
f -> f: append eventually his form
|
||||
f -> fft
|
||||
deactivate f
|
||||
end
|
||||
|
||||
fft -> eft
|
||||
deactivate fft
|
||||
|
||||
eft -> se: build eventual subform for the export itsefl
|
||||
activate se
|
||||
se -> se: append eventually his form
|
||||
se -> eft
|
||||
deactivate se
|
||||
|
||||
se -> Controller: return a well-build form
|
||||
deactivate se
|
||||
|
||||
Controller -> User: render the page with the form
|
||||
deactivate Controller
|
||||
@enduml
|
||||
|
BIN
docs/source/_static/puml/exports/processing_export.png
Normal file
After Width: | Height: | Size: 178 KiB |
164
docs/source/_static/puml/exports/processing_export.puml
Normal file
@@ -0,0 +1,164 @@
|
||||
@startuml
|
||||
|
||||
participant Controller
|
||||
participant ExportManager as em
|
||||
participant SelectedExport as se
|
||||
participant Formatter as f
|
||||
collections aggregators as aa
|
||||
collections filters as ff
|
||||
|
||||
activate Controller
|
||||
Controller -> em: ExportManager::generate(string 'export_alias', Centers[], $data, $formatterdata)
|
||||
activate em
|
||||
note left
|
||||
$data contains the data
|
||||
from the export form
|
||||
(export form + aggregators form +
|
||||
filters forms)
|
||||
end note
|
||||
|
||||
|
||||
em -> se: Export::initiateQuery($modifiers, $acl, $data)
|
||||
activate se
|
||||
note left
|
||||
$modifiers contains the selected modifiers
|
||||
and their data. Usually you can ignore this.
|
||||
$acl contains the list of selected centers. The
|
||||
export must check that the user has the right
|
||||
to compute the export for this center
|
||||
$data the data from the export form (if the export
|
||||
build a form with `buildForm` function
|
||||
end note
|
||||
|
||||
create Query as q
|
||||
se -> q: the export will create a query
|
||||
se -> em: return the query
|
||||
deactivate se
|
||||
|
||||
alt The query is a "native sql query"
|
||||
|
||||
note over em, se
|
||||
When the export create a native query, there aren't any filters
|
||||
or aggregators which can alter the query.
|
||||
|
||||
We immediatly get the results from the query.
|
||||
end note
|
||||
|
||||
else "The query is a query builder"
|
||||
|
||||
note over em, se
|
||||
As the query is a query builder, filters and formatters
|
||||
will be able to alter the query.
|
||||
end note
|
||||
|
||||
loop for each filter selected by user
|
||||
em -> ff: FilterInterface::alterQuery(query, $data)
|
||||
activate ff
|
||||
note over em, ff
|
||||
The filter will alter query, adding his own clause (i.e. WHERE clause). It is
|
||||
his responsability to avoid removing clauses added by other filters / aggregators.
|
||||
|
||||
The ExportManager will also transmit the $data filled by the user (if the
|
||||
filter has a form) to each filter / aggregator.
|
||||
end note
|
||||
ff -> q: append some clauses
|
||||
deactivate ff
|
||||
end
|
||||
|
||||
loop for each aggregator selected by user
|
||||
note over em, aa
|
||||
As of filter, aggregators will append their
|
||||
own clauses in the query (usually GROUP BY clause).
|
||||
end note
|
||||
em -> aa: AggregatorInterface::alterQuery(query, data)
|
||||
activate aa
|
||||
|
||||
aa -> q: append some clauses
|
||||
deactivate aa
|
||||
end
|
||||
end alt
|
||||
|
||||
note over se
|
||||
The query is now ready. We will fetch the results from databases.
|
||||
|
||||
The Export is responsible for getting the results
|
||||
end note
|
||||
|
||||
em -> se: Export::getResult(query, $data)
|
||||
activate se
|
||||
|
||||
se -> q: getResult()
|
||||
activate q
|
||||
q -> se: return result
|
||||
destroy q
|
||||
|
||||
se -> em: return result
|
||||
deactivate se
|
||||
|
||||
em -> f: FormatterInterface::getResponse()
|
||||
activate f
|
||||
|
||||
note over f, ff
|
||||
The formatter will ask the export, and each aggregators the keys they
|
||||
are responsible for.
|
||||
|
||||
Then, for each of those keys, he will ask to each participant
|
||||
(Export or aggregator) a Closure, which will render the value in
|
||||
results into a human readable string.
|
||||
end note
|
||||
|
||||
f -> se: getQueryKeys
|
||||
activate se
|
||||
se -> f: return string[]
|
||||
|
||||
loop for each keys the export is responsible for
|
||||
f -> se: ExportInterface::getLabel()
|
||||
create "closure for key export" as closuree
|
||||
se -> closuree: create a closure
|
||||
se -> f: return closure
|
||||
end
|
||||
|
||||
loop for each aggregator
|
||||
f -> aa: getQueryKeys()
|
||||
activate aa
|
||||
aa -> f: return string[]
|
||||
deactivate aa
|
||||
|
||||
loop for each keys the aggregator is responsible for
|
||||
f -> aa: getLabel()
|
||||
activate aa
|
||||
create "closure for key aggregators" as closureg
|
||||
aa -> closureg: create a closure
|
||||
aa -> f: return closure
|
||||
deactivate aa
|
||||
end
|
||||
end
|
||||
|
||||
loop over results
|
||||
note over f, closureg
|
||||
Each row in result will be transformed in a human readable format by the closure.
|
||||
|
||||
The human readable string will be appended by the Formatter in his response (a Spreadsheet, CSV file, ...)
|
||||
end note
|
||||
|
||||
f -> closuree: call
|
||||
activate closuree
|
||||
closuree -> f: return a human readable format for this value
|
||||
deactivate closuree
|
||||
|
||||
f -> closureg: call
|
||||
activate closureg
|
||||
closureg -> f: return a human readable format for the value
|
||||
deactivate g
|
||||
|
||||
f -> f: append the result in his response
|
||||
end
|
||||
|
||||
f -> em: return a Response
|
||||
deactivate f
|
||||
|
||||
em -> Controller: return a Response
|
||||
deactivate em
|
||||
deactivate Controller
|
||||
|
||||
@enduml
|
BIN
docs/source/_static/puml/pagination-sequence.png
Normal file
After Width: | Height: | Size: 15 KiB |
20
docs/source/_static/puml/pagination-sequence.puml
Normal file
@@ -0,0 +1,20 @@
|
||||
@startuml
|
||||
|
||||
actor "controller, service, ..." as x
|
||||
participant "paginator"
|
||||
participant "Request"
|
||||
|
||||
x -> paginator: getCurrentPage()
|
||||
activate paginator
|
||||
|
||||
paginator -> Request: read the `page` parameter in GET request
|
||||
activate Request
|
||||
Request -> paginator
|
||||
deactivate Request
|
||||
|
||||
paginator -> paginator: construct a page object for current page number
|
||||
paginator -> x: return the `page`
|
||||
deactivate paginator
|
||||
|
||||
@enduml
|
||||
|
After Width: | Height: | Size: 306 KiB |