Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
0.00% |
0 / 59 |
|
0.00% |
0 / 3 |
CRAP | |
0.00% |
0 / 1 |
| RssController | |
0.00% |
0 / 59 |
|
0.00% |
0 / 3 |
30 | |
0.00% |
0 / 1 |
| beforeFilter | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
| viewClasses | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| index | |
0.00% |
0 / 56 |
|
0.00% |
0 / 1 |
12 | |||
| 1 | <?php |
| 2 | declare(strict_types=1); |
| 3 | |
| 4 | namespace App\Controller; |
| 5 | |
| 6 | use App\Utility\SettingsManager; |
| 7 | use Cake\Cache\Cache; |
| 8 | use Cake\Event\EventInterface; |
| 9 | use Cake\Routing\Router; |
| 10 | use Cake\View\XmlView; |
| 11 | |
| 12 | /** |
| 13 | * RssController handles the generation of RSS feeds for the application. |
| 14 | */ |
| 15 | class RssController extends AppController |
| 16 | { |
| 17 | /** |
| 18 | * Before filter method to allow unauthenticated access to the index action. |
| 19 | * |
| 20 | * @param \Cake\Event\EventInterface $event The event object. |
| 21 | * @return void |
| 22 | */ |
| 23 | public function beforeFilter(EventInterface $event): void |
| 24 | { |
| 25 | parent::beforeFilter($event); |
| 26 | $this->Authentication->allowUnauthenticated(['index']); |
| 27 | } |
| 28 | |
| 29 | /** |
| 30 | * Specifies the view classes to be used for rendering the RSS feed. |
| 31 | * |
| 32 | * @return array An array of view class names. |
| 33 | */ |
| 34 | public function viewClasses(): array |
| 35 | { |
| 36 | return [XmlView::class]; |
| 37 | } |
| 38 | |
| 39 | /** |
| 40 | * Generates the RSS feed for the latest articles. |
| 41 | * |
| 42 | * This method fetches published articles, constructs the RSS feed metadata, |
| 43 | * and sets the necessary view options and response headers. |
| 44 | * |
| 45 | * @return void |
| 46 | */ |
| 47 | public function index(): void |
| 48 | { |
| 49 | $currentLang = $this->request->getParam('lang', 'en'); |
| 50 | $siteName = SettingsManager::read('SEO.siteName'); |
| 51 | |
| 52 | $articlesTable = $this->fetchTable('Articles'); |
| 53 | |
| 54 | // Get published articles |
| 55 | $cacheKey = $this->cacheKey; |
| 56 | $articles = Cache::read($cacheKey, 'content'); |
| 57 | if (!$articles) { |
| 58 | $articles = $articlesTable->find('all') |
| 59 | ->select(['id', 'title', 'slug', 'summary', 'created']) |
| 60 | ->where([ |
| 61 | 'kind' => 'article', |
| 62 | 'is_published' => true, |
| 63 | ]) |
| 64 | ->orderByDesc('created') |
| 65 | ->all(); |
| 66 | Cache::write($cacheKey, $articles, 'content'); |
| 67 | } |
| 68 | |
| 69 | $siteUrl = Router::url(['_name' => 'home', 'lang' => $currentLang], true); |
| 70 | |
| 71 | // Build channel metadata |
| 72 | $channelData = [ |
| 73 | 'title' => __('Latest Articles from {0}', $siteName), |
| 74 | 'link' => $siteUrl, |
| 75 | 'description' => __('Latest articles and updates from our website'), |
| 76 | 'language' => $currentLang, |
| 77 | 'copyright' => 'Copyright ' . date('Y') . ' ' . $siteName, |
| 78 | 'generator' => $siteName, |
| 79 | 'docs' => 'https://www.sitemaps.org/protocol.html', |
| 80 | ]; |
| 81 | |
| 82 | // Add image data |
| 83 | $channelData['image'] = [ |
| 84 | 'url' => Router::url('/img/logo.png', true), |
| 85 | 'title' => __('Latest Articles from {0}', $siteName), |
| 86 | 'link' => $siteUrl, |
| 87 | ]; |
| 88 | |
| 89 | // Add items |
| 90 | foreach ($articles as $article) { |
| 91 | $articleUrl = Router::url([ |
| 92 | '_name' => 'article-by-slug', |
| 93 | 'slug' => $article->slug, |
| 94 | 'lang' => $currentLang, |
| 95 | ], true); |
| 96 | |
| 97 | $channelData['item'][] = [ |
| 98 | 'title' => $article->title, |
| 99 | 'link' => $articleUrl, |
| 100 | 'description' => strip_tags($article->summary), |
| 101 | 'pubDate' => $article->created->format('r'), |
| 102 | 'guid' => $articleUrl, |
| 103 | 'category' => __('Articles'), |
| 104 | 'author' => SettingsManager::read('Email.reply_email') . ' (' . $siteName . ')', |
| 105 | ]; |
| 106 | } |
| 107 | |
| 108 | $this->viewBuilder() |
| 109 | ->setOption('rootNode', 'rss') |
| 110 | ->setOption('serialize', ['@version', 'channel']); |
| 111 | |
| 112 | $this->set([ |
| 113 | '@version' => '2.0', |
| 114 | 'channel' => $channelData, |
| 115 | ]); |
| 116 | |
| 117 | // Set response type and cache headers |
| 118 | $this->response = $this->response |
| 119 | ->withType('xml') |
| 120 | ->withHeader('Content-Type', 'text/xml; charset=UTF-8') |
| 121 | ->withCache('-1 minute', '-1 minute'); |
| 122 | } |
| 123 | } |