diff --git a/publishable/config/larecipe.php b/publishable/config/larecipe.php index 7cef328c..d6f5b4a1 100644 --- a/publishable/config/larecipe.php +++ b/publishable/config/larecipe.php @@ -204,5 +204,16 @@ 'replacement' => '', ] ] - ] + ], + + /* + |-------------------------------------------------------------------------- + | Other Options + |-------------------------------------------------------------------------- + */ + + 'git' => [ + 'enabled' => true, + ], + ]; diff --git a/resources/views/docs.blade.php b/resources/views/docs.blade.php index 57d435e1..0a3fecae 100644 --- a/resources/views/docs.blade.php +++ b/resources/views/docs.blade.php @@ -5,6 +5,15 @@ @include('larecipe::partials.sidebar')
+ @if($authors && !$authors->isEmpty()) +
+ @if(($authorsCount = count($authors)) > 1) + {{$authorsCount}} authors ({{$authors[0]['name']}} and others) + @else + By {{$authors[0]['name']}} + @endif +
+ @endif {!! $content !!} @include('larecipe::plugins.forum')
diff --git a/src/Contracts/GitService.php b/src/Contracts/GitService.php new file mode 100644 index 00000000..69f824c3 --- /dev/null +++ b/src/Contracts/GitService.php @@ -0,0 +1,12 @@ +documentation = $documentation; + $this->gitService = $gitService; $this->docsRoute = route('larecipe.index'); $this->defaultVersion = config('larecipe.versions.default'); @@ -53,6 +57,7 @@ public function get($version, $page = null, $data = []) $this->prepareTitle() ->prepareCanonical() + ->prepareAuthors() ->prepareSection($version, $page); return $this; @@ -120,6 +125,22 @@ protected function prepareCanonical() return $this; } + protected function prepareAuthors() + { + if (!$this->gitService->isGitInstalled() || !config('larecipe.git.enabled')) { + return $this; + } + + $pagePath = base_path(config('larecipe.docs.path').'/'.$this->version.'/'.$this->sectionPage.'.md'); + + $this->authors = $this->gitService + ->getFileShortLog($pagePath) + ->sortByDesc('commits') + ->values(); + + return $this; + } + /** * Check if the given version is in the published versions. * diff --git a/src/Http/Controllers/DocumentationController.php b/src/Http/Controllers/DocumentationController.php index 6657cfce..a129716b 100644 --- a/src/Http/Controllers/DocumentationController.php +++ b/src/Http/Controllers/DocumentationController.php @@ -84,6 +84,7 @@ public function show($version, $page = null) 'versions' => $documentation->publishedVersions, 'currentSection' => $documentation->currentSection, 'canonical' => $documentation->canonical, + 'authors' => $documentation->authors, ], $documentation->statusCode); } } diff --git a/src/LaRecipeServiceProvider.php b/src/LaRecipeServiceProvider.php index 2732295b..b105f2a4 100644 --- a/src/LaRecipeServiceProvider.php +++ b/src/LaRecipeServiceProvider.php @@ -8,9 +8,11 @@ use BinaryTorch\LaRecipe\Commands\ThemeCommand; use BinaryTorch\LaRecipe\Commands\InstallCommand; use BinaryTorch\LaRecipe\Contracts\MarkdownParser; +use BinaryTorch\LaRecipe\Contracts\GitService as GitServiceContract; use BinaryTorch\LaRecipe\Services\ParseDownMarkdownParser; use BinaryTorch\LaRecipe\Facades\LaRecipe as LaRecipeFacade; use BinaryTorch\LaRecipe\Commands\GenerateDocumentationCommand; +use BinaryTorch\LaRecipe\Services\GitService; class LaRecipeServiceProvider extends ServiceProvider { @@ -57,6 +59,7 @@ public function register() } $this->app->bind(MarkdownParser::class, ParseDownMarkdownParser::class); + $this->app->bind(GitServiceContract::class, GitService::class); $this->app->alias('LaRecipe', LaRecipeFacade::class); diff --git a/src/Services/GitService.php b/src/Services/GitService.php new file mode 100644 index 00000000..3bddf024 --- /dev/null +++ b/src/Services/GitService.php @@ -0,0 +1,30 @@ +run(); + + return $process->getExitCode() != 127; + } + + public function getFileShortlog(string $filePath): Collection + { + $process = new Process(['git', 'shortlog', '-sn', 'HEAD', '--', $filePath]); + $process->run(); + + return collect(explode("\n", $process->getOutput()))->slice(0, -1)->map(function ($logLine) { + [$commits, $name] = explode("\t", trim($logLine)); + return compact('commits', 'name'); + }); +; + } +} \ No newline at end of file diff --git a/src/Traits/HasDocumentationAttributes.php b/src/Traits/HasDocumentationAttributes.php index 79ea8fdf..a52937eb 100644 --- a/src/Traits/HasDocumentationAttributes.php +++ b/src/Traits/HasDocumentationAttributes.php @@ -16,6 +16,7 @@ trait HasDocumentationAttributes protected $statusCode = 200; protected $publishedVersions; protected $defaultVersionUrl; + protected $authors; /** * @return string @@ -88,4 +89,12 @@ public function getPublishedVersionsAttribute() { return $this->publishedVersions; } + + /** + * @return array + */ + public function getAuthorsAttribute() + { + return $this->authors; + } } diff --git a/tests/Feature/DisplayGitAuthorsTest.php b/tests/Feature/DisplayGitAuthorsTest.php new file mode 100644 index 00000000..cc5a5c29 --- /dev/null +++ b/tests/Feature/DisplayGitAuthorsTest.php @@ -0,0 +1,93 @@ +get('/docs/1.0') + ->assertOk() + ->assertViewHas('authors', function($authors) { + return is_null($authors); + }); + } + + /** @test */ + public function if_git_is_not_installed_page_will_be_displayed_without_errors() + { + App::instance(GitServiceContract::class, new DummyGitService(false, [])); + + $this->get('/docs/1.0') + ->assertOk() + ->assertViewHas('authors', function($authors) { + return is_null($authors); + }); + } + + /** @test */ + public function if_git_is_installed_but_no_authors_are_found_page_will_be_displayed_without_errors() + { + App::instance(GitServiceContract::class, new DummyGitService(true, [])); + + $response = $this->get('/docs/1.0'); + $response->assertOk(); + $response->assertViewHas('authors', function($authors) { + return $authors->isEmpty(); + }); + } + + /** @test */ + public function check_if_one_author_is_displayed_on_the_view() { + App::instance(GitServiceContract::class, new DummyGitService(true, [ + [ + 'name' => 'The Tester', + 'commits' => 1, + ] + ])); + + $this->get('/docs/1.0') + ->assertOk() + ->assertSee('By The Tester'); + } + + /** @test */ + public function check_if_multiple_authors_are_displayed_on_the_view() { + App::instance(GitServiceContract::class, new DummyGitService(true, [ + [ + 'name' => 'The Tester', + 'commits' => 5, + ], + [ + 'name' => 'Best Contributer', + 'commits' => 10, + ], + [ + 'name' => 'The Documenter', + 'commits' => 7, + ], + ])); + + $this->get('/docs/1.0') + ->assertOk() + ->assertSee('3 authors (Best Contributer and others)'); + } +} diff --git a/tests/Fixtures/DummyGitService.php b/tests/Fixtures/DummyGitService.php new file mode 100644 index 00000000..de06d8d0 --- /dev/null +++ b/tests/Fixtures/DummyGitService.php @@ -0,0 +1,27 @@ +isGitInstalled = $isGitInstalled; + $this->authorsArray = $authorsArray; + } + + public function isGitInstalled(): bool + { + return $this->isGitInstalled; + } + + public function getFileShortlog(string $filePath): Collection + { + return collect($this->authorsArray); + } +} \ No newline at end of file