checkColumnExists('menu_links', 'type'); } /** Add menu_links.type */ public function upgrade_menuType() { // id_root is temporary and will be renamed to id_menu later in this migration sqlQuery( "ALTER TABLE menu_links ADD COLUMN id_root INT UNSIGNED DEFAULT NULL AFTER id, ADD COLUMN type TINYINT UNSIGNED DEFAULT 2 NOT NULL AFTER id_root, ADD COLUMN name_short VARCHAR(50) DEFAULT '' AFTER name, ADD COLUMN code VARCHAR(50) DEFAULT NULL UNIQUE AFTER name_short, ADD COLUMN url VARCHAR(255) DEFAULT NULL AFTER code, ADD COLUMN id_block INT(11) UNSIGNED NULL AFTER data, ADD COLUMN template VARCHAR(255) DEFAULT '' NOT NULL AFTER id_block, ADD COLUMN meta_title VARCHAR(70) NULL AFTER template, ADD COLUMN meta_description VARCHAR(250) NULL AFTER meta_title, ADD COLUMN meta_keywords VARCHAR(100) NULL AFTER meta_description, ADD COLUMN old_id_page INT(11) NULL AFTER meta_keywords" ); sqlQuery('ALTER TABLE menu_links MODIFY parent INT UNSIGNED DEFAULT NULL;'); sqlQuery('ALTER TABLE menu_links ENGINE InnoDB;'); sqlQuery('ALTER TABLE menu_links MODIFY link VARCHAR(255) DEFAULT NULL;'); sqlQuery("UPDATE menu_links SET link=NULL WHERE link='';"); $menu = Config::get()['Menu']['own'] ?? []; $i = 0; foreach ($menu as $menuID => $menu) { $existing = sqlQuery('SELECT * FROM menu_links WHERE id=:menuID', ['menuID' => $menuID])->fetch(); if ($existing) { // re-insert row with required id (so it can be used for this menu) unset($existing['id']); $this->insertSQL('menu_links', $existing); $newID = sqlInsertId(); sqlQuery('UPDATE menu_links SET parent=:new_parent WHERE parent=:old_parent', [ 'new_parent' => $newID, 'old_parent' => $menuID, ]); $this->deleteSQL('menu_links', ['id' => $menuID]); } sqlQuery('INSERT INTO menu_links (id, id_root, parent, list_order, type, name, name_short) VALUES (:id, :id_root, NULL, :position, :type, :name, :name_short);', [ 'id' => $menuID, 'id_root' => $menuID, 'position' => $i++, 'type' => MenuUtil::TYPE_GROUP, 'name' => $menu['name'], 'name_short' => $menu['title'], ]); sqlQuery('UPDATE menu_links SET parent=:parent WHERE id_menu=:id_menu AND parent IS NULL', [ 'parent' => $menuID, 'id_menu' => $menuID, ]); sqlQuery('UPDATE menu_links SET id_root=:id_root WHERE id_menu=:id_menu', [ 'id_root' => $menuID, 'id_menu' => $menuID, ]); // move orphans to their root and set figure = N sqlQuery('DELETE m1 FROM menu_links m1 LEFT JOIN menu_links m2 ON m2.id = m1.parent WHERE m2.id IS NULL AND m1.parent IS NOT NULL;'); } // edit type of menu_links foreach (sqlQuery('SELECT * FROM menu_links WHERE type = :type', ['type' => MenuUtil::TYPE_LINK]) as $menuLink) { if (empty($menuLink['link'])) { if (!empty(sqlQuery('SELECT * FROM menu_links WHERE parent = :id', ['id' => $menuLink['id']])->fetch())) { $this->updateSQL('menu_links', ['type' => MenuUtil::TYPE_GROUP], ['id' => $menuLink['id']]); } else { $this->updateSQL('menu_links', ['link' => '/'], ['id' => $menuLink['id']]); } } } // drop id_menu sqlQuery('ALTER TABLE menu_links DROP COLUMN id_menu;'); sqlQuery('ALTER TABLE menu_links CHANGE id_root id_menu INT UNSIGNED DEFAULT NULL;'); sqlQuery(' ALTER TABLE menu_links ADD CONSTRAINT menu_links_fk_parent FOREIGN KEY (parent) REFERENCES menu_links(id) ON UPDATE CASCADE ON DELETE CASCADE, ADD CONSTRAINT menu_links_fk_id_menu FOREIGN KEY (id_menu) REFERENCES menu_links(id) ON UPDATE CASCADE ON DELETE CASCADE;'); sqlQuery('CREATE UNIQUE INDEX menu_links_uix_url ON menu_links(url);'); sqlQuery(" CREATE TABLE photos_menu_relation ( id_photo INT(11) UNSIGNED DEFAULT 0 NOT NULL, id_menu INT UNSIGNED DEFAULT 0 NOT NULL, show_in_lead ENUM('Y', 'N') DEFAULT 'N' NOT NULL, active ENUM('Y', 'N') DEFAULT 'Y' NOT NULL, date_added DATETIME DEFAULT '0000-00-00 00:00:00' NOT NULL, position INT NULL, CONSTRAINT id_photo UNIQUE (id_photo, id_menu), CONSTRAINT photos_menu_relation_ibfk_1 FOREIGN KEY (id_photo) REFERENCES photos(id) ON UPDATE CASCADE ON DELETE CASCADE, CONSTRAINT photos_menu_relation_ibfk_2 FOREIGN KEY (id_menu) REFERENCES menu_links(id) ON UPDATE CASCADE ON DELETE CASCADE ); "); $this->upgradeOK(); } public function check_menuTranslation() { return findModule(\Modules::TRANSLATIONS) && $this->checkColumnExists('menu_links_translations', 'id_block'); } /** Add menu_links_translations.id_block... */ public function upgrade_menuTranslation() { sqlQuery( 'ALTER TABLE menu_links_translations ADD COLUMN name_short VARCHAR(50) DEFAULT NULL AFTER name, ADD COLUMN url VARCHAR(255) DEFAULT NULL AFTER name_short, ADD COLUMN id_block INT(11) UNSIGNED NULL, ADD COLUMN meta_title VARCHAR(70) NULL, ADD COLUMN meta_description VARCHAR(250) NULL, ADD COLUMN meta_keywords VARCHAR(100) NULL;' ); $this->upgradeOK(); } public function check_html_pages() { return !$this->checkTableIsEmpty('html_pages'); } /** Remove html_pages table */ public function upgrade_html_pages() { $pages = sqlQuery('SELECT * FROM html_pages'); $menuLinks = sqlQuery('SELECT * FROM menu_links')->fetchAll(); if (findModule(\Modules::TRANSLATIONS)) { /** @var MenuLinksTranslation $translationService */ $translationService = ServiceContainer::getService(MenuLinksTranslation::class); } $usedLabels = []; $unclassifiedPagesCounter = 0; foreach ($pages as $page) { $menuLink = null; // homepage vytvorit v systemovych strankach if ($page['label'] == 'leadpage') { $this->insertSQL('pages', [ 'id_block' => $page['id_block'], 'code' => 'system-home', 'type' => 'HOME', ]); continue; } $label = null; if (!empty($page['label']) && !in_array($page['label'], $usedLabels)) { if ($seoUrl = translate_shop($page['label'], 'SEO_URL', true)) { $pattern = '/'.$seoUrl.'/'; $label = $page['label']; } else { $pattern = '/_p'.$page['id'].'.html/'; } } else { $pattern = '/_p'.$page['id'].'.html/'; } // zkusit najit menu_link patrici ke strance foreach ($menuLinks as $link) { if ($this->isExternalUrl($link['link'])) { continue; } if (preg_match($pattern, $link['link'])) { if ($label && strpos($link['link'], '.html')) { continue; } $menuLink = $link; if ($label) { $usedLabels[] = $label; } break; } } // pokud nebylo nic nalezeno a je nastaven label, tak zkusit hledat jeste jednou podle odkazu stranky if (!$menuLink && !empty($page['label'])) { $pattern = '/_p'.$page['id'].'.html/'; foreach ($menuLinks as $link) { if ($this->isExternalUrl($link['link'])) { continue; } if (preg_match($pattern, $link['link'])) { $menuLink = $link; // pokud ma label URL, tak ji nastavit if ($seoUrl = translate_shop($page['label'], 'SEO_URL', true)) { $menuLink['link'] = $seoUrl; } $label = $page['label']; $usedLabels[] = $page['label']; break; } } } $iLabel = null; if (!empty($page['label'])) { if (!$this->selectSQL('menu_links', ['code' => $page['label']])->fetch()) { $iLabel = $page['label']; } } $updateName = false; if ($menuLink) { // aktualizovat menu_links podle stranky $url = parse_url($menuLink['link']); $url = ltrim($url['path'] ?? $menuLink['link'], '/'); if (!preg_match('/.+(.html)$/', $url)) { $url = trim($url, '/').'/'; } $update = [ 'type' => MenuUtil::TYPE_PAGE, 'id_block' => $page['id_block'], 'template' => $page['template'], 'meta_title' => $page['meta_title'], 'meta_description' => $page['meta_description'], 'meta_keywords' => $page['meta_keywords'], 'url' => $url, 'link' => null, 'old_id_page' => $page['id'], ]; if ($iLabel) { $update['code'] = $iLabel; } if ($menuLink['name'] != $page['name']) { $update['name'] = $page['name']; $update['name_short'] = $menuLink['name']; $updateName = true; } $this->updateSQL('menu_links', $update, ['id' => $menuLink['id']]); if (findModule(\Modules::TRANSLATIONS)) { if (!$label) { sqlQuery( 'UPDATE menu_links_translations SET url = link, link = null WHERE id_menu_link = :id_menu_link', [ 'id_menu_link' => $menuLink['id'], ] ); } else { // translate label and insert it into translations $this->translateLabel($menuLink['id'], $label, $translationService); } } $menuLinkId = $menuLink['id']; } else { // vytvorit novy menu_link mimo strukturu se strankou $updateName = true; $insertLink = StringUtil::slugify($page['name']).'_p'.$page['id'].'.html'; if (!empty($page['label']) && !in_array($page['label'], $usedLabels) && $link = translate_shop($page['label'], 'SEO_URL', true)) { $insertLink = $link.'/'; } $this->insertSQL('menu_links', [ 'type' => MenuUtil::TYPE_PAGE, 'code' => $iLabel, 'name' => $page['name'], 'list_order' => $unclassifiedPagesCounter++, 'id_block' => $page['id_block'], 'url' => $insertLink, 'template' => $page['template'], 'meta_title' => $page['meta_title'], 'meta_description' => $page['meta_description'], 'meta_keywords' => $page['meta_keywords'], 'old_id_page' => $page['id'], ]); $menuLinkId = sqlInsertId(); } // photos_pages_relation -> photos_menu_relation $photos = sqlQueryBuilder()->select('*')->from('photos_pages_relation') ->where(Operator::equals(['id_page' => $page['id']])) ->orderBy('position')->execute(); foreach ($photos as $photo) { $this->insertSQL('photos_menu_relation', [ 'id_photo' => $photo['id_photo'], 'id_menu' => $menuLinkId, 'show_in_lead' => $photo['show_in_lead'], 'active' => $photo['active'], 'date_added' => $photo['date_added'], 'position' => $photo['position'], ]); } // pokud jsou preklady, tak premigrovat taky if (findModule(\Modules::TRANSLATIONS)) { $translations = sqlQuery('SELECT * FROM html_pages_translations WHERE id_html_page = :id', ['id' => $page['id']]); foreach ($translations as $translation) { $values = [ 'meta_title' => $translation['meta_title'], 'meta_description' => $translation['meta_description'], 'meta_keywords' => $translation['meta_keywords'], ]; if ($updateName) { $values['name'] = $translation['name']; $menuLinkName = sqlQuery('SELECT name FROM menu_links_translations WHERE id_menu_link = :id AND id_language = :id_language', [ 'id' => $menuLinkId, 'id_language' => $translation['id_language'], ])->fetchColumn(); if (!empty($menuLinkName)) { $values['name_short'] = $menuLinkName; } } $translationService->saveSingleObject($translation['id_language'], $menuLinkId, $values); } sqlQuery('DELETE FROM html_pages_translations'); } } // promazat html_pages tabulky sqlQuery('DELETE FROM html_pages'); $this->upgradeOK(); } /** * @param $translationService MenuLinksTranslation */ private function translateLabel($menuLinkId, $label, $translationService) { $languageContext = ServiceContainer::getService(LanguageContext::class); $originalLanguage = $languageContext->getActiveId(); foreach ($languageContext->getSupported() as $language) { if ($languageContext->getDefaultId() == $language->getId()) { continue; } $languageContext->activate($language->getId()); $url = translate($label, 'SEO_URL').'/'; $translationService->saveSingleObject($language->getId(), $menuLinkId, ['link' => null, 'url' => $url]); } $languageContext->activate($originalLanguage); } private function isExternalUrl($url) { $cfg = Config::get(); $url = parse_url($url); if (!empty($url['host'])) { if (strpos($url['host'], trim($cfg['Addr']['print'], '/')) !== false) { return false; } return true; } return false; } public function check_OldIdPageColumn() { return $this->checkColumnExists('menu_links', 'old_id_page'); } /** Add old_id_page column into menu_links table */ public function upgrade_OldIdPageColumn() { sqlQuery('ALTER TABLE menu_links ADD COLUMN old_id_page INT(11) NULL AFTER meta_keywords'); $this->upgradeOK(); } public function check_CodeColumn() { return $this->checkColumnExists('menu_links', 'code'); } /** Add code column into menu_links table */ public function upgrade_CodeColumn() { sqlQuery('ALTER TABLE menu_links ADD COLUMN code VARCHAR(50) DEFAULT NULL UNIQUE AFTER name_short'); $this->upgradeOK(); } public function check_DeleteHtmlPagesTableTranslations() { return !$this->checkTableExists('html_pages_translations'); } /** Delete html_pages_translations table */ public function upgrade_DeleteHtmlPagesTableTranslations() { sqlQuery('DROP TABLE html_pages_translations'); $this->upgradeOK(); } public function check_DeleteHtmlPagesTableRelation() { return !$this->checkTableExists('photos_pages_relation'); } /** Delete photos_pages_relation table */ public function upgrade_DeleteHtmlPagesTableRelation() { sqlQuery('DROP TABLE photos_pages_relation'); $this->upgradeOK(); } public function check_DeleteHtmlPagesBlocksTable() { return !$this->checkTableExists('html_pages_blocks'); } /** Delete html_pages and html_pages_blocks tables */ public function upgrade_DeleteHtmlPagesBlocksTable() { sqlQuery('DROP TABLE html_pages_blocks'); $this->upgradeOK(); } public function check_DeleteHtmlPagesTable() { return !$this->checkTableExists('html_pages'); } /** Delete html_pages and photos_pages_relation tables */ public function upgrade_DeleteHtmlPagesTable() { sqlQuery('DROP TABLE html_pages'); $this->upgradeOK(); } public function check_MenuLinkSlash() { return $this->checkDataMigration(16); } /** Ensure menu_links links begin with a slash */ public function upgrade_MenuLinkSlash() { $this->addSlashPrefixToLink(); $this->commitDataMigration(16); $this->upgradeOK(); } public function check_MenuLinkSlash2() { return $this->checkDataMigration(17); } /** Ensure menu_links links begin with a slash */ public function upgrade_MenuLinkSlash2() { $this->addSlashPrefixToLink(); $this->commitDataMigration(17); $this->upgradeOK(); } public function addSlashPrefixToLink(): void { $qb = sqlQueryBuilder()->select('*')->from('menu_links') ->where("link != '' AND link IS NOT NULL AND link NOT LIKE '/%' AND link NOT LIKE 'http://%' AND link NOT LIKE 'https://%'"); foreach ($qb->execute() as $menuLink) { if (!preg_match('@^(/|[a-zA-Z]{3,6}://|mailto:|tel:)@', $menuLink['link'])) { sqlQueryBuilder()->update('menu_links')->directValues(['link' => '/'.$menuLink['link']]) ->where(Operator::equals(['id' => $menuLink['id']])) ->execute(); } } } public function check_sectionsTemplateColumn() { return $this->checkColumnExists('sections', 'template'); } /** Add template column to sections */ public function upgrade_sectionsTemplateColumn() { sqlQuery("ALTER TABLE sections ADD COLUMN template VARCHAR(255) DEFAULT '' NOT NULL AFTER data"); } }