Files
Cloud-CMS/lib/Service/ReportService.php

432 lines
12 KiB
PHP
Raw Permalink Normal View History

2025-12-02 10:32:59 -05:00
<?php
/*
* Copyright (C) 2023 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Service;
use Illuminate\Support\Str;
use Psr\Container\ContainerInterface;
use Slim\Http\ServerRequest as Request;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Xibo\Entity\ReportResult;
use Xibo\Event\ConnectorReportEvent;
use Xibo\Factory\SavedReportFactory;
use Xibo\Helper\SanitizerService;
use Xibo\Report\ReportInterface;
use Xibo\Storage\StorageServiceInterface;
use Xibo\Storage\TimeSeriesStoreInterface;
use Xibo\Support\Exception\InvalidArgumentException;
use Xibo\Support\Exception\NotFoundException;
/**
* Class ReportScheduleService
* @package Xibo\Service
*/
class ReportService implements ReportServiceInterface
{
/**
* @var ContainerInterface
*/
public $container;
/**
* @var StorageServiceInterface
*/
private $store;
/**
* @var TimeSeriesStoreInterface
*/
private $timeSeriesStore;
/**
* @var LogServiceInterface
*/
private $log;
/**
* @var ConfigServiceInterface
*/
private $config;
/**
* @var SanitizerService
*/
private $sanitizer;
/**
* @var SavedReportFactory
*/
private $savedReportFactory;
/** @var EventDispatcherInterface */
private $dispatcher;
/**
* @inheritdoc
*/
public function __construct($container, $store, $timeSeriesStore, $log, $config, $sanitizer, $savedReportFactory)
{
$this->container = $container;
$this->store = $store;
$this->timeSeriesStore = $timeSeriesStore;
$this->log = $log;
$this->config = $config;
$this->sanitizer = $sanitizer;
$this->savedReportFactory = $savedReportFactory;
}
/** @inheritDoc */
public function setDispatcher(EventDispatcherInterface $dispatcher): ReportServiceInterface
{
$this->dispatcher = $dispatcher;
return $this;
}
public function getDispatcher(): EventDispatcherInterface
{
if ($this->dispatcher === null) {
$this->dispatcher = new EventDispatcher();
}
return $this->dispatcher;
}
/**
* @inheritdoc
*/
public function listReports()
{
$reports = [];
$files = array_merge(glob(PROJECT_ROOT . '/reports/*.report'), glob(PROJECT_ROOT . '/custom/*.report'));
foreach ($files as $file) {
$config = json_decode(file_get_contents($file));
$config->file = Str::replaceFirst(PROJECT_ROOT, '', $file);
// Compatibility check
if (!isset($config->feature) || !isset($config->category)) {
continue;
}
// Check if only allowed for admin
if ($this->container->get('user')->userTypeId != 1) {
if (isset($config->adminOnly) && !empty($config->adminOnly)) {
continue;
}
}
// Check Permissions
if (!$this->container->get('user')->featureEnabled($config->feature)) {
continue;
}
$reports[$config->category][] = $config;
}
$this->log->debug('Reports found in total: '.count($reports));
// Get reports that are allowed by connectors
$event = new ConnectorReportEvent();
$this->getDispatcher()->dispatch($event, ConnectorReportEvent::$NAME);
$connectorReports = $event->getReports();
// Merge built in reports and connector reports
if (count($connectorReports) > 0) {
$reports = array_merge($reports, $connectorReports);
}
foreach ($reports as $k => $report) {
usort($report, function ($a, $b) {
if (empty($a->sort_order) || empty($b->sort_order)) {
return 0;
}
return $a->sort_order - $b->sort_order;
});
$reports[$k] = $report;
}
return $reports;
}
/**
* @inheritdoc
*/
public function getReportByName($reportName)
{
foreach ($this->listReports() as $reports) {
foreach ($reports as $report) {
if ($report->name == $reportName) {
$this->log->debug('Get report by name: '.json_encode($report, JSON_PRETTY_PRINT));
return $report;
}
}
}
//throw error
throw new NotFoundException(__('Get Report By Name: No file to return'));
}
/**
* @inheritdoc
*/
public function getReportClass($reportName)
{
foreach ($this->listReports() as $reports) {
foreach ($reports as $report) {
if ($report->name == $reportName) {
if ($report->class == '') {
throw new NotFoundException(__('Report class not found'));
}
$this->log->debug('Get report class: '.$report->class);
return $report->class;
}
}
}
// throw error
throw new NotFoundException(__('Get report class: No file to return'));
}
/**
* @inheritdoc
*/
public function createReportObject($className)
{
if (!\class_exists($className)) {
throw new NotFoundException(__('Class %s not found', $className));
}
/** @var ReportInterface $object */
$object = new $className();
$object
->setCommonDependencies(
$this->store,
$this->timeSeriesStore
)
->useLogger($this->log)
->setFactories($this->container);
return $object;
}
/**
* @inheritdoc
*/
public function getReportScheduleFormData($reportName, Request $request)
{
$this->log->debug('Populate form title and hidden fields');
$className = $this->getReportClass($reportName);
$object = $this->createReportObject($className);
// Populate form title and hidden fields
return $object->getReportScheduleFormData($this->sanitizer->getSanitizer($request->getParams()));
}
/**
* @inheritdoc
*/
public function setReportScheduleFormData($reportName, Request $request)
{
$this->log->debug('Set Report Schedule form data');
$className = $this->getReportClass($reportName);
$object = $this->createReportObject($className);
// Set Report Schedule form data
return $object->setReportScheduleFormData($this->sanitizer->getSanitizer($request->getParams()));
}
/**
* @inheritdoc
*/
public function generateSavedReportName($reportName, $filterCriteria)
{
$this->log->debug('Generate Saved Report name');
$className = $this->getReportClass($reportName);
$object = $this->createReportObject($className);
$filterCriteria = json_decode($filterCriteria, true);
return $object->generateSavedReportName($this->sanitizer->getSanitizer($filterCriteria));
}
/**
* @inheritdoc
*/
public function getSavedReportResults($savedreportId, $reportName)
{
$className = $this->getReportClass($reportName);
$object = $this->createReportObject($className);
$savedReport = $this->savedReportFactory->getById($savedreportId);
// Open a zipfile and read the json
$zipFile = $this->config->getSetting('LIBRARY_LOCATION') .'savedreport/'. $savedReport->fileName;
// Do some pre-checks on the arguments we have been provided
if (!file_exists($zipFile)) {
throw new InvalidArgumentException(__('File does not exist'));
}
// Open the Zip file
$zip = new \ZipArchive();
if (!$zip->open($zipFile)) {
throw new InvalidArgumentException(__('Unable to open ZIP'));
}
// Get the reportscheduledetails
$json = json_decode($zip->getFromName('reportschedule.json'), true);
// Retrieve the saved report result array
$results = $object->getSavedReportResults($json, $savedReport);
$this->log->debug('Saved Report results'. json_encode($results, JSON_PRETTY_PRINT));
// Return data to build chart
return $results;
}
/**
* @inheritdoc
*/
public function convertSavedReportResults($savedreportId, $reportName)
{
$className = $this->getReportClass($reportName);
$object = $this->createReportObject($className);
$savedReport = $this->savedReportFactory->getById($savedreportId);
// Open a zipfile and read the json
$zipFile = $this->config->getSetting('LIBRARY_LOCATION') . $savedReport->storedAs;
// Do some pre-checks on the arguments we have been provided
if (!file_exists($zipFile)) {
throw new InvalidArgumentException(__('File does not exist'));
}
// Open the Zip file
$zip = new \ZipArchive();
if (!$zip->open($zipFile)) {
throw new InvalidArgumentException(__('Unable to open ZIP'));
}
// Get the old json (saved report)
$oldjson = json_decode($zip->getFromName('reportschedule.json'), true);
// Restructure the old json to new json
$json = $object->restructureSavedReportOldJson($oldjson);
// Format the JSON as schemaVersion 2
$fileName = tempnam($this->config->getSetting('LIBRARY_LOCATION') . '/temp/', 'reportschedule');
$out = fopen($fileName, 'w');
fwrite($out, json_encode($json));
fclose($out);
$zip = new \ZipArchive();
$result = $zip->open($zipFile, \ZipArchive::CREATE | \ZipArchive::OVERWRITE);
if ($result !== true) {
throw new InvalidArgumentException(__('Can\'t create ZIP. Error Code: %s', $result));
}
$zip->addFile($fileName, 'reportschedule.json');
$zip->close();
// Remove the JSON file
unlink($fileName);
}
/**
* @inheritdoc
*/
public function runReport($reportName, $filterCriteria, $user)
{
$this->log->debug('Run the report to get results');
$className = $this->getReportClass($reportName);
$object = $this->createReportObject($className);
// Set userId
$object->setUser($user);
$filterCriteria = json_decode($filterCriteria, true);
// Retrieve the result array
return $object->getResults($this->sanitizer->getSanitizer($filterCriteria));
}
/**
* @inheritdoc
*/
public function getReportEmailTemplate($reportName)
{
$className = $this->getReportClass($reportName);
$object = $this->createReportObject($className);
// Set Report Schedule form data
return $object->getReportEmailTemplate();
}
/**
* @inheritdoc
*/
public function getSavedReportTemplate($reportName)
{
$className = $this->getReportClass($reportName);
$object = $this->createReportObject($className);
// Set Report Schedule form data
return $object->getSavedReportTemplate();
}
/**
* @inheritdoc
*/
public function getReportChartScript($savedreportId, $reportName)
{
/* @var ReportResult $results */
$results = $this->getSavedReportResults($savedreportId, $reportName);
$className = $this->getReportClass($reportName);
$object = $this->createReportObject($className);
// Set Report Schedule form data
return $object->getReportChartScript($results);
}
}