template('admin/settings.sys', ['options' => &$options]); } /** * Рекомендуемое содержимое файла robots.txt * @return string */ public function robotsTemplate(): string { return join("\n", [ 'User-agent: *', 'Disallow: /admin', 'Disallow: /cabinet/', 'Disallow: */?c', 'Disallow: /*.php', 'Disallow: *?print', 'Disallow: /away/', 'Disallow: /companies/*/contact/', 'Disallow: /companies/promote', 'Disallow: /item/promote', 'Disallow: /rss/', 'Sitemap: {sitemap}', ]); } /** * Путь для хранения файлов SEO * @return string */ public function pathSEO(): string { return $this->app->path('seo'); } /** * URL к файлам SEO * @param string $subDomain поддомен (опционально) * @return string */ public function urlSEO(string $subDomain = ''): string { return Url::to('/', [ 'subdomains' => (!empty($subDomain) ? [$subDomain] : []), 'lang' => false, ]); } /** * Время перегенерации файла sitemap.xml * @return int */ public function sitemapGenerateTimeout(): int { return $this->config('seo.sitemap.generate.timeout', 86400, TYPE_UINT); } /** * Обновить файл Sitemap.xml принудительно * @return bool */ public function sitemapRefresh(): bool { $cronManager = $this->app->cronManager(); if (! $cronManager->isEnabled()) { return false; } return $cronManager->executeOnce('seo', 'cronSitemapXML'); } /** * Формирование Sitemap.xml для мультидоменной конфигурации сайта * @param array $geo фильтр по региону * @param string $file название файла * @param string $path путь к файлу * @param string $url урл к файлу */ protected function generateSitemapXMLSubdomains($geo, $file, $path, $url) { # строим XML $data = []; if (! $geo['id']) { # для основного домена # Посадочные страницы if ($this->landingPagesEnabled()) { # непривязанные к модулям $data['landingpages_not_joined'] = $this->model->landingpagesSitemapXmlData(true, '', [ 'filter' => ['notJoined' => 1], ]); # с не пустыми категориями для всех регионов $data['landingpages_listings_items'] = $this->model->landingpagesSitemapXmlData(true, '', [ 'filter' => ['withListingsItems' => 0], ]); } # Блог if ($this->app->moduleExists('blog')) { $data['blog'] = Blog::model()->postsSitemapXmlData(); } } else { # для поддоменов (страна / регион / город) # Посадочные страницы if ($this->landingPagesEnabled()) { # только для категорий с объявлениями в указанном регионе $data['landingpages_listings_items'] = $this->model->landingpagesSitemapXmlData(true, '', [ 'filter' => ['withListingsItems' => $geo['id']], 'subdomains' => [$geo['keyword']], ]); # непривязанные к модулям $data['landingpages_not_joined'] = $this->model->landingpagesSitemapXmlData(true, '', [ 'filter' => ['notJoined' => 1], 'subdomains' => [$geo['keyword']], ]); } if (! empty($geo['city'])) { # для города добавляем компании в этом городе if ($this->app->moduleExists('business')) { $data['companies'] = Business::model()->companiesSitemapXmlData(['geo_city' => $geo['id']]); } # для города добавляем объявления в этом городе $sql = []; $region = Geo::regionParents($geo['id']); foreach ($region['db'] as $k => $v) { if (empty($v)) { unset($region['db'][$k]); } } $sql[':geo_path'] = ['geo_path LIKE :regionQuery', ':regionQuery' => '-' . join('-', $region['db']) . '-']; $data['items'] = Listings::model()->itemsSitemapXmlData($sql); } } # Дополнительно $data = $this->app->filter('site.cron.sitemapXML', $data, ['geo' => $geo, 'file' => $file, 'path' => $path, 'url' => $url]); # Строим XML ini_set('memory_limit', '2048M'); $sitemap = new SitemapFile(); $sitemap->buildIterator($data, $file, $path, $url, true); } /** * Формирование Sitemap.xml для однодоменной конфигурации сайта * @param string $file название файла * @param string $path путь к файлу * @param string $url урл к файлу * @return void */ protected function generateSitemapXMLSingleDomain(string $file, string $path, string $url) { $data = []; # Посадочные страницы if ($this->landingPagesEnabled()) { $data['landingpages'] = $this->model->landingpagesSitemapXmlData(); } # Статические страницы $data['pages'] = Site::model()->pagesSitemapXmlData(); # Блог if ($this->app->moduleExists('blog')) { $data['blog'] = Blog::model()->postsSitemapXmlData(); } # Компании todo move to business if ($this->app->moduleExists('business')) { $data['companies'] = Business::model()->companiesSitemapXmlData(); } # Объявления $data['items'] = Listings::model()->itemsSitemapXmlData(); # Дополнительно $data = $this->app->filter('site.cron.sitemapXML', $data); # Строим XML ini_set('memory_limit', '2048M'); $sitemap = new SitemapFile(); $sitemap->setPing($this->config('site.sitemapXML.ping', true, TYPE_BOOL)); $sitemap->buildIterator($data, $file, $path, $url, true); } /** * Проверка работает или нет механизм изменения файла robots.txt * @param bool $resetCache сбросить кеш * @param array $msg @ref сообщения об ошибках и рекомендации * @return bool */ public function robotsEnabled($resetCache = false, &$msg = []): bool { $publicDir = str_replace($this->app->basePath(), DS, $this->app->publicPath()); $robots = $this->app->publicPath(static::ROBOTS_FILE); if (file_exists($robots)) { $msg[] = _t('seo', 'To be able to manage these settings, you need to delete the source file [file] [delete_link]', [ 'file' => '' . $publicDir . static::ROBOTS_FILE . '', 'delete_link' => '' . _te('', 'Delete') . '', ]); config::save('seo_robots_template', file_get_contents($robots)); return false; } $sitemap = $this->app->publicPath(static::SITEMAP_FILE); if (file_exists($sitemap)) { $msg[] = _t('seo', 'To be able to manage these settings, you need to delete the source file [file] [delete_link]', [ 'file' => '' . $publicDir . static::SITEMAP_FILE . '', 'delete_link' => '' . _te('', 'Delete') . '', ]); return false; } $allow = config::get('seo_robots_rewrite', false); if ($resetCache) { $url = $this->router->url('seo-robots-txt'); $returnCode = 1; Files::downloadFile($url, false, ['setErrors' => false, 'returnCode' => &$returnCode, 'timeout' => 5]); $result = ($returnCode >= 200 && $returnCode <= 300); if (! $result) { if ($returnCode == 401) { $msg[] = _t('seo', 'Failed to check access to file [file], access to the site is limited by password.', ['file' => 'robots.txt']); } else { $msg[] = _t('seo', 'Check the server settings, at the current settings php does not process calls to the [robots] file by search robots. [a] Details [/a] [more] If the following line is in the Nginx settings: [code1] Note it on the following: [code2] [/more]', [ 'robots' => ' Robots.txt ', 'a' => '', '/a' => '', 'more' => '
', 'code1' => '
location = /robots.txt {access_log off; log_not_found off;}
', 'code2' => '
location = /robots.txt {access_log off; log_not_found off; try_files $uri @rewrites;} ', '/more' => '
', ]); } } else { $url = $this->router->url('seo-sitemap-xml', ['check' => 1]); $returnCode = 1; Files::downloadFile($url, false, ['setErrors' => false, 'returnCode' => &$returnCode, 'timeout' => 5]); $result = ($returnCode >= 200 && $returnCode <= 300); if (! $result) { if ($returnCode == 401) { $msg[] = _t('seo', 'Failed to check access to file [file], access to the site is limited by password.', ['file' => 'Sitemap.xml']); } else { $msg[] = _t('seo', 'Check the settings of the files cached by the web server; at the current settings, PHP does not process calls to the Sitemap.xml file by search robots.'); } } } $allow = $result; config::save('seo_robots_rewrite', $allow); } return $allow; } /** * Формирование списка директорий/файлов требующих проверки на наличие прав записи * @return array */ public function writableCheck() { $dirs[$this->pathSEO()] = 'dir-only'; return array_merge(parent::writableCheck(), $dirs); } }