From 4f4ddfc4051a0f493fcd66aa3ca2b617d4652146 Mon Sep 17 00:00:00 2001 From: Bugo Date: Mon, 22 Jan 2024 10:50:48 +0500 Subject: [PATCH 01/45] Refactor code --- Sources/Optimus/Addons/AbstractAddon.php | 28 ++ .../Optimus/{addons => Addons}/EhPortal.php | 24 +- .../{addons => Addons}/ExampleAddon.php | 12 +- Sources/Optimus/Addons/EzPortal.php | 71 +++ .../Optimus/{addons => Addons}/IdnConvert.php | 21 +- .../{addons => Addons}/LightPortal.php | 30 +- Sources/Optimus/Addons/PrettyUrls.php | 67 +++ Sources/Optimus/Addons/SimpleSEF.php | 55 +++ .../Optimus/{addons => Addons}/TinyPortal.php | 48 +- .../{addons => Addons}/TopicDescriptions.php | 15 +- Sources/Optimus/{addons => Addons}/index.php | 0 Sources/Optimus/Events/AddonEvent.php | 35 ++ Sources/Optimus/Events/AddonListener.php | 26 + Sources/Optimus/Events/DispatcherFactory.php | 31 ++ Sources/Optimus/Events/index.php | 7 + Sources/Optimus/Handlers/AddonHandler.php | 102 ++++ .../{Boards.php => Handlers/BoardHandler.php} | 40 +- Sources/Optimus/Handlers/CounterHandler.php | 58 +++ Sources/Optimus/Handlers/ErrorHandler.php | 56 +++ Sources/Optimus/Handlers/FaviconHandler.php | 35 ++ Sources/Optimus/Handlers/FrontPageHandler.php | 47 ++ Sources/Optimus/Handlers/HandlerLoader.php | 39 ++ Sources/Optimus/Handlers/MetaHandler.php | 121 +++++ Sources/Optimus/Handlers/RedirectHandler.php | 43 ++ .../Optimus/Handlers/SearchTermHandler.php | 130 +++++ .../SettingHandler.php} | 137 +++--- Sources/Optimus/Handlers/SitemapHandler.php | 92 ++++ .../{Keywords.php => Handlers/TagHandler.php} | 101 ++-- Sources/Optimus/Handlers/TitleHandler.php | 45 ++ .../{Topics.php => Handlers/TopicHandler.php} | 86 ++-- Sources/Optimus/Handlers/index.php | 7 + Sources/Optimus/Integration.php | 446 +----------------- Sources/Optimus/Robots.php | 122 ----- Sources/Optimus/Subs.php | 135 ------ Sources/Optimus/Task.php | 68 --- Sources/Optimus/{ => Tasks}/Sitemap.php | 228 +++++---- Sources/Optimus/Tasks/index.php | 7 + Sources/Optimus/Utils/Copyright.php | 30 ++ Sources/Optimus/Utils/Input.php | 86 ++++ Sources/Optimus/Utils/RobotsGenerator.php | 132 ++++++ Sources/Optimus/Utils/Str.php | 56 +++ Sources/Optimus/Utils/index.php | 7 + Sources/Optimus/addons/PrettyUrls.php | 59 --- Sources/Optimus/addons/SimpleSEF.php | 49 -- Sources/Optimus/app.php | 209 +------- Sources/Optimus/composer.json | 18 + Themes/default/Optimus.template.php | 38 +- 47 files changed, 1890 insertions(+), 1409 deletions(-) create mode 100644 Sources/Optimus/Addons/AbstractAddon.php rename Sources/Optimus/{addons => Addons}/EhPortal.php (63%) rename Sources/Optimus/{addons => Addons}/ExampleAddon.php (74%) create mode 100644 Sources/Optimus/Addons/EzPortal.php rename Sources/Optimus/{addons => Addons}/IdnConvert.php (51%) rename Sources/Optimus/{addons => Addons}/LightPortal.php (60%) create mode 100644 Sources/Optimus/Addons/PrettyUrls.php create mode 100644 Sources/Optimus/Addons/SimpleSEF.php rename Sources/Optimus/{addons => Addons}/TinyPortal.php (57%) rename Sources/Optimus/{addons => Addons}/TopicDescriptions.php (68%) rename Sources/Optimus/{addons => Addons}/index.php (100%) create mode 100644 Sources/Optimus/Events/AddonEvent.php create mode 100644 Sources/Optimus/Events/AddonListener.php create mode 100644 Sources/Optimus/Events/DispatcherFactory.php create mode 100644 Sources/Optimus/Events/index.php create mode 100644 Sources/Optimus/Handlers/AddonHandler.php rename Sources/Optimus/{Boards.php => Handlers/BoardHandler.php} (78%) create mode 100644 Sources/Optimus/Handlers/CounterHandler.php create mode 100644 Sources/Optimus/Handlers/ErrorHandler.php create mode 100644 Sources/Optimus/Handlers/FaviconHandler.php create mode 100644 Sources/Optimus/Handlers/FrontPageHandler.php create mode 100644 Sources/Optimus/Handlers/HandlerLoader.php create mode 100644 Sources/Optimus/Handlers/MetaHandler.php create mode 100644 Sources/Optimus/Handlers/RedirectHandler.php create mode 100644 Sources/Optimus/Handlers/SearchTermHandler.php rename Sources/Optimus/{Settings.php => Handlers/SettingHandler.php} (79%) create mode 100644 Sources/Optimus/Handlers/SitemapHandler.php rename Sources/Optimus/{Keywords.php => Handlers/TagHandler.php} (87%) create mode 100644 Sources/Optimus/Handlers/TitleHandler.php rename Sources/Optimus/{Topics.php => Handlers/TopicHandler.php} (71%) create mode 100644 Sources/Optimus/Handlers/index.php delete mode 100644 Sources/Optimus/Robots.php delete mode 100644 Sources/Optimus/Subs.php delete mode 100644 Sources/Optimus/Task.php rename Sources/Optimus/{ => Tasks}/Sitemap.php (54%) create mode 100644 Sources/Optimus/Tasks/index.php create mode 100644 Sources/Optimus/Utils/Copyright.php create mode 100644 Sources/Optimus/Utils/Input.php create mode 100644 Sources/Optimus/Utils/RobotsGenerator.php create mode 100644 Sources/Optimus/Utils/Str.php create mode 100644 Sources/Optimus/Utils/index.php delete mode 100644 Sources/Optimus/addons/PrettyUrls.php delete mode 100644 Sources/Optimus/addons/SimpleSEF.php create mode 100644 Sources/Optimus/composer.json diff --git a/Sources/Optimus/Addons/AbstractAddon.php b/Sources/Optimus/Addons/AbstractAddon.php new file mode 100644 index 0000000..e458551 --- /dev/null +++ b/Sources/Optimus/Addons/AbstractAddon.php @@ -0,0 +1,28 @@ +dispatcher = (new DispatcherFactory())(); + } +} \ No newline at end of file diff --git a/Sources/Optimus/addons/EhPortal.php b/Sources/Optimus/Addons/EhPortal.php similarity index 63% rename from Sources/Optimus/addons/EhPortal.php rename to Sources/Optimus/Addons/EhPortal.php index 0515c8d..5679619 100644 --- a/Sources/Optimus/addons/EhPortal.php +++ b/Sources/Optimus/Addons/EhPortal.php @@ -1,36 +1,38 @@ dispatcher->subscribeTo('robots.rules', [$this, 'changeRobots']); + $this->dispatcher->subscribeTo('sitemap.links', [$this, 'changeSitemap']); } - public function optimusRobots(array &$custom_rules, string $url_path) + public function changeRobots(object $object): void { - $custom_rules[] = "Allow: " . $url_path . "/*page=*"; + $object->getTarget()->customRules[] = "Allow: " . $object->getTarget()->urlPath . "/*page=*"; } - public function optimusSitemap(array &$links) + public function changeSitemap(object $object): void { global $smcFunc, $scripturl; @@ -50,9 +52,7 @@ public function optimusSitemap(array &$links) while ($row = $smcFunc['db_fetch_assoc']($request)) { $url = $scripturl . '?page=' . $row['namespace']; - call_integration_hook('integrate_optimus_create_sef_url', array(&$url)); - - $links[] = array( + $object->getTarget()->links[] = array( 'loc' => $url ); } diff --git a/Sources/Optimus/addons/ExampleAddon.php b/Sources/Optimus/Addons/ExampleAddon.php similarity index 74% rename from Sources/Optimus/addons/ExampleAddon.php rename to Sources/Optimus/Addons/ExampleAddon.php index f5d0c68..94bf0b2 100644 --- a/Sources/Optimus/addons/ExampleAddon.php +++ b/Sources/Optimus/Addons/ExampleAddon.php @@ -1,27 +1,29 @@ hideLockedTopicsForSpiders(); } - public function menuButtons() + public function hideLockedTopicsForSpiders(): void { global $context; diff --git a/Sources/Optimus/Addons/EzPortal.php b/Sources/Optimus/Addons/EzPortal.php new file mode 100644 index 0000000..29ba276 --- /dev/null +++ b/Sources/Optimus/Addons/EzPortal.php @@ -0,0 +1,71 @@ +dispatcher->subscribeTo('robots.rules', [$this, 'changeRobots']); + $this->dispatcher->subscribeTo('sitemap.links', [$this, 'changeSitemap']); + } + + public function changeRobots(object $object): void + { + global $ezpSettings; + + if (! empty($ezpSettings['ezp_pages_seourls'])) + $object->getTarget()->customRules[] = "Allow: " . $object->getTarget()->urlPath . "/pages/"; + else + $object->getTarget()->customRules[] = "Allow: " . $object->getTarget()->urlPath . "/*ezportal;sa=page;p=*"; + } + + public function changeSitemap(object $object): void + { + global $smcFunc, $ezpSettings, $boardurl, $scripturl; + + $request = $smcFunc['db_query']('', ' + SELECT id_page, date, title, permissions + FROM {db_prefix}ezp_page + WHERE {int:guests} IN (permissions) + ORDER BY id_page DESC', + array( + 'guests' => -1 // The page must be available to guests + ) + ); + + while ($row = $smcFunc['db_fetch_assoc']($request)) { + if (! empty($ezpSettings['ezp_pages_seourls']) && function_exists('MakeSEOUrl')) { + $url = $boardurl . '/pages/' . MakeSEOUrl($row['title']) . '-' . $row['id_page']; + } else { + $url = $scripturl . '?action=ezportal;sa=page;p=' . $row['id_page']; + } + + $object->getTarget()->links[] = array( + 'loc' => $url, + 'lastmod' => $row['date'] + ); + } + + $smcFunc['db_free_result']($request); + } +} diff --git a/Sources/Optimus/addons/IdnConvert.php b/Sources/Optimus/Addons/IdnConvert.php similarity index 51% rename from Sources/Optimus/addons/IdnConvert.php rename to Sources/Optimus/Addons/IdnConvert.php index 705c403..5955d0e 100644 --- a/Sources/Optimus/addons/IdnConvert.php +++ b/Sources/Optimus/Addons/IdnConvert.php @@ -1,33 +1,35 @@ dispatcher->subscribeTo('robots.rules', [$this, 'changeRobots']); + $this->dispatcher->subscribeTo('sitemap.links', [$this, 'changeSitemap']); } - public function optimusRobots() + public function changeRobots(): void { global $boardurl, $scripturl; @@ -35,9 +37,10 @@ public function optimusRobots() $scripturl = $boardurl . '/index.php'; } - public function optimusSitemap(array &$links) + public function changeSitemap(object $object): void { - foreach ($links as $id => $entry) - $links[$id]['loc'] = iri_to_url($entry['loc']); + foreach ($object->getTarget()->links as &$url) { + $url['loc'] = iri_to_url($url['loc']); + } } } diff --git a/Sources/Optimus/addons/LightPortal.php b/Sources/Optimus/Addons/LightPortal.php similarity index 60% rename from Sources/Optimus/addons/LightPortal.php rename to Sources/Optimus/Addons/LightPortal.php index 671696b..58bb265 100644 --- a/Sources/Optimus/addons/LightPortal.php +++ b/Sources/Optimus/Addons/LightPortal.php @@ -1,66 +1,66 @@ dispatcher->subscribeTo('robots.rules', [$this, 'changeRobots']); + $this->dispatcher->subscribeTo('sitemap.links', [$this, 'changeSitemap']); } - public function optimusRobots(array &$custom_rules, string $url_path) + public function changeRobots(object $object): void { if (! defined('LP_PAGE_PARAM')) return; - $custom_rules[] = "Allow: " . $url_path . "/*" . LP_PAGE_PARAM; + $object->getTarget()->customRules[] = "Allow: " . $object->getTarget()->urlPath . "/*" . LP_PAGE_PARAM; } - public function optimusSitemap(array &$links) + public function changeSitemap(object $object): void { - global $smcFunc, $scripturl, $modSettings; + global $modSettings, $smcFunc, $scripturl; if (! class_exists('\Bugo\LightPortal\Integration')) return; - $start_year = (int) op_config('optimus_start_year', 0); + $startYear = (int) ($modSettings['optimus_start_year'] ?? 0); $request = $smcFunc['db_query']('', ' SELECT page_id, alias, GREATEST(created_at, updated_at) AS date FROM {db_prefix}lp_pages WHERE status = {int:status} AND created_at <= {int:current_time} - AND permissions IN ({array_int:permissions})' . ($start_year ? ' + AND permissions IN ({array_int:permissions})' . ($startYear ? ' AND YEAR(FROM_UNIXTIME(created_at)) >= {int:start_year}' : '') . ' ORDER BY page_id DESC', array( 'status' => 1, // The page must be active 'current_time' => time(), 'permissions' => array(1, 3), // The page must be available to guests - 'start_year' => $start_year + 'start_year' => $startYear ) ); while ($row = $smcFunc['db_fetch_assoc']($request)) { $url = $scripturl . '?' . ($modSettings['lp_page_param'] ?? 'page') . '=' . $row['alias']; - call_integration_hook('integrate_optimus_create_sef_url', array(&$url)); - - $links[] = array( + $object->getTarget()->links[] = array( 'loc' => $url, 'lastmod' => $row['date'] ); diff --git a/Sources/Optimus/Addons/PrettyUrls.php b/Sources/Optimus/Addons/PrettyUrls.php new file mode 100644 index 0000000..16090b0 --- /dev/null +++ b/Sources/Optimus/Addons/PrettyUrls.php @@ -0,0 +1,67 @@ +addSupportKeywordsAction(); + + $this->dispatcher->subscribeTo('robots.rules', [$this, 'changeRobots']); + $this->dispatcher->subscribeTo('sitemap.rewrite_content', [$this, 'rewriteContent']); + } + + public function addSupportKeywordsAction(): void + { + global $context; + + if (isset($context['pretty']['action_array'])) + $context['pretty']['action_array'][] = 'keywords'; + } + + public function changeRobots(object $object): void + { + global $modSettings; + + $object->getTarget()->useSef = ! empty($modSettings['pretty_enable_filters']) + && is_file(dirname(__DIR__, 2) . '/PrettyUrls-Filters.php'); + } + + public function rewriteContent(object $object): void + { + global $sourcedir, $modSettings, $context, $smcFunc; + + $pretty = $sourcedir . '/PrettyUrls-Filters.php'; + if (! file_exists($pretty) || empty($modSettings['pretty_enable_filters'])) + return; + + if (! function_exists('pretty_rewrite_buffer')) + require_once($pretty); + + if (! isset($context['session_var'])) + $context['session_var'] = substr(md5($smcFunc['random_int']() . session_id() . $smcFunc['random_int']()), 0, rand(7, 12)); + + $context['pretty']['search_patterns'] = ['~()([^#<]+)~']; + $context['pretty']['replace_patterns'] = ['~()([^<]+)~']; + + if (function_exists('pretty_rewrite_buffer')) + pretty_rewrite_buffer($object->getTarget()->content); + } +} diff --git a/Sources/Optimus/Addons/SimpleSEF.php b/Sources/Optimus/Addons/SimpleSEF.php new file mode 100644 index 0000000..801a38a --- /dev/null +++ b/Sources/Optimus/Addons/SimpleSEF.php @@ -0,0 +1,55 @@ + 0]); + + $this->dispatcher->subscribeTo('robots.rules', [$this, 'changeRobots']); + $this->dispatcher->subscribeTo('sitemap.sef_links', [$this, 'createSefLinks']); + } + + public function changeRobots(object $object): void + { + global $modSettings; + + $object->getTarget()->useSef = ! empty($modSettings['simplesef_enable']) + && is_file(dirname(__DIR__, 2) . '/SimpleSEF.php'); + } + + public function createSefLinks(object $object): void + { + if (! class_exists('\SimpleSEF')) + return; + + $sef = new \SimpleSEF(); + $method = method_exists('\SimpleSEF', 'getSefUrl') ? 'getSefUrl' : 'create_sef_url'; + + foreach ($object->getTarget()->links as &$url) { + $url['loc'] = $sef->$method($url['loc']); + } + } +} diff --git a/Sources/Optimus/addons/TinyPortal.php b/Sources/Optimus/Addons/TinyPortal.php similarity index 57% rename from Sources/Optimus/addons/TinyPortal.php rename to Sources/Optimus/Addons/TinyPortal.php index 766c2c0..0081a21 100644 --- a/Sources/Optimus/addons/TinyPortal.php +++ b/Sources/Optimus/Addons/TinyPortal.php @@ -1,6 +1,4 @@ -prepareArticleMeta(); + + $this->dispatcher->subscribeTo('robots.rules', [$this, 'changeRobots']); + $this->dispatcher->subscribeTo('sitemap.links', [$this, 'changeSitemap']); } - public function prepareArticleMeta() + public function prepareArticleMeta(): void { global $context, $settings, $scripturl; - if (! op_is_get('page') || empty($context['TPortal']['article'])) + if (! Input::isGet('page') || empty($context['TPortal']['article'])) return; $pattern = $context['TPortal']['article']['rendertype'] == 'bbc' ? '/\[img.*]([^\]\[]+)\[\/img\]/U' : '/getTarget()->commonRules[] = "Allow: " . $object->getTarget()->urlPath . "/*page"; } - public function optimusSitemap(array &$links) + public function changeSitemap(object $object): void { - global $smcFunc, $scripturl; + global $modSettings, $smcFunc, $scripturl; if (! class_exists('\TinyPortal\Integrate')) return; - $start_year = (int) op_config('optimus_start_year', 0); + $startYear = (int) ($modSettings['optimus_start_year'] ?? 0); $request = $smcFunc['db_query']('', ' SELECT a.id, a.date, a.shortname @@ -63,23 +69,21 @@ public function optimusSitemap(array &$links) INNER JOIN {db_prefix}tp_variables AS v ON (a.category = v.id) WHERE a.approved = {int:approved} AND a.off = {int:off_status} - AND {int:guests} IN (v.value3)' . ($start_year ? ' + AND {int:guests} IN (v.value3)' . ($startYear ? ' AND YEAR(FROM_UNIXTIME(a.date)) >= {int:start_year}' : '') . ' ORDER BY a.id DESC', array( 'approved' => 1, // The article must be approved 'off_status' => 0, // The article must be active 'guests' => -1, // The article category must be available to guests - 'start_year' => $start_year + 'start_year' => $startYear ) ); while ($row = $smcFunc['db_fetch_assoc']($request)) { $url = $scripturl . '?page=' . ($row['shortname'] ?: $row['id']); - call_integration_hook('integrate_optimus_create_sef_url', array(&$url)); - - $links[] = array( + $object->getTarget()->links[] = array( 'loc' => $url, 'lastmod' => $row['date'] ); diff --git a/Sources/Optimus/addons/TopicDescriptions.php b/Sources/Optimus/Addons/TopicDescriptions.php similarity index 68% rename from Sources/Optimus/addons/TopicDescriptions.php rename to Sources/Optimus/Addons/TopicDescriptions.php index c175c24..d382d8e 100644 --- a/Sources/Optimus/addons/TopicDescriptions.php +++ b/Sources/Optimus/Addons/TopicDescriptions.php @@ -1,6 +1,4 @@ -name; + } + + public function getTarget(): object + { + return $this->target; + } +} diff --git a/Sources/Optimus/Events/AddonListener.php b/Sources/Optimus/Events/AddonListener.php new file mode 100644 index 0000000..80c30fa --- /dev/null +++ b/Sources/Optimus/Events/AddonListener.php @@ -0,0 +1,26 @@ +getAll(); + + if (empty($addons)) + return; + + $dispatcher = (new DispatcherFactory())(); + + foreach ($addons as $addon) { + $this->loadLanguages($addon); + + if (isset(self::$loaded[$addon])) { + continue; + } + + $class = $this->getClassName($addon); + + $dispatcher->subscribeTo($addon, new AddonListener()); + $dispatcher->dispatch(new AddonEvent($addon, new $class)); + + self::$loaded[$addon] = true; + } + } + + private function getAll(): ?array + { + if (! is_dir(dirname(__DIR__ ) . '/Addons')) + return []; + + if (($addons = cache_get_data('optimus_addons', 3600)) === null) { + foreach ((new FilesystemIterator(dirname(__DIR__ ) . '/Addons')) as $object) { + $filename = $object->getBasename(); + if ($object->isFile()) { + $addons[] = str_replace('.php', '', $filename); + } + + if ($object->isDir() && is_file($object->getPathname() . '/' . $filename . '.php')) { + $addons[] = $filename . '|' . $filename; + } + } + + $addons = array_diff($addons, ['AbstractAddon', 'index']); + + cache_put_data('optimus_addons', $addons, 3600); + } + + return $addons; + } + + private function getClassName(string $addon): string + { + return '\Bugo\Optimus\Addons\\' . str_replace('|', '\\', $addon); + } + + private function loadLanguages(string $addon): void + { + global $user_info, $txt; + + if (empty($txt)) + return; + + $languages = array_merge(['english'], [$user_info['language'] ?? null]); + $baseDir = dirname(__DIR__ ) . '/Addons/' . explode('|', $addon)[0] . '/langs/'; + + foreach ($languages as $lang) { + $langFile = $baseDir . $lang . '.php'; + + if (is_file($langFile)) { + require_once $langFile; + } + } + } +} \ No newline at end of file diff --git a/Sources/Optimus/Boards.php b/Sources/Optimus/Handlers/BoardHandler.php similarity index 78% rename from Sources/Optimus/Boards.php rename to Sources/Optimus/Handlers/BoardHandler.php index feeb898..44c27c1 100644 --- a/Sources/Optimus/Boards.php +++ b/Sources/Optimus/Handlers/BoardHandler.php @@ -1,11 +1,7 @@ -' . sprintf($txt['optimus_goto_main_page'], $scripturl); + } + + // No access? + if ($board_info['error'] === 'access') { + send_http_status(403); + + $context['page_title'] = $txt['optimus_403_page_title']; + $context['error_title'] = $txt['optimus_403_h2']; + $context['error_message'] = $txt['optimus_403_h3'] . '
' . sprintf($txt['optimus_goto_main_page'], $scripturl); + } + + if ($board_info['error'] === 'exist' || $board_info['error'] === 'access') { + addInlineJavaScript(' + let error_block = document.getElementById("fatal_error"); + error_block.classList.add("centertext"); + error_block.nextElementSibling.querySelector("a.button").setAttribute("href", "javascript:history.go(-1)");', true); + } + } +} \ No newline at end of file diff --git a/Sources/Optimus/Handlers/FaviconHandler.php b/Sources/Optimus/Handlers/FaviconHandler.php new file mode 100644 index 0000000..664ee52 --- /dev/null +++ b/Sources/Optimus/Handlers/FaviconHandler.php @@ -0,0 +1,35 @@ + (new $handler)(), $this->handlers); + } +} \ No newline at end of file diff --git a/Sources/Optimus/Handlers/MetaHandler.php b/Sources/Optimus/Handlers/MetaHandler.php new file mode 100644 index 0000000..979e3d8 --- /dev/null +++ b/Sources/Optimus/Handlers/MetaHandler.php @@ -0,0 +1,121 @@ + $value) { + foreach ($value as $k => $v) { + if ($k === 'property' && in_array($v, $meta)) + $context['meta_tags'][$key] = array_merge(array('prefix' => 'og: https://ogp.me/ns#'), $value); + + if ($v === 'og:image') { + $og_image_key = $key; + + if (! empty($context['optimus_og_image'])) { + $image_data[0] = $context['optimus_og_image']['width']; + $image_data[1] = $context['optimus_og_image']['height']; + $image_data['mime'] = $context['optimus_og_image']['mime']; + } + } + } + } + + if (! empty($image_data)) { + $context['meta_tags'] = array_merge( + array_slice($context['meta_tags'], 0, $og_image_key + 1, true), + array( + array('prefix' => 'og: https://ogp.me/ns#', 'property' => 'og:image:type', 'content' => $image_data['mime']) + ), + array( + array('prefix' => 'og: https://ogp.me/ns#', 'property' => 'og:image:width', 'content' => $image_data[0]) + ), + array( + array('prefix' => 'og: https://ogp.me/ns#', 'property' => 'og:image:height', 'content' => $image_data[1]) + ), + array_slice($context['meta_tags'], $og_image_key + 1, null, true) + ); + } + + // Various types + if (! empty($context['optimus_og_type'])) { + $type = key($context['optimus_og_type']); + $context['meta_tags'][] = array('prefix' => 'og: https://ogp.me/ns#', 'property' => 'og:type', 'content' => $type); + $optimus_custom_types = array_filter($context['optimus_og_type'][$type]); + + foreach ($optimus_custom_types as $property => $content) { + if (is_array($content)) { + foreach ($content as $value) { + $context['meta_tags'][] = array('prefix' => $type . ': https://ogp.me/ns/' . $type . '#', 'property' => $type . ':' . $property, 'content' => $value); + } + } else { + $context['meta_tags'][] = array('prefix' => $type . ': https://ogp.me/ns/' . $type . '#', 'property' => $type . ':' . $property, 'content' => $content); + } + } + } + + if ($context['current_action'] == 'profile' && Input::isRequest('u')) { + $context['meta_tags'][] = array('prefix' => 'og: https://ogp.me/ns#', 'property' => 'og:type', 'content' => 'profile'); + } + + // Twitter cards + if (! empty($modSettings['optimus_tw_cards']) && isset($context['canonical_url'])) { + $context['meta_tags'][] = array('property' => 'twitter:card', 'content' => 'summary'); + $context['meta_tags'][] = array('property' => 'twitter:site', 'content' => '@' . $modSettings['optimus_tw_cards']); + + if (! empty($settings['og_image'])) + $context['meta_tags'][] = array('property' => 'twitter:image', 'content' => $settings['og_image']); + } + + // Facebook + if (! empty($modSettings['optimus_fb_appid'])) + $context['meta_tags'][] = array('prefix' => 'fb: https://ogp.me/ns/fb#', 'property' => 'fb:app_id', 'content' => $modSettings['optimus_fb_appid']); + + // Metatags + if (! empty($modSettings['optimus_meta'])) { + $tags = array_filter(unserialize($modSettings['optimus_meta'])); + + foreach ($tags as $name => $value) { + $context['meta_tags'][] = array('name' => $name, 'content' => $value); + } + } + } +} \ No newline at end of file diff --git a/Sources/Optimus/Handlers/RedirectHandler.php b/Sources/Optimus/Handlers/RedirectHandler.php new file mode 100644 index 0000000..265e88c --- /dev/null +++ b/Sources/Optimus/Handlers/RedirectHandler.php @@ -0,0 +1,43 @@ + $row['phrase'], + 'scale' => round(($row['hit'] * 100) / $scale), + 'hit' => $row['hit'] + ); + } + + $smcFunc['db_free_result']($request); + + cache_put_data('optimus_search_terms', $context['search_terms'], 3600); + } + + $this->showTopSearchTerms(); + } + + public function searchParams(): void + { + global $modSettings, $smcFunc; + + if (! Input::request('search') || empty($modSettings['optimus_log_search'])) + return; + + $search_string = un_htmlspecialchars(Input::request('search')); + + if (empty($search_string)) + return; + + $request = $smcFunc['db_query']('', ' + SELECT id_term + FROM {db_prefix}optimus_search_terms + WHERE phrase = {string:phrase} + LIMIT 1', + [ + 'phrase' => $search_string + ] + ); + + [$id] = $smcFunc['db_fetch_row']($request); + $smcFunc['db_free_result']($request); + + if (empty($id)) { + $smcFunc['db_insert']('insert', + '{db_prefix}optimus_search_terms', + [ + 'phrase' => 'string-255', + 'hit' => 'int' + ], + [$search_string, 1], + ['id_term'] + ); + } else { + $smcFunc['db_query']('', ' + UPDATE {db_prefix}optimus_search_terms + SET hit = hit + 1 + WHERE id_term = {int:id_term}', + [ + 'id_term' => $id + ] + ); + } + } + + private function showTopSearchTerms(): void + { + global $context; + + if (empty($context['search_terms']) || ! $this->canViewSearchTerms()) + return; + + loadTemplate('Optimus'); + + $context['template_layers'][] = 'search_terms'; + } + + private function canViewSearchTerms(): bool + { + global $modSettings; + + if (empty($modSettings['optimus_log_search'])) + return false; + + return allowedTo('optimus_view_search_terms'); + } +} \ No newline at end of file diff --git a/Sources/Optimus/Settings.php b/Sources/Optimus/Handlers/SettingHandler.php similarity index 79% rename from Sources/Optimus/Settings.php rename to Sources/Optimus/Handlers/SettingHandler.php index ae91776..07097b4 100644 --- a/Sources/Optimus/Settings.php +++ b/Sources/Optimus/Handlers/SettingHandler.php @@ -1,11 +1,7 @@ - $dump) { if (isset($dump[1]) && $dump[1] == 'meta_keywords') { @@ -42,7 +43,7 @@ public function modifyBasicSettings(array &$config_vars) } } - public function adminAreas(array &$admin_areas) + public function adminAreas(array &$admin_areas): void { global $settings, $txt; @@ -57,7 +58,7 @@ public function adminAreas(array &$admin_areas) content: "\f717"; }'); - if (op_request('area') === 'optimus') + if (Input::request('area') === 'optimus') loadCSSFile('optimus/optimus.css'); $admin_areas['config']['areas']['optimus'] = array( @@ -78,7 +79,7 @@ public function adminAreas(array &$admin_areas) ); } - public function adminSearch(array &$language_files, array &$include_files, array &$settings_search) + public function adminSearch(array $language_files, array $include_files, array &$settings_search): void { $settings_search[] = array(array($this, 'basicTabSettings'), 'area=optimus;sa=basic'); $settings_search[] = array(array($this, 'extraTabSettings'), 'area=optimus;sa=extra'); @@ -86,7 +87,7 @@ public function adminSearch(array &$language_files, array &$include_files, array $settings_search[] = array(array($this, 'sitemapTabSettings'), 'area=optimus;sa=sitemap'); } - public function actions() + public function actions(): void { global $context, $txt, $sourcedir, $smcFunc; @@ -146,7 +147,7 @@ public function actions() ) ); - $this->{$subActions[op_request('sa')]}(); + $this->{$subActions[Input::request('sa')]}(); } /** @@ -163,28 +164,28 @@ public function basicTabSettings(bool $return_config = false) ['optimus_forum_index' => sprintf($txt['forum_index'], $context['forum_name'])] ); - $config_vars = array( - array('title', 'optimus_main_page'), - array('text', 'optimus_forum_index', 80, 'value' => un_htmlspecialchars($modSettings['optimus_forum_index'] ?? '')), - array('large_text', 'optimus_description', 'value' => un_htmlspecialchars($modSettings['optimus_description'] ?? ''), 'subtext' => $txt['optimus_description_subtext']), - array('large_text', 'meta_keywords', 'subtext' => $txt['meta_keywords_note']), - array('title', 'optimus_all_pages'), - array('select', 'optimus_board_extend_title', $txt['optimus_board_extend_title_set']), - array('select', 'optimus_topic_extend_title', $txt['optimus_topic_extend_title_set']), + $config_vars = [ + ['title', 'optimus_main_page'], + ['text', 'optimus_forum_index', 80, 'value' => un_htmlspecialchars($modSettings['optimus_forum_index'] ?? '')], + ['large_text', 'optimus_description', 'value' => un_htmlspecialchars($modSettings['optimus_description'] ?? ''), 'subtext' => $txt['optimus_description_subtext']], + ['large_text', 'meta_keywords', 'subtext' => $txt['meta_keywords_note']], + ['title', 'optimus_all_pages'], + ['select', 'optimus_board_extend_title', $txt['optimus_board_extend_title_set']], + ['select', 'optimus_topic_extend_title', $txt['optimus_topic_extend_title_set']], '', - array('check', 'optimus_topic_description'), - array('check', 'optimus_allow_change_topic_desc', 'subtext' => $txt['optimus_allow_change_topic_desc_subtext']), + ['check', 'optimus_topic_description'], + ['check', 'optimus_allow_change_topic_desc', 'subtext' => $txt['optimus_allow_change_topic_desc_subtext']], '', - array('check', 'optimus_allow_change_topic_keywords', 'subtext' => $txt['optimus_allow_change_topic_keywords_subtext']), - array('check', 'optimus_show_keywords_block'), - array('check', 'optimus_show_keywords_on_message_index'), - array('check', 'optimus_allow_keyword_phrases'), - array('check', 'optimus_use_color_tags'), + ['check', 'optimus_allow_change_topic_keywords', 'subtext' => $txt['optimus_allow_change_topic_keywords_subtext']], + ['check', 'optimus_show_keywords_block'], + ['check', 'optimus_show_keywords_on_message_index'], + ['check', 'optimus_allow_keyword_phrases'], + ['check', 'optimus_use_color_tags'], '', - array('check', 'optimus_correct_http_status'), - array('title', 'optimus_extra_settings'), - array('check', 'optimus_log_search'), - ); + ['check', 'optimus_correct_http_status'], + ['title', 'optimus_extra_settings'], + ['check', 'optimus_log_search'], + ]; // Mod authors can add own options call_integration_hook('integrate_optimus_basic_settings', array(&$config_vars)); @@ -192,14 +193,14 @@ public function basicTabSettings(bool $return_config = false) if ($return_config) return $config_vars; - if (op_is_get('save')) { + if (Input::isGet('save')) { checkSession(); - if (op_is_post('optimus_forum_index')) - $_POST['optimus_forum_index'] = op_filter('optimus_forum_index'); + if (Input::isPost('optimus_forum_index')) + $_POST['optimus_forum_index'] = Input::filter('optimus_forum_index'); - if (op_is_post('optimus_description')) - $_POST['optimus_description'] = op_filter('optimus_description'); + if (Input::isPost('optimus_description')) + $_POST['optimus_description'] = Input::filter('optimus_description'); call_integration_hook('integrate_save_optimus_basic_settings'); @@ -240,14 +241,14 @@ public function extraTabSettings(bool $return_config = false) if ($return_config) return $config_vars; - if (op_is_get('save')) { + if (Input::isGet('save')) { checkSession(); - if (op_is_post('optimus_fb_appid')) - $_POST['optimus_fb_appid'] = op_filter('optimus_fb_appid'); + if (Input::isPost('optimus_fb_appid')) + $_POST['optimus_fb_appid'] = Input::filter('optimus_fb_appid'); - if (op_is_post('optimus_tw_cards')) - $_POST['optimus_tw_cards'] = str_replace('@', '', op_filter('optimus_tw_cards')); + if (Input::isPost('optimus_tw_cards')) + $_POST['optimus_tw_cards'] = str_replace('@', '', Input::filter('optimus_tw_cards')); call_integration_hook('integrate_save_optimus_extra_settings'); @@ -279,7 +280,7 @@ public function faviconTabSettings(bool $return_config = false) $context['sub_template'] = 'favicon'; - if (op_is_get('save')) { + if (Input::isGet('save')) { checkSession(); $save_vars = $config_vars; @@ -291,7 +292,7 @@ public function faviconTabSettings(bool $return_config = false) prepareDBSettingContext($config_vars); } - public function metatagsTabSettings() + public function metatagsTabSettings(): void { global $context, $txt, $scripturl, $modSettings; @@ -303,15 +304,15 @@ public function metatagsTabSettings() $config_vars = []; - if (op_is_get('save')) { + if (Input::isGet('save')) { checkSession(); $save_vars = $config_vars; saveDBSettings($save_vars); $meta = []; - if (op_is_post('custom_tag_name') && op_is_post('custom_tag_value')) { - $custom_tag = filter_input_array(INPUT_POST, FILTER_DEFAULT); + if (Input::isPost('custom_tag_name') && Input::isPost('custom_tag_value')) { + $custom_tag = filter_input_array(INPUT_POST); $custom_tag['custom_tag_name'] = array_filter($custom_tag['custom_tag_name']); foreach ($custom_tag['custom_tag_name'] as $key => $value) { @@ -326,7 +327,7 @@ public function metatagsTabSettings() prepareDBSettingContext($config_vars); } - public function redirectTabSettings() + public function redirectTabSettings(): void { global $context, $txt, $scripturl, $modSettings; @@ -338,15 +339,15 @@ public function redirectTabSettings() $config_vars = []; - if (op_is_get('save')) { + if (Input::isGet('save')) { checkSession(); $save_vars = $config_vars; saveDBSettings($save_vars); $redirect = []; - if (op_is_post('custom_redirect_from') && op_is_post('custom_redirect_to')) { - $custom_redirect = filter_input_array(INPUT_POST, FILTER_DEFAULT); + if (Input::isPost('custom_redirect_from') && Input::isPost('custom_redirect_to')) { + $custom_redirect = filter_input_array(INPUT_POST); $custom_redirect['custom_redirect_from'] = array_filter($custom_redirect['custom_redirect_from']); foreach ($custom_redirect['custom_redirect_from'] as $to => $from) { @@ -361,7 +362,7 @@ public function redirectTabSettings() prepareDBSettingContext($config_vars); } - public function counterTabSettings() + public function counterTabSettings(): void { global $context, $txt, $scripturl; @@ -382,7 +383,7 @@ public function counterTabSettings() array('text', 'optimus_ignored_actions') ); - if (op_is_get('save')) { + if (Input::isGet('save')) { checkSession(); $save_vars = $config_vars; @@ -394,7 +395,7 @@ public function counterTabSettings() prepareDBSettingContext($config_vars); } - public function robotsTabSettings() + public function robotsTabSettings(): void { global $context, $txt, $scripturl, $boarddir; @@ -404,18 +405,18 @@ public function robotsTabSettings() $config_vars = []; - $robots_path = (op_server('document_root') ?: $boarddir) . '/robots.txt'; + $robots_path = (Input::server('document_root') ?: $boarddir) . '/robots.txt'; $context['robots_content'] = is_writable($robots_path) ? file_get_contents($robots_path) : ''; - (new Robots())->generate(); + (new RobotsGenerator)->generate(); - if (op_is_get('save')) { + if (Input::isGet('save')) { checkSession(); $save_vars = $config_vars; saveDBSettings($save_vars); - file_put_contents($robots_path, op_filter('optimus_robots'), LOCK_EX); + file_put_contents($robots_path, Input::filter('optimus_robots'), LOCK_EX); redirectexit('action=admin;area=optimus;sa=robots'); } @@ -423,7 +424,7 @@ public function robotsTabSettings() prepareDBSettingContext($config_vars); } - public function htaccessTabSettings() + public function htaccessTabSettings(): void { global $context, $txt, $scripturl, $boarddir; @@ -433,10 +434,10 @@ public function htaccessTabSettings() $config_vars = []; - $htaccess_path = (op_server('document_root') ?: $boarddir) . '/.htaccess'; + $htaccess_path = (Input::server('document_root') ?: $boarddir) . '/.htaccess'; $context['htaccess_content'] = is_writable($htaccess_path) ? file_get_contents($htaccess_path) : ''; - if (op_is_get('save')) { + if (Input::isGet('save')) { checkSession(); $save_vars = $config_vars; @@ -491,7 +492,7 @@ public function sitemapTabSettings(bool $return_config = false) if ($return_config) return $config_vars; - if (op_is_get('save')) { + if (Input::isGet('save')) { checkSession(); // Recreate a sitemap after save settings @@ -499,15 +500,15 @@ public function sitemapTabSettings(bool $return_config = false) DELETE FROM {db_prefix}background_tasks WHERE task_class = {string:task_class}', array( - 'task_class' => '\Bugo\Optimus\Task' + 'task_class' => '\Bugo\Optimus\Tasks\Sitemap' ) ); - if (op_is_post('optimus_sitemap_enable')) { + if (Input::isPost('optimus_sitemap_enable')) { $smcFunc['db_insert']('insert', '{db_prefix}background_tasks', array('task_file' => 'string-255', 'task_class' => 'string-255', 'task_data' => 'string'), - array('$sourcedir/Optimus/Task.php', '\Bugo\Optimus\Task', ''), + array('$sourcedir/Optimus/Tasks/Sitemap.php', '\Bugo\Optimus\Tasks\Sitemap', ''), array('id_task') ); } @@ -523,7 +524,7 @@ public function sitemapTabSettings(bool $return_config = false) prepareDBSettingContext($config_vars); } - private function addDefaultSettings($settings) + private function addDefaultSettings($settings): void { global $modSettings; diff --git a/Sources/Optimus/Handlers/SitemapHandler.php b/Sources/Optimus/Handlers/SitemapHandler.php new file mode 100644 index 0000000..48bd19f --- /dev/null +++ b/Sources/Optimus/Handlers/SitemapHandler.php @@ -0,0 +1,92 @@ + $settings['theme_url'] . '/css/index.css', + '{sitemap}' => $txt['optimus_sitemap_title'], + '{mobile}' => $txt['optimus_mobile'], + '{images}' => $txt['optimus_images'], + '{news}' => $txt['optimus_news'], + '{video}' => $txt['optimus_video'], + '{index}' => $txt['optimus_index'], + '{total_files}' => $txt['optimus_total_files'], + '{total_urls}' => $txt['optimus_total_urls'], + '{url}' => $txt['url'], + '{last_modified}' => $txt['optimus_last_modified'], + '{frequency}' => $txt['optimus_frequency'], + '{priority}' => $txt['optimus_priority'], + '{direct_link}' => $txt['optimus_direct_link'], + '{caption}' => $txt['optimus_caption'], + '{thumbnail}' => $txt['optimus_thumbnail'], + '{optimus}' => OP_NAME + )); + + echo $content; + + obExit(false); + } + + public function preLogStats(array &$no_stat_actions): void + { + $no_stat_actions['sitemap_xsl'] = true; + } + + public function addSitemapLink(): void + { + global $modSettings, $txt, $boarddir, $forum_copyright, $boardurl, $context; + + if ( + ! empty($modSettings['optimus_sitemap_enable']) + && ! empty($modSettings['optimus_sitemap_link']) + && isset($txt['optimus_sitemap_title']) + && is_file($boarddir . '/sitemap.xml') + ) { + $forum_copyright .= ' | ' . $txt['optimus_sitemap_title'] . ''; + + $context['html_headers'] .= "\n\t" . ''; + } + } +} \ No newline at end of file diff --git a/Sources/Optimus/Keywords.php b/Sources/Optimus/Handlers/TagHandler.php similarity index 87% rename from Sources/Optimus/Keywords.php rename to Sources/Optimus/Handlers/TagHandler.php index 2ba39d1..1ed29ca 100644 --- a/Sources/Optimus/Keywords.php +++ b/Sources/Optimus/Handlers/TagHandler.php @@ -1,11 +1,7 @@ - ' . $txt['optimus_tags'] . ' '; foreach ($context['optimus_keywords'] as $id => $keyword) { - $keywords .= '' . $keyword . ''; + $keywords .= '' . $keyword . ''; } $keywords .= ''; @@ -102,17 +107,17 @@ public function prepareDisplayContext(array &$output, array &$message, int $coun } } - public function createTopic(array &$msgOptions, array &$topicOptions, array &$posterOptions) + public function createTopic(array $msgOptions, array $topicOptions, array $posterOptions): void { if (! $this->canChange()) return; - $keywords = op_xss(op_request('optimus_keywords', [])); + $keywords = Input::xss(Input::request('optimus_keywords', [])); $this->add($keywords, $topicOptions['id'], $posterOptions['id']); } - public function postEnd() + public function postEnd(): void { global $context, $txt, $modSettings; @@ -120,7 +125,7 @@ public function postEnd() return; if ($context['is_new_topic']) { - $context['optimus']['keywords'] = op_xss(op_request('optimus_keywords', '')); + $context['optimus']['keywords'] = Input::xss(Input::request('optimus_keywords', '')); } else { $this->displayTopic(); $context['optimus']['keywords'] = empty($context['optimus_keywords']) ? [] : array_values($context['optimus_keywords']); @@ -189,7 +194,7 @@ public function postEnd() });', true); } - public function modifyPost(array &$messages_columns, array &$update_parameters, array &$msgOptions, array &$topicOptions, array &$posterOptions) + public function modifyPost(array $messages_columns, array $update_parameters, array $msgOptions, array $topicOptions, array $posterOptions): void { if (empty($topicOptions['first_msg']) || $topicOptions['first_msg'] != $msgOptions['id']) return; @@ -197,7 +202,7 @@ public function modifyPost(array &$messages_columns, array &$update_parameters, $this->modify($topicOptions['id'], $posterOptions['id']); } - public function removeTopics(array $topics) + public function removeTopics(array $topics): void { global $smcFunc; @@ -213,7 +218,7 @@ public function removeTopics(array $topics) ); } - public function showTheSame() + public function showTheSame(): void { global $context, $settings, $txt, $scripturl, $sourcedir; @@ -227,7 +232,7 @@ public function showTheSame() background:url(' . $settings['default_images_url'] . '/optimus.png) no-repeat 0 0 !important; }'); - $context['optimus_keyword_id'] = (int) op_request('id', 0); + $context['optimus_keyword_id'] = (int) Input::request('id', 0); loadTemplate('Optimus'); $context['template_layers'][] = 'keywords'; @@ -315,7 +320,7 @@ public function showTheSame() 'additional_rows' => array( array( 'position' => 'below_table_data', - 'value' => 'Powered by ' . op_link(), + 'value' => 'Powered by ' . Copyright::getLink(), 'class' => 'smalltext centertext' ) ) @@ -387,7 +392,7 @@ public function getTotalCountByKeyId(): int return (int) $num; } - public function showAllWithFrequency() + public function showAllWithFrequency(): void { global $context, $txt, $scripturl, $sourcedir; @@ -445,7 +450,7 @@ public function showAllWithFrequency() 'additional_rows' => array( array( 'position' => 'below_table_data', - 'value' => 'Powered by ' . op_link(), + 'value' => 'Powered by ' . Copyright::getLink(), 'class' => 'smalltext centertext', ) ) @@ -454,7 +459,7 @@ public function showAllWithFrequency() require_once($sourcedir . '/Subs-List.php'); createList($listOptions); - if (! empty($context['current_page']) && $context['current_page'] != (int) op_request('start')) + if (! empty($context['current_page']) && $context['current_page'] != (int) Input::request('start')) send_http_status(404); $context['sub_template'] = 'show_list'; @@ -496,7 +501,7 @@ public function getTotalCount(): int { global $smcFunc; - $request = $smcFunc['db_query']('', ' + $request = $smcFunc['db_query']('', /** @lang text */ ' SELECT COUNT(id) FROM {db_prefix}optimus_keywords LIMIT 1', @@ -537,11 +542,11 @@ private function getNameById(int $id = 0): string * * Запрашиваем из базы данных 10 наиболее похожих ключевых слов (когда добавляем новое) */ - private function prepareSearchData() + private function prepareSearchData(): void { global $smcFunc; - $query = $smcFunc['htmltrim'](op_filter('q')); + $query = $smcFunc['htmltrim'](Input::filter('q')); if (empty($query)) exit; @@ -570,11 +575,11 @@ private function prepareSearchData() exit(json_encode($data)); } - public function displayTopic() + public function displayTopic(): void { global $context, $modSettings; - if (is_off('optimus_show_keywords_block')) + if (empty($modSettings['optimus_show_keywords_block'])) return; if (empty($context['current_topic']) || ! empty($context['optimus']['keywords'])) @@ -594,7 +599,7 @@ private function getKeywords(): array global $smcFunc; if (($keywords = cache_get_data('optimus_topic_keywords', 3600)) === null) { - $request = $smcFunc['db_query']('', ' + $request = $smcFunc['db_query']('', /** @lang text */ ' SELECT k.id, k.name, lk.topic_id FROM {db_prefix}optimus_keywords AS k INNER JOIN {db_prefix}optimus_log_keywords AS lk ON (k.id = lk.keyword_id) @@ -616,7 +621,9 @@ private function getKeywords(): array private function getRandomColor(string $key): string { - if (is_off('optimus_use_color_tags')) + global $modSettings; + + if (empty($modSettings['optimus_use_color_tags'])) return ''; $hash = -105; @@ -628,7 +635,7 @@ private function getRandomColor(string $key): string return $hsl . '; color: #fff'; } - private function add(array $keywords, int $topic, int $user) + private function add(array $keywords, int $topic, int $user): void { if (empty($keywords) || empty($topic) || empty($user)) return; @@ -685,7 +692,7 @@ private function addToDatabase(string $keyword): int ); } - private function addNoteToLogTable(int $keyword_id, int $topic, int $user) + private function addNoteToLogTable(int $keyword_id, int $topic, int $user): void { global $smcFunc; @@ -709,14 +716,14 @@ private function addNoteToLogTable(int $keyword_id, int $topic, int $user) ); } - private function modify(int $topic, int $user) + private function modify(int $topic, int $user): void { global $context; if (! $this->canChange()) return; - $keywords = op_xss(op_request('optimus_keywords', [])); + $keywords = Input::xss(Input::request('optimus_keywords', [])); // Check if the keywords have been changed $this->displayTopic(); @@ -732,7 +739,7 @@ private function modify(int $topic, int $user) $this->remove($del_keywords, $topic); } - private function remove(array $keywords, int $topic) + private function remove(array $keywords, int $topic): void { global $smcFunc; @@ -769,7 +776,7 @@ private function remove(array $keywords, int $topic) ) ); - $smcFunc['db_query']('', ' + $smcFunc['db_query']('', /** @lang text */ ' DELETE FROM {db_prefix}optimus_keywords WHERE id NOT IN (SELECT keyword_id FROM {db_prefix}optimus_log_keywords)', array() @@ -780,12 +787,12 @@ private function remove(array $keywords, int $topic) private function canChange(): bool { - global $context, $topic; + global $context, $topic, $modSettings; if (! isset($context['user']['started'])) $context['user']['started'] = empty($topic); - return is_on('optimus_allow_change_topic_keywords') && ( + return ! empty($modSettings['optimus_allow_change_topic_keywords']) && ( allowedTo('optimus_add_keywords_any') || (! empty($context['user']['started']) && allowedTo('optimus_add_keywords_own')) ); } diff --git a/Sources/Optimus/Handlers/TitleHandler.php b/Sources/Optimus/Handlers/TitleHandler.php new file mode 100644 index 0000000..056e09c --- /dev/null +++ b/Sources/Optimus/Handlers/TitleHandler.php @@ -0,0 +1,45 @@ +hooks(); } - public function prepareOgImage() + public function prepareOgImage(): void { - global $context, $scripturl, $settings; + global $modSettings, $context, $scripturl, $settings; - if (is_off('optimus_og_image') || empty($context['topicinfo']['id_first_msg'])) + if (empty($modSettings['optimus_og_image']) || empty($context['topicinfo']['id_first_msg'])) return; $first_message_id = $context['topicinfo']['id_first_msg']; @@ -52,7 +56,7 @@ public function prepareOgImage() 'url' => $settings['og_image'], 'width' => $attachments[$key]['width'], 'height' => $attachments[$key]['height'], - 'mime' => $attachments[$key]['mime_type'] + 'mime' => $attachments[$key]['mime_type'], ); } @@ -63,36 +67,40 @@ public function prepareOgImage() } } - public function loadPermissions(array &$permissionGroups, array &$permissionList) + public function loadPermissions(array $permissionGroups, array &$permissionList): void { - if (is_on('optimus_allow_change_topic_desc')) + global $modSettings; + + if (! empty($modSettings['optimus_allow_change_topic_desc'])) $permissionList['membergroup']['optimus_add_descriptions'] = array(true, 'general', 'view_basic_info'); } - public function displayTopic(array &$topic_selects) + public function displayTopic(array &$topic_selects): void { + global $modSettings; + if (! in_array('ms.modified_time AS topic_modified_time', $topic_selects)) $topic_selects[] = 'ms.modified_time AS topic_modified_time'; - if ((is_on('optimus_topic_description') || is_on('optimus_og_image')) && ! in_array('ms.body AS topic_first_message', $topic_selects)) + if ((! empty($modSettings['optimus_topic_description']) || ! empty($modSettings['optimus_og_image'])) && ! in_array('ms.body AS topic_first_message', $topic_selects)) $topic_selects[] = 'ms.body AS topic_first_message'; - if (is_on('optimus_allow_change_topic_desc')) + if (! empty($modSettings['optimus_allow_change_topic_desc'])) $topic_selects[] = 't.optimus_description'; } /** * Prepare a description and additional Open Graph data */ - public function menuButtons() + public function menuButtons(): void { - global $context, $board_info; + global $context, $modSettings, $board_info; if (empty($context['first_message'])) return; // Generated description from the text of the first post of the topic - if (is_on('optimus_topic_description')) + if (! empty($modSettings['optimus_topic_description'])) $this->makeDescriptionFromFirstMessage(); // Use own description of topic @@ -106,20 +114,20 @@ public function menuButtons() $context['topicinfo']['topic_modified_time']), 'author' => empty($context['topicinfo']['topic_started_name']) ? null : $context['topicinfo']['topic_started_name'], 'section' => $board_info['name'], - 'tag' => $context['optimus_keywords'] ?? null + 'tag' => $context['optimus_keywords'] ?? null, ); } - public function beforeCreateTopic(array &$msgOptions, array &$topicOptions, array &$posterOptions, array &$topic_columns, array &$topic_parameters) + public function beforeCreateTopic(array $msgOptions, array $topicOptions, array $posterOptions, array &$topic_columns, array &$topic_parameters): void { if (! $this->canChangeDescription()) return; $topic_columns['optimus_description'] = 'string-255'; - $topic_parameters[] = op_xss(op_request('optimus_description', '')); + $topic_parameters[] = Input::xss(Input::request('optimus_description', '')); } - public function modifyPost(array &$messages_columns, array &$update_parameters, array &$msgOptions, array &$topicOptions) + public function modifyPost(array $messages_columns, array $update_parameters, array $msgOptions, array $topicOptions): void { if (empty($topicOptions['first_msg']) || $topicOptions['first_msg'] != $msgOptions['id']) return; @@ -127,12 +135,12 @@ public function modifyPost(array &$messages_columns, array &$update_parameters, $this->modifyDescription($topicOptions['id']); } - public function postEnd() + public function postEnd(): void { global $context, $smcFunc, $txt; if ($context['is_new_topic']) { - $context['optimus']['description'] = op_xss(op_request('optimus_description', '')); + $context['optimus']['description'] = Input::xss(Input::request('optimus_description', '')); } else { $request = $smcFunc['db_query']('', ' SELECT optimus_description, id_member_started @@ -140,7 +148,7 @@ public function postEnd() WHERE id_topic = {int:id_topic} LIMIT 1', array( - 'id_topic' => $context['current_topic'] + 'id_topic' => $context['current_topic'], ) ); @@ -159,12 +167,12 @@ public function postEnd() 'attributes' => array( 'id' => 'optimus_description', 'maxlength' => 255, - 'value' => $context['optimus']['description'] - ) + 'value' => $context['optimus']['description'], + ), ); } - private function makeDescriptionFromFirstMessage() + private function makeDescriptionFromFirstMessage(): void { global $context; @@ -175,17 +183,17 @@ private function makeDescriptionFromFirstMessage() censorText($body); - $context['meta_description'] = op_teaser($body); + $context['meta_description'] = Str::teaser($body); } - private function modifyDescription(int $topic) + private function modifyDescription(int $topic): void { global $smcFunc; if (! $this->canChangeDescription()) return; - $description = op_xss(op_request('optimus_description', '')); + $description = Input::xss(Input::request('optimus_description', '')); $smcFunc['db_query']('', ' UPDATE {db_prefix}topics @@ -193,19 +201,19 @@ private function modifyDescription(int $topic) WHERE id_topic = {int:current_topic}', array( 'description' => shorten_subject(strip_tags(parse_bbc($description)), 200), - 'current_topic' => $topic + 'current_topic' => $topic, ) ); } private function canChangeDescription(): bool { - global $context, $topic; + global $context, $topic, $modSettings; if (! isset($context['user']['started'])) $context['user']['started'] = empty($topic); - return is_on('optimus_allow_change_topic_desc') && ( + return ! empty($modSettings['optimus_allow_change_topic_desc']) && ( allowedTo('optimus_add_descriptions_any') || (! empty($context['user']['started']) && allowedTo('optimus_add_descriptions_own')) ); } diff --git a/Sources/Optimus/Handlers/index.php b/Sources/Optimus/Handlers/index.php new file mode 100644 index 0000000..167ba08 --- /dev/null +++ b/Sources/Optimus/Handlers/index.php @@ -0,0 +1,7 @@ +hooks(); - - (new Settings)->hooks(); - (new Boards)->hooks(); - (new Topics)->hooks(); - - Subs::runAddons(); + new HandlerLoader(); } - private function hooks() + public function __invoke(): void { - add_integration_function('integrate_actions', __CLASS__ . '::actions#', false, __FILE__); - add_integration_function('integrate_pre_log_stats', __CLASS__ . '::preLogStats#', false, __FILE__); add_integration_function('integrate_load_theme', __CLASS__ . '::loadTheme#', false, __FILE__); - add_integration_function('integrate_menu_buttons', __CLASS__ . '::menuButtons#', false, __FILE__); - add_integration_function('integrate_theme_context', __CLASS__ . '::themeContext#', false, __FILE__); add_integration_function('integrate_load_permissions', __CLASS__ . '::loadPermissions#', false, __FILE__); - add_integration_function('integrate_search_params', __CLASS__ . '::searchParams#', false, __FILE__); add_integration_function('integrate_credits', __CLASS__ . '::credits#', false, __FILE__); } - public function actions(array &$actions) - { - global $scripturl; - - $actions['sitemap_xsl'] = array(false, array($this, 'xsl')); - - // Redirects - $redirects = is_on('optimus_redirect') ? unserialize(op_config('optimus_redirect')) : []; - - if (empty($redirects)) - return; - - if (isset($_SERVER['QUERY_STRING']) && isset($redirects[$_SERVER['QUERY_STRING']])) { - $url = $scripturl . '?'; - $to = $redirects[$_SERVER['QUERY_STRING']]; - - if (strpos($to, 'http') === 0) - $url = ''; - - header('location: ' . $url . $to, true, 302); - } - } - - public function preLogStats(array &$no_stat_actions) - { - $no_stat_actions['sitemap_xsl'] = true; - } - - public function loadTheme() + public function loadTheme(): void { loadLanguage('Optimus/Optimus'); - - $this->changeFrontPageTitle(); - $this->addCounters(); } - public function menuButtons() + public function loadPermissions(array $permissionGroups, array &$permissionList): void { - $this->addFavicon(); - $this->addFrontPageDescription(); - $this->prepareErrorCodes(); - $this->addSitemapLink(); - $this->prepareSearchTerms(); - } - - public function themeContext() - { - $this->extendTitles(); - $this->prepareMetaTags(); - } + global $modSettings; - public function loadPermissions(array &$permissionGroups, array &$permissionList) - { - if (is_on('optimus_log_search')) + if (! empty($modSettings['optimus_log_search'])) $permissionList['membergroup']['optimus_view_search_terms'] = array(false, 'general', 'view_basic_info'); } - public function searchParams() - { - global $smcFunc; - - if (! op_request('search') || is_off('optimus_log_search')) - return; - - $search_string = un_htmlspecialchars(op_request('search')); - - if (empty($search_string)) - return; - - $request = $smcFunc['db_query']('', ' - SELECT id_term - FROM {db_prefix}optimus_search_terms - WHERE phrase = {string:phrase} - LIMIT 1', - array( - 'phrase' => $search_string - ) - ); - - [$id] = $smcFunc['db_fetch_row']($request); - $smcFunc['db_free_result']($request); - - if (empty($id)) { - $smcFunc['db_insert']('insert', - '{db_prefix}optimus_search_terms', - array( - 'phrase' => 'string-255', - 'hit' => 'int' - ), - array($search_string, 1), - array('id_term') - ); - } else { - $smcFunc['db_query']('', ' - UPDATE {db_prefix}optimus_search_terms - SET hit = hit + 1 - WHERE id_term = {int:id_term}', - array( - 'id_term' => $id - ) - ); - } - } - - public function credits() - { - global $context; - - $context['credits_modifications'][] = op_link() . ' © 2010–2023, Bugo'; - } - - public function xsl() - { - global $modSettings, $settings, $txt; - - ob_end_clean(); - - if (! empty($modSettings['enableCompressedOutput'])) - @ob_start('ob_gzhandler'); - else - ob_start(); - - header('content-type: text/xsl; charset=UTF-8'); - - $content = file_get_contents($settings['default_theme_dir'] . '/css/optimus/sitemap.xsl'); - - $content = strtr($content, array( - '{link}' => $settings['theme_url'] . '/css/index.css', - '{sitemap}' => $txt['optimus_sitemap_title'], - '{mobile}' => $txt['optimus_mobile'], - '{images}' => $txt['optimus_images'], - '{news}' => $txt['optimus_news'], - '{video}' => $txt['optimus_video'], - '{index}' => $txt['optimus_index'], - '{total_files}' => $txt['optimus_total_files'], - '{total_urls}' => $txt['optimus_total_urls'], - '{url}' => $txt['url'], - '{last_modified}' => $txt['optimus_last_modified'], - '{frequency}' => $txt['optimus_frequency'], - '{priority}' => $txt['optimus_priority'], - '{direct_link}' => $txt['optimus_direct_link'], - '{caption}' => $txt['optimus_caption'], - '{thumbnail}' => $txt['optimus_thumbnail'], - '{optimus}' => OP_NAME - )); - - echo $content; - - obExit(false); - } - - private function changeFrontPageTitle() - { - global $txt; - - if (is_on('optimus_forum_index')) - $txt['forum_index'] = op_config('optimus_forum_index'); - } - - private function addCounters() - { - global $context; - - if (isset($_SERVER['HTTP_USER_AGENT']) && stripos($_SERVER['HTTP_USER_AGENT'], 'Lighthouse') !== false) - return; - - if (op_is_request('xml') || in_array($context['current_action'], explode(',', op_config('optimus_ignored_actions', '')))) - return; - - if (is_on('optimus_head_code')) { - $head = explode(PHP_EOL, trim(op_config('optimus_head_code'))); - - foreach ($head as $part) - $context['html_headers'] .= "\n\t" . $part; - } - - if (is_on('optimus_stat_code')) { - $stat = explode(PHP_EOL, trim(op_config('optimus_stat_code'))); - - foreach ($stat as $part) - $context['insert_after_template'] .= "\n\t" . $part; - } - - if (is_on('optimus_count_code')) { - loadTemplate('Optimus'); - $context['template_layers'][] = 'footer_counters'; - - if (is_on('optimus_counters_css')) - addInlineCss(op_config('optimus_counters_css')); - } - } - - private function addFavicon() + public function credits(): void { global $context; - if (is_on('optimus_favicon_text')) { - $favicon = explode(PHP_EOL, trim(op_config('optimus_favicon_text'))); - - foreach ($favicon as $fav_line) - $context['html_headers'] .= "\n\t" . $fav_line; - } - } - - private function addFrontPageDescription() - { - global $context; - - if (empty($context['current_action']) && empty(op_server('query_string')) && empty(op_server('argv')) && is_on('optimus_description')) { - $context['meta_description'] = op_xss(op_config('optimus_description')); - } - } - - private function prepareErrorCodes() - { - global $board_info, $context, $txt, $scripturl; - - if (is_off('optimus_correct_http_status') || empty($board_info['error'])) - return; - - // Does not page exist? - if ($board_info['error'] === 'exist') { - send_http_status(404); - - $context['page_title'] = $txt['optimus_404_page_title']; - $context['error_title'] = $txt['optimus_404_h2']; - $context['error_message'] = $txt['optimus_404_h3'] . '
' . sprintf($txt['optimus_goto_main_page'], $scripturl); - } - - // No access? - if ($board_info['error'] === 'access') { - send_http_status(403); - - $context['page_title'] = $txt['optimus_403_page_title']; - $context['error_title'] = $txt['optimus_403_h2']; - $context['error_message'] = $txt['optimus_403_h3'] . '
' . sprintf($txt['optimus_goto_main_page'], $scripturl); - } - - if ($board_info['error'] === 'exist' || $board_info['error'] === 'access') { - addInlineJavaScript(' - let error_block = document.getElementById("fatal_error"); - error_block.classList.add("centertext"); - error_block.nextElementSibling.querySelector("a.button").setAttribute("href", "javascript:history.go(-1)");', true); - } - } - - private function addSitemapLink() - { - global $txt, $boarddir, $forum_copyright, $boardurl, $context; - - if (is_on('optimus_sitemap_enable') && is_on('optimus_sitemap_link') && isset($txt['optimus_sitemap_title']) && is_file($boarddir . '/sitemap.xml')) { - $forum_copyright .= ' | ' . $txt['optimus_sitemap_title'] . ''; - - $context['html_headers'] .= "\n\t" . ''; - } - } - - private function prepareSearchTerms() - { - global $context, $smcFunc; - - if (($context['current_action'] !== 'search' && $context['current_action'] !== 'search2') || is_off('optimus_log_search')) - return; - - if (($context['search_terms'] = cache_get_data('optimus_search_terms', 3600)) === null) { - $request = $smcFunc['db_query']('', ' - SELECT phrase, hit - FROM {db_prefix}optimus_search_terms - ORDER BY hit DESC - LIMIT 30' - ); - - $scale = 1; - while ($row = $smcFunc['db_fetch_assoc']($request)) { - if ($scale < $row['hit']) - $scale = $row['hit']; - - $context['search_terms'][] = array( - 'text' => $row['phrase'], - 'scale' => round(($row['hit'] * 100) / $scale), - 'hit' => $row['hit'] - ); - } - - $smcFunc['db_free_result']($request); - - cache_put_data('optimus_search_terms', $context['search_terms'], 3600); - } - - $this->showTopSearchTerms(); - } - - private function extendTitles() - { - global $board_info, $context; - - if (SMF === 'SSI') - return; - - // Board titles - if (! empty($board_info['total_topics']) && is_on('optimus_board_extend_title')) { - op_config('optimus_board_extend_title') == 1 - ? $context['page_title_html_safe'] = $context['forum_name'] . ' - ' . $context['page_title_html_safe'] - : $context['page_title_html_safe'] = $context['page_title_html_safe'] . ' - ' . $context['forum_name']; - } - - // Topic titles - if (! empty($context['first_message']) && is_on('optimus_topic_extend_title')) { - op_config('optimus_topic_extend_title') == 1 - ? $context['page_title_html_safe'] = $context['forum_name'] . ' - ' . $board_info['name'] . ' - ' . $context['page_title_html_safe'] - : $context['page_title_html_safe'] = $context['page_title_html_safe'] . ' - ' . $board_info['name'] . ' - ' . $context['forum_name']; - } - } - - private function prepareMetaTags() - { - global $context, $settings; - - if (is_on('optimus_forum_index')) - $context['page_title_html_safe'] = un_htmlspecialchars($context['page_title_html_safe']); - - if (! empty($context['robot_no_index'])) - return; - - $meta = [ - 'og:site_name', - 'og:title', - 'og:url', - 'og:image', - 'og:description' - ]; - - foreach ($context['meta_tags'] as $key => $value) { - foreach ($value as $k => $v) { - if ($k === 'property' && in_array($v, $meta)) - $context['meta_tags'][$key] = array_merge(array('prefix' => 'og: https://ogp.me/ns#'), $value); - - if ($v === 'og:image') { - $og_image_key = $key; - - if (! empty($context['optimus_og_image'])) { - $image_data[0] = $context['optimus_og_image']['width']; - $image_data[1] = $context['optimus_og_image']['height']; - $image_data['mime'] = $context['optimus_og_image']['mime']; - } - } - } - } - - if (! empty($image_data)) { - $context['meta_tags'] = array_merge( - array_slice($context['meta_tags'], 0, $og_image_key + 1, true), - array( - array('prefix' => 'og: https://ogp.me/ns#', 'property' => 'og:image:type', 'content' => $image_data['mime']) - ), - array( - array('prefix' => 'og: https://ogp.me/ns#', 'property' => 'og:image:width', 'content' => $image_data[0]) - ), - array( - array('prefix' => 'og: https://ogp.me/ns#', 'property' => 'og:image:height', 'content' => $image_data[1]) - ), - array_slice($context['meta_tags'], $og_image_key + 1, null, true) - ); - } - - // Various types - if (! empty($context['optimus_og_type'])) { - $type = key($context['optimus_og_type']); - $context['meta_tags'][] = array('prefix' => 'og: https://ogp.me/ns#', 'property' => 'og:type', 'content' => $type); - $optimus_custom_types = array_filter($context['optimus_og_type'][$type]); - - foreach ($optimus_custom_types as $property => $content) { - if (is_array($content)) { - foreach ($content as $value) { - $context['meta_tags'][] = array('prefix' => $type . ': https://ogp.me/ns/' . $type . '#', 'property' => $type . ':' . $property, 'content' => $value); - } - } else { - $context['meta_tags'][] = array('prefix' => $type . ': https://ogp.me/ns/' . $type . '#', 'property' => $type . ':' . $property, 'content' => $content); - } - } - } - - if ($context['current_action'] == 'profile' && op_is_request('u')) { - $context['meta_tags'][] = array('prefix' => 'og: https://ogp.me/ns#', 'property' => 'og:type', 'content' => 'profile'); - } - - // Twitter cards - if (is_on('optimus_tw_cards') && isset($context['canonical_url'])) { - $context['meta_tags'][] = array('property' => 'twitter:card', 'content' => 'summary'); - $context['meta_tags'][] = array('property' => 'twitter:site', 'content' => '@' . op_config('optimus_tw_cards')); - - if (! empty($settings['og_image'])) - $context['meta_tags'][] = array('property' => 'twitter:image', 'content' => $settings['og_image']); - } - - // Facebook - if (is_on('optimus_fb_appid')) - $context['meta_tags'][] = array('prefix' => 'fb: https://ogp.me/ns/fb#', 'property' => 'fb:app_id', 'content' => op_config('optimus_fb_appid')); - - // Metatags - if (is_on('optimus_meta')) { - $tags = array_filter(unserialize(op_config('optimus_meta'))); - - foreach ($tags as $name => $value) { - $context['meta_tags'][] = array('name' => $name, 'content' => $value); - } - } - } - - private function showTopSearchTerms() - { - global $context; - - if (empty($context['search_terms']) || ! $this->canViewSearchTerms()) - return; - - loadTemplate('Optimus'); - - $context['template_layers'][] = 'search_terms'; - } - - private function canViewSearchTerms(): bool - { - return is_on('optimus_log_search') && allowedTo('optimus_view_search_terms'); + $context['credits_modifications'][] = Copyright::getLink() . ' © 2010–' . date('Y') . ', Bugo'; } } diff --git a/Sources/Optimus/Robots.php b/Sources/Optimus/Robots.php deleted file mode 100644 index 8408529..0000000 --- a/Sources/Optimus/Robots.php +++ /dev/null @@ -1,122 +0,0 @@ -url_path = parse_url($boardurl, PHP_URL_PATH) ?? ''; - $this->rules[] = 'User-agent: *'; - - // Mod authors can add or change generated rules - call_integration_hook('integrate_optimus_robots', array(&$this->custom_rules, $this->url_path, - &$this->use_sef)); - - $this->addRules(); - $this->addNews(); - $this->addAssets(); - $this->addSitemaps(); - - $new_robots = implode('
', str_replace("|", '', $this->rules)); - $context['new_robots_content'] = parse_bbc('[code]' . $new_robots . '[/code]'); - } - - private function addRules() - { - if ($this->use_sef) { - foreach ($this->actions as $action) - $this->rules[] = 'Disallow: ' . $this->url_path . '/' . $action . '/'; - } else { - $this->rules[] = 'Disallow: ' . $this->url_path . '/*action'; - } - - $this->rules[] = 'Disallow: ' . $this->url_path . '/*PHPSESSID'; - - if (! $this->use_sef) { - $this->rules[] = 'Disallow: ' . $this->url_path . '/*;'; - } - - // Front page - $this->rules[] = 'Allow: ' . $this->url_path . '/$'; - - // Content - if (! $this->use_sef) { - if (is_on('queryless_urls')) { - $this->rules[] = 'Allow: ' . $this->url_path . "/*board,*.0.html$\nAllow: " . $this->url_path . '/*topic,*.0.html$'; - } else { - $this->rules[] = 'Allow: ' . $this->url_path . "/*board=*.0$\nAllow: " . $this->url_path . '/*topic=*.0$'; - } - } - - // Add custom rules - $this->rules = array_merge($this->rules, $this->custom_rules); - } - - private function addNews() - { - $this->rules[] = is_on('xmlnews_enable') ? 'Allow: ' . $this->url_path . '/*.xml' : ''; - } - - private function addAssets() - { - $this->rules[] = "Allow: /*.css$\nAllow: /*.js$\nAllow: /*.png$\nAllow: /*.jpg$\nAllow: /*.gif$"; - } - - private function addSitemaps() - { - global $boardurl, $boarddir, $sourcedir, $scripturl; - - $mapFile = 'sitemap.xml'; - $gzFile = 'sitemap.xml.gz'; - - $mapFile = file_exists($boarddir . '/' . $mapFile) ? $boardurl . '/' . $mapFile : ''; - $gzFile = file_exists($boarddir . '/' . $gzFile) ? $boardurl . '/' . $gzFile : ''; - - $sitemapModInstalled = file_exists($sourcedir . '/Sitemap.php'); - - if ($sitemapModInstalled) - $this->rules[] = 'Allow: ' . $this->url_path . '/*sitemap'; - - if (! empty($mapFile) || ! empty($gzFile) || $sitemapModInstalled) - $this->rules[] = '|'; - - if ($sitemapModInstalled) - $this->rules[] = 'Sitemap: ' . $scripturl . '?action=sitemap;xml'; - - if (! empty($gzFile)) - $this->rules[] = 'Sitemap: ' . $gzFile; - elseif (! empty($mapFile)) - $this->rules[] = 'Sitemap: ' . $mapFile; - } -} diff --git a/Sources/Optimus/Subs.php b/Sources/Optimus/Subs.php deleted file mode 100644 index 119cf94..0000000 --- a/Sources/Optimus/Subs.php +++ /dev/null @@ -1,135 +0,0 @@ - and duplicate spaces - $text = preg_replace('~\s+~', ' ', strip_tags(str_replace('
', ' ', $text))); - - // Remove all urls - $text = preg_replace('~http(s)?://(.*)\s~U', '', $text); - - // Additional replacements - $text = strtr($text, array(' ' => ' ', '&nbsp;' => ' ', '"' => '')); - - $sentences = preg_split('/(\.|\?|\!)(\s)/', $text); - - // Limit given text - $text = shorten_subject($text, $length); - - if (count($sentences) <= $num_sentences) - return trim($text); - - $stop_at = 0; - foreach ($sentences as $i => $sentence) { - $stop_at += $smcFunc['strlen']($sentence); - if ($i >= $num_sentences - 1) - break; - } - - $stop_at += ($num_sentences * 2); - - return trim($smcFunc['substr']($text, 0, $stop_at)); - } - - public static function runAddons() - { - static $loadedAddons = []; - - $addons = self::getAddons(); - - if (empty($addons)) - return; - - foreach ($addons as $addon) { - $class = self::getClassName($addon); - - if (class_exists($class)) { - self::loadAddonLanguages($addon); - - if (! isset($loadedAddons[$class])) { - new $class; - - $loadedAddons[$class] = $addon; - } - } - } - } - - private static function getAddons(): ?array - { - if (! is_dir(__DIR__ . '/addons')) - return []; - - if (($addons = cache_get_data('optimus_addons', 3600)) === null) { - foreach ((new \FilesystemIterator(__DIR__ . '/addons')) as $object) { - $filename = $object->getBasename(); - if ($object->isFile()) { - $addons[] = str_replace('.php', '', $filename); - } - - if ($object->isDir() && is_file($object->getPathname() . '/' . $filename . '.php')) { - $addons[] = $filename . '|' . $filename; - } - } - - $addons = array_diff($addons, ['index']); - - cache_put_data('optimus_addons', $addons, 3600); - } - - return $addons; - } - - private static function getClassName(string $addon): string - { - return __NAMESPACE__ . '\Addons\\' . str_replace('|', '\\', $addon); - } - - private static function loadAddonLanguages(string $addon) - { - global $user_info, $txt; - - if (empty($txt)) - return; - - $languages = array_merge(['english'], [$user_info['language'] ?? null]); - $base_dir = __DIR__ . '/addons/' . explode('|', $addon)[0] . '/langs/'; - - foreach ($languages as $lang) { - $lang_file = $base_dir . $lang . '.php'; - - if (is_file($lang_file)) - require_once $lang_file; - } - } -} diff --git a/Sources/Optimus/Task.php b/Sources/Optimus/Task.php deleted file mode 100644 index 188c931..0000000 --- a/Sources/Optimus/Task.php +++ /dev/null @@ -1,68 +0,0 @@ - 'string-255', 'task_class' => 'string-255', 'task_data' => 'string', 'claimed_time' => 'int'), - array('$sourcedir/Optimus/Task.php', '\Bugo\Optimus\Task', '', time() + ($frequency * 24 * 60 * 60)), - array('id_task') - ); - - return true; - } -} diff --git a/Sources/Optimus/Sitemap.php b/Sources/Optimus/Tasks/Sitemap.php similarity index 54% rename from Sources/Optimus/Sitemap.php rename to Sources/Optimus/Tasks/Sitemap.php index d217ba6..251884c 100644 --- a/Sources/Optimus/Sitemap.php +++ b/Sources/Optimus/Tasks/Sitemap.php @@ -1,8 +1,4 @@ -dispatcher = (new DispatcherFactory())(); + + $this->createXml(); + + $frequency = 1; + if (! empty($modSettings['optimus_update_frequency'])) { + $frequency = match ($modSettings['optimus_update_frequency']) { + 1 => 3, + 2 => 7, + 3 => 14, + default => 30, + }; + } + + $smcFunc['db_insert']('insert', + '{db_prefix}background_tasks', + array('task_file' => 'string-255', 'task_class' => 'string-255', 'task_data' => 'string', 'claimed_time' => 'int'), + array('$sourcedir/Optimus/Tasks/Sitemap.php', '\Bugo\Optimus\Tasks\Sitemap', '', time() + ($frequency * 24 * 60 * 60)), + array('id_task') + ); + + return true; + } + + public function createXml(): void { global $modSettings, $context, $boardurl, $boarddir; @@ -33,25 +80,25 @@ public static function createXml() $modSettings['disableQueryCheck'] = true; $modSettings['pretty_bufferusecache'] = false; - $max_items = op_config('optimus_sitemap_items_display', self::MAX_ITEMS); + $maxItems = $modSettings['optimus_sitemap_items_display'] ?? self::MAX_ITEMS; - $sitemap_counter = 0; + $sitemapCounter = 0; $items = []; - $getLinks = fn() => yield from self::getLinks(); + $getLinks = fn() => yield from $this->getLinks(); foreach ($getLinks() as $counter => $entry) { - if (! empty($counter) && $counter % $max_items == 0) - $sitemap_counter++; + if (! empty($counter) && $counter % $maxItems == 0) + $sitemapCounter++; $entry['lastmod'] = (int) $entry['lastmod']; - $items[$sitemap_counter][] = array( + $items[$sitemapCounter][] = array( 'loc' => $entry['loc'], - 'lastmod' => empty($entry['lastmod']) ? null : self::getDateIso8601($entry['lastmod']), - 'changefreq' => empty($entry['lastmod']) ? null : self::getFrequency($entry['lastmod']), - 'priority' => empty($entry['lastmod']) ? null : self::getPriority($entry['lastmod']), + 'lastmod' => empty($entry['lastmod']) ? null : $this->getDateIso8601($entry['lastmod']), + 'changefreq' => empty($entry['lastmod']) ? null : $this->getFrequency($entry['lastmod']), + 'priority' => empty($entry['lastmod']) ? null : $this->getPriority($entry['lastmod']), //'image' => $entry['image'] ?? null ); } @@ -60,7 +107,7 @@ public static function createXml() return; // The update frequency of the main page - if (is_off('optimus_main_page_frequency')) + if (empty($modSettings['optimus_main_page_frequency'])) $items[0][0]['changefreq'] = 'always'; // The priority of the main page @@ -70,43 +117,46 @@ public static function createXml() loadTemplate('Optimus'); - if ($sitemap_counter > 0) { - $gz_maps = []; - for ($number = 0; $number <= $sitemap_counter; $number++) { + if ($sitemapCounter > 0) { + $gzMaps = []; + for ($number = 0; $number <= $sitemapCounter; $number++) { $context['sitemap'] = $items[$number]; ob_start(); template_sitemap_xml(); - $content = ob_get_clean(); + $this->content = ob_get_clean(); - call_integration_hook('integrate_optimus_sitemap_rewrite_content', array(&$content)); + // Some mods should rewrite full content (PrettyURLs, etc.) + $this->dispatcher->dispatch(new AddonEvent('sitemap.rewrite_content', $this)); - $gz_maps[$number] = self::createFile($boarddir . '/sitemap_' . $number . '.xml', $content); + $gzMaps[$number] = $this->createFile($boarddir . '/sitemap_' . $number . '.xml', $this->content); } $context['sitemap'] = []; - for ($number = 0; $number <= $sitemap_counter; $number++) - $context['sitemap'][$number]['loc'] = $boardurl . '/sitemap_' . $number . '.xml' . (! empty($gz_maps[$number]) ? '.gz' : ''); + for ($number = 0; $number <= $sitemapCounter; $number++) { + $context['sitemap'][$number]['loc'] = $boardurl . '/sitemap_' . $number . '.xml' . (! empty($gzMaps[$number]) ? '.gz' : ''); + } ob_start(); template_sitemapindex_xml(); - $content = ob_get_clean(); + $this->content = ob_get_clean(); } else { $context['sitemap'] = $items[0]; ob_start(); template_sitemap_xml(); - $content = ob_get_clean(); + $this->content = ob_get_clean(); - call_integration_hook('integrate_optimus_sitemap_rewrite_content', array(&$content)); + // Some mods should rewrite full content (PrettyURLs, etc.) + $this->dispatcher->dispatch(new AddonEvent('sitemap.rewrite_content', $this)); } - self::createFile($boarddir . '/sitemap.xml', $content); + $this->createFile($boarddir . '/sitemap.xml', $this->content); ignore_user_abort(false); } - public static function getLastDate(array $links): int + public function getLastDate(array $links): int { if (empty($links)) return time(); @@ -114,45 +164,49 @@ public static function getLastDate(array $links): int $data = array_values(array_values($links)); $dates = []; - foreach ($data as $value) + foreach ($data as $value) { $dates[] = (int) $value['lastmod']; + } return max($dates); } - public static function getLinks(): array + public function getLinks(): array { global $context, $modSettings, $boardurl; if (! isset($context['optimus_ignored_boards'])) $context['optimus_ignored_boards'] = []; - if (is_on('recycle_board')) + if (! empty($modSettings['recycle_board'])) $context['optimus_ignored_boards'][] = (int) $modSettings['recycle_board']; - $links = array_merge(self::getBoardLinks(), self::getTopicLinks()); + $this->links = array_merge($this->getBoardLinks(), $this->getTopicLinks()); - // Mod authors can add or process their own links - call_integration_hook('integrate_optimus_sitemap', array(&$links)); + // Modders can add custom links + $this->dispatcher->dispatch(new AddonEvent('sitemap.links', $this)); // Adding the main page $home = array( 'loc' => $boardurl . '/', - 'lastmod' => is_on('optimus_main_page_frequency') ? self::getLastDate($links) : time() + 'lastmod' => empty($modSettings['optimus_main_page_frequency']) ? time() : $this->getLastDate($this->links) ); - array_unshift($links, $home); + // Modders can process links with SEF handler + $this->dispatcher->dispatch(new AddonEvent('sitemap.sef_links', $this)); - return $links; + array_unshift($this->links, $home); + + return $this->links; } - private static function getBoardLinks(): array + private function getBoardLinks(): array { - global $smcFunc, $context, $scripturl; + global $modSettings, $smcFunc, $context, $scripturl; - $start_year = (int) op_config('optimus_start_year', 0); + $startYear = (int) ($modSettings['optimus_start_year'] ?? 0); - $request = $smcFunc['db_query']('', ' + $request = $smcFunc['db_query']('', /** @lang text */ ' SELECT b.id_board, GREATEST(m.poster_time, m.modified_time) AS last_date FROM {db_prefix}boards AS b LEFT JOIN {db_prefix}messages AS m ON (b.id_last_msg = m.id_msg) @@ -165,14 +219,14 @@ private static function getBoardLinks(): array )' . (! empty($context['optimus_ignored_boards']) ? ' AND b.id_board NOT IN ({array_int:ignored_boards})' : '') . ' AND b.redirect = {string:empty_string} - AND b.num_posts > {int:num_posts}' . ($start_year ? ' + AND b.num_posts > {int:num_posts}' . ($startYear ? ' AND YEAR(FROM_UNIXTIME(m.poster_time)) >= {int:start_year}' : '') . ' ORDER BY b.id_board DESC', array( 'ignored_boards' => $context['optimus_ignored_boards'], 'empty_string' => '', 'num_posts' => 0, - 'start_year' => $start_year + 'start_year' => $startYear ) ); @@ -180,16 +234,14 @@ private static function getBoardLinks(): array while ($row = $smcFunc['db_fetch_assoc']($request)) { $context['optimus_open_boards'][] = $row['id_board']; - if (is_on('optimus_sitemap_boards')) { - $board_url = $scripturl . '?board=' . $row['id_board'] . '.0'; + if (! empty($modSettings['optimus_sitemap_boards'])) { + $boardUrl = $scripturl . '?board=' . $row['id_board'] . '.0'; - if (is_on('queryless_urls')) - $board_url = $scripturl . '/board,' . $row['id_board'] . '.0.html'; - - call_integration_hook('integrate_optimus_create_sef_url', array(&$board_url)); + if (! empty($modSettings['queryless_urls'])) + $boardUrl = $scripturl . '/board,' . $row['id_board'] . '.0.html'; $links[] = array( - 'loc' => $board_url, + 'loc' => $boardUrl, 'lastmod' => $row['last_date'] ); } @@ -200,9 +252,9 @@ private static function getBoardLinks(): array return $links; } - private static function getTopicLinks(): array + private function getTopicLinks(): array { - global $db_temp_cache, $db_cache, $smcFunc, $context, $scripturl; + global $db_temp_cache, $db_cache, $modSettings, $smcFunc, $context, $scripturl; $start = 0; $limit = 1000; @@ -211,22 +263,22 @@ private static function getTopicLinks(): array $db_temp_cache = $db_cache; $db_cache = []; - $start_year = (int) op_config('optimus_start_year', 0); - $num_replies = (int) op_config('optimus_sitemap_topics_num_replies', 0); - $total_rows = (int) (is_on('optimus_sitemap_all_topic_pages') ? op_config('totalMessages', 0) : op_config('totalTopics', 0)); + $startYear = (int) ($modSettings['optimus_start_year'] ?? 0); + $num_replies = (int) ($modSettings['optimus_sitemap_topics_num_replies'] ?? 0); + $total_rows = (int) (empty($modSettings['optimus_sitemap_all_topic_pages']) ? ($modSettings['totalTopics'] ?? 0) : ($modSettings['totalMessages'] ?? 0)); $links = []; $topics = []; $images = []; - $messages_per_page = (int) op_config('defaultMaxMessages', 0); + $messages_per_page = (int) ($modSettings['defaultMaxMessages'] ?? 0); while ($start < $total_rows) { @set_time_limit(600); if (function_exists('apache_reset_timeout')) @apache_reset_timeout(); - if (is_on('optimus_sitemap_all_topic_pages')) { + if (! empty($modSettings['optimus_sitemap_all_topic_pages'])) { $request = $smcFunc['db_query']('', ' SELECT t.id_topic, t.num_replies, m.id_msg, GREATEST(m.poster_time, m.modified_time) AS last_date, a.id_attach, a.filename FROM {db_prefix}messages AS m @@ -234,7 +286,7 @@ private static function getTopicLinks(): array LEFT JOIN {db_prefix}attachments AS a ON (a.id_msg = t.id_first_msg AND a.attachment_type = 0 AND a.width <> 0 AND a.height <> 0 AND a.approved = 1) WHERE t.id_board IN ({array_int:open_boards}) AND t.num_replies >= {int:num_replies} - AND t.approved = {int:is_approved}' . ($start_year ? ' + AND t.approved = {int:is_approved}' . ($startYear ? ' AND YEAR(FROM_UNIXTIME(GREATEST(m.poster_time, m.modified_time))) >= {int:start_year}' : '') . ' ORDER BY t.id_topic DESC, last_date LIMIT {int:start}, {int:limit}', @@ -242,15 +294,15 @@ private static function getTopicLinks(): array 'open_boards' => $context['optimus_open_boards'], 'num_replies' => $num_replies, 'is_approved' => 1, - 'start_year' => $start_year, + 'start_year' => $startYear, 'start' => $start, 'limit' => $limit ) ); while ($row = $smcFunc['db_fetch_assoc']($request)) { - $total_pages = ceil($row['num_replies'] / $messages_per_page); - $page_start = 0; + $totalPages = ceil($row['num_replies'] / $messages_per_page); + $pageStart = 0; if (! empty($row['id_attach']) && ! isset($images[$row['id_topic']])) { $images[$row['id_topic']] = [ @@ -259,17 +311,17 @@ private static function getTopicLinks(): array ]; } - if (empty($total_pages)) { - $topics[$row['id_topic']][$page_start][$row['id_msg']] = $row['last_date']; + if (empty($totalPages)) { + $topics[$row['id_topic']][$pageStart][$row['id_msg']] = $row['last_date']; } else { - for ($i = 0; $i <= $total_pages; $i++) { - $topics[$row['id_topic']][$page_start][$row['id_msg']] = $row['last_date']; + for ($i = 0; $i <= $totalPages; $i++) { + $topics[$row['id_topic']][$pageStart][$row['id_msg']] = $row['last_date']; - if (count($topics[$row['id_topic']][$page_start]) <= $messages_per_page) + if (count($topics[$row['id_topic']][$pageStart]) <= $messages_per_page) break; - $topics[$row['id_topic']][$page_start] = array_slice($topics[$row['id_topic']][$page_start], 0, $messages_per_page, true); - $page_start += $messages_per_page; + $topics[$row['id_topic']][$pageStart] = array_slice($topics[$row['id_topic']][$pageStart], 0, $messages_per_page, true); + $pageStart += $messages_per_page; } } } @@ -281,7 +333,7 @@ private static function getTopicLinks(): array LEFT JOIN {db_prefix}attachments AS a ON (a.id_msg = t.id_first_msg AND a.attachment_type = 0 AND a.width <> 0 AND a.height <> 0 AND a.approved = 1) WHERE t.id_board IN ({array_int:open_boards}) AND t.num_replies >= {int:num_replies} - AND t.approved = {int:is_approved}' . ($start_year ? ' + AND t.approved = {int:is_approved}' . ($startYear ? ' AND YEAR(FROM_UNIXTIME(GREATEST(m.poster_time, m.modified_time))) >= {int:start_year}' : '') . ' ORDER BY t.id_topic DESC, last_date DESC LIMIT {int:start}, {int:limit}', @@ -289,19 +341,17 @@ private static function getTopicLinks(): array 'open_boards' => $context['optimus_open_boards'], 'num_replies' => $num_replies, 'is_approved' => 1, - 'start_year' => $start_year, + 'start_year' => $startYear, 'start' => $start, 'limit' => $limit ) ); while ($row = $smcFunc['db_fetch_assoc']($request)) { - $topic_url = $scripturl . '?topic=' . $row['id_topic'] . '.0'; - - if (is_on('queryless_urls')) - $topic_url = $scripturl . '/topic,' . $row['id_topic'] . '.0.html'; + $topicUrl = $scripturl . '?topic=' . $row['id_topic'] . '.0'; - call_integration_hook('integrate_optimus_create_sef_url', array(&$topic_url)); + if (! empty($modSettings['queryless_urls'])) + $topicUrl = $scripturl . '/topic,' . $row['id_topic'] . '.0.html'; if (! empty($row['id_attach']) && ! isset($images[$row['id_topic']])) { $images[$row['id_topic']] = [ @@ -311,7 +361,7 @@ private static function getTopicLinks(): array } $links[$row['id_topic']] = array( - 'loc' => $topic_url, + 'loc' => $topicUrl, 'lastmod' => $row['last_date'], 'image' => $images[$row['id_topic']] ?? [] ); @@ -324,13 +374,11 @@ private static function getTopicLinks(): array } foreach ($topics as $topic_id => $topic_data) { - foreach ($topic_data as $page_start => $dates) { - $topic_url = is_on('queryless_urls') ? $scripturl . '/topic,' . $topic_id . '.' . $page_start . '.html' : $scripturl . '?topic=' . $topic_id . '.' . $page_start; - - call_integration_hook('integrate_optimus_create_sef_url', array(&$topic_url)); + foreach ($topic_data as $pageStart => $dates) { + $topicUrl = empty($modSettings['queryless_urls']) ? $scripturl . '?topic=' . $topic_id . '.' . $pageStart : $scripturl . '/topic,' . $topic_id . '.' . $pageStart . '.html'; $links[] = array( - 'loc' => $topic_url, + 'loc' => $topicUrl, 'lastmod' => max($dates), 'image' => $images[$topic_id] ?? [] ); @@ -343,7 +391,7 @@ private static function getTopicLinks(): array return array_values($links); } - private static function getDateIso8601(int $timestamp): string + private function getDateIso8601(int $timestamp): string { if (empty($timestamp)) return ''; @@ -353,7 +401,7 @@ private static function getDateIso8601(int $timestamp): string return date('Y-m-d\TH:i:s', $timestamp) . $gmt; } - private static function getFrequency(int $timestamp): string + private function getFrequency(int $timestamp): string { $frequency = time() - $timestamp; @@ -369,7 +417,7 @@ private static function getFrequency(int $timestamp): string return 'yearly'; } - private static function getPriority(int $timestamp): string + private function getPriority(int $timestamp): string { $diff = floor((time() - $timestamp) / 60 / 60 / 24); @@ -383,7 +431,7 @@ private static function getPriority(int $timestamp): string return '0.2'; } - private static function createFile(string $path, string $data): bool + private function createFile(string $path, string $data): bool { fclose(fopen($path, "a+b")); @@ -396,8 +444,8 @@ private static function createFile(string $path, string $data): bool flock($fp, LOCK_UN); fclose($fp); - // If filesize > 50MB, then create gz version - if (filesize($path) > (50 * 1024 * 1024)) { + // If filesize > 50 MB, then create sitemap.xml.gz version + if (function_exists('gzencode') && filesize($path) > (50 * 1024 * 1024)) { fclose(fopen($path . '.gz', "a+b")); if (! $fpgz = fopen($path . '.gz', 'w+b')) diff --git a/Sources/Optimus/Tasks/index.php b/Sources/Optimus/Tasks/index.php new file mode 100644 index 0000000..6777639 --- /dev/null +++ b/Sources/Optimus/Tasks/index.php @@ -0,0 +1,7 @@ +' . OP_NAME . ''; + } +} diff --git a/Sources/Optimus/Utils/Input.php b/Sources/Optimus/Utils/Input.php new file mode 100644 index 0000000..6cac6ae --- /dev/null +++ b/Sources/Optimus/Utils/Input.php @@ -0,0 +1,86 @@ + $value) { + $_REQUEST[$key] = $value; + } + + return false; + } + + return $_REQUEST[$name] ?? $defaultValue; + } + + public static function server(string $name = ''): mixed + { + if (empty($name)) + return $_SERVER; + + $name = strtoupper($name); + + return $_SERVER[$name] ?? getenv($name) ?? null; + } + + public static function isRequest($name): bool + { + return isset($_REQUEST[$name]); + } + + public static function isPost($name): bool + { + return isset($_POST[$name]); + } + + public static function isGet($name): bool + { + return isset($_GET[$name]); + } + + public static function xss(string|array $data): string|array + { + global $smcFunc; + + if (is_array($data)) { + return array_map('self::xss', $data); + } + + return $smcFunc['htmlspecialchars']($data, ENT_QUOTES); + } + + public static function filter( + string $key, + string $filter = 'string', + int $input = INPUT_POST + ): array|string + { + $filter = match ($filter) { + 'int' => FILTER_VALIDATE_INT, + 'bool' => FILTER_VALIDATE_BOOLEAN, + 'url' => FILTER_VALIDATE_URL, + default => FILTER_DEFAULT, + }; + + return self::xss(filter_input($input, $key, $filter)); + } +} diff --git a/Sources/Optimus/Utils/RobotsGenerator.php b/Sources/Optimus/Utils/RobotsGenerator.php new file mode 100644 index 0000000..0122fe5 --- /dev/null +++ b/Sources/Optimus/Utils/RobotsGenerator.php @@ -0,0 +1,132 @@ +urlPath = parse_url($boardurl, PHP_URL_PATH) ?? ''; + $this->rules[] = 'User-agent: *'; + + // Mod authors can add or change generated rules + $dispatcher = (new DispatcherFactory())(); + $dispatcher->dispatch(new AddonEvent('robots.rules', $this)); + + $this->addRules(); + $this->addNews(); + $this->addAssets(); + $this->addSitemaps(); + + $new_robots = implode('
', str_replace("|", '', $this->rules)); + $context['new_robots_content'] = parse_bbc('[code]' . $new_robots . '[/code]'); + } + + private function addRules(): void + { + global $modSettings; + + if ($this->useSef) { + foreach ($this->actions as $action) { + $this->rules[] = 'Disallow: ' . $this->urlPath . '/' . $action . '/'; + } + } else { + $this->rules[] = 'Disallow: ' . $this->urlPath . '/*action'; + } + + $this->rules[] = 'Disallow: ' . $this->urlPath . '/*PHPSESSID'; + + if (! $this->useSef) { + $this->rules[] = 'Disallow: ' . $this->urlPath . '/*;'; + } + + // Front page + $this->rules[] = 'Allow: ' . $this->urlPath . '/$'; + + // Content + if (! $this->useSef) { + if (empty($modSettings['queryless_urls'])) { + $this->rules[] = 'Allow: ' . $this->urlPath . "/*board=*.0$\nAllow: " . $this->urlPath . '/*topic=*.0$'; + } else { + $this->rules[] = 'Allow: ' . $this->urlPath . "/*board,*.0.html$\nAllow: " . $this->urlPath . '/*topic,*.0.html$'; + } + } + + // Add custom rules + $this->rules = array_merge($this->rules, $this->customRules); + } + + private function addNews(): void + { + global $modSettings; + + $this->rules[] = empty($modSettings['xmlnews_enable']) ? '' : 'Allow: ' . $this->urlPath . '/*.xml'; + } + + private function addAssets(): void + { + $this->rules[] = "Allow: /*.css$\nAllow: /*.js$\nAllow: /*.png$\nAllow: /*.jpg$\nAllow: /*.gif$"; + } + + private function addSitemaps(): void + { + global $boardurl, $boarddir, $sourcedir, $scripturl; + + $mapFile = 'sitemap.xml'; + $gzFile = 'sitemap.xml.gz'; + + $mapFile = file_exists($boarddir . '/' . $mapFile) ? $boardurl . '/' . $mapFile : ''; + $gzFile = file_exists($boarddir . '/' . $gzFile) ? $boardurl . '/' . $gzFile : ''; + + $sitemapModInstalled = file_exists($sourcedir . '/Sitemap.php'); + + if ($sitemapModInstalled) + $this->rules[] = 'Allow: ' . $this->urlPath . '/*sitemap'; + + if (! empty($mapFile) || ! empty($gzFile) || $sitemapModInstalled) + $this->rules[] = '|'; + + if ($sitemapModInstalled) + $this->rules[] = 'Sitemap: ' . $scripturl . '?action=sitemap;xml'; + + if (! empty($gzFile)) + $this->rules[] = 'Sitemap: ' . $gzFile; + elseif (! empty($mapFile)) + $this->rules[] = 'Sitemap: ' . $mapFile; + } +} diff --git a/Sources/Optimus/Utils/Str.php b/Sources/Optimus/Utils/Str.php new file mode 100644 index 0000000..f6e6412 --- /dev/null +++ b/Sources/Optimus/Utils/Str.php @@ -0,0 +1,56 @@ + and duplicate spaces + $text = preg_replace('~\s+~', ' ', strip_tags(str_replace('
', ' ', $text))); + + // Remove all urls + $text = preg_replace('~http(s)?://(.*)\s~U', '', $text); + + // Additional replacements + $text = strtr($text, array(' ' => ' ', '&nbsp;' => ' ', '"' => '')); + + $sentences = preg_split('/([.?!])(\s)/', $text); + + // Limit given text + $text = shorten_subject($text, $length); + + if (count($sentences) <= $num_sentences) + return trim($text); + + $stop_at = 0; + foreach ($sentences as $i => $sentence) { + $stop_at += $smcFunc['strlen']($sentence); + if ($i >= $num_sentences - 1) + break; + } + + $stop_at += ($num_sentences * 2); + + return trim($smcFunc['substr']($text, 0, $stop_at)); + } +} diff --git a/Sources/Optimus/Utils/index.php b/Sources/Optimus/Utils/index.php new file mode 100644 index 0000000..6777639 --- /dev/null +++ b/Sources/Optimus/Utils/index.php @@ -0,0 +1,7 @@ +)([^#<]+)~']; - $context['pretty']['replace_patterns'] = ['~()([^<]+)~']; - - if (function_exists('pretty_rewrite_buffer')) - $content = pretty_rewrite_buffer($content); - } -} diff --git a/Sources/Optimus/addons/SimpleSEF.php b/Sources/Optimus/addons/SimpleSEF.php deleted file mode 100644 index b2246f1..0000000 --- a/Sources/Optimus/addons/SimpleSEF.php +++ /dev/null @@ -1,49 +0,0 @@ - 0]); - } - - public function optimusRobots(array &$custom_rules, string $url_path, bool &$use_sef) - { - $use_sef = is_on('simplesef_enable') && is_file(dirname(__DIR__, 2) . '/SimpleSEF.php'); - } - - public function optimusCreateSefUrl(string &$url) - { - if (! class_exists('\SimpleSEF')) - return; - - $method = method_exists('\SimpleSEF', 'getSefUrl') ? 'getSefUrl' : 'create_sef_url'; - - $url = (new \SimpleSEF)->$method($url); - } -} diff --git a/Sources/Optimus/app.php b/Sources/Optimus/app.php index affe525..5f8d11d 100644 --- a/Sources/Optimus/app.php +++ b/Sources/Optimus/app.php @@ -1,198 +1,25 @@ - $value) { - $_SESSION[$key] = $value; - } - - return; - } - - return $_SESSION[$name] ?? null; - } -} - -if (! function_exists('op_is_request')) { - function op_is_request($name): bool - { - return isset($_REQUEST[$name]); - } -} - -if (! function_exists('op_is_post')) { - function op_is_post($name): bool - { - return isset($_POST[$name]); - } -} - -if (! function_exists('op_is_get')) { - function op_is_get($name): bool - { - return isset($_GET[$name]); - } -} - -if (! function_exists('op_request')) { - function op_request($name, $defaultValue = false) - { - if (is_array($name)) { - foreach ($name as $key => $value) { - $_REQUEST[$key] = $value; - } - - return false; - } - - return $_REQUEST[$name] ?? $defaultValue; - } -} - -if (! function_exists('is_on')) { - function is_on($setting): bool - { - $modSettings = op_global('modSettings'); - - return ! empty($modSettings[$setting]); - } -} - -if (! function_exists('is_off')) { - function is_off($setting): bool - { - return ! is_on($setting); - } -} - -if (! function_exists('is_set')) { - function is_set($setting): bool - { - $modSettings = op_global('modSettings'); - - return isset($modSettings[$setting]); - } -} - -if (! function_exists('op_config')) { - function op_config($setting, $defaultValue = null) - { - $modSettings = op_global('modSettings'); - - return $modSettings[$setting] ?? $defaultValue; - } -} - -if (! function_exists('op_xss')) { - function op_xss($data) - { - $smcFunc = op_global('smcFunc'); - - if (is_array($data)) - return array_map('op_xss', $data); - - return $smcFunc['htmlspecialchars']($data, ENT_QUOTES); - } -} - -if (! function_exists('op_filter')) { - function op_filter($key, $filter = 'string', $input = INPUT_POST) - { - switch ($filter) { - case 'int': - $filter = FILTER_VALIDATE_INT; - break; - - case 'bool': - $filter = FILTER_VALIDATE_BOOLEAN; - break; - - case 'url': - $filter = FILTER_VALIDATE_URL; - break; - - default: - $filter = FILTER_DEFAULT; - } - - return op_xss(filter_input($input, $key, $filter)); - } -} - -if (! function_exists('op_link')) { - function op_link(): string - { - $user_info = op_global('user_info'); - - $link = $user_info['language'] === 'russian' ? 'https://dragomano.ru/mods/optimus' : 'https://custom.simplemachines.org/mods/index.php?mod=2659'; - - return '' . OP_NAME . ''; - } -} - -new \Bugo\Optimus\Integration(); +(new Integration())(); diff --git a/Sources/Optimus/composer.json b/Sources/Optimus/composer.json new file mode 100644 index 0000000..f399904 --- /dev/null +++ b/Sources/Optimus/composer.json @@ -0,0 +1,18 @@ +{ + "require": { + "ext-curl": "*", + "ext-zlib": "*", + "league/event": "^3.0" + }, + "autoload": { + "psr-4": { + "Bugo\\Optimus\\": "" + } + }, + "config": { + "vendor-dir": "Libs", + "optimize-autoloader": true, + "preferred-install": "dist", + "sort-packages": true + } +} diff --git a/Themes/default/Optimus.template.php b/Themes/default/Optimus.template.php index 908dcc8..9672b95 100644 --- a/Themes/default/Optimus.template.php +++ b/Themes/default/Optimus.template.php @@ -1,6 +1,6 @@ '; } -function template_metatags() +function template_metatags(): void { global $context, $txt; @@ -81,7 +81,7 @@ function template_metatags() } } - echo ' + echo /** @lang text */ ' @@ -108,7 +108,7 @@ function addNewTag() { '; } -function template_redirect() +function template_redirect(): void { global $context, $txt; @@ -149,7 +149,7 @@ function template_redirect() '; } - echo ' + echo /** @lang text */ '
'; } function template_counters(): void { - global $context, $txt, $modSettings; - echo ' -
+
-

', $txt['optimus_counters'], '

+

', Lang::$txt['optimus_counters'], '

- +
- ', $txt['optimus_head_code_subtext'], ' + ', Lang::$txt['optimus_head_code_subtext'], '
- +
- +
- ', $txt['optimus_stat_code_subtext'], ' + ', Lang::$txt['optimus_stat_code_subtext'], '
- +
- +
- +
- +
- +
- +
- ', $txt['optimus_ignored_actions_subtext'], ' + ', Lang::$txt['optimus_ignored_actions_subtext'], '
- +
'; } function template_robots(): void { - global $context, $txt; - echo ' -
+
-

', $txt['optimus_robots_title'], '

+

', Lang::$txt['optimus_robots_title'], '

-

', $txt['optimus_rules'], '

+

', Lang::$txt['optimus_rules'], '

- ', $txt['optimus_rules_hint'], ' - ', $context['new_robots_content'], ' + ', Lang::$txt['optimus_rules_hint'], ' + ', Utils::$context['new_robots_content'], '
-

', $txt['optimus_links_title'], '

+

', Lang::$txt['optimus_links_title'], '

    '; - foreach ($txt['optimus_links'] as $link) { + foreach (Lang::$txt['optimus_links'] as $link) { echo '
  • ', $link[0], '
  • '; } @@ -267,14 +259,14 @@ function template_robots(): void

    robots.txt

- +

'; @@ -282,21 +274,19 @@ function template_robots(): void function template_htaccess(): void { - global $txt, $context; - echo '
-

', $txt['optimus_htaccess_title'], '

+

', Lang::$txt['optimus_htaccess_title'], '

-
+
- +
'; @@ -308,41 +298,37 @@ function template_footer_counters_above() function template_footer_counters_below(): void { - global $modSettings; - - if (!empty($modSettings['optimus_count_code'])) + if (! empty(Config::$modSettings['optimus_count_code'])) echo ' -
', $modSettings['optimus_count_code'], '
'; +
', Config::$modSettings['optimus_count_code'], '
'; } function template_sitemap_xml(): void { - global $modSettings, $scripturl, $context; - - $imageNamespace = empty($modSettings['optimus_sitemap_add_found_images']) ? '' : ' xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"'; + $imageNamespace = empty(Config::$modSettings['optimus_sitemap_add_found_images']) ? '' : ' xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"'; echo /** @lang text */ ' - + '; - foreach ($context['sitemap'] as $item) { + foreach (Utils::$context['sitemap'] as $item) { echo ' ', $item['loc'], ''; - if (!empty($item['lastmod'])) + if ( empty($item['lastmod'])) echo ' ', $item['lastmod'], ''; - if (!empty($item['changefreq'])) + if (! empty($item['changefreq'])) echo ' ', $item['changefreq'], ''; - if (!empty($item['priority'])) + if (! empty($item['priority'])) echo ' ', $item['priority'], ''; - if (!empty($item['image'])) { + if (! empty($item['image'])) { echo ' ' . $item['image']['loc'] . ' @@ -360,13 +346,11 @@ function template_sitemap_xml(): void function template_sitemapindex_xml(): void { - global $scripturl, $context; - echo ' - + '; - foreach ($context['sitemap'] as $item) + foreach (Utils::$context['sitemap'] as $item) echo ' ', $item['loc'], ' @@ -378,12 +362,10 @@ function template_sitemapindex_xml(): void function template_keywords_above(): void { - global $context; - echo '

- ' . $context['page_title'] . ' + ' . Utils::$context['page_title'] . '

'; } @@ -394,27 +376,25 @@ function template_keywords_below() function template_search_terms_above(): void { - global $txt, $context, $scripturl; - echo '
-

', $txt['optimus_top_queries'], '

+

', Lang::$txt['optimus_top_queries'], '

'; - if (!empty($context['search_terms'])) { + if (! empty(Utils::$context['search_terms'])) { echo '
'; $i = 0; $rows = ''; - foreach ($context['search_terms'] as $data) { + foreach (Utils::$context['search_terms'] as $data) { if ($data['hit'] > 10) { $i++; $rows .= '["' . $data['text'] . '",' . $data['hit'] . '],'; } } - if (!empty($rows)) { + if (! empty($rows)) { echo /** @lang text */ '