Skip to content

Commit

Permalink
Merge pull request #435 from hydephp/improve-rss-feed-image-handling
Browse files Browse the repository at this point in the history
Improve RSS image handling and feed and sitemap generation processes
  • Loading branch information
caendesilva authored May 20, 2022
2 parents 8d87a9d + a017c40 commit 0c5f80f
Show file tree
Hide file tree
Showing 13 changed files with 443 additions and 68 deletions.
13 changes: 13 additions & 0 deletions .github/dev-docs/console-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,19 @@ Maybe the most important command is the Build command, which -- you guessed it -

> *Before v0.25.x this option was called `--pretty`
#### Sitemaps and RSS feeds

Sitemaps and RSS feeeds require that you have a `site_url` set, (and that you have not disabled them).

When the features are avaliable the build commnad will generate a sitemap and RSS feed.

You can also rebuild just the sitemap and RSS feed by using their respective commands:

```bash
php hyde build:sitemap
php hyde build:rss
```

### Build a single file
```bash
php hyde rebuild <filepath>
Expand Down
40 changes: 21 additions & 19 deletions .github/dev-docs/troubleshooting.md

Large diffs are not rendered by default.

80 changes: 80 additions & 0 deletions src/Actions/FindsContentLengthForImageObject.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

namespace Hyde\Framework\Actions;

use Hyde\Framework\Contracts\ActionContract;
use Hyde\Framework\Models\Image;
use Illuminate\Support\Facades\Http;
use Symfony\Component\Console\Output\OutputInterface;

/**
* @see \Tests\Feature\FindsContentLengthForImageObjectTest
*/
class FindsContentLengthForImageObject implements ActionContract
{
protected Image $image;

/**
* Testing adding console debug output.
*/
protected OutputInterface $output;

public function __construct(Image $image)
{
$this->image = $image;

$this->output = new \Symfony\Component\Console\Output\ConsoleOutput();
}

public function execute(): int
{
if ($this->isImageStoredRemotely()) {
return $this->fetchRemoteImageInformation();
}

return $this->fetchLocalImageInformation();
}

protected function isImageStoredRemotely(): bool
{
return str_starts_with($this->image->getSource(), 'http');
}

protected function fetchRemoteImageInformation(): int
{
$this->write('<fg=gray> ></> <fg=gray>Fetching remote image information for '.basename($this->image->getSource()).'...</>');

$response = Http::withHeaders([
'User-Agent' => config('hyde.http_user_agent', 'RSS Request Client'),
])->head($this->image->getSource());

$headers = $response->headers();

if (array_key_exists('Content-Length', $headers)) {
return (int) key(array_flip($headers['Content-Length']));
}

$this->write(' > <comment>Warning:</comment> Could not find content length in headers for '.basename($this->image->getSource().'!'));
$this->write(' <fg=gray> Using default content length of 0. '.'</>');
$this->write(' <fg=gray> Is the image path valid? '.($this->image->getSource()).'</>');

return 0;
}

protected function fetchLocalImageInformation(): int
{
if (! file_exists($this->image->getSource())) {
$this->write(' > <comment>Warning:</comment> Could not find image file at '.$this->image->getSource().'!');
$this->write(' <fg=gray> Using default content length of 0. '.'</>');

return 0;
}

return filesize($this->image->getSource());
}

protected function write(string $string): void
{
$this->output->writeln($string);
}
}
65 changes: 65 additions & 0 deletions src/Commands/HydeBuildRssFeedCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

namespace Hyde\Framework\Commands;

use Hyde\Framework\Hyde;
use Hyde\Framework\Services\RssFeedService;
use LaravelZero\Framework\Commands\Command;

/**
* Hyde Command to run the Build Process for the RSS Feed.
*
* @see \Tests\Feature\Commands\HydeBuildRssFeedCommandTest
*/
class HydeBuildRssFeedCommand extends Command
{
/**
* The signature of the command.
*
* @var string
*/
protected $signature = 'build:rss {--no-api : (Not yet supported)}';

/**
* The description of the command.
*
* @var string
*/
protected $description = 'Generate the RSS feed';

/**
* Execute the console command.
*
* @return int
*/
public function handle(): int
{
$actionTime = microtime(true);

if (! $this->runPreflightCheck()) {
return 1;
}

$this->comment('Generating RSS feed...');
file_put_contents(Hyde::getSiteOutputPath(RssFeedService::getDefaultOutputFilename()), RssFeedService::generateFeed());
$this->line(' > Created <info>'.RssFeedService::getDefaultOutputFilename().'</> in '.$this->getExecutionTimeInMs($actionTime)."ms\n");

return 0;
}

protected function runPreflightCheck(): bool
{
if (! RssFeedService::canGenerateFeed()) {
$this->error('Cannot generate an RSS feed, please check your configuration.');

return false;
}

return true;
}

protected function getExecutionTimeInMs(float $timeStart): float
{
return number_format(((microtime(true) - $timeStart) * 1000), 2);
}
}
65 changes: 65 additions & 0 deletions src/Commands/HydeBuildSitemapCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

namespace Hyde\Framework\Commands;

use Hyde\Framework\Hyde;
use Hyde\Framework\Services\SitemapService;
use LaravelZero\Framework\Commands\Command;

/**
* Hyde Command to run the Build Process for the Sitemap.
*
* @see \Tests\Feature\Commands\HydeBuildSitemapCommandTest
*/
class HydeBuildSitemapCommand extends Command
{
/**
* The signature of the command.
*
* @var string
*/
protected $signature = 'build:sitemap';

/**
* The description of the command.
*
* @var string
*/
protected $description = 'Generate the sitemap.xml';

/**
* Execute the console command.
*
* @return int
*/
public function handle(): int
{
$actionTime = microtime(true);

if (! $this->runPreflightCheck()) {
return 1;
}

$this->comment('Generating sitemap...');
file_put_contents(Hyde::getSiteOutputPath('sitemap.xml'), SitemapService::generateSitemap());
$this->line(' > Created <info>sitemap.xml</> in '.$this->getExecutionTimeInMs($actionTime)."ms\n");

return 0;
}

protected function runPreflightCheck(): bool
{
if (! SitemapService::canGenerateSitemap()) {
$this->error('Cannot generate sitemap.xml, please check your configuration.');

return false;
}

return true;
}

protected function getExecutionTimeInMs(float $timeStart): float
{
return number_format(((microtime(true) - $timeStart) * 1000), 2);
}
}
16 changes: 3 additions & 13 deletions src/Commands/HydeBuildStaticSiteCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Hyde\Framework\Services\DiscoveryService;
use Hyde\Framework\Services\RssFeedService;
use Hyde\Framework\Services\SitemapService;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\File;
use LaravelZero\Framework\Commands\Command;
Expand Down Expand Up @@ -136,17 +137,11 @@ public function runPostBuildActions(): void
}

if (SitemapService::canGenerateSitemap()) {
$actionTime = microtime(true);
$this->comment('Generating sitemap...');
file_put_contents(Hyde::getSiteOutputPath('sitemap.xml'), SitemapService::generateSitemap());
$this->line(' > Created <info>sitemap.xml</> in '.$this->getExecutionTimeInMs($actionTime)."ms\n");
Artisan::call('build:sitemap', outputBuffer: $this->output);
}

if (RssFeedService::canGenerateFeed()) {
$actionTime = microtime(true);
$this->comment('Generating RSS feed...');
file_put_contents(Hyde::getSiteOutputPath(RssFeedService::getDefaultOutputFilename()), RssFeedService::generateFeed());
$this->line(' > Created <info>'.RssFeedService::getDefaultOutputFilename().'</> in '.$this->getExecutionTimeInMs($actionTime)."ms\n");
Artisan::call('build:rss', outputBuffer: $this->output);
}
}

Expand Down Expand Up @@ -207,9 +202,4 @@ private function runNodeCommand(string $command, string $message, ?string $actio
$output ?? '<fg=red>Could not '.($actionMessage ?? 'run script').'! Is NPM installed?</>'
);
}

protected function getExecutionTimeInMs(float $timeStart): float
{
return number_format(((microtime(true) - $timeStart) * 1000), 2);
}
}
2 changes: 2 additions & 0 deletions src/HydeServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ function () {
Commands\HydePublishViewsCommand::class,
Commands\HydeRebuildStaticSiteCommand::class,
Commands\HydeBuildStaticSiteCommand::class,
Commands\HydeBuildSitemapCommand::class,
Commands\HydeBuildRssFeedCommand::class,
Commands\HydeMakePostCommand::class,
Commands\HydeMakePageCommand::class,
Commands\HydeValidateCommand::class,
Expand Down
7 changes: 7 additions & 0 deletions src/Models/Image.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Hyde\Framework\Models;

use Hyde\Framework\Actions\FindsContentLengthForImageObject;

/**
* Holds the information for an image.
*/
Expand Down Expand Up @@ -98,6 +100,11 @@ public function getSource(): ?string
return $this->uri ?? $this->path ?? null;
}

public function getContentLength(): int
{
return (new FindsContentLengthForImageObject($this))->execute();
}

public function getImageAuthorAttributionString(): ?string
{
if (isset($this->author)) {
Expand Down
13 changes: 6 additions & 7 deletions src/Services/RssFeedService.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Hyde\Framework\Services;

use Hyde\Framework\Helpers\Features;
use Hyde\Framework\Hyde;
use Hyde\Framework\Models\MarkdownPost;
use SimpleXMLElement;
Expand Down Expand Up @@ -63,13 +64,11 @@ protected function addAdditionalItemData(SimpleXMLElement $item, MarkdownPost $p
$item->addChild('category', $post->category);
}

// Only support local images, as remote images would take extra time to make HTTP requests to get length
// Image front matter must be in the form: _media/image.png
if (isset($post->image) && isset($post->image->path)) {
if (isset($post->image)) {
$image = $item->addChild('enclosure');
$image->addAttribute('url', Hyde::uriPath('media/'.basename($post->image->path)));
$image->addAttribute('type', str_ends_with($post->image->path, '.png') ? 'image/png' : 'image/jpeg');
$image->addAttribute('length', filesize(Hyde::path('_media/'.basename($post->image->path))));
$image->addAttribute('url', isset($post->image->path) ? Hyde::uriPath('media/'.basename($post->image->path)) : $post->image->getSource());
$image->addAttribute('type', str_ends_with($post->image->getSource(), '.png') ? 'image/png' : 'image/jpeg');
$image->addAttribute('length', $post->image->getContentLength());
}
}

Expand Down Expand Up @@ -135,6 +134,6 @@ public static function generateFeed(): string

public static function canGenerateFeed(): bool
{
return (Hyde::uriPath() !== false) && config('hyde.generateRssFeed', true);
return (Hyde::uriPath() !== false) && config('hyde.generateRssFeed', true) && Features::hasBlogPosts();
}
}
Loading

0 comments on commit 0c5f80f

Please sign in to comment.