Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion config/opendxp/routing.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ opendxp_admin_page_display_preview_image:
# we need to have this outside of /admin scope, to be reachable publicly
opendxp_admin_document_document_diff_versions_html:
path: /__admin/document/diff-versions-html
defaults: { _controller: OpenDxp\Bundle\AdminBundle\Controller\Admin\Document\DocumentController::diffVersionsHtmlAction }
defaults: { _controller: OpenDxp\Bundle\AdminBundle\Controller\Admin\Document\DocumentVersionController::diffVersionsHtmlAction }
84 changes: 81 additions & 3 deletions config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ services:
public: true
tags: ['controller.service_arguments']

OpenDxp\Bundle\AdminBundle\Controller\Admin\IndexController:
public: true
OpenDxp\Maintenance\ExecutorInterface:
alias: OpenDxp\Maintenance\Executor

OpenDxp\Bundle\AdminBundle\Handler\Admin\Statistics\StatisticsHandler:
arguments:
$httpClient: '@opendxp.http_client'
tags: [ 'controller.service_arguments' ]

#
# COMMANDS
Expand Down Expand Up @@ -91,6 +92,7 @@ services:
#

OpenDxp\Bundle\AdminBundle\Service\Workflow\ActionsButtonService: ~
OpenDxp\Bundle\AdminBundle\Service\Workflow\WorkflowElementResolver: ~

#
# Admin Config Services
Expand All @@ -102,3 +104,79 @@ services:
#
OpenDxp\Bundle\AdminBundle\Service\ElementServiceInterface:
class: OpenDxp\Bundle\AdminBundle\Service\ElementService

OpenDxp\Bundle\AdminBundle\Service\CustomLoginUrlGenerator: ~

OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface:
class: OpenDxp\Bundle\AdminBundle\Service\AdminUserContext

OpenDxp\Bundle\AdminBundle\Service\Element\SessionService: ~
OpenDxp\Bundle\AdminBundle\Service\Element\EditLockService: ~

OpenDxp\Bundle\AdminBundle\Service\Document\DocumentPayloadMapper: ~
OpenDxp\Bundle\AdminBundle\Service\Document\DocumentPersistenceCoordinator: ~

OpenDxp\Bundle\AdminBundle\Service\DataObject\DataObjectPayloadMapper: ~
OpenDxp\Bundle\AdminBundle\Service\DataObject\DataObjectPersistenceCoordinator: ~
OpenDxp\Bundle\AdminBundle\Service\DataObject\DataObjectGridService: ~

OpenDxp\Bundle\AdminBundle\Service\Asset\AssetPayloadMapper: ~
OpenDxp\Bundle\AdminBundle\Service\Asset\AssetPersistenceCoordinator: ~
OpenDxp\Bundle\AdminBundle\Service\Asset\AssetUploadService: ~
OpenDxp\Bundle\AdminBundle\Service\Asset\AssetGridService: ~

OpenDxp\Bundle\AdminBundle\Service\Grid\GridColumnConfigService: ~
OpenDxp\Bundle\AdminBundle\Service\Grid\AssetGridColumnConfigResolver: ~
OpenDxp\Bundle\AdminBundle\Service\Grid\DataObjectGridColumnConfigResolver: ~
OpenDxp\Bundle\AdminBundle\Service\Grid\GridExportService: ~
OpenDxp\Bundle\AdminBundle\Service\Grid\GridBatchService: ~

OpenDxp\Bundle\AdminBundle\Service\Cache\OpenDxpCacheClearingService: ~
OpenDxp\Bundle\AdminBundle\Service\Cache\SymfonyCacheClearingService: ~

OpenDxp\Bundle\AdminBundle\Service\Translation\AdminSearchTermResolver: ~

#
# Factories
#
OpenDxp\Bundle\AdminBundle\Factory\:
resource: '../src/Factory'

#
# Builder
#
OpenDxp\Bundle\AdminBundle\Builder\:
resource: '../src/Builder'

#
# HTTP / Value Resolvers
#
OpenDxp\Bundle\AdminBundle\Http\:
resource: '../src/Http'

#
# Enrichers
#
OpenDxp\Bundle\AdminBundle\Enricher\:
resource: '../src/Enricher'

#
# Handlers
#
OpenDxp\Bundle\AdminBundle\Handler\:
resource: '../src/Handler'
bind:
$documentNoteTypes: '%opendxp_admin.dataObjects.notes_events.types%'
$assetNoteTypes: '%opendxp_admin.assets.notes_events.types%'
$objectNoteTypes: '%opendxp_admin.documents.notes_events.types%'

OpenDxp\Bundle\AdminBundle\Handler\Document\AddDocument\AddDocumentHandler:
arguments:
$documentClassResolver: '@opendxp.class.resolver.document'
$defaultDocumentController: '%opendxp.documents.default_controller%'

#
# Security Voters
#
OpenDxp\Bundle\AdminBundle\Security\Voter\:
resource: '../src/Security/Voter'
5 changes: 0 additions & 5 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,3 @@ parameters:
count: 1
path: src/DependencyInjection/OpenDxpAdminExtension.php

-
message: '#^Trait OpenDxp\\Bundle\\AdminBundle\\EventListener\\Traits\\ControllerTypeTrait is used zero times and is not analysed\.$#'
identifier: trait.unused
count: 1
path: src/EventListener/Traits/ControllerTypeTrait.php
2 changes: 1 addition & 1 deletion public/js/opendxp/object/helpers/customLayoutEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -828,7 +828,7 @@ opendxp.object.helpers.customLayoutEditor = Class.create({
this.layoutComboStore.reload();
this.data = res.data;
} else {
Ext.Msg.alert(t('error'), t(res.msg));
Ext.Msg.alert(t('error'), t(res.message));
}
} catch (e) {
this.saveOnError();
Expand Down
244 changes: 244 additions & 0 deletions src/Builder/AdminSettingsAssembler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
<?php

/**
* OpenDXP
*
* This source file is licensed under the GNU General Public License version 3 (GPLv3).
*
* Full copyright and license information is available in
* LICENSE.md which is distributed with this source code.
*
* @copyright Copyright (c) OpenDXP (https://www.opendxp.io)
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3)
*/

declare(strict_types=1);

namespace OpenDxp\Bundle\AdminBundle\Builder;

use Doctrine\DBAL\Connection;
use OpenDxp;
use OpenDxp\Bundle\AdminBundle\Dto\Admin\AdminSettingsDto;
use OpenDxp\Bundle\AdminBundle\Factory\DashboardFactory;
use OpenDxp\Bundle\AdminBundle\Dto\Admin\StatisticsDto;
use OpenDxp\Bundle\AdminBundle\Perspective\Config as PerspectiveConfig;
use OpenDxp\Bundle\AdminBundle\Security\CsrfProtectionHandler;
use OpenDxp\Bundle\AdminBundle\System\AdminConfig;
use OpenDxp\Bundle\CoreBundle\OptionsProvider\SelectOptionsOptionsProvider;
use OpenDxp\Config;
use OpenDxp\Image\HtmlToImage;
use OpenDxp\Maintenance\Executor;
use OpenDxp\Model\Asset;
use OpenDxp\Model\DataObject\ClassDefinition\CustomLayout;
use OpenDxp\Model\Document;
use OpenDxp\Model\Document\DocType;
use OpenDxp\Model\Element\Service;
use OpenDxp\Model\Property\Predefined;
use OpenDxp\Model\User;
use OpenDxp\SystemSettingsConfig;
use OpenDxp\Tool;
use OpenDxp\Tool\Admin;
use OpenDxp\Tool\MaintenanceModeHelperInterface;
use OpenDxp\Version;
use OpenDxp\Video;
use OpenDxp\Bundle\AdminBundle\Handler\Admin\Settings\SettingsPayload;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\HttpKernel\KernelInterface;

final class AdminSettingsAssembler
{
public function __construct(
private readonly Config $config,
private readonly DashboardFactory $dashboardFactory,
private readonly CsrfProtectionHandler $csrfProtection,
private readonly Executor $maintenanceExecutor,
private readonly MaintenanceModeHelperInterface $maintenanceModeHelper,
private readonly UrlGeneratorInterface $urlGenerator,
private readonly Connection $db,
private readonly KernelInterface $kernel,
private readonly RequestStack $requestStack,
#[Autowire('%opendxp_admin.custom_admin_route_name%')]
private readonly string $customAdminRouteName,
#[Autowire('%secret%')]
private readonly string $secret,
) {}

public function createSettings(SettingsPayload $payload, User $user): AdminSettingsDto
{
$config = $this->config;
$systemSettings = SystemSettingsConfig::get();
$adminSettings = AdminConfig::get();

$runtimePerspective = PerspectiveConfig::getRuntimePerspective($user);
$dashboard = $this->dashboardFactory->create();

try {
$adminEntrypointUrl = $this->urlGenerator->generate(
$this->customAdminRouteName,
[],
UrlGeneratorInterface::ABSOLUTE_URL
);
} catch (\Exception) {
$adminEntrypointUrl = null;
}

$requiredLanguages = $systemSettings['general']['valid_languages'];
if (array_key_exists('required_languages', $systemSettings['general'])) {
$requiredLanguages = $systemSettings['general']['required_languages'];
}

$maxUpload = OpenDxp\Helper\FileSystemHelper::filesizeToBytes(ini_get('upload_max_filesize') . 'B');
$maxPost = OpenDxp\Helper\FileSystemHelper::filesizeToBytes(ini_get('post_max_size') . 'B');
$uploadBytes = min($maxUpload, $maxPost) ?: $maxUpload;

$sessionGcMaxlifetime = (int) ini_get('session.gc_maxlifetime') ?: 120;

$maintenanceActive = false;
if (($lastExecution = $this->maintenanceExecutor->getLastExecution()) && time() - $lastExecution < 3660) {
$maintenanceActive = true;
}

$mailIncomplete = false;
if (isset($config['email']) && $systemSettings['email']) {
if (OpenDxp::inDebugMode() && empty($systemSettings['email']['debug']['email_addresses'])) {
$mailIncomplete = true;
}
if (empty($config['email']['sender']['email'])) {
$mailIncomplete = true;
}
}

$notificationsEnabled = (bool) $config['notifications']['enabled'];

return new AdminSettingsDto(
instanceId: $this->buildInstanceId(),
version: Version::getVersion(),
build: Version::getRevision(),
debug: OpenDxp::inDebugMode(),
devMode: OpenDxp::inDevMode(),
disableMinifyJs: OpenDxp::disableMinifyJs(),
environment: $this->kernel->getEnvironment(),
sessionId: htmlentities($payload->sessionId, ENT_QUOTES, 'UTF-8'),

language: $payload->locale,
websiteLanguages: Admin::reorderWebsiteLanguages($user, $systemSettings['general']['valid_languages'], true),
requiredLanguages: $requiredLanguages,

chromiumAvailable: HtmlToImage::isSupported(),
videoConverterAvailable: Video::isAvailable(),

debugAdminTranslations: (bool) $systemSettings['general']['debug_admin_translations'],
generateDocumentPreviews: (bool) $config['documents']['generate_preview'],
disableAssetTreePreview: (bool) $adminSettings['assets']['disable_tree_preview'],
hideEditImage: (bool) $adminSettings['assets']['hide_edit_image'],
dependencyEnabled: $config['dependency']['enabled'],

mainDomain: $systemSettings['general']['domain'],
customAdminEntrypointUrl: $adminEntrypointUrl,
timezone: $config['general']['timezone'] ?: date_default_timezone_get(),
tileLayerUrlTemplate: $config['maps']['tile_layer_url_template'],
geocodingUrlTemplate: $config['maps']['geocoding_url_template'],
reverseGeocodingUrlTemplate: $config['maps']['reverse_geocoding_url_template'],
hostname: htmlentities(Tool::getHostname(), ENT_QUOTES, 'UTF-8'),
assetDefaultUploadPath: $config['assets']['default_upload_path'],

assetTreePagingLimit: $config['assets']['tree_paging_limit'],
documentTreePagingLimit: $config['documents']['tree_paging_limit'],
objectTreePagingLimit: $config['objects']['tree_paging_limit'],

documentAutoSaveInterval: $config['documents']['auto_save_interval'],
objectAutoSaveInterval: $config['objects']['auto_save_interval'],

perspective: $runtimePerspective,
availablePerspectives: PerspectiveConfig::getAvailablePerspectives($user),
disabledPortlets: $dashboard->getDisabledPortlets(),

imageThumbnailsWriteable: (new Asset\Image\Thumbnail\Config())->isWriteable(),
videoThumbnailsWriteable: (new Asset\Video\Thumbnail\Config())->isWriteable(),
documentTypesWriteable: (new DocType())->isWriteable(),
predefinedPropertiesWriteable: (new Predefined())->isWriteable(),
predefinedAssetMetadataWriteable: (new \OpenDxp\Model\Metadata\Predefined())->isWriteable(),
perspectivesWriteable: PerspectiveConfig::isWriteable(),
customViewsWriteable: \OpenDxp\Bundle\AdminBundle\CustomView\Config::isWriteable(),
classDefinitionWriteable: !isset($_SERVER['OPENDXP_CLASS_DEFINITION_WRITABLE']) || (bool) $_SERVER['OPENDXP_CLASS_DEFINITION_WRITABLE'],
objectCustomLayoutWriteable: (new CustomLayout())->isWriteable(),
selectOptionsWriteable: (new \OpenDxp\Model\DataObject\SelectOptions\Config())->isWriteable(),

assetSearchTypes: Asset::getTypes(),
documentTypesConfiguration: Document::getTypesConfiguration(),
documentSearchTypes: Document::getTypes(),
documentValidTypes: array_values(array_filter(Document::getTypes(), fn ($t) => $t !== 'folder')),
documentEmailSearchTypes: $config['documents']['email_search'],
selectOptionsProviderClass: SelectOptionsOptionsProvider::class,

uploadMaxFilesize: (int) $uploadBytes,
sessionGcMaxlifetime: $sessionGcMaxlifetime,

maintenanceActive: $maintenanceActive,
maintenanceMode: $this->maintenanceModeHelper->isActive(),

mailConfigured: !$mailIncomplete,
mailDefaultAddress: $config['email']['sender']['email'] ?? null,

customViews: $this->buildCustomViews(),

notificationsEnabled: $notificationsEnabled,
checkNewNotificationEnabled: $notificationsEnabled && (bool) $config['notifications']['check_new_notification']['enabled'],
checkNewNotificationInterval: $config['notifications']['check_new_notification']['interval'] * 1000,

csrfToken: $this->csrfProtection->getCsrfToken($this->requestStack->getSession()),
);
}

public function createStatistics(): StatisticsDto
{
try {
$dbVersion = $this->db->fetchOne('SELECT VERSION()');
} catch (\Throwable) {
$dbVersion = null;
}

return new StatisticsDto(
instanceId: $this->buildInstanceId(),
revision: Version::getRevision(),
version: Version::getVersion(),
majorVersion: Version::getMajorVersion(),
phpVersion: PHP_VERSION,
dbVersion: is_string($dbVersion) ? $dbVersion : null,
bundles: array_keys($this->kernel->getBundles()),
);
}

private function buildInstanceId(): string
{
try {
return sha1(substr($this->secret, 3, -3));
} catch (\Exception) {
return 'not-set';
}
}

private function buildCustomViews(): array
{
$cvData = [];
foreach (\OpenDxp\Bundle\AdminBundle\CustomView\Config::get() as $node) {
$tmpData = $node;
$treeType = $tmpData['treetype'] ?: 'object';
$rootNode = Service::getElementByPath($treeType, $tmpData['rootfolder']);

if ($rootNode) {
$tmpData['rootId'] = $rootNode->getId();
$tmpData['allowedClasses'] = $tmpData['classes'] ?? null;
$tmpData['showroot'] = (bool) $tmpData['showroot'];

if ($rootNode->isAllowed('list')) {
$cvData[] = $tmpData;
}
}
}

return $cvData;
}
}
Loading
Loading