diff --git a/.gitignore b/.gitignore index 1be27b4..5a22558 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,3 @@ /vendor -composer.phar composer.lock -.DS_Store -/coverage \ No newline at end of file +.phpunit.result.cache \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 2bf9f76..6b95047 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,12 @@ language: php php: - - 5.6 - - 7.0 - - 7.1 + - 7.2 + - 7.3 before_script: - travis_retry composer self-update - - travis_retry composer install --prefer-dist --no-interaction --dev + - travis_retry composer install --prefer-dist --no-interaction script: - vendor/bin/phpunit diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..750d7d8 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,19 @@ +# Contributing + +There are no strict rules when contributing, but here are my suggestions. + +> I use a combination of [Gitflow Workflow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow) and [Forking Workflow](https://www.atlassian.com/git/tutorials/comparing-workflows/forking-workflow). + +## Steps + +1. Fork the repository. +2. Create a branch depending on the issue that you are working on. *See reference table bellow.* +3. Do you work and commit. +4. Create a Pull Request to the `develop` branch. + +### Branch naming reference + +- `feature` - New functionality or refactoring. +- `bugfix` - Fixes existing code +- `hotfix` - Urgent production fix. Use this if there is a huge bug. +- `support` - Documentation updates & stuff like that. \ No newline at end of file diff --git a/license.md b/LICENSE.md similarity index 96% rename from license.md rename to LICENSE.md index a2a441b..2e0f966 100644 --- a/license.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015-2016 Mario Bašić +Copyright (c) 2015-2019 Mario Bašić Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/README.md b/README.md new file mode 100644 index 0000000..4af068a --- /dev/null +++ b/README.md @@ -0,0 +1,118 @@ +# Ekko + +PHP function for marking navigation items active. + +[![Become a Patron](https://img.shields.io/badge/Become%20a-Patron-f96854.svg?style=for-the-badge)](https://www.patreon.com/laravelista) + +> Starting with version `2.0.0`, there is no backward compatibility with the previous releases. + +The default output value is for [Bootstrap](http://getbootstrap.com), but it can be changed to anything. + +## Features + +- Single function +- Five lines of code +- Framework agnostic +- Supports wildcards & arrays +- Documented +- Tested + +## Installation + +From the command line: + +```bash +composer require laravelista/ekko +``` + +## Overview + +To mark a menu item active in [Bootstrap](http://getbootstrap.com/components/#navbar), you need to add a `active` CSS class to the `
Something that is only visible on the `about` page.
+@endif +``` + +## Usage + +This package consists of only one function `is_active`. The function accepts an `input` which can be a string or an array of strings, and an `output` which can be anything. The default output is `active`. + +### Static URLs + +You will use this for your static (non changing) pages. + +``` +Something that is only visible on the `home` route.
-@endif -``` - -## API - -There are two ways of using Ekko in your application, by using a facade `Ekko::isActiveURL('/about')` or by using a helper function `isActiveURL('/about')` or `is_active_route('/about')`. - -### isActiveRoute, is_active_route - -Compares given route name with current route name. - -```php -isActiveRoute($routeName, $output = "active") -``` - -```php -is_active_route($routeName, $output = "active") -``` - -**Examples:** - -If the current route is `home`, function `isActiveRoute('home')` would return *string* `active`. - -_The `*` wildcard can be used for resource routes._ - -Function `isActiveRoute('user.*')` would return *string* `active` for any current route which begins with `user`. - -### isActiveURL, is_active_url - -Compares given URL path with current URL path. - -```php -isActiveURL($url, $output = "active") -``` - -```php -is_active_url($url, $output = "active") -``` - -**Examples:** - -If the current URL path is `/about`, function `isActiveURL('/about')` would return *string* `active`. - -### isActiveMatch, is_active_match - -Detects if the given string is found in the current URL. - -```php -isActiveMatch($string, $output = "active") -``` - -```php -is_active_match($string, $output = "active") -``` - -**Examples:** - -If the current URL path is `/about` or `/insideout`, function `isActiveMatch('out')` would return *string* `active`. - -### areActiveRoutes, are_active_routes - -Compares given array of route names with current route name. - -```php -areActiveRoutes(array $routeNames, $output = "active") -``` - -```php -are_active_routes(array $routeNames, $output = "active") -``` - -**Examples:** - -If the current route is `product.index` or `product.show`, function `areActiveRoutes(['product.index', 'product.show'])` would return *string* `active`. - -_The `*` wildcard can be used for resource routes, including nested routes._ - -Function `areActiveRoutes(['user.*', 'product.*'])` would return *string* `active` for any current route which begins with `user` or `product`. - -### areActiveURLs, are_active_urls - -Compares given array of URL paths with current URL path. - -```php -areActiveURLs(array $urls, $output = "active") -``` - -```php -are_active_urls(array $urls, $output = "active") -``` - -**Examples:** - -If the current URL path is `/product` or `/product/create`, function `areActiveURLs(['/product', '/product/create'])` would return *string* `active`. - -## Credits - -Many thanks to: - -- [@Jono20201](https://github.com/Jono20201) for implementing helper functions -- [@judgej](https://github.com/judgej) for implementing route wildcards diff --git a/src/Laravelista/Ekko/Ekko.php b/src/Laravelista/Ekko/Ekko.php deleted file mode 100644 index d05a53c..0000000 --- a/src/Laravelista/Ekko/Ekko.php +++ /dev/null @@ -1,111 +0,0 @@ -route = $route; - $this->url = $url; - } - - /** - * Compares given route name with current route name. - * Any section of the route name can be replaced with a * wildcard. - * Example: user.* - * - * @param string $routeName - * @param string $output - * @return boolean - */ - public function isActiveRoute($routeName, $output = "active") - { - if (strpos($routeName, '*') !== false) { - // Quote all RE characters, then undo the quoted '*' characters to match any - // sequence of non-'.' characters. - $regex = '/^' . str_replace(preg_quote('*'), '[^.]*?', preg_quote($routeName, '/')) . '/'; - if (preg_match($regex, $this->route->currentRouteName())) { - return $output; - } - - } elseif ($this->route->currentRouteName() == $routeName) { - return $output; - } - - return null; - } - - /** - * Compares given URL with current URL. - * - * @param string $url - * @param string $output - * @return boolean - */ - public function isActiveURL($url, $output = "active") - { - if ($this->url->current() == $this->url->to($url)) { - return $output; - } - - return null; - } - - /** - * Detects if the given string is found in the current URL. - * - * @param string $string - * @param string $output - * @return boolean - */ - public function isActiveMatch($string, $output = "active") - { - if (strpos($this->url->current(), $string) !== false) { - return $output; - } - - return null; - } - - /** - * Compares given array of route names with current route name. - * - * @param array $routeNames - * @param string $output - * @return boolean - */ - public function areActiveRoutes(array $routeNames, $output = "active") - { - foreach ($routeNames as $routeName) { - if ($this->isActiveRoute($routeName, true)) { - return $output; - } - } - - return null; - } - - /** - * Compares given array of URLs with current URL. - * - * @param array $urls - * @param string $output - * @return boolean - */ - public function areActiveURLs(array $urls, $output = "active") - { - foreach ($urls as $url) { - if ($this->isActiveURL($url, true)) { - return $output; - } - } - - return null; - } - -} diff --git a/src/Laravelista/Ekko/EkkoServiceProvider.php b/src/Laravelista/Ekko/EkkoServiceProvider.php deleted file mode 100644 index 981c338..0000000 --- a/src/Laravelista/Ekko/EkkoServiceProvider.php +++ /dev/null @@ -1,39 +0,0 @@ -app->singleton(Ekko::class, function ($app) { - return new Ekko( - $app['router'], - $app['url'] - ); - }); - } - - /** - * Get the services provided by the provider. - * - * @return array - */ - public function provides() - { - return [Ekko::class]; - } -} diff --git a/src/Laravelista/Ekko/Facades/Ekko.php b/src/Laravelista/Ekko/Facades/Ekko.php deleted file mode 100644 index 2b18302..0000000 --- a/src/Laravelista/Ekko/Facades/Ekko.php +++ /dev/null @@ -1,11 +0,0 @@ -isActiveRoute($routeName, $output); - } -} - -if (!function_exists('is_active_route')) { - /** - * @param $routeName - * @param string $output - * - * @return string - */ - function is_active_route($routeName, $output = "active") - { - return isActiveRoute($routeName, $output); - } -} - -if (!function_exists('isActiveURL')) { - /** - * @param $url - * @param string $output - * - * @return string - */ - function isActiveURL($url, $output = "active") - { - return app(Ekko::class)->isActiveURL($url, $output); - } -} - -if (!function_exists('is_active_url')) { - /** - * @param $url - * @param string $output - * - * @return string - */ - function is_active_url($url, $output = "active") - { - return isActiveURL($url, $output); - } -} - -if (!function_exists('isActiveMatch')) { - /** - * @param $string - * @param string $output - * - * @return string - */ - function isActiveMatch($string, $output = "active") - { - return app(Ekko::class)->isActiveMatch($string, $output); - } -} - -if (!function_exists('is_active_match')) { - /** - * @param $string - * @param string $output - * - * @return string - */ - function is_active_match($string, $output = "active") - { - return isActiveMatch($string, $output); - } -} - -if (!function_exists('areActiveRoutes')) { - /** - * @param array $routeNames - * @param string $output - * - * @return string - */ - function areActiveRoutes(array $routeNames, $output = "active") - { - return app(Ekko::class)->areActiveRoutes($routeNames, $output); - } -} - -if (!function_exists('are_active_routes')) { - /** - * @param array $routeNames - * @param string $output - * - * @return string - */ - function are_active_routes(array $routeNames, $output = "active") - { - return areActiveRoutes($routeNames, $output); - } -} - -if (!function_exists('areActiveURLs')) { - /** - * @param array $urls - * @param string $output - * - * @return string - */ - function areActiveURLs(array $urls, $output = "active") - { - return app(Ekko::class)->areActiveURLs($urls, $output); - } -} - -if (!function_exists('are_active_urls')) { - /** - * @param array $urls - * @param string $output - * - * @return string - */ - function are_active_urls(array $urls, $output = "active") - { - return areActiveURLs($urls, $output); - } -} diff --git a/src/is_active.php b/src/is_active.php new file mode 100644 index 0000000..e0796b3 --- /dev/null +++ b/src/is_active.php @@ -0,0 +1,14 @@ + 0 ? $output : null; + } + + $regex = '/^' . str_replace(preg_quote('*'), '[^.]*?', preg_quote($input, '/')) . '$/'; + + return preg_match($regex, strtok($_SERVER['REQUEST_URI'], '?')) ? $output : null; + } +} diff --git a/tests/.gitkeep b/tests/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/tests/EkkoTest.php b/tests/EkkoTest.php deleted file mode 100644 index 488a4ed..0000000 --- a/tests/EkkoTest.php +++ /dev/null @@ -1,125 +0,0 @@ -shouldReceive('currentRouteName')->times(8)->andReturn('users.index'); - - $url = m::mock(\Illuminate\Routing\UrlGenerator::class); - - $ekko = new Ekko($router, $url); - - $this->assertEquals("active", $ekko->isActiveRoute('users.index')); - $this->assertEquals("hello", $ekko->isActiveRoute('users.index', 'hello')); - $this->assertEquals(null, $ekko->isActiveRoute('clients.index')); - $this->assertEquals(null, $ekko->isActiveRoute('clients.index', 'hello')); - - // Wildcard support - $this->assertEquals("active", $ekko->isActiveRoute('users.*')); - $this->assertEquals("hello", $ekko->isActiveRoute('users.*', 'hello')); - $this->assertEquals(null, $ekko->isActiveRoute('clients.*')); - $this->assertEquals(null, $ekko->isActiveRoute('clients.*', 'hello')); - } - - /** @test */ - public function it_detects_active_url() - { - $router = m::mock(\Illuminate\Routing\Router::class); - - $url = m::mock(\Illuminate\Routing\UrlGenerator::class); - $url->shouldReceive('current')->times(4)->andReturn('/users'); - $url->shouldReceive('to')->times(4)->andReturn('/users', '/users', 'users', '/users/preview'); - - $ekko = new Ekko($router, $url); - - $this->assertEquals("active", $ekko->isActiveURL('/users')); - $this->assertEquals("hello", $ekko->isActiveURL('/users', 'hello')); - $this->assertEquals(null, $ekko->isActiveURL('users')); - $this->assertEquals(null, $ekko->isActiveURL('/users/preview', 'hello')); - } - - /** @test */ - public function it_detects_active_match_in_url() - { - $router = m::mock(\Illuminate\Routing\Router::class); - - $url = m::mock(\Illuminate\Routing\UrlGenerator::class); - $url->shouldReceive('current')->times(4) - ->andReturn('/somewhere-over-the-rainbow'); - - $ekko = new Ekko($router, $url); - - $this->assertEquals("active", $ekko->isActiveMatch('over-the-rainbow')); - $this->assertEquals("hello", $ekko->isActiveMatch('over-the-rainbow', "hello")); - $this->assertEquals(null, $ekko->isActiveMatch('under-the-rainbow')); - $this->assertEquals(null, $ekko->isActiveMatch('under-the-rainbow', 'hello')); - } - - /** @test */ - public function it_detects_active_routes_by_name() - { - $router = m::mock(\Illuminate\Routing\Router::class); - $router->shouldReceive('currentRouteName')->times(8)->andReturn('users.index'); - - $url = m::mock(\Illuminate\Routing\UrlGenerator::class); - - $ekko = new Ekko($router, $url); - - $this->assertEquals("active", $ekko->areActiveRoutes(['users.index'])); - $this->assertEquals("hello", $ekko->areActiveRoutes(['users.index'], 'hello')); - $this->assertEquals(null, $ekko->areActiveRoutes(['clients.index'])); - $this->assertEquals(null, $ekko->areActiveRoutes(['clients.index'], 'hello')); - - // Wildcard support - $this->assertEquals("active", $ekko->areActiveRoutes(['users.*'])); - $this->assertEquals("hello", $ekko->areActiveRoutes(['users.*'], 'hello')); - $this->assertEquals(null, $ekko->areActiveRoutes(['clients.*'])); - $this->assertEquals(null, $ekko->areActiveRoutes(['clients.*'], 'hello')); - } - - /** @test */ - public function it_detects_deep_active_routes_by_name() - { - $router = m::mock(\Illuminate\Routing\Router::class); - $router->shouldReceive('currentRouteName')->times(4)->andReturn('frontend.users.show.stats'); - - $url = m::mock(\Illuminate\Routing\UrlGenerator::class); - - $ekko = new Ekko($router, $url); - - // Wildcard support - $this->assertEquals("active", $ekko->areActiveRoutes(['frontend.users.*'])); - $this->assertEquals("hello", $ekko->areActiveRoutes(['frontend.users.*'], 'hello')); - $this->assertEquals(null, $ekko->areActiveRoutes(['clients.*'])); - $this->assertEquals(null, $ekko->areActiveRoutes(['clients.*'], 'hello')); - } - - /** @test */ - public function it_detects_active_routes_by_url() - { - $router = m::mock(\Illuminate\Routing\Router::class); - - $url = m::mock(\Illuminate\Routing\UrlGenerator::class); - $url->shouldReceive('current')->times(4)->andReturn('/users'); - $url->shouldReceive('to')->times(4)->andReturn('/users', '/users', 'users', '/users/preview'); - - $ekko = new Ekko($router, $url); - - $this->assertEquals("active", $ekko->areActiveURLs(['/users'])); - $this->assertEquals("hello", $ekko->areActiveURLs(['/users'], 'hello')); - $this->assertEquals(null, $ekko->areActiveURLs(['users'])); - $this->assertEquals(null, $ekko->areActiveURLs(['/users/preview'], 'hello')); - } - -} diff --git a/tests/IsActiveTest.php b/tests/IsActiveTest.php new file mode 100644 index 0000000..508cc6d --- /dev/null +++ b/tests/IsActiveTest.php @@ -0,0 +1,46 @@ +assertTrue(is_active('/', true)); + $this->assertTrue(is_active('*', true)); // you can do this, but why? + + $this->assertNull(is_active('/test')); + $this->assertNull(is_active('')); + } + + public function testWildcards() + { + $_SERVER['REQUEST_URI'] = '/user/3/edit'; + + $this->assertTrue(is_active('/user/3/edit', true)); + $this->assertTrue(is_active('/user/*/edit', true)); + $this->assertTrue(is_active('/user/3/*', true)); + $this->assertTrue(is_active('/user/*', true)); + $this->assertTrue(is_active('/user*', true)); + + $this->assertNull(is_active('/user')); + $this->assertNull(is_active('/user/3')); + $this->assertNull(is_active('/user/3/')); + } + + public function testMatches() + { + $_SERVER['REQUEST_URI'] = '/article/a-brown-fox-jumps-over-a-burning-bridge'; + + $this->assertTrue(is_active('*fox*', true)); + $this->assertTrue(is_active('*fox*bridge', true)); + $this->assertTrue(is_active('*bridge', true)); + $this->assertTrue(is_active('/article/*', true)); + + $this->assertNull(is_active('/article')); + $this->assertNull(is_active('/a-brown-fox')); + $this->assertNull(is_active('bridge')); + } +} \ No newline at end of file