* このモジュールは Sitemapプロトコルに対応した XMLファイルを出力する. * EC-CUBE インストールディレクトリの htmlディレクトリへ配置することにより動作する. * * このモジュールにより, 以下のページのサイトマップが生成される. * 1. $staticURL で指定したページ * 2. 管理画面のデザイン管理から生成したページ * 3. 公開されているすべての商品一覧ページ * 4. 公開されているすべての商品詳細ページ * * このモジュールを設置後, 各検索エンジンにサイトマップを登録することにより, 検索エンジンの * インデックス化が促進される. * * @see https://www.google.com/webmasters/tools/siteoverview?hl=ja * @see https://siteexplorer.search.yahoo.com/mysites * * @author Kentaro Ohkouchi * @version $Id:sitemap.php 15532 2007-08-31 14:39:46Z nanasess * * :TODO: 各ページの changefreq や priority を指定できるようにする * :TODO: filemtime 関数を使えば、静的なページの更新時間も取得できそう */ class LC_Page_Sitemap extends LC_Page_Ex { // }}} // {{{ properties /** 動的に生成しないページの配列 */ var $staticURL; /** ページリスト */ var $arrPageList; // }}} // {{{ functions /** * Page を初期化する. * * @return void */ function init() { parent::init(); $this->staticURL = array(); $this->staticURL[] = HTTP_URL . 'rss/' . DIR_INDEX_PATH; } /** * Page のプロセス. * * @return void */ function process() { // ページのデータを取得 // FIXME PCサイトのみに限定している。ある程度妥当だとは思うが、よりベターな方法はないだろうか。 $this->arrPageList = $this->getPageData('device_type_id = ?', DEVICE_TYPE_PC); //キャッシュしない(念のため) header('Paragrama: no-cache'); //XMLテキスト header('Content-type: application/xml; charset=utf-8'); // 必ず UTF-8 として出力 mb_http_output('UTF-8'); ob_start('mb_output_handler'); echo '' . "\n"; echo '' . "\n"; // TOPページを処理 $arrTopPagesList = $this->getTopPage($this->arrPageList); $this->createSitemap($arrTopPagesList[0]['url'], $this->date2W3CDatetime($arrTopPagesList[0]['update_date']), 'daily', 1.0); // 静的なページを処理 foreach ($this->staticURL as $url) { $this->createSitemap($url, '', 'daily', 1.0); } // 編集可能ページを処理 $arrEditablePagesList = $this->getEditablePage($this->arrPageList); foreach ($arrEditablePagesList as $arrEditablePage) { $this->createSitemap($arrEditablePage['url'], $this->date2W3CDatetime($arrEditablePage['update_date'])); } // 商品一覧ページを処理 $arrProductPagesList = $this->getAllProducts(); foreach ($arrProductPagesList as $arrProductPage) { $this->createSitemap($arrProductPage['url'], '', 'daily'); } // 商品詳細ページを処理 $arrDetailPagesList = $this->getAllDetail(); foreach ($arrDetailPagesList as $arrDetailPage) { $this->createSitemap($arrDetailPage['url'], $this->date2W3CDatetime($arrDetailPage['update_date'])); } echo '' . "\n"; } /** * デストラクタ. * * @return void */ function destroy() { parent::destroy(); } /** * Sitemap の を生成する. * * @param string $loc ページの URL ※必須 * @param string $lastmod ファイルの最終更新日 YYYY-MM-DD or W3C Datetime 形式 * @param string $changefreq ページの更新頻度 * @param double $priority URL の優先度 * @return Sitemap 形式の * @see https://www.google.com/webmasters/tools/docs/ja/protocol.html#xmlTagDefinitions * TODO Smarty に移行すべき? */ function createSitemap($loc, $lastmod = '', $changefreq = '', $priority = '') { printf("\t\n"); printf("\t\t%s\n", htmlentities($loc, ENT_QUOTES, 'UTF-8')); if (!empty($lastmod)) { printf("\t\t%s\n", $lastmod); } if (!empty($changefreq)) { printf("\t\t%s\n", $changefreq); } if (!empty($priority)) { printf("\t\t%01.1f\n", $priority); } printf("\t\n"); } /** * TOPページの情報を取得する. * * @param array $arrPageList すべてのページ情報の配列 * @return array TOPページの情報 */ function getTopPage($arrPageList) { $arrRet = array(); foreach ($arrPageList as $arrPage) { if ($arrPage['page_id'] == '1') { $arrRet[0] = $arrPage; return $arrRet; } } } /** * すべての編集可能ページの情報を取得する. * * @param array $arrPageList すべてのページ情報の配列 * @return array 編集可能ページ */ function getEditablePage($arrPageList) { $arrRet = array(); foreach ($arrPageList as $arrPage) { if ($arrPage['page_id'] > 4) { $arrRet[] = $arrPage; } } return $arrRet; } /** * すべての商品一覧ページを取得する. * * @return array 検索エンジンからアクセス可能な商品一覧ページの情報 */ function getAllProducts() { // XXX: 商品登録の無いカテゴリは除外する方が良い気もする $objQuery = SC_Query_Ex::getSingletonInstance(); $sql = 'SELECT category_id FROM dtb_category WHERE del_flg = 0'; $result = $objQuery->getAll($sql); $arrRet = array(); foreach ($result as $row) { // :TODO: カテゴリの最終更新日を取得できるようにする $arrPage['url'] = HTTP_URL . 'products/list.php?category_id=' . $row['category_id']; $arrRet[] = $arrPage; } return $arrRet; } /** * すべての商品詳細ページを取得する. * * @return array 検索エンジンからアクセス可能な商品詳細ページの情報 */ function getAllDetail() { $objQuery = SC_Query_Ex::getSingletonInstance(); $sql = 'SELECT product_id, update_date FROM dtb_products WHERE del_flg = 0 AND status = 1'; $result = $objQuery->getAll($sql); $arrRet = array(); foreach ($result as $row) { $arrPage['update_date'] = $row['update_date']; $arrPage['url'] = HTTP_URL . substr(P_DETAIL_URLPATH, strlen(ROOT_URLPATH)) . $row['product_id']; $arrRet[] = $arrPage; } return $arrRet; } /** * ブロック情報を取得する. * * @param string $where WHERE句 * @param array $arrVal WHERE句の値を格納した配列 * @return array $arrPageList ブロック情報 */ function getPageData($where = '', $arrVal = '') { $objQuery = SC_Query_Ex::getSingletonInstance(); // DB操作オブジェクト $sql = ''; // データ取得SQL生成用 $arrRet = array(); // データ取得用 // SQL生成(url と update_date 以外は不要?) $sql .= ' SELECT'; $sql .= ' page_id'; // ページID $sql .= ' ,page_name'; // 名称 $sql .= ' ,url'; // URL $sql .= ' ,filename'; // ファイル名称 $sql .= ' ,header_chk '; // ヘッダー使用FLG $sql .= ' ,footer_chk '; // フッター使用FLG $sql .= ' ,author'; // authorタグ $sql .= ' ,description'; // descriptionタグ $sql .= ' ,keyword'; // keywordタグ $sql .= ' ,update_url'; // 更新URL $sql .= ' ,create_date'; // データ作成日 $sql .= ' ,update_date'; // データ更新日 $sql .= ' FROM '; $sql .= ' dtb_pagelayout'; // where句の指定があれば追加 if ($where != '') { $sql .= ' WHERE ' . $where; } $sql .= ' ORDER BY page_id'; $arrPageList = $objQuery->getAll($sql, $arrVal); // URL にプロトコルの記載が無い場合、HTTP_URL を前置する。 foreach ($arrPageList as $key => $value) { $arrPage =& $arrPageList[$key]; if (!preg_match('|^https?://|i', $arrPage['url'])) { $arrPage['url'] = HTTP_URL . $arrPage['url']; } $arrPage['url'] = preg_replace('|/' . preg_quote(DIR_INDEX_FILE) . '$|', '/' . DIR_INDEX_PATH, $arrPage['url']); } unset($arrPage); return $arrPageList; } /** * date形式の文字列を W3C Datetime 形式に変換して出力する. * * @param date $date 変換する日付 * @return void */ function date2W3CDatetime($date) { $arr = array(); // 正規表現で文字列を抽出 preg_match('/^([0-9]{4})-([0-9]{2})-([0-9]{2}) ([0-9]{2}):([0-9]{2}):([0-9]{2})/', $date, $arr); // :TODO: time zone も取得するべき... return sprintf('%04d-%02d-%02dT%02d:%02d:%02d+09:00', $arr[1], $arr[2], $arr[3], $arr[4], $arr[5], $arr[6]); } }