553 lines
24 KiB
PHP
Executable File
553 lines
24 KiB
PHP
Executable File
<?php
|
|
|
|
namespace Modules\Seo\Controllers;
|
|
|
|
use App\Controllers\BaseAdminController;
|
|
use Kernel\View;
|
|
use Symfony\Component\HttpFoundation\Request;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
use App\Models\Module;
|
|
use Modules\Seo\Models\Redirect;
|
|
use Illuminate\Database\Capsule\Manager as DB;
|
|
use Phpml\Tokenization\WhitespaceTokenizer;
|
|
use Phpml\FeatureExtraction\TfIdfTransformer;
|
|
use Phpml\FeatureExtraction\TokenCountVectorizer;
|
|
|
|
class SeoController extends BaseAdminController
|
|
{
|
|
private $moduleName = 'seo';
|
|
|
|
/**
|
|
* Display SEO settings.
|
|
*
|
|
* @param Request $request
|
|
* @return Response
|
|
*/
|
|
public function index(Request $request): Response
|
|
{
|
|
$user = $this->getCurrentUser();
|
|
if (!$user || !$user->hasPermission('seo.view')) {
|
|
return new Response($this->view->render('components/alerts.twig', [
|
|
'type' => 'danger',
|
|
'message' => 'Access denied.'
|
|
]), 403);
|
|
}
|
|
|
|
$module = Module::where('modules_name', $this->moduleName)->first();
|
|
if (!$module) {
|
|
return new Response('Module not found', 404);
|
|
}
|
|
|
|
$settings = $module->modules_settings ?: [];
|
|
$redirects = Redirect::orderBy('created_at', 'desc')->get();
|
|
|
|
return $this->renderAdminView('@seo/admin/index.twig', [
|
|
'page_title' => 'SEO Settings',
|
|
'settings' => $settings,
|
|
'redirects' => $redirects
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Save SEO settings.
|
|
*
|
|
* @param Request $request
|
|
* @return Response
|
|
*/
|
|
public function save(Request $request): Response
|
|
{
|
|
try {
|
|
$user = $this->getCurrentUser();
|
|
if (!$user || !$user->hasPermission('seo.manage')) {
|
|
return new Response($this->view->render('components/alerts.twig', [
|
|
'type' => 'danger',
|
|
'message' => 'Access denied.'
|
|
]), 403);
|
|
}
|
|
|
|
$module = Module::where('modules_name', $this->moduleName)->first();
|
|
if (!$module) {
|
|
$this->addFlash('error', 'Módulo SEO não encontrado no banco de dados.');
|
|
return $this->redirect('/admin/seo');
|
|
}
|
|
|
|
$data = $request->request->all();
|
|
$settings = $module->modules_settings ?: [];
|
|
|
|
// Handle image upload
|
|
if ($request->files->has('og_image_file')) {
|
|
$file = $request->files->get('og_image_file');
|
|
if ($file && $file->isValid()) {
|
|
$uploadDir = dirname(__DIR__, 4) . '/storage/uploads/seo';
|
|
if (!is_dir($uploadDir)) {
|
|
mkdir($uploadDir, 0755, true);
|
|
}
|
|
|
|
// Delete old image
|
|
if (!empty($settings['og_image'])) {
|
|
$oldPath = $uploadDir . '/' . $settings['og_image'];
|
|
if (file_exists($oldPath) && is_file($oldPath)) {
|
|
@unlink($oldPath);
|
|
}
|
|
}
|
|
|
|
$filename = 'og_default_' . uniqid() . '.' . $file->guessExtension();
|
|
$file->move($uploadDir, $filename);
|
|
$settings['og_image'] = $filename;
|
|
}
|
|
}
|
|
|
|
$fields = [
|
|
'meta_title', 'meta_description', 'meta_keywords',
|
|
'og_title', 'og_description',
|
|
'twitter_card', 'robots_txt', 'google_analytics_id', 'google_site_verification',
|
|
'json_ld_type', 'json_ld_name', 'json_ld_description', 'json_ld_url',
|
|
'json_ld_contact_phone', 'json_ld_contact_email', 'json_ld_address',
|
|
'json_ld_city', 'json_ld_state', 'json_ld_zip', 'json_ld_country',
|
|
'json_ld_social_links',
|
|
'sitemap_freq_static', 'sitemap_prio_static',
|
|
'sitemap_freq_blog', 'sitemap_prio_blog',
|
|
'sitemap_freq_news', 'sitemap_prio_news',
|
|
'sitemap_freq_guide', 'sitemap_prio_guide',
|
|
'sitemap_freq_events', 'sitemap_prio_events',
|
|
'sitemap_freq_faq', 'sitemap_prio_faq',
|
|
'sitemap_freq_expositor', 'sitemap_prio_expositor',
|
|
'sitemap_freq_albums', 'sitemap_prio_albums',
|
|
'sitemap_freq_pages', 'sitemap_prio_pages',
|
|
'sitemap_freq_photoessays', 'sitemap_prio_photoessays'
|
|
];
|
|
|
|
foreach ($fields as $field) {
|
|
$settings[$field] = $data[$field] ?? null;
|
|
}
|
|
|
|
// Handle checkboxes explicitly
|
|
$checkboxes = [
|
|
'sitemap_include_static', 'sitemap_include_blog', 'sitemap_include_news',
|
|
'sitemap_include_guide', 'sitemap_include_events', 'sitemap_include_faq',
|
|
'sitemap_include_expositor', 'sitemap_include_albums',
|
|
'sitemap_include_pages', 'sitemap_include_photoessays'
|
|
];
|
|
foreach ($checkboxes as $cb) {
|
|
$settings[$cb] = isset($data[$cb]) ? '1' : '0';
|
|
}
|
|
|
|
$module->modules_settings = $settings;
|
|
$module->save();
|
|
|
|
// Also update physical robots.txt if requested
|
|
if (isset($data['update_robots_file']) && $data['update_robots_file'] == '1') {
|
|
$this->updateRobotsFile($settings['robots_txt'] ?? '');
|
|
}
|
|
|
|
$this->addFlash('success', __('seo.save_success'));
|
|
} catch (\Exception $e) {
|
|
\App\Helpers\Log::error('Error saving SEO settings: ' . $e->getMessage());
|
|
$this->addFlash('error', 'Erro ao salvar: ' . $e->getMessage());
|
|
}
|
|
|
|
return $this->redirect('/admin/seo');
|
|
}
|
|
|
|
/**
|
|
* Generate SEO suggestions using local PHP-ML.
|
|
*/
|
|
public function generateAi(Request $request): Response
|
|
{
|
|
try {
|
|
$user = $this->getCurrentUser();
|
|
if (!$user || !$user->hasPermission('seo.manage')) {
|
|
return new Response(json_encode(['success' => false, 'error' => 'Access denied']), 403, ['Content-Type' => 'application/json']);
|
|
}
|
|
|
|
$corpus = [];
|
|
|
|
// 1. Gather text from various modules to analyze
|
|
if (DB::schema()->hasTable('blog_posts')) {
|
|
$corpus[] = DB::table('blog_posts')->where('posts_status', 'published')->pluck('posts_title')->implode(' ');
|
|
$corpus[] = DB::table('blog_posts')->where('posts_status', 'published')->pluck('posts_content')->implode(' ');
|
|
}
|
|
|
|
if (DB::schema()->hasTable('news')) {
|
|
$corpus[] = DB::table('news')->where('news_status', 'published')->pluck('news_title')->implode(' ');
|
|
}
|
|
|
|
if (DB::schema()->hasTable('commercialguide_businesses')) {
|
|
$corpus[] = DB::table('commercialguide_businesses')->where('businesses_status', 'approved')->pluck('businesses_name')->implode(' ');
|
|
}
|
|
|
|
if (DB::schema()->hasTable('pages')) {
|
|
$corpus[] = DB::table('pages')->where('pages_is_published', 1)->pluck('pages_title')->implode(' ');
|
|
$corpus[] = DB::table('pages')->where('pages_is_published', 1)->pluck('pages_content')->implode(' ');
|
|
}
|
|
|
|
$allText = strtolower(strip_tags(implode(' ', $corpus)));
|
|
$allText = preg_replace('/[[:punct:]]/', '', $allText); // Remove punctuation
|
|
|
|
// Stop words in PT-BR
|
|
$stopWords = ['de', 'a', 'o', 'que', 'e', 'do', 'da', 'em', 'um', 'para', 'com', 'no', 'na', 'uma', 'os', 'as', 'dos', 'das', 'seu', 'sua', 'ou', 'quando', 'muito', 'nos', 'já', 'eu', 'também', 'só', 'pelo', 'pela', 'até', 'isso', 'ela', 'entre', 'depois', 'sem', 'mesmo', 'aos', 'seus', 'quem', 'nas', 'me', 'esse', 'eles', 'você', 'essa', 'num', 'nem', 'suas', 'meu', 'às', 'minha', 'numa', 'pelos', 'elas', 'qual', 'nós', 'lhe', 'deles', 'essas', 'esses', 'pelas', 'este', 'dele', 'tu', 'te', 'vocês', 'vos', 'lhes', 'meus', 'minhas', 'teu', 'tua', 'teus', 'tuas', 'nosso', 'nossa', 'nossos', 'nossas', 'está', 'estão', 'estivemos', 'estiveram', 'estava', 'estávamos', 'estavam', 'estivera', 'estivéramos', 'esteja', 'estejamos', 'estejam', 'estivesse', 'estivéssemos', 'estivessem', 'estiver', 'estivermos', 'estiverem', 'tenho', 'tem', 'temos', 'têm', 'tinha', 'tínhamos', 'tinham', 'tive', 'teve', 'tivemos', 'tiveram', 'tivera', 'tivéramos', 'tenha', 'tenhamos', 'tenham', 'tivesse', 'tivéssemos', 'tivessem', 'tiver', 'tivermos', 'tiverem', 'houve', 'houvemos', 'houveram', 'houvera', 'houvéramos', 'haja', 'hajamos', 'hajam', 'houvesse', 'houvéssemos', 'houvessem', 'houver', 'houvermos', 'houverem', 'como', 'mais', 'foi', 'foram', 'sou', 'somos', 'são', 'era', 'éramos', 'eram', 'seja', 'sejamos', 'sejam', 'fosse', 'fôssemos', 'fossem', 'for', 'formos', 'forem', 'seria', 'seríamos', 'seriam', 'será', 'serão', 'tenho', 'tem', 'temos', 'têm', 'tinha', 'tínhamos', 'tinham', 'tive', 'teve', 'tivemos', 'tiveram', 'tivera', 'tivéramos', 'tenha', 'tenhamos', 'tenham', 'tivesse', 'tivéssemos', 'tivessem', 'tiver', 'tivermos', 'tiverem', 'houve', 'houvemos', 'houveram', 'houvera', 'houvéramos', 'haja', 'hajamos', 'hajam', 'houvesse', 'houvéssemos', 'houvessem', 'houver', 'houvermos', 'houverem', 'mais', 'menos', 'muito', 'tudo', 'nada', 'sempre', 'nunca', 'talvez', 'ainda', 'assim', 'apenas', 'quase', 'então', 'entanto', 'pode', 'podem', 'poderia', 'deveria', 'querer', 'fazer', 'ir', 'ver', 'dar', 'vir', 'saber', 'parecer', 'falar', 'dizer'];
|
|
|
|
// Tokenize and count
|
|
$tokenizer = new WhitespaceTokenizer();
|
|
$tokens = $tokenizer->tokenize($allText);
|
|
$tokens = array_filter($tokens, function($t) use ($stopWords) {
|
|
return strlen($t) > 3 && !in_array($t, $stopWords);
|
|
});
|
|
|
|
$counts = array_count_values($tokens);
|
|
arsort($counts);
|
|
|
|
$topKeywords = array_slice(array_keys($counts), 0, 10);
|
|
$topThree = array_slice($topKeywords, 0, 3);
|
|
|
|
$siteName = $_SESSION['bus_name'] ?? 'SysCMS';
|
|
|
|
if (empty($topThree)) {
|
|
$suggestedTitle = $siteName . " - Portal de Conteúdo";
|
|
$suggestedDesc = "Bem-vindo ao portal " . $siteName . ". Aqui você encontra as melhores informações, notícias e serviços da nossa região.";
|
|
$topKeywords = ['portal', 'noticias', 'serviços', 'empresas', 'eventos'];
|
|
} else {
|
|
$suggestedTitle = ucwords(implode(' | ', $topThree)) . ' - ' . $siteName;
|
|
$suggestedDesc = "Confira o melhor em " . implode(', ', array_slice($topKeywords, 0, 5)) . " no portal " . $siteName . ". Conteúdo atualizado, guia de empresas, eventos e muito mais!";
|
|
}
|
|
|
|
$sitemapUrl = url('/sitemap.xml');
|
|
$suggestedRobots = "# Gerado automaticamente via IA do SysCMS\n";
|
|
$suggestedRobots .= "User-agent: *\n";
|
|
$suggestedRobots .= "Allow: /\n";
|
|
$suggestedRobots .= "Disallow: /admin/\n";
|
|
$suggestedRobots .= "Disallow: /login\n";
|
|
$suggestedRobots .= "Disallow: /register\n";
|
|
$suggestedRobots .= "Disallow: /api/\n";
|
|
$suggestedRobots .= "Disallow: /vendor/\n";
|
|
$suggestedRobots .= "Disallow: /storage/logs/\n";
|
|
$suggestedRobots .= "\nSitemap: {$sitemapUrl}";
|
|
|
|
\App\Helpers\Log::info('SEO AI suggestions generated successfully');
|
|
|
|
return new Response(json_encode([
|
|
'success' => true,
|
|
'title' => $suggestedTitle,
|
|
'description' => $suggestedDesc,
|
|
'keywords' => implode(', ', $topKeywords),
|
|
'robots_txt' => $suggestedRobots
|
|
]), 200, ['Content-Type' => 'application/json']);
|
|
} catch (\Exception $e) {
|
|
\App\Helpers\Log::error('AI Generation error: ' . $e->getMessage());
|
|
return new Response(json_encode(['success' => false, 'error' => $e->getMessage()]), 500, ['Content-Type' => 'application/json']);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add a redirect.
|
|
*/
|
|
public function addRedirect(Request $request): Response
|
|
{
|
|
$user = $this->getCurrentUser();
|
|
if (!$user || !$user->hasPermission('seo.manage')) {
|
|
return new Response('Access denied', 403);
|
|
}
|
|
|
|
$from = $request->request->get('redirects_from');
|
|
$to = $request->request->get('redirects_to');
|
|
$code = (int) $request->request->get('redirects_code', 301);
|
|
|
|
if ($from && $to) {
|
|
Redirect::create([
|
|
'redirects_from' => $from,
|
|
'redirects_to' => $to,
|
|
'redirects_code' => $code,
|
|
'redirects_is_active' => true
|
|
]);
|
|
$this->addFlash('success', __('seo.redirect_added'));
|
|
}
|
|
|
|
return $this->redirect('admin/seo#redirects');
|
|
}
|
|
|
|
/**
|
|
* Delete a redirect.
|
|
*/
|
|
public function deleteRedirect(Request $request, $id): Response
|
|
{
|
|
$user = $this->getCurrentUser();
|
|
if (!$user || !$user->hasPermission('seo.manage')) {
|
|
return new Response('Access denied', 403);
|
|
}
|
|
|
|
$redirect = Redirect::find($id);
|
|
if ($redirect) {
|
|
$redirect->delete();
|
|
$this->addFlash('success', __('seo.redirect_deleted'));
|
|
}
|
|
|
|
return $this->redirect('admin/seo#redirects');
|
|
}
|
|
|
|
/**
|
|
* Update physical robots.txt file in public directory.
|
|
*/
|
|
private function updateRobotsFile($content)
|
|
{
|
|
$path = dirname(__DIR__, 4) . '/public/robots.txt';
|
|
@file_put_contents($path, $content);
|
|
}
|
|
|
|
/**
|
|
* User Manual for SEO.
|
|
*/
|
|
public function manual(Request $request): Response
|
|
{
|
|
return $this->renderAdminView('seo/views/admin/manual.twig', [
|
|
'title' => 'Manual do Usuário - SEO'
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Generate Sitemap XML.
|
|
* This is a public route.
|
|
*/
|
|
public function sitemap(Request $request): Response
|
|
{
|
|
$module = Module::where('modules_name', $this->moduleName)->first();
|
|
$settings = $module->modules_settings ?: [];
|
|
|
|
$xml = '<?xml version="1.0" encoding="UTF-8"?>';
|
|
$xml .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
|
|
|
|
// Static Pages
|
|
if (($settings['sitemap_include_static'] ?? '1') == '1') {
|
|
$freq = $settings['sitemap_freq_static'] ?? 'weekly';
|
|
$prio = $settings['sitemap_prio_static'] ?? '1.0';
|
|
|
|
$urls = [
|
|
url('/'),
|
|
url('/login'),
|
|
url('/register'),
|
|
url('/about'),
|
|
url('/contact'),
|
|
url('/services')
|
|
];
|
|
|
|
foreach ($urls as $u) {
|
|
$xml .= '<url>';
|
|
$xml .= '<loc>' . htmlspecialchars($u) . '</loc>';
|
|
$xml .= '<changefreq>' . $freq . '</changefreq>';
|
|
$xml .= '<priority>' . $prio . '</priority>';
|
|
$xml .= '</url>';
|
|
}
|
|
}
|
|
|
|
// Blog Posts (if module exists and enabled)
|
|
if (($settings['sitemap_include_blog'] ?? '1') == '1' && DB::schema()->hasTable('blog_posts')) {
|
|
$freq = $settings['sitemap_freq_blog'] ?? 'daily';
|
|
$prio = $settings['sitemap_prio_blog'] ?? '0.8';
|
|
|
|
$posts = DB::table('blog_posts')
|
|
->where('posts_status', 'published')
|
|
->orderBy('created_at', 'desc')
|
|
->get();
|
|
|
|
foreach ($posts as $post) {
|
|
$xml .= '<url>';
|
|
$xml .= '<loc>' . htmlspecialchars(url('blog/' . $post->posts_slug)) . '</loc>';
|
|
$xml .= '<lastmod>' . date('Y-m-d', strtotime($post->posts_published_at ?: ($post->updated_at ?: $post->created_at))) . '</lastmod>';
|
|
$xml .= '<changefreq>' . $freq . '</changefreq>';
|
|
$xml .= '<priority>' . $prio . '</priority>';
|
|
$xml .= '</url>';
|
|
}
|
|
}
|
|
|
|
// News (if module exists and enabled)
|
|
if (($settings['sitemap_include_news'] ?? '1') == '1' && DB::schema()->hasTable('news')) {
|
|
$freq = $settings['sitemap_freq_news'] ?? 'daily';
|
|
$prio = $settings['sitemap_prio_news'] ?? '0.7';
|
|
|
|
$news = DB::table('news')
|
|
->where('news_status', 'published')
|
|
->orderBy('created_at', 'desc')
|
|
->get();
|
|
|
|
foreach ($news as $item) {
|
|
$xml .= '<url>';
|
|
$xml .= '<loc>' . htmlspecialchars(url('news/show/' . $item->news_slug)) . '</loc>';
|
|
$xml .= '<lastmod>' . date('Y-m-d', strtotime($item->news_published_at ?: ($item->updated_at ?: $item->created_at))) . '</lastmod>';
|
|
$xml .= '<changefreq>' . $freq . '</changefreq>';
|
|
$xml .= '<priority>' . $prio . '</priority>';
|
|
$xml .= '</url>';
|
|
}
|
|
}
|
|
|
|
// Commercial Guide (if enabled)
|
|
if (($settings['sitemap_include_guide'] ?? '1') == '1' && DB::schema()->hasTable('commercialguide_businesses')) {
|
|
$freq = $settings['sitemap_freq_guide'] ?? 'monthly';
|
|
$prio = $settings['sitemap_prio_guide'] ?? '0.7';
|
|
|
|
$businesses = DB::table('commercialguide_businesses')
|
|
->where('businesses_status', 'approved')
|
|
->get();
|
|
|
|
foreach ($businesses as $b) {
|
|
$xml .= '<url>';
|
|
$xml .= '<loc>' . htmlspecialchars(url('commercialguide/profile/' . $b->businesses_slug)) . '</loc>';
|
|
$xml .= '<lastmod>' . date('Y-m-d', strtotime($b->updated_at ?: $b->created_at)) . '</lastmod>';
|
|
$xml .= '<changefreq>' . $freq . '</changefreq>';
|
|
$xml .= '<priority>' . $prio . '</priority>';
|
|
$xml .= '</url>';
|
|
}
|
|
}
|
|
|
|
// Events Guide (if enabled)
|
|
if (($settings['sitemap_include_events'] ?? '1') == '1' && DB::schema()->hasTable('eventsguide_events')) {
|
|
$freq = $settings['sitemap_freq_events'] ?? 'daily';
|
|
$prio = $settings['sitemap_prio_events'] ?? '0.7';
|
|
|
|
$events = DB::table('eventsguide_events')
|
|
->where('events_status', 'approved')
|
|
->get();
|
|
|
|
foreach ($events as $e) {
|
|
$xml .= '<url>';
|
|
$xml .= '<loc>' . htmlspecialchars(url('eventsguide/show/' . $e->events_slug)) . '</loc>';
|
|
$xml .= '<lastmod>' . date('Y-m-d', strtotime($e->updated_at ?: $e->created_at)) . '</lastmod>';
|
|
$xml .= '<changefreq>' . $freq . '</changefreq>';
|
|
$xml .= '<priority>' . $prio . '</priority>';
|
|
$xml .= '</url>';
|
|
}
|
|
}
|
|
|
|
// FAQ (if enabled)
|
|
if (($settings['sitemap_include_faq'] ?? '1') == '1' && DB::schema()->hasTable('faqs')) {
|
|
$freq = $settings['sitemap_freq_faq'] ?? 'monthly';
|
|
$prio = $settings['sitemap_prio_faq'] ?? '0.5';
|
|
|
|
$faqs = DB::table('faqs')
|
|
->where('faqs_active', 1)
|
|
->where('faqs_visibility', 'public')
|
|
->get();
|
|
|
|
if (count($faqs) > 0) {
|
|
$xml .= '<url>';
|
|
$xml .= '<loc>' . htmlspecialchars(url('faq')) . '</loc>';
|
|
$xml .= '<changefreq>' . $freq . '</changefreq>';
|
|
$xml .= '<priority>' . $prio . '</priority>';
|
|
$xml .= '</url>';
|
|
}
|
|
}
|
|
|
|
// Expositor / Vitrine (if enabled)
|
|
if (($settings['sitemap_include_expositor'] ?? '1') == '1' && DB::schema()->hasTable('expositor_items')) {
|
|
$freq = $settings['sitemap_freq_expositor'] ?? 'daily';
|
|
$prio = $settings['sitemap_prio_expositor'] ?? '0.8';
|
|
|
|
$items = DB::table('expositor_items')
|
|
->where('items_status', 'published')
|
|
->where('items_visibility', 'public')
|
|
->get();
|
|
|
|
foreach ($items as $item) {
|
|
$xml .= '<url>';
|
|
$xml .= '<loc>' . htmlspecialchars(url('vitrine/show/' . $item->items_slug)) . '</loc>';
|
|
$xml .= '<lastmod>' . date('Y-m-d', strtotime($item->updated_at ?: $item->created_at)) . '</lastmod>';
|
|
$xml .= '<changefreq>' . $freq . '</changefreq>';
|
|
$xml .= '<priority>' . $prio . '</priority>';
|
|
$xml .= '</url>';
|
|
}
|
|
|
|
// Expositor Categories
|
|
if (DB::schema()->hasTable('expositor_categories')) {
|
|
$categories = DB::table('expositor_categories')->get();
|
|
foreach ($categories as $cat) {
|
|
$xml .= '<url>';
|
|
$xml .= '<loc>' . htmlspecialchars(url('vitrine/category/' . $cat->categories_slug)) . '</loc>';
|
|
$xml .= '<changefreq>weekly</changefreq>';
|
|
$xml .= '<priority>0.5</priority>';
|
|
$xml .= '</url>';
|
|
}
|
|
}
|
|
}
|
|
|
|
// Photo Albums (if enabled)
|
|
if (($settings['sitemap_include_albums'] ?? '1') == '1' && DB::schema()->hasTable('photos_albums')) {
|
|
$freq = $settings['sitemap_freq_albums'] ?? 'monthly';
|
|
$prio = $settings['sitemap_prio_albums'] ?? '0.6';
|
|
|
|
$albums = DB::table('photos_albums')
|
|
->where('photos_albums_status', 'published')
|
|
->where('photos_albums_visibility', 'public')
|
|
->get();
|
|
|
|
foreach ($albums as $album) {
|
|
$xml .= '<url>';
|
|
$xml .= '<loc>' . htmlspecialchars(url('photosalbum/show/' . $album->photos_albums_slug)) . '</loc>';
|
|
$xml .= '<lastmod>' . date('Y-m-d', strtotime($album->updated_at ?: $album->created_at)) . '</lastmod>';
|
|
$xml .= '<changefreq>' . $freq . '</changefreq>';
|
|
$xml .= '<priority>' . $prio . '</priority>';
|
|
$xml .= '</url>';
|
|
}
|
|
}
|
|
|
|
// Custom Pages (PageBuilder)
|
|
if (($settings['sitemap_include_pages'] ?? '1') == '1' && DB::schema()->hasTable('pages')) {
|
|
$freq = $settings['sitemap_freq_pages'] ?? 'monthly';
|
|
$prio = $settings['sitemap_prio_pages'] ?? '0.7';
|
|
|
|
$pages = DB::table('pages')
|
|
->where('pages_is_published', 1)
|
|
->where('pages_public', 1)
|
|
->get();
|
|
|
|
foreach ($pages as $p) {
|
|
$xml .= '<url>';
|
|
$xml .= '<loc>' . htmlspecialchars(url($p->pages_slug)) . '</loc>';
|
|
$xml .= '<lastmod>' . date('Y-m-d', strtotime($p->updated_at ?: $p->created_at)) . '</lastmod>';
|
|
$xml .= '<changefreq>' . $freq . '</changefreq>';
|
|
$xml .= '<priority>' . $prio . '</priority>';
|
|
$xml .= '</url>';
|
|
}
|
|
}
|
|
|
|
// Photo Essays
|
|
if (($settings['sitemap_include_photoessays'] ?? '1') == '1' && DB::schema()->hasTable('photoessays')) {
|
|
$freq = $settings['sitemap_freq_photoessays'] ?? 'monthly';
|
|
$prio = $settings['sitemap_prio_photoessays'] ?? '0.6';
|
|
|
|
$essays = DB::table('photoessays')
|
|
->where('essay_status', 'approved')
|
|
->get();
|
|
|
|
foreach ($essays as $e) {
|
|
$xml .= '<url>';
|
|
$xml .= '<loc>' . htmlspecialchars(url('photoessay/show/' . $e->essay_slug)) . '</loc>';
|
|
$xml .= '<lastmod>' . date('Y-m-d', strtotime($e->updated_at ?: $e->created_at)) . '</lastmod>';
|
|
$xml .= '<changefreq>' . $freq . '</changefreq>';
|
|
$xml .= '<priority>' . $prio . '</priority>';
|
|
$xml .= '</url>';
|
|
}
|
|
}
|
|
|
|
$xml .= '</urlset>';
|
|
|
|
$response = new Response($xml);
|
|
$response->headers->set('Content-Type', 'application/xml');
|
|
return $response;
|
|
}
|
|
|
|
/**
|
|
* Serve virtual robots.txt if file doesn't exist or we want to override.
|
|
*/
|
|
public function robots(Request $request): Response
|
|
{
|
|
$module = Module::where('modules_name', $this->moduleName)->first();
|
|
$content = "User-agent: *\nDisallow: /admin/\nAllow: /";
|
|
|
|
if ($module && isset($module->modules_settings['robots_txt'])) {
|
|
$content = $module->modules_settings['robots_txt'];
|
|
}
|
|
|
|
$response = new Response($content);
|
|
$response->headers->set('Content-Type', 'text/plain');
|
|
return $response;
|
|
}
|
|
}
|