レトロなコーポレートサイトをモダンに替えてみた

1990年代のインターネット黎明期から存在し、社員による手作りで継ぎ足し継ぎ足し更新してきたコーポレートサイトを全面リニューアルしてみました。

# 現行の問題点
ネット界隈で時折話題になる「[阿部寛のホームページ](http://abehiroshi.la.coocan.jp/)」と比べれば、CSSや**div**タグも普通に使われていて、さほどデザインにレトロさを感じません。
ですが、次の点で保守性が著しく劣っていました。端的に言うと、ホームページの更新程度に余計な工数がかかる仕組みだったのです。

- すべてのページで内容が同じ**ヘッダー（グロナビ）**、**サイドバー**、**フッター**が、すべてのHTMLファイルに直書きされているため、この共通箇所に変更があると、基本すべてのファイルで修正が必要
- CSSの書き方が全体的に冗長過ぎて膨大なため、製品情報やFAQをひとつ追加するだけでも多くの修正が必要
- トップページにありがちな新着情報もHTMLに直書きのため、更新の都度、HTMLを理解する人の作業が必要
- 一部のページで昔流行った**map**タグ（**クライアントサイド・クリッカブルマップ**）が多用されており、修正手順が煩雑
- PC向けサイトと携帯向けサイトが別々でコンテンツの一元管理ができていないため、加えて携帯向けサイトの修正も必要な場合がある

これらを解消していったので共有します。

# サイト制作環境準備
## WordPressのインストール
会社が契約するレンタルサーバ事業者（[エックスサーバー](https://business.xserver.ne.jp/)）のコントロールパネルに管理者アカウントでログインします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/6739bfc2-2c98-6bfd-58e2-220695bb8ff0.png)
SSHが使える状態になかったので、これを有効にし、ターミナルにログインするための秘密鍵をダウンロードします。
WordPressのインストールやMySQLの設定も、この画面から簡単に行えるようですので、合わせてやっておきます。
サーバにインストールしたら、ローンチまでの間、忘れずにアクセス制限（Basic認証）をかけておきましょう。

もしWordPressの管理者パスワードを失念したら、次のSQLでリセットできます（実際忘れた）。
エックスサーバーでは[phpMyAdmin](https://www.phpmyadmin.net/)が標準で使えるので、そこから実行してもOKです。

```sql
UPDATE wp_users SET user_pass = MD5('foo') WHERE user_login = 'bar';
```

# サイト制作開始
WordPressとターミナルが使えるようになったので、早速制作していきましょう。

グロナビをクリックすると最初に表示される上位のページ（会社案内みたいな）は、WordPressの固定ページテンプレート（page-スラッグ名.php）でゼロからデザインしますが、その他の膨大にあった製品情報紹介ページやトラブルシュートFAQページは、HTMLをそのままコピーし、メインコンテンツ部に差し込む設計としました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/f3b8ab97-1931-4e67-afb4-c5117e5129e0.png)
もちろんレスポンシブWebデザイン対応です。

## 固定ページテンプレート作成
更新頻度が高いもの、例えばキャンペーン情報とかセール案内等はテンプレートを作成せず、WordPress5.0から使えるブロックエディタ（Gutenberg）でユーザ部門が自ら作成・更新いただく運用としました。ブロックエディタを使う限りHTML/CSSの知識は不要です。

### HTMLの置き換え

- **table**タグがレイアウト目的で使われているなら、**div**タグ＋CSSのGridやFlexbox、もしくは**ul**タグや**dl**タグに置き換えます。**table**タグが表を作るために使われているなら、目的に合致するのでそのままです。
- 往年の`<font color="red" size="2">ほげほげ</font>`などで書かれた見出しは、CSS＋**h2**タグなどに置き換えます。

### CSSの置き換え
6万行を超える冗長なCSSにメスを入れます。課題は色々ありましたが、ここでは分かり易い例をひとつ挙げます。

#### 従来のHTML/CSS
```html:HTML
<div class="foo">
  <div class="bar1"> ... </div>
  <div class="bar2"> ... </div>
    :
  <div class="bar9"> ... </div>
```

```css:CSS
.bar1 {
  margin: 0px 0px 10px;
  padding: 0px;
  border: 0px solid;
  width: 560px;
  height: 25px;
  color: #000000;
  background: #ff0000;
  line-height: 2em;
  font-weight: 600;
}
.bar2 {
  margin: 0px 0px 10px;
  padding: 0px;
  border: 0px solid;
  width: 560px;
  height: 25px;
  color: #000000;
  background: #00ff00;
  line-height: 2em;
  font-weight: 600;
}
```

一見すると気づきませんが、bar1とbar2の違いは[background](https://developer.mozilla.org/ja/docs/Web/CSS/background)だけです。
なので共通部分をひとつに纏めて見通しを良くします。

#### 修正後
```css:CSS
.foo > div {
  margin: 0px 0px 10px;
  padding: 0px;
  border: 0px solid;
  width: 560px;
  height: 25px;
  color: #000;
  line-height: 2em;
  font-weight: 600;
}
.bar1 {
  background: #f00;
}
.bar2 {
  background: #0f0;
}
```

`>`はセレクタのひとつで直下を表します。

SCSS記法なら変数が使えるので、さらに保守性が高まります。
この場合、SCSSで記述すると自動でCSSにコンパイルしてくれる[WP-SCSS](https://wordpress.org/plugins/wp-scss/)というプラグインが便利です。

```scss:SCSS
$bgcolor1: #f00; /* 変数を定義 */
$bgcolor2: #0f0; /* 変数を定義 */
.foo {
  & > div {
    margin: 0px 0px 10px;
    padding: 0px;
    border: 0px solid;
    width: 560px;
    height: 25px;
    color: #000;
    line-height: 2em;
    font-weight: 600;
  }
  .bar1 {
    background: $bgcolor1;
  }
  .bar2 {
    background: $bgcolor2;
  }
}
```

### クリッカブルマップの置き換え
1990年代に流行したクリッカブルマップです。今日ではあまり見かけません。
ホバー時に色を変えるなどリンクであることを分かり易くするのがクリッカブルマップでは不可能で（画像を用意すればできるけど）、クリックできることに気づいてもらえないので個人的には好きではありません。

#### 従来のHTML/CSS
```html:HTML
<div class="japan">
  <img src="images/japan.png" width="561" height="549" usemap="#jp" border="0">
  <map name="jp">
    <area shape="rect" coords="399,100,437,116" href="#sapporo" alt="札幌支店">
    <area shape="rect" coords="439,203,478,221" href="#morioka" alt="盛岡営業所">
    <area shape="rect" coords="439,224,478,240" href="#sendai" alt="仙台支店">
    <area shape="rect" coords="435,357,473,373" href="#tokyo" alt="東京本社">
    <area shape="rect" coords="435,376,475,392" href="#kanagawa" alt="神奈川営業所">
    <area shape="rect" coords="319,280,358,295" href="#niigata" alt="新潟支店">
    <area shape="rect" coords="335,380,375,397" href="#hamamatsu" alt="浜松営業所">
    <area shape="rect" coords="335,399,377,416" href="#nagoya" alt="名古屋支店">
    <area shape="rect" coords="244,335,285,351" href="#oosaka" alt="大阪支店">
    <area shape="rect" coords="154,311,196,328" href="#okayama" alt="岡山営業所">
    <area shape="rect" coords="51,364,90,381" href="#fukuoka" alt="福岡支店">
  </map>
```

座標指定だとレスポンシブ対応が面倒なので、CSSで置き換えます。

#### 修正後
```html:HTML
<div class="japan">
  <img src="<?php bloginfo('stylesheet_directory'); ?>/images/japan.png">
  <div class="kanto">
    <h5>関東</h5>
    <ul>
      <li><a href="#tokyo">東京</a></li>
      <li><a href="#kanagawa">神奈川</a></li>
    </ul>
  </div>
  <div class="tohoku">
    <h5>東北</h5>
    <ul>
      <li><a href="#morioka">盛岡</a></li>
      <li><a href="#sendai">仙台</a></li>
    </ul>
  </div>
  :
```

親要素に`position:relative`を指定し、クリック対象となる子要素に`position:absolute`を指定して重ね合わせます。
このとき`top`や`left`に、ピクセルやポイント等の絶対単位ではなく、百分率の相対単位を使うのが肝です。

```scss:SCSS
.japan {
  position: relative;
  img {
    display: block;
    margin: 0 auto;
    transition-duration: .3s;
    &:hover {
      opacity: .7;
      transition-duration: .15s;
    }
  }
  & > div {
    position: absolute;
    border-radius: 0 0 9px 9px;
    h5 {
      background-color: rgba(0,0,0,.6);
      text-align: center;
      color: #fff;
      font-size: 1.2em;
      letter-spacing: .2em;
    }
    ul {
      margin: 0;
      padding: 0 15px;
      background-color: rgba(0,0,0,.15);
      li {
        font-size: 1em;
        line-height: 1.5;
      }
      a {
        color: $base_color;
        &:hover {
          color: #f00;
        }
      }
    }
    &:hover {
      box-shadow: 0 0 12px 6px #fff;
    }
  }
  .kanto {
    top: 60%;
    left: 77%;
    border:#31958b solid 5px;
  }
  .tohoku {
    top: 28%;
    left: 78%;
    border:#127b91 solid 5px;
  }
```
##### 動作イメージ
![map](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/1bf10493-cb65-5893-efb7-4179cf4c4f80.gif)

### 新着情報表示の置き換え
これまではHTMLに直書きでしたので、社内でITリテラシーの高い人が対応していました。
新着情報の表示箇所を次のようなコードに置き換え、社内の誰でも新着情報を投稿できるようにしました。

```php:PHP
<h2>新着情報</h2>
<dl class="topix">
<?php
  $args = array(
    'posts_per_page' => 3 // 表示件数
  );
  $posts = get_posts( $args );
  foreach ( $posts as $post ): // ループ開始
  setup_postdata( $post ); // 記事データ取得
?>
  <dt><?php the_time('Y年m月d日'); ?></dt>
  <dd>
    <a href="<?php the_permalink(); ?>">
    <?php
    if(mb_strlen($post->post_title, 'UTF-8')>40){
      $title= mb_substr($post->post_title, 0, 40, 'UTF-8');
      echo $title.'…';
    }else{
      echo $post->post_title;
    }
    ?>
    </a>
  </dd>
<?php
  endforeach; // ループ終了
  wp_reset_postdata(); // 直前のクエリを復元
?>
</dl>
```

## 膨大なHTMLファイル群の移行
主要なコンテンツはゼロからデザインし書き起こしましたが、他にも、製品情報紹介ページやトラブルシュートFAQのページが沢山あります。
これらをすべてWordPressで完全に置き換えるのは容易ではありません。

そこで、HTMLファイルからPHPプログラムを実行できるようにし、WordPressで定義したヘッダー／サイドバー／フッターを、従来のHTMLで表示できるようにします。プログラム内部処理的に主従関係が入れ替わるだけで結果的には同じことです。

最初にすべてのHTMLファイルで修正が必要なことに変わりませんが、この方式ならツールを書けば一括で変換できます。

### HTMLからWordPressの機能を使う方法
エックスサーバーのドキュメントルートは`public_html`になります。この直下に次のディレクトリを作成します。

1. WordPressを置くディレクトリ【例：/home/foo/example.co.jp/public_html/wp】
1. 従来のHTMLファイルを置くディレクトリ【例：/home/foo/example.co.jp/public_html/static_html】

HTMLディレクトリの直下に次の`.htaccess`を置き、このディレクトリ下に置いた「*.html」がPHPを実行できるようにします。*fcgid-script* はホスティング先によって異なるので確認して下さい。

```apache:.htaccess
AddHandler fcgid-script .html .htm
```

HTMLファイルの先頭で、WordPressのモジュールをインクルードし、WordPressの関数（機能）が使えるようにします。

```php
include_once('WordPressを置いたディレクトリ/wp-load.php');
```

続いて、ヘッダー等を表示させたい箇所にWordPressの関数を挿入します。

- ヘッダー <font color="#aaa">▶</font> get_header関数でheader.phpテンプレートファイルを組み込む
- サイドバー <font color="#aaa">▶</font> get_sidebar関数でsidebar.phpテンプレートファイルを組み込む
- フッター <font color="#aaa">▶</font> get_footer関数でfooter.phpテンプレートファイルを組み込む

#### 従来のHTML

```html:HTML（イメージ）
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
  <head>
    <title>foo</title>
    <meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS" />
    :
    <link rel="stylesheet" href="../xxxstyle.css" type="text/css" />
    :
    <script>/* Google Analytics */</script>
  </head>
  <body>
    <div class="logo"> <!-- ロゴ --> </div>
    <div class="menu"> <!-- グロナビ --> </div>
    <div class="content"> <!-- メインコンテンツ --> </div>
    <div class="sidebar"> <!-- サイドバー --> </div>
    <div class="footer"> <!-- フッター --> </div>
  </body>
</html>
```

Salesforce社のMAツールPardotで**プロスペクト**（**リード**よりも商品やサービスの購入見込みが高い顧客のこと）の訪問履歴を追うための**トラッキングコード**が`</body>`の前あたりに埋め込まれていますが、本記事では省略しました。

:::note
Google Analyticsでも訪問履歴は追えますが、Salesforceではプロスペクトに紐づくので、商談が進んでいたりマーケティングメールを送信した顧客が自社サイトを見ている、といった、営業に有益な情報を取得できます。
:::

#### 修正後

```php:PHPコードを含むHTML（イメージ）
<?php
include_once('/home/foo/example.co.jp/public_html/wp-load.php'); // WordPressの関数を呼べるようにする
add_action('wp_head', function(){ /* アクションフック */ ?>
<link rel="stylesheet" href="foo/bar.css" type="text/css" />
<link rel="stylesheet" href="foo/baz.css" type="text/css" />
<?php });
get_header(); /* ヘッダー出力 */
?>
<div id="content">  <!-- 従来のHTMLのメインコンテンツ部分を挿入 -->
  ：
</div>
<div class="sidebar"><?php get_sidebar(); /* サイドバー出力 */ ?></div>
<div class="footer"><?php get_footer(); /* フッター出力 */ ?></div>
```

修正したHTMLをブラウザで表示し、WordPressのヘッダー等があることを確認します。

# 移行ツールの作成
HTMLファイルは数百あるので、ひとつひとつ手作業で修正していくのは現実的ではありません。
次のような仕様でツールを作成し、一括で変換します。

- すべてのCSSファイルに対して、エンコーディング宣言を先頭に追加する
- すべてのHTMLファイルに対して、メインコンテンツ部分を抽出し、前後にWordPress用のPHPタグを追加する

また、全ファイルとも長らくWindows環境で作成されてきたので文字コードがShift-JISになっており、WordPressと連携するとブラウザで文字化けするため、UTF-8に変換します。

**sed**なら、<font color="blue">`<head>`～`</head>`の範囲から`<link>`定義行を抽出してインデント削除、さらにインラインJavaScriptの抽出まで</font>ワンライナーで書けます。

```bash
sed -n '/<head>/,\@</head>@{/<link/{s/^[ \t]*//p};{\@<script type="text/javascript">@,\@/</script>@p}}'
```

`<body>`～`</body>`の範囲も同様です。
これをすべてのHTMLファイルに適用するだけなので**bash**で十分です。
移行ツールは一度しか使わないため、この際コードの保守性は無視して良いでしょう。

Windows環境の`Thumbs.db`（サムネイルキャッシュ）が大量に残っていたので、移行ツールを作成する前に削除しておきます。

```bash
find . -name '*.db' | xargs rm
```

## 完成版
完成した移行ツールを、本記事用に少し改変して晒します。
数百あったHTML/CSSファイルが一発で変換できました（JavaScriptは外部ファイル化されておらず、すべてインラインで埋め込まれていました）。

```bash:bash
# すべてのCSSファイルに対して適用
for FILE in `find . -name '*.css'`; do
  nkf -w -Lu --overwrite $FILE         # UTF-8に変換
  sed -i '1i @charset "utf-8";' $FILE  # 先頭にエンコーディング宣言の追加
done

# すべてのHTMLファイルに対して適用
for FILE in `find . -name '*.html' -o -name '*.htm'`; do
  echo $FILE
  nkf -w -Lu --overwrite $FILE  # UTF-8に変換

  # HEAD部の抽出
  cp /dev/null $HEADFILE
  sed -n '/<head>/,\@</head>@{/<link/{s/^[ \t]*//p};{\@<script type="text/javascript">@,\@/</script>@p}}' $FILE > $HEADFILE

  # BODY部の抽出
  cp /dev/null $BODYFILE
  sed -n '/<body>/,\@</body>@p' $FILE > $BODYFILE
  if [[ "$FILE" =~ "（条件）" ]]; then
    # メインコンテンツの抽出
    sed -n '/"content"/,/"footer"/p' $BODYFILE | head -n -1 > $ADDFILE
    TITLE="ほげほげ"
  fi

  # 一時ファイル作成
  cat << EOF > $OUTFILE
<?php // WordPressで使用します。変更しないで下さい
include_once('/home/foo/example.co.jp/public_html/wp-load.php');
add_action('wp_head', function(){ ?>
EOF
  cat $HEADFILE >> $OUTFILE
  cat << EOF >> $OUTFILE
<?php });
add_filter('pre_get_document_title', function(){ // タイトルの変更
  return '$TITLE － ほげほげ株式会社';
});
get_header();
?>
<div id="content"><div class="container"><div class="d-flex flex-wrap"><div class="flex-grow-1 me-5">
<h2>$TITLE</h2><div class="legacy-page">
<!-- BEGIN LEGACY CODE -->
EOF
  cat $ADDFILE >> $OUTFILE
  cat << EOF >> $OUTFILE
<!-- END LEGACY CODE -->
</div></div><div><?php get_sidebar(); ?></div></div></div></div><?php get_footer(); ?>
EOF

  # 修正内容を確定
  mv $OUTFILE $FILE
done
```

# 新サイト公開時の設定
## 301リダイレクトの設定
URLが変わるため、新サイト公開時にリダイレクトの設定を行います。

### 固定ページへのリダイレクト

```apache:.htaccess
RewriteEngine On
RewriteRule ^(product|company|faq|recruit)\.html$ https://www.example.co.jp/$1 [R=301,L]
```

### 従来の膨大なHTMLファイルのリダイレクト
配布しているカタログに、各製品情報URLのQRコードを掲載しているため、必須になります。
これもbashで一括生成し`.htaccess`に追加しました。
沢山ありましたが記述できる`RewriteRule`の数に上限は無いようです（※リダイレクト回数には上限あり）。

```bash:bash
for FILE in `find . -name '*.html' -o -name '*.htm'`; do
  SRC=${FILE:2}
  echo "RewriteRule ^(${SRC//./\.})$ https://www.example.co.jp/static_html/\$1 [R=301,L]"
done
```

ちなみにですが、QRコード画像ファイルの大量生成も、Pythonなら数行で出来てしまいますよ！

```:in.csv
PRODUCT_CODE,URL,IMG_FILE
XXXXXX,https://www.example.com/product/foo,XXXXXX.png
YYYYYY,https://www.example.com/product/bar,YYYYYY.png
  :
```

```python:Python
import csv
import qrcode

with open('in.csv') as f:
  reader = csv.DictReader(f)
  for row in reader:
    img = qrcode.make(row['URL'])
    img.save('./img/' + row['IMG_FILE'])
```

リダイレクトチェッカーを公開するサイトも沢山あるので活用しましょう。

https://abashi.net/redirect-checker

## セキュリティの設定
WordPressの編集者権限をユーザ部門に渡すため、社外から管理画面にアクセスする場合はパスワード認証を要求するようにします（固定IPアドレスの契約が必要）。
これで、退職者が出た場合でもWordPressアカウントのパスワードを変えずに済みます。

以下は一般的なサーバでの設定方法です。エックスサーバーではコントロールパネルから類似の設定が行えます。

```bash:.htdigestの作成
$ a2enmod auth_digest
$ htdigest -c ファイル名 "Digest Auth" xxxxx
```

```apache:httpd.conf
<files wp-login.php>
    <RequireAny>
        AuthType Digest
        AuthName "Digest Auth"
        AuthUserFile ファイル名
        Require valid-user
        Require ip xxx.xxx.xxx.xxx
    </RequireAny>
</files>
```

[Adminimize](https://wordpress.org/plugins/adminimize/)というプラグインも併用して管理画面のメニューを極力シンプルにします。
表示項目が多いと難しそうな印象を与えてしまい、ユーザ部門に運用を受け入れてもらえない可能性があるからです。

# 今後の改善点
数百に及ぶ製品情報は Microsoft Access のデータベースで管理しています。これをサーバのMySQLにインポートし、PHPプログラムでHTMLレンダリングする仕組みにすれば、個別にページを作成する手間が省けます。
**ヘッドレスCMS**を活用するのも面白いかもしれません。
これは今後の改善点とし、会社に提案しようと思います。