module_title = _t('sitemap', 'Sitemap'); } /** * Формируем варианты "Открывать ссылку в..."(targets) * @param bool $asOptions * @param string $currentTarget текущий(выбранный) target * @return array|string */ protected function getTargets($asOptions = true, $currentTarget = '_self') { $targetsList = [ '_self' => _t('sitemap', 'In the Current Window'), '_blank' => _t('sitemap', 'In a New Window'), ]; return ($asOptions ? HTML::selectOptions($targetsList, $currentTarget) : $targetsList); } /** * Проверка target на корректность * @param string $sTarget @ref */ protected function checkTarget(&$sTarget) { if (empty($sTarget) || !array_key_exists($sTarget, $this->getTargets(false))) { $sTarget = '_self'; } } /** * Проверка типа раздела на корректность * @param int $nType @ref тип раздела * @param bool $bAdd при добавлении * @return bool */ protected function checkType(&$nType, $bAdd = true) { if ( empty($nType) || !in_array($nType, [ static::typeMenu, static::typePage, static::typeLink, static::typeLinkModuleMethod, ]) ) { $nType = static::typeLink; return false; } if ($bAdd && !$this->isAdminFordev() && ($nType === static::typeLinkModuleMethod)) { $nType = static::typeLink; return false; } return true; } /** * Проверяем корректность ссылки указанной для пункта меню * @param string $sLink ссылка * @param bool $bRequired ссылка обязательна * @return string */ protected function checkLink($sLink, $bRequired = true) { if (empty($sLink) || $sLink == '#') { if ($bRequired) { $this->errors->set(_t('sitemap', 'Enter correct link')); } } else { if ( ($pos = stripos($sLink, static::MACROS_SITEURL)) !== false || ($pos = stripos($sLink, '{siteurl:')) !== false ) { if ($pos !== 0) { $sLink = substr($sLink, $pos); } } elseif (($pos = stripos($sLink, '{route:')) !== false) { // no changes } else { if (preg_match('/^(http|https|ftp):\/\//xisu', $sLink) !== 1) { if (strpos($sLink, 'www.') === 0) { $sLink = 'http' . ($this->app->httpsOnly() ? 's' : '') . '://' . $sLink; } else { if ($sLink[0] !== '/') { $sLink = '/' . $sLink; } } } } } return $sLink; } /** * Сбрасываем кеш разделов меню * @return void */ protected function resetCache() { foreach ($this->locale->getLanguages() as $language) { Cache::delete('sitemap:menu:' . $language); } } /** * Подготавливаем разделы/пункты меню * @param array $menu разделы/пункты меню * @param string|null $languageKey ключ языка, по-умолчанию текущий * @return array */ protected function prepareMenu($menu, $languageKey = null) { $aRes = array(); $languageKey = (!empty($languageKey) ? $languageKey : $this->locale->current()); $languagePrefix = $this->locale->getLanguageUrlPrefix($languageKey, false); foreach ($menu as $v) { if ($v['type'] == static::typeMenu) { if (!empty($v['sub'])) { $v['sub'] = $this->prepareMenu($v['sub'], $languageKey); # добавляем ссылку первого вложенного пункта меню к самому меню if (!empty($v['sub']) && empty($v['link'])) { $firstSub = reset($v['sub']); $v['link'] = $firstSub['link']; } } } else { # корректируем относительные ссылки (если необходимо) if (!empty($v['link']) && $v['link'][0] === '/' && strpos($v['link'], '//') !== 0) { # "/page.html" => "{siteurl}/page.html" $v['link'] = ($this->_correctRelativeLinks ? static::MACROS_SITEURL : $languagePrefix) . '/' . ltrim($v['link'], '/\\ '); } } $key = (!empty($v['keyword']) && !isset($aRes[$v['keyword']]) ? $v['keyword'] : $v['id']); $aRes[$key] = $v; } return $aRes; } /** * Обработка копирования данных локализации * @param $from * @param $to */ public function onLocaleDataCopy($from, $to) { $this->resetCache(); } /** * Получаем замену макроса по его ключу * @param string $key ключ макроса * @param string|null $lang ключ языка * @return string */ protected function getMacrosReplacement($key, $lang = null) { $lang = (!empty($lang) ? $lang : $this->locale->current()); switch ($key) { case static::MACROS_SITEURL: return SITEURL . $this->locale->getLanguageUrlPrefix($lang, false); case Url::HOST_PLACEHOLDER: return SITEHOST . $this->locale->getLanguageUrlPrefix($lang, false); } return $key; } /** * Выполняем замену макросов в тексте * @param string $text текст * @param string $languageKey ключ языка * @return string */ protected function replaceMacros($text, $languageKey) { if (mb_stripos($text, '{') === false) { return $text; } $replace = array( static::MACROS_SITEURL => $this->getMacrosReplacement(static::MACROS_SITEURL, $languageKey), Url::HOST_PLACEHOLDER => $this->getMacrosReplacement(Url::HOST_PLACEHOLDER, $languageKey), ); # макрос вида {siteurl:module} if (preg_match('/\{siteurl\:([a-z0-9\-]+)\}/iu', $text, $matches) && ! empty($matches[1])) { $replace[$matches[0]] = Url::to('', ['lang' => $languageKey]); } elseif (preg_match('/\{sitehost\:([a-zA-Z0-9\-]+)\}/iu', $text, $matches) && ! empty($matches[1])) { # макрос вида {sitehost:key} switch ($matches[1]) { case 'nolang': { $replace[$matches[0]] = SITEHOST; } break; default: { $replace[$matches[0]] = SITEHOST . $this->locale->getLanguageUrlPrefix($languageKey, false); } } } elseif (mb_stripos(ltrim($text), '{route:') === 0 && preg_match('/\{route\:([a-zA-Z0-9\-\.]+)(\:[a-zA-Z0-9\-]+)?\}/iu', $text, $matches) && ! empty($matches[1])) { # макрос вида {route:key} if (! empty($matches[2]) && $matches[2] === ':rel') { $replace[$matches[0]] = $this->router->url($matches[1], [], ['relative' => true]); } else { $replace[$matches[0]] = $this->router->url($matches[1], [], ['lang' => $languageKey]); } } return strtr($text, $replace); } /** * Выполняем замену макросов во всех пунктах меню (рекурсивно) * @param array $menu @ref пункты меню * @param string $languageKey ключ языка */ protected function replaceMacrosRecursive(array &$menu, $languageKey) { foreach ($menu as &$v) { if ($v['type'] == static::typeMenu) { if (!empty($v['sub'])) { $this->replaceMacrosRecursive($v['sub'], $languageKey); } } # подставляем макросы $v['link'] = $this->replaceMacros($v['link'], $languageKey); if (mb_strpos($v['link'], '//') === 0) { $v['link'] = $this->app->request()->scheme() . '://' . mb_substr($v['link'], 2); } } unset($v); } protected function checkOwnedByRecursive(array &$menu) { foreach ($menu as $k => &$v) { if ($v['type'] == static::typeMenu) { if (!empty($v['sub'])) { $this->checkOwnedByRecursive($v['sub']); } } if (! empty($v['owned_by'])) { if (! $this->app->pluginExists($v['owned_by'])) { // todo lazy init unset($menu[$k]); } } } unset($v); } /** * Добавить пункт меню * @param string $keyword * @param string|array $title заголовок * @param array $data * @return bool|int */ public function menuItemAdd($keyword, $title, array $data = []) { if (empty($keyword)) { return false; } if (empty($title)) { return false; } $data = $this->defaults($data, [ 'pidKeyword' => 'main', 'keyword' => $keyword, 'title' => $title, 'type' => static::typeLink, 'link' => '//' . Url::HOST_PLACEHOLDER, 'target' => '_self', 'style' => '', 'is_system' => 1, 'allow_submenu' => 0, 'enabled' => 0, 'extension' => null, ]); $pidKeyword = $data['pidKeyword']; unset($data['pidKeyword']); $extension = $data['extension']; unset($data['extension']); if (! isset($data['owned_by']) && $extension) { /** @var \bff\extend\Extension $extension */ if (method_exists($extension, 'getName')) { $data['owned_by'] = $extension->getName(); } } $exist = $this->model->itemDataByFilter(['keyword' => $keyword]); if (! empty($exist['id'])) { return true; } $pid = $this->model->itemDataByFilter(['keyword' => $pidKeyword]); if (empty($pid['id'])) { return false; } foreach ($this->model->langItems as $k => $v) { if (isset($data[$k])) { $data[$k] = $this->locale->fill([], $data[$k]); } } return $this->model->itemCreate($pid['id'], $pid, $data); } }