diff --git a/docs/components/reports.rst b/docs/components/reports.rst index fe732426..987462f1 100644 --- a/docs/components/reports.rst +++ b/docs/components/reports.rst @@ -1,4 +1,220 @@ Reports ####### +To add and render custom Reports in Mautic, your Plugin needs to listen to three events: +- ``\Mautic\ReportBundle\ReportEvents::REPORT_ON_BUILD`` +- ``ReportEvents::REPORT_ON_GENERATE`` +- ``ReportEvents::REPORT_ON_GRAPH_GENERATE`` + +This guide walks you through defining a custom Report, generating Report data, and rendering graphs. + +.. vale off +Defining the Report +------------------- + +.. vale on +Use the ``ReportEvents::REPORT_ON_BUILD`` event to define: + +- The Report context +- Available columns +- Available filters - defaults to columns +- Available graphs + +Column definition +----------------- + +Each column array can include the following properties: + +.. vale on + +.. list-table:: + :header-rows: 1 + + * - Key + - Required? + - Type + - Description + * - ``label`` + - REQUIRED + - string + - The language string for the column. + * - type + - REQUIRED + - string + - Column type. + * - alias + - OPTIONAL + - string + - An alias for the returned value. Useful in conjunction with ``formula``. + * - formula + - OPTIONAL + - string + - SQL formula instead of a column. for example. ``SUBSTRING_INDEX(e.type, \'.\', 1)``. + * - link + - OPTIONAL + - string + - Route name to convert the value into a hyperlink. Used usually with an ID of an Entity. The route must accept ``objectAction`` and ``objectId`` parameters. + +.. vale off + +Filter Definition +----------------- + +.. vale on +Filters are optional. If you don't define them, the system defaults to using the column definitions. However, filters can provide additional options such as dropdown select lists. + +Additional filter keys include: + +.. list-table:: + :header-rows: 1 + + * - Key + - Required? + - Type + - Description + * - list + - OPTIONAL + - array + - Used when ``type`` is select for a filter. Provides the dropdown options for a select input. Format should be ``value => label``. + * - operators + - OPTIONAL + - array + - Custom list of operators to allow for this filter. See ``Mautic\ReportBundle\Builder\MauticReportBuilder::OPERATORS`` for a examples. + +.. vale off + +Generate the QueryBuilder +--------------------------- + +The system dispatches the ``ReportEvents::REPORT_ON_GENERATE`` event when it needs to generate and display a report. In this function, the plugin defines the QueryBuilder object used to generate the table data. +Call ``$event->checkContext()`` to determine if the requested report is the subscriber's report. + +Use Doctrine’s DBAL layer QueryBuilder for the ``ReportEvents::REPORT_ON_GENERATE`` event by obtaining it with ``$qb = $event->getQueryBuilder();``. +There are a number of helper functions to append joins for commonly used relationships such as category, leads, ip address, etc. Refer to the ``ReportGeneratorEvent`` class for more details. + +Generating Graphs +----------------- + +Use the ``ReportEvents::REPORT_ON_GRAPH_GENERATE`` event to render graphs for your report. + +- Check the report context with ``$event->checkContext()``. +- Clone the base ``QueryBuilder`` to manipulate queries safely. +- Use classes like ``LineChart`` and ``ChartQuery`` to generate and render graph data. + +For supported chart types and options, refer to the ``ChartQuery`` and ``LineChart`` helper classes in the Mautic codebase. + +Example: HelloWorld Report Subscriber +------------------------------------- + +Below is an example Plugin file located at:: + + plugins\HelloWorldBundle\EventListener\ReportSubscriber.php + +This file subscribes to Report events and provides custom logic for adding new tables, columns, filters, and graphs. + +.. code-block:: php + + namespace MauticPlugin\HelloWorldBundle\EventListener; + + use Mautic\CoreBundle\EventListener\CommonSubscriber; + use Mautic\CoreBundle\Helper\GraphHelper; + use Mautic\ReportBundle\Event\ReportBuilderEvent; + use Mautic\ReportBundle\Event\ReportGeneratorEvent; + use Mautic\ReportBundle\Event\ReportGraphEvent; + use Mautic\ReportBundle\ReportEvents; + use Mautic\CoreBundle\Helper\Chart\ChartQuery; + use Mautic\CoreBundle\Helper\Chart\LineChart; + + class ReportSubscriber extends CommonSubscriber + { + public static function getSubscribedEvents() + { + return [ + ReportEvents::REPORT_ON_BUILD => ['onReportBuilder', 0], + ReportEvents::REPORT_ON_GENERATE => ['onReportGenerate', 0], + ReportEvents::REPORT_ON_GRAPH_GENERATE => ['onReportGraphGenerate', 0], + ]; + } + + public function onReportBuilder(ReportBuilderEvent $event) + { + if ($event->checkContext(['worlds'])) { + $prefix = 'w.'; + $columns = [ + $prefix . 'visit_count' => [ + 'label' => 'mautic.hellobundle.report.visit_count', + 'type' => 'int', + ], + $prefix . 'world' => [ + 'label' => 'mautic.hellobundle.report.world', + 'type' => 'text', + ], + ]; + + $columns = $filters = array_merge( + $columns, + $event->getStandardColumns($prefix), + $event->getCategoryColumns() + ); + + $filters[$prefix . 'world']['type'] = 'select'; + $filters[$prefix . 'world']['list'] = [ + 'earth' => 'Earth', + 'mars' => 'Mars', + ]; + + $event->addTable('worlds', [ + 'display_name' => 'mautic.helloworld.worlds', + 'columns' => $columns, + 'filters' => $filters, + ]); + + $event->addGraph('worlds', 'line', 'mautic.hellobundle.graph.line.visits'); + } + } + + public function onReportGenerate(ReportGeneratorEvent $event) + { + $context = $event->getContext(); + if ($context == 'worlds') { + $qb = $event->getQueryBuilder(); + $qb->from(MAUTIC_TABLE_PREFIX . 'worlds', 'w'); + $event->addCategoryLeftJoin($qb, 'w'); + $event->setQueryBuilder($qb); + } + } + + public function onReportGraphGenerate(ReportGraphEvent $event) + { + if (!$event->checkContext('worlds')) { + return; + } + + $graphs = $event->getRequestedGraphs(); + $qb = $event->getQueryBuilder(); + + foreach ($graphs as $graph) { + $queryBuilder = clone $qb; + $options = $event->getOptions($graph); + $chartQuery = clone $options['chartQuery']; + $chartQuery->applyDateFilters($queryBuilder, 'date_added', 'v'); + + switch ($graph) { + case 'mautic.hellobundle.graph.line.visits': + $chart = new LineChart(null, $options['dateFrom'], $options['dateTo']); + $chartQuery->modifyTimeDataQuery($queryBuilder, 'date_added', 'v'); + $visits = $chartQuery->loadAndBuildTimeData($queryBuilder); + $chart->setDataset( + $options['translator']->trans('mautic.hellobundle.graph.line.visits'), + $visits + ); + $data = $chart->render(); + $data['name'] = $graph; + $data['iconClass'] = 'fa-tachometer'; + $event->setGraph($graph, $data); + break; + } + } + } + } diff --git a/docs/index.rst b/docs/index.rst index df41b368..a41f2af5 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -166,6 +166,7 @@ There are several ways to support Mautic other than contributing with code. rest_api/point_groups rest_api/reports rest_api/text_messages + rest_api/stages .. toctree:: :maxdepth: 2 diff --git a/docs/rest_api/stages.rst b/docs/rest_api/stages.rst new file mode 100644 index 00000000..071ce4aa --- /dev/null +++ b/docs/rest_api/stages.rst @@ -0,0 +1,452 @@ +Stages +####### + +Use this endpoint to obtain details on Mautic’s contact stages. + +.. code-block:: php + + newAuth($settings); + $apiUrl = "https://your-mautic.com"; + $api = new MauticApi(); + $stageApi = $api->newApi("stages", $auth, $apiUrl); + + +.. vale off + +Get a Stage +************ + +.. vale on + +.. code-block:: php + + get($id); + + + +Get an individual Stage by ID. + +.. vale off + +**HTTP Request** + +.. vale on + +``GET /stages/ID`` + +**Response** + +``Expected Response Code: 200`` + +.. code-block:: json + + + { + "stage": { + "id": 47, + "isPublished": 1, + "dateAdded": "2015-07-21T12:27:12-05:00", + "createdBy": 1, + "createdByUser": "Joe Smith", + "dateModified": "2015-07-21T14:12:03-05:00", + "modifiedBy": 1, + "modifiedByUser": "Joe Smith", + "name": "Stage A", + "category": null, + "description": "This is my first stage created via API.", + "weight": 0, + "publishUp": "2015-07-21T14:12:03-05:00", + "publishDown": "2015-07-21T14:12:03-05:00" + } + +**Stage Properties** + +.. list-table:: + :header-rows: 1 + + * - Name + - Type + - Description + * - ``id`` + - int + - ID of the Stage + * - ``isPublished`` + - boolean + - Whether the stage is published + * - ``dateAdded`` + - datetime + - Date/time stage was created + * - ``createdBy`` + - int + - ID of the user that created the stage + * - ``createdByUser`` + - string + - Name of the user that created the stage + * - ``dateModified`` + - datetime/null + - Date/time stage was last modified + * - ``modifiedBy`` + - int + - ID of the user that last modified the stage + * - ``modifiedByUser`` + - string + - Name of the user that last modified the stage + * - ``name`` + - string` + - Stage name + * - ``category`` + - int + - Stage category ID + * - ``description`` + - string + - Stage description + * - ``weight`` + - int + - Stage's weight + * - ``publishUp`` + - datetime + - Date/time stage should be published + * - ``publishDown`` + - datetime + - Date/time stage should be unpublished + +.. vale off + +List Contact Stages +********************* + +.. vale on + +.. code-block:: php + + getList($searchFilter, $start, $limit, $orderBy, $orderByDir, $publishedOnly, $minimal); + +.. vale off + +**HTTP Request** + +.. vale on + +``GET /stages`` + +**Response** + +``Expected Response Code: 200`` + +.. code-block:: json + + { + "total": 4, + "stages": [ + { + "id": 47, + "isPublished": 1, + "dateAdded": "2015-07-21T12:27:12-05:00", + "createdBy": 1, + "createdByUser": "Joe Smith", + "dateModified": "2015-07-21T14:12:03-05:00", + "modifiedBy": 1, + "modifiedByUser": "Joe Smith", + "name": "Stage A", + "category": null, + "description": "This is my first stage created via API.", + "weight": 0, + "publishUp": "2015-07-21T14:12:03-05:00", + "publishDown": "2015-07-21T14:12:03-05:00" + }, + ... + ] +} + +**Stage Properties** + +.. list-table:: + :header-rows: 1 + + * - Name + - Type + - Description + * - ``total`` + - int + - Count of all stages + * - ``id`` + - int + - ID of the stage + * - ``isPublished`` + - boolean + - Whether the stage is published + * - ``dateAdded`` + - datetime + - Date/time stage was created + * - ``createdBy`` + - int + - ID of the user that created the stage + * - ``createdByUser`` + - string + - Name of the user that created the stage + * - ``dateModified`` + - datetime + - Date/time stage was last modified + * - ``modifiedBy`` + - int + - ID of the user that last modified the stage + * - ``modifiedByUser`` + - string + - Name of the user that last modified the stage + * - ``name`` + - string` + - Stage name + * - ``category`` + - int + - Stage category ID + * - ``description`` + - string + - Stage description + * - ``weight`` + - int + - Stage's weight + * - ``publishUp`` + - datetime + - Date/time stage should be published + * - ``publishDown`` + - datetime + - Date/time stage should be unpublished + +.. vale off + +Create Stage +************** + +Create a new stage. + +.. vale on + +.. code-block:: php + + 'Stage A', + 'weight' => 5, + 'description' => 'This is my first stage created via API.', + 'isPublished' => 1 + ); + + $stage = $stageApi->create($data); + +.. vale off + +**HTTP Request** + +.. vale on + +``POST /stages/new`` + +**POST Parameters** + +.. list-table:: + :header-rows: 1 + + * - Name + - Description + * - ``name`` + - Stage name is the only required field + * - ``weight`` + - int + * - ``description`` + - A description of the stage. + * - ``isPublished`` + - A value of 0 or 1 + + +**Response** + +``Expected Response Code: 201`` + +**Properties** + +Same as `Get Stage <#get-stage>`_. + +.. vale off + +Edit Stage +************ + +.. vale on + +.. code-block:: php + 'New stage name', + 'isPublished' => 0 + ); + + // Create new a stage of ID 1 is not found? + $createIfNotFound = true; + + $stage = $stageApi->edit($id, $data, $createIfNotFound); + +Edit a new Stage. Note that this supports PUT or PATCH depending on the desired behavior. + +**PUT** creates a stage if the given ID does not exist and clears all the stage information, adds the information from the request. +**PATCH** fails if the stage with the given ID does not exist and updates the stage field values with the values form the request. + +.. vale off + +**HTTP Request** + +.. vale on + +To edit a Stage and return a 404 if the Stage isn't found: + +``PATCH /stages/ID/edit`` + +To edit a Asset and create a new one if the Asset isn't found: + +``PUT /stages/ID/edit`` + +**POST Parameters** + +.. list-table:: + :header-rows: 1 + + * - Name + - Description + * - ``name`` + - Stage name is the only required field + * - ``alias`` + - Name alias generated automatically if not set + * - ``description`` + - A description of the stage. + * - ``isPublished`` + - A value of 0 or 1. + * - ``weight`` + - int + +**Response** + +If ``PUT``\ , the expected response code if editing the Asset is ``200`` or ``201`` if created. + +If using ``PATCH``\ , the expected response code is ``200``. + +**Properties** + +Same as `Get Stage <#get-stage>`_. + +.. vale off + +Delete Stage +************** + +.. vale on + +.. code-block:: php + + delete($id); + +Delete a stage. + +.. vale off + +**HTTP Request** + +.. vale on + +``DELETE /stages/ID/delete`` + +**Response** + +``Expected Response Code: 200`` + +**Properties** + +Same as `Get Stage <#get-stage>`_. + +.. vale off + +Add Contact to a Stage +************************ + +.. vale on + +.. code-block:: php + + addContact($stageId, $contactId); + if (!isset($response['success'])) { + // handle error + } + +Manually add a contact to a specific stage. + +.. vale off + +**HTTP Request** + +.. vale on + +``POST /stages/STAGE_ID/contact/CONTACT_ID/add`` + +**Response** + +``Expected Response Code: 200`` + +.. code-block:: json + { + "success": true + } + + +.. vale off + +Remove Contact from a Stage +***************************** + +.. vale on + +.. code-block:: php + + removeContact($stageId, $contactId); + if (!isset($response['success'])) { + // handle error + } + +Manually remove a contact from a specific stage. + +.. vale off + +**HTTP Request** + +.. vale on + +``POST /stages/STAGE_ID/contact/CONTACT_ID/remove`` + +**Response** + +``Expected Response Code: 200`` + +.. code-block:: json + { + "success": true + } \ No newline at end of file