Skip to content

Commit

Permalink
Merge branch '4.x' of github.com:contao/contao into feature/language-…
Browse files Browse the repository at this point in the history
…dependent-module-configuration
  • Loading branch information
bytehead committed Jan 13, 2022
2 parents 4d18783 + 4166f4e commit 9920305
Show file tree
Hide file tree
Showing 13 changed files with 256 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@
use Contao\Backend;
use Contao\CoreBundle\Framework\ContaoFramework;
use Contao\CoreBundle\Routing\Page\PageRegistry;
use Contao\CoreBundle\Routing\Page\PageRoute;
use Contao\CoreBundle\ServiceAnnotation\Callback;
use Contao\DataContainer;
use Contao\PageModel;
use Contao\StringUtil;
use Twig\Environment;

class PageRoutingListener
Expand All @@ -34,19 +36,22 @@ public function __construct(ContaoFramework $framework, PageRegistry $pageRegist
}

/**
* @Callback(table="tl_page", target="fields.routePath.load")
*
* @param mixed $value
* @Callback(table="tl_page", target="fields.routePath.input_field")
*/
public function loadRoutePath($value, DataContainer $dc): string
public function generateRoutePath(DataContainer $dc): string
{
$pageModel = $this->framework->getAdapter(PageModel::class)->findByPk($dc->id);

if (null === $pageModel) {
return '';
}

return $this->pageRegistry->getRoute($pageModel)->getPath();
return $this->twig->render(
'@ContaoCore/Backend/be_route_path.html.twig',
[
'path' => $this->getPathWithParameters($this->pageRegistry->getRoute($pageModel)),
]
);
}

/**
Expand Down Expand Up @@ -86,7 +91,7 @@ public function generateRouteConflicts(DataContainer $dc): string

$conflicts[] = [
'page' => $aliasPage,
'route' => $this->pageRegistry->getRoute($aliasPage),
'path' => $this->getPathWithParameters($this->pageRegistry->getRoute($aliasPage)),
'editUrl' => $backendAdapter->addToUrl(sprintf('act=edit&id=%s&popup=1&nb=1', $aliasPage->id)),
];
}
Expand Down Expand Up @@ -118,4 +123,15 @@ private function buildUrl(string $alias, string $urlPrefix, string $urlSuffix):

return $url;
}

private function getPathWithParameters(PageRoute $route): string
{
$path = $route->getPath();

foreach ($route->getRequirements() as $name => $regexp) {
$path = preg_replace('/{[!]?('.preg_quote($name, '/').')}/', '{<span class="tl_tip" title="'.StringUtil::specialchars($regexp).'">$1</span>}', $path);
}

return $path;
}
}
4 changes: 1 addition & 3 deletions core-bundle/src/Resources/contao/dca/tl_page.php
Original file line number Diff line number Diff line change
Expand Up @@ -254,9 +254,7 @@
'routePath' => array
(
'exclude' => true,
'inputType' => 'text',
'eval' => array('disabled'=>true, 'tl_class'=>'w50 clr'),
// load_callback from PageRoutingListener
// input_field_callback from PageRoutingListener
),
'routePriority' => array
(
Expand Down
7 changes: 7 additions & 0 deletions core-bundle/src/Resources/contao/themes/flexible/basic.css
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,13 @@ p {
.widget .mce-tinymce {
margin:3px 0;
}
.widget p.info {
margin:2px 0;
padding:7px;
background:#f3f3f5;
line-height:1.3;
border-radius:3px;
}

/* Forms */
optgroup {
Expand Down

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions core-bundle/src/Resources/contao/themes/flexible/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,11 @@ h2.sub_headline {
.tl_new,.tl_new a {
color:#d68c23;
}
.widget .tl_error,.widget .tl_confirm,.widget .tl_info,.widget .tl_new {
margin:1px 0;
padding:8px 10px 8px 30px;
background-position:9px 9px;
}

/* Filter */
.tl_panel,.tl_version_panel {
Expand Down

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions core-bundle/src/Resources/public/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -1320,11 +1320,11 @@ var Backend =
});

// Links and input elements
['a[title]', 'input[title]', 'button[title]', 'time[title]'].each(function(el) {
['a[title]', 'input[title]', 'button[title]', 'time[title]', 'span[title]'].each(function(el) {
new Tips.Contao($$(el).filter(function(i) {
return i.title != ''
}), {
offset: {x:0, y:((el == 'time[title]') ? 26 : 30)}
offset: {x:0, y:((el == 'time[title]' || el == 'span[title]') ? 26 : 30)}
});
});
},
Expand Down
2 changes: 1 addition & 1 deletion core-bundle/src/Resources/public/core.min.js

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<td colspan="1" class="tl_file_list">{{ conflict.page.title }}</td>
<td colspan="1" class="tl_file_list">{{ ('PTY.'~conflict.page.type~'.0')|trans([], 'contao_default') }}</td>
<td colspan="1" class="tl_file_list">{{ conflict.page.alias }}</td>
<td colspan="1" class="tl_file_list">{{ conflict.route.path }}</td>
<td colspan="1" class="tl_file_list">{{ conflict.path|raw }}</td>
<td colspan="1" class="tl_file_list">{{ conflict.page.routePriority }}</td>
<td class="tl_file_list tl_right_nowrap">
<a
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{% trans_default_domain 'contao_tl_page' %}
<div class="w50 widget">
<h3>{{ 'tl_page.routePath.0'|trans }}</h3>
<p class="info">{{ path|raw }}</p>
<p class="tl_help tl_tip" title="">{{ 'tl_page.routePath.1'|trans }}</p>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,15 @@
use Contao\CoreBundle\Tests\TestCase;
use Contao\DataContainer;
use Contao\PageModel;
use PHPUnit\Framework\MockObject\MockObject;
use Twig\Environment;

class PageRoutingListenerTest extends TestCase
{
public function testGetsPathFromPageRoute(): void
/**
* @dataProvider routePathProvider
*/
public function testGetsPathFromPageRoute(string $path, array $requirements, string $expected): void
{
$pageModel = $this->mockClassWithProperties(PageModel::class);

Expand All @@ -36,13 +40,7 @@ public function testGetsPathFromPageRoute(): void
;

$framework = $this->mockContaoFramework([PageModel::class => $pageAdapter]);

$pageRoute = $this->createMock(PageRoute::class);
$pageRoute
->expects($this->once())
->method('getPath')
->willReturn('foobar')
;
$pageRoute = $this->mockPageRoute($path, $requirements);

$pageRegistry = $this->createMock(PageRegistry::class);
$pageRegistry
Expand All @@ -52,10 +50,62 @@ public function testGetsPathFromPageRoute(): void
->willReturn($pageRoute)
;

$twig = $this->createMock(Environment::class);
$twig
->expects($this->once())
->method('render')
->with(
'@ContaoCore/Backend/be_route_path.html.twig',
[
'path' => $expected,
]
)
->willReturn('foobar')
;

$dc = $this->mockClassWithProperties(DataContainer::class, ['id' => 42]);
$listener = new PageRoutingListener($framework, $pageRegistry, $this->createMock(Environment::class));

$this->assertSame('foobar', $listener->loadRoutePath('', $dc));
$listener = new PageRoutingListener($framework, $pageRegistry, $twig);
$listener->generateRoutePath($dc);
}

public function routePathProvider(): \Generator
{
yield 'Path without parameters' => [
'foobar',
[],
'foobar',
];

yield 'Ignores unknown parameters in path' => [
'foo/bar/{baz}.html',
[],
'foo/bar/{baz}.html',
];

yield 'Ignores unknown parameters' => [
'foo/bar/{baz}.html',
['bar' => 'baz'],
'foo/bar/{baz}.html',
];

yield 'Replaces parameter' => [
'foo/{bar}.html',
['bar' => '.+'],
'foo/{<span class="tl_tip" title=".+">bar</span>}.html',
];

yield 'Replaces parameters' => [
'foo/{bar}/{baz}.html',
['bar' => '.+', 'baz' => '\d+'],
'foo/{<span class="tl_tip" title=".+">bar</span>}/{<span class="tl_tip" title="\d+">baz</span>}.html',
];

yield 'Handles parameters starting with exclamation point' => [
'foo/{!bar}.html',
['bar' => '.+'],
'foo/{<span class="tl_tip" title=".+">bar</span>}.html',
];
}

public function testReturnsEmptyPathIfPageModelIsNotFound(): void
Expand All @@ -79,7 +129,7 @@ public function testReturnsEmptyPathIfPageModelIsNotFound(): void
$dc = $this->mockClassWithProperties(DataContainer::class, ['id' => 42]);
$listener = new PageRoutingListener($framework, $pageRegistry, $this->createMock(Environment::class));

$this->assertSame('', $listener->loadRoutePath('foobar', $dc));
$this->assertSame('', $listener->generateRoutePath($dc));
}

public function testGeneratesRoutingConflicts(): void
Expand Down Expand Up @@ -117,9 +167,9 @@ public function testGeneratesRoutingConflicts(): void
];

$aliasRoutes = [
$this->createMock(PageRoute::class),
$this->createMock(PageRoute::class),
$this->createMock(PageRoute::class),
$this->mockPageRoute('foo'),
$this->mockPageRoute('bar'),
$this->mockPageRoute('baz'),
];

$pageAdapter = $this->mockAdapter(['findWithDetails', 'findSimilarByAlias']);
Expand Down Expand Up @@ -181,17 +231,17 @@ public function testGeneratesRoutingConflicts(): void
'conflicts' => [
[
'page' => $aliasPages[0],
'route' => $aliasRoutes[0],
'path' => 'foo',
'editUrl' => 'editUrl',
],
[
'page' => $aliasPages[1],
'route' => $aliasRoutes[1],
'path' => 'bar',
'editUrl' => 'editUrl',
],
[
'page' => $aliasPages[2],
'route' => $aliasRoutes[2],
'path' => 'baz',
'editUrl' => 'editUrl',
],
],
Expand Down Expand Up @@ -484,4 +534,25 @@ public function testSkipsSimilarPageIfUrlDoesNotMatch(): void

$this->assertSame('', $listener->generateRouteConflicts($dc));
}

/**
* @return PageRoute&MockObject
*/
private function mockPageRoute(string $path, array $requirements = []): PageRoute
{
$route = $this->createMock(PageRoute::class);
$route
->expects($this->once())
->method('getPath')
->willReturn($path)
;

$route
->expects($this->once())
->method('getRequirements')
->willReturn($requirements)
;

return $route;
}
}
53 changes: 45 additions & 8 deletions manager-bundle/src/Command/MaintenanceModeCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
*/
class MaintenanceModeCommand extends Command
{
protected static $defaultName = 'contao:maintenance-mode';

private string $maintenanceFilePath;
private Environment $twig;
private Filesystem $filesystem;
Expand All @@ -42,27 +44,36 @@ public function __construct(string $maintenanceFilePath, Environment $twig, File
protected function configure(): void
{
$this
->setName('contao:maintenance-mode')
->addArgument('state', InputArgument::REQUIRED, 'Use "enable" to enable and "disable" to disable the maintenance mode. If the state is already the desired one, nothing happens. You can also use "on" and "off".')
->addArgument('state', InputArgument::OPTIONAL, 'Use "enable" to enable and "disable" to disable the maintenance mode. If the state is already the desired one, nothing happens. You can also use "on" and "off".')
->addOption('template', 't', InputOption::VALUE_REQUIRED, 'Allows to take a different Twig template name when enabling the maintenance mode.', '@ContaoCore/Error/service_unavailable.html.twig')
->addOption('templateVars', null, InputOption::VALUE_OPTIONAL, 'Add custom template variables to the Twig template when enabling the maintenance mode (provide as JSON).', '{}')
->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, json)', 'txt')
->setDescription('Changes the state of the system maintenance mode.')
;
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$shouldEnable = \in_array($input->getArgument('state'), ['enable', 'on'], true);
$io = new SymfonyStyle($input, $output);
$state = $input->getArgument('state');

if ($shouldEnable) {
if (\in_array($state, ['enable', 'on'], true)) {
$this->enable($input->getOption('template'), $input->getOption('templateVars'));
$io->success('Maintenance mode enabled.');
} else {
$this->outputResult($input, $output, true, true);

return 0;
}

if (\in_array($state, ['disable', 'off'], true)) {
$this->disable();
$io->success('Maintenance mode disabled.');
$this->outputResult($input, $output, false, true);

return 0;
}

$isEnabled = $this->filesystem->exists($this->maintenanceFilePath);

$this->outputResult($input, $output, $isEnabled, false);

return 0;
}

Expand All @@ -86,4 +97,30 @@ private function disable(): void
{
$this->filesystem->remove($this->maintenanceFilePath);
}

private function outputResult(InputInterface $input, OutputInterface $output, bool $enabled, bool $toggled): void
{
if ('json' === $input->getOption('format')) {
$output->writeln(json_encode(
[
'enabled' => $enabled,
'maintenanceFilePath' => $this->maintenanceFilePath,
],
JSON_THROW_ON_ERROR
));

return;
}

$io = new SymfonyStyle($input, $output);
$message = 'Maintenance mode '.($toggled ? '' : 'is ').($enabled ? 'enabled' : 'disabled');

if ($toggled) {
$io->success($message);
} elseif ($enabled) {
$io->note($message);
} else {
$io->info($message);
}
}
}

0 comments on commit 9920305

Please sign in to comment.