diff --git a/.github/workflows/coding-style.yml b/.github/workflows/coding-style.yml new file mode 100644 index 000000000..4a708c531 --- /dev/null +++ b/.github/workflows/coding-style.yml @@ -0,0 +1,31 @@ +name: Coding Style + +on: [push, pull_request] + +jobs: + nette_cc: + name: Nette Code Checker + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: shivammathur/setup-php@v1 + with: + php-version: 7.4 + coverage: none + + - run: composer create-project nette/code-checker temp/code-checker ^3 --no-progress + - run: php temp/code-checker/code-checker --strict-types --no-progress + + + nette_cs: + name: Nette Coding Standard + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: shivammathur/setup-php@v1 + with: + php-version: 7.4 + coverage: none + + - run: composer create-project nette/coding-standard temp/coding-standard ^2 --no-progress + - run: php temp/coding-standard/ecs check src tests --config temp/coding-standard/coding-standard-php71.yml diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml new file mode 100644 index 000000000..e129028ce --- /dev/null +++ b/.github/workflows/static-analysis.yml @@ -0,0 +1,21 @@ +name: Static Analysis (only informative) + +on: + push: + branches: + - master + +jobs: + phpstan: + name: PHPStan + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: shivammathur/setup-php@v1 + with: + php-version: 7.4 + coverage: none + + - run: composer install --no-progress --prefer-dist + - run: composer phpstan + continue-on-error: true # is only informative diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 000000000..708879fd6 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,56 @@ +name: Tests + +on: [push, pull_request] + +jobs: + tests: + runs-on: ubuntu-latest + strategy: + matrix: + php: ['7.1', '7.2', '7.3', '7.4'] + + fail-fast: false + + name: PHP ${{ matrix.php }} tests + steps: + - uses: actions/checkout@v2 + - uses: shivammathur/setup-php@v1 + with: + php-version: ${{ matrix.php }} + coverage: none + + - run: composer install --no-progress --prefer-dist + - run: vendor/bin/tester tests -s -C + - if: failure() + run: for i in $(find tests -name \*.actual); do echo "--- $i"; cat $i; echo; echo; done + shell: bash + + + lowest_dependencies: + name: Lowest Dependencies + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: shivammathur/setup-php@v1 + with: + php-version: 7.1 + coverage: none + + - run: composer update --no-progress --prefer-dist --prefer-lowest --prefer-stable + - run: vendor/bin/tester tests -s -C + + + code_coverage: + name: Code Coverage + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: shivammathur/setup-php@v1 + with: + php-version: 7.4 + coverage: none + + - run: composer install --no-progress --prefer-dist + - run: wget https://github.com/satooshi/php-coveralls/releases/download/v1.0.1/coveralls.phar + - run: vendor/bin/tester -p phpdbg tests -s -C --coverage ./coverage.xml --coverage-src ./src + - run: php coveralls.phar --verbose --config tests/.coveralls.yml diff --git a/.travis.yml b/.travis.yml index 67a74fc5c..2f495a05d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,6 +32,7 @@ jobs: - name: Nette Code Checker + php: 7.4 install: - travis_retry composer create-project nette/code-checker temp/code-checker ^3 --no-progress script: @@ -39,6 +40,7 @@ jobs: - name: Nette Coding Standard + php: 7.4 install: - travis_retry composer create-project nette/coding-standard temp/coding-standard ^2 --no-progress script: @@ -51,6 +53,7 @@ jobs: - stage: Code Coverage + php: 7.4 script: - vendor/bin/tester -p phpdbg tests -s --coverage ./coverage.xml --coverage-src ./src after_script: diff --git a/composer.json b/composer.json index 8e7d7fa14..fed58a6a3 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "description": "🏆 Nette Application: a full-stack component-based MVC kernel for PHP that helps you write powerful and modern web applications. Write less, have cleaner code and your work will bring you joy.", "keywords": ["nette", "mvc", "framework", "component-based", "routing", "seo", "mvp", "presenter", "control", "forms"], "homepage": "https://nette.org", - "license": ["BSD-3-Clause", "GPL-2.0", "GPL-3.0"], + "license": ["BSD-3-Clause", "GPL-2.0-only", "GPL-3.0-only"], "authors": [ { "name": "David Grudl", @@ -26,12 +26,12 @@ "latte/latte": "Allows using Latte in templates" }, "require-dev": { - "nette/tester": "^2.0.1", + "nette/tester": "^2.3.1", "nette/di": "^v3.0", "nette/forms": "^3.0", "nette/robot-loader": "^3.2", "nette/security": "^3.0", - "latte/latte": "^2.5.1", + "latte/latte": "^2.6", "tracy/tracy": "^2.6", "mockery/mockery": "^1.0", "phpstan/phpstan-nette": "^0.12" @@ -39,12 +39,11 @@ "conflict": { "nette/di": "<3.0-stable", "nette/forms": "<3.0", - "latte/latte": "<2.4", + "latte/latte": "<2.6", "tracy/tracy": "<2.5" }, "autoload": { - "classmap": ["src/"], - "files": ["src/compatibility.php"] + "classmap": ["src/"] }, "minimum-stability": "dev", "scripts": { diff --git a/src/Application/IPresenterFactory.php b/src/Application/IPresenterFactory.php index c20ce05d1..db2106fae 100644 --- a/src/Application/IPresenterFactory.php +++ b/src/Application/IPresenterFactory.php @@ -15,7 +15,6 @@ */ interface IPresenterFactory { - /** * Generates and checks presenter class name. * @throws InvalidPresenterException diff --git a/src/Application/IResponse.php b/src/Application/IResponse.php index ae3258a3f..1d788449c 100644 --- a/src/Application/IResponse.php +++ b/src/Application/IResponse.php @@ -17,7 +17,6 @@ */ interface IResponse { - /** * Sends response to output. */ diff --git a/src/Application/UI/Component.php b/src/Application/UI/Component.php index 411233a18..f0108eba4 100644 --- a/src/Application/UI/Component.php +++ b/src/Application/UI/Component.php @@ -330,3 +330,6 @@ public function error(string $message = '', int $httpCode = Nette\Http\IResponse throw new Nette\Application\BadRequestException($message, $httpCode); } } + + +class_exists(PresenterComponent::class); diff --git a/src/Application/UI/ComponentReflection.php b/src/Application/UI/ComponentReflection.php index d7523c240..4c472fd5e 100644 --- a/src/Application/UI/ComponentReflection.php +++ b/src/Application/UI/ComponentReflection.php @@ -310,3 +310,6 @@ public static function getClassesAndTraits(string $class): array return $res; } } + + +class_exists(PresenterComponentReflection::class); diff --git a/src/Application/UI/IRenderable.php b/src/Application/UI/IRenderable.php index 5cd114aa1..5c051037e 100644 --- a/src/Application/UI/IRenderable.php +++ b/src/Application/UI/IRenderable.php @@ -15,7 +15,6 @@ */ interface IRenderable { - /** * Forces control to repaint. */ diff --git a/src/Application/UI/IStatePersistent.php b/src/Application/UI/IStatePersistent.php index 9130c6d7a..8112b56cb 100644 --- a/src/Application/UI/IStatePersistent.php +++ b/src/Application/UI/IStatePersistent.php @@ -15,7 +15,6 @@ */ interface IStatePersistent { - /** * Loads state informations. */ diff --git a/src/Application/UI/ITemplate.php b/src/Application/UI/ITemplate.php index 25d74889b..22e7a3eaf 100644 --- a/src/Application/UI/ITemplate.php +++ b/src/Application/UI/ITemplate.php @@ -15,7 +15,6 @@ */ interface ITemplate { - /** * Renders template to output. */ diff --git a/src/Application/UI/Presenter.php b/src/Application/UI/Presenter.php index e1db614b0..a5702b810 100644 --- a/src/Application/UI/Presenter.php +++ b/src/Application/UI/Presenter.php @@ -179,6 +179,13 @@ public function getUniqueId(): string } + public function isModuleCurrent(string $module): bool + { + $current = Helpers::splitName($this->getName())[0]; + return Nette\Utils\Strings::startsWith($current . ':', ltrim($module . ':', ':')); + } + + /********************* interface IPresenter ****************d*g**/ @@ -1061,11 +1068,15 @@ public function restoreRequest(string $key): void } $request = clone $session[$key][1]; unset($session[$key]); - $request->setFlag(Application\Request::RESTORED, true); $params = $request->getParameters(); $params[self::FLASH_KEY] = $this->getFlashKey(); $request->setParameters($params); - $this->sendResponse(new Responses\ForwardResponse($request)); + if ($request->isMethod('POST')) { + $request->setFlag(Application\Request::RESTORED, true); + $this->sendResponse(new Responses\ForwardResponse($request)); + } else { + $this->redirectUrl($this->requestToUrl($request)); + } } diff --git a/src/Bridges/ApplicationLatte/TemplateFactory.php b/src/Bridges/ApplicationLatte/TemplateFactory.php index e34e0fec4..a2ddf66c2 100644 --- a/src/Bridges/ApplicationLatte/TemplateFactory.php +++ b/src/Bridges/ApplicationLatte/TemplateFactory.php @@ -100,6 +100,11 @@ public function createTemplate(UI\Control $control = null): UI\ITemplate }); } + if ($presenter) { + $latte->addFunction('isLinkCurrent', [$presenter, 'isLinkCurrent']); + $latte->addFunction('isModuleCurrent', [$presenter, 'isModuleCurrent']); + } + // default parameters $template->user = $this->user; $template->baseUrl = $this->httpRequest ? rtrim($this->httpRequest->getUrl()->withoutUserInfo()->getBaseUrl(), '/') : null; diff --git a/src/Bridges/ApplicationLatte/UIMacros.php b/src/Bridges/ApplicationLatte/UIMacros.php index 4a6773baf..09730528f 100644 --- a/src/Bridges/ApplicationLatte/UIMacros.php +++ b/src/Bridges/ApplicationLatte/UIMacros.php @@ -114,7 +114,7 @@ public function macroControl(MacroNode $node, PhpWriter $writer) public function macroLink(MacroNode $node, PhpWriter $writer) { $node->modifiers = preg_replace('#\|safeurl\s*(?=\||$)#Di', '', $node->modifiers); - return $writer->using($node) + return $writer->using($node, $this->getCompiler()) ->write('echo %escape(%modify(' . ($node->name === 'plink' ? '$this->global->uiPresenter' : '$this->global->uiControl') . '->link(%node.word, %node.array?)))' diff --git a/src/Bridges/ApplicationTracy/RoutingPanel.php b/src/Bridges/ApplicationTracy/RoutingPanel.php index 449eeba6a..7cf1a8ff2 100644 --- a/src/Bridges/ApplicationTracy/RoutingPanel.php +++ b/src/Bridges/ApplicationTracy/RoutingPanel.php @@ -99,8 +99,10 @@ public function getPanel(): string private function analyse(Nette\Routing\Router $router, string $module = ''): void { if ($router instanceof Routers\RouteList) { - foreach ($router as $subRouter) { - $this->analyse($subRouter, $module . $router->getModule()); + if ($router->match($this->httpRequest)) { + foreach ($router as $subRouter) { + $this->analyse($subRouter, $module . $router->getModule()); + } } return; } diff --git a/src/compatibility.php b/src/compatibility.php index 587f20b39..cf55aa4bc 100644 --- a/src/compatibility.php +++ b/src/compatibility.php @@ -7,5 +7,22 @@ declare(strict_types=1); -class_alias(Nette\Application\UI\Component::class, Nette\Application\UI\PresenterComponent::class); -class_alias(Nette\Application\UI\ComponentReflection::class, Nette\Application\UI\PresenterComponentReflection::class); +namespace Nette\Application\UI; + +if (false) { + /** @deprecated use Nette\Application\UI\Component */ + class PresenterComponent + { + } +} elseif (!class_exists(PresenterComponent::class)) { + class_alias(Component::class, PresenterComponent::class); +} + +if (false) { + /** @deprecated use Nette\Application\UI\ComponentReflection */ + class PresenterComponentReflection + { + } +} elseif (!class_exists(PresenterComponentReflection::class)) { + class_alias(ComponentReflection::class, PresenterComponentReflection::class); +} diff --git a/tests/Bridges.Latte/UIMacros.isLinkCurrent.phpt b/tests/Bridges.Latte/UIMacros.isLinkCurrent.phpt index 79ea6e5c6..23bc3c157 100644 --- a/tests/Bridges.Latte/UIMacros.isLinkCurrent.phpt +++ b/tests/Bridges.Latte/UIMacros.isLinkCurrent.phpt @@ -6,16 +6,28 @@ declare(strict_types=1); -use Nette\Bridges\ApplicationLatte\UIMacros; use Tester\Assert; require __DIR__ . '/../bootstrap.php'; +Tester\Environment::bypassFinals(); + $latte = new Latte\Engine; + +$latteFactory = Mockery::mock(Nette\Bridges\ApplicationLatte\ILatteFactory::class); +$latteFactory->shouldReceive('create')->andReturn($latte); + +$presenter = Mockery::mock(Nette\Application\UI\Presenter::class); +$presenter->shouldReceive('getPresenterIfExists')->andReturn($presenter); +$presenter->shouldReceive('getHttpResponse')->andReturn((Mockery::mock(Nette\Http\IResponse::class))->shouldIgnoreMissing()); +$presenter->shouldIgnoreMissing(); + +$factory = new Nette\Bridges\ApplicationLatte\TemplateFactory($latteFactory); +$factory->createTemplate($presenter); + $latte->setLoader(new Latte\Loaders\StringLoader); -UIMacros::install($latte->getCompiler()); Assert::matchFile(__DIR__ . '/expected/UIMacros.isLinkCurrent.phtml', $latte->compile( 'n:href before n:class @@ -29,4 +41,6 @@ Assert::matchFile(__DIR__ . '/expected/UIMacros.isLinkCurrent.phtml', $latte->co {ifCurrent}empty{/ifCurrent} {ifCurrent default}default{/ifCurrent} + +custom function ')); diff --git a/tests/Bridges.Latte/UIMacros.link.2.phpt b/tests/Bridges.Latte/UIMacros.link.2.phpt index 3e4a50c0a..80243c381 100644 --- a/tests/Bridges.Latte/UIMacros.link.2.phpt +++ b/tests/Bridges.Latte/UIMacros.link.2.phpt @@ -117,4 +117,4 @@ EOD EOD -, $params), [''' => "'"])); +, $params), [''' => "'", ''' => "'"])); diff --git a/tests/Bridges.Latte/UIMacros.renderSnippets5.phpt b/tests/Bridges.Latte/UIMacros.renderSnippets5.phpt new file mode 100644 index 000000000..242d37d0f --- /dev/null +++ b/tests/Bridges.Latte/UIMacros.renderSnippets5.phpt @@ -0,0 +1,64 @@ +setLoader(new Latte\Loaders\StringLoader); + UIMacros::install($latte->getCompiler()); + $latte->addProvider('uiControl', $this); + $latte->addProvider('snippetBridge', new SnippetBridge($this)); + $latte->render($template, ['presenter' => $this]); + } +} + + +$presenter = new TestPresenter; +$presenter->snippetMode = true; +$presenter->redrawControl('foo'); +$presenter->render('