bootstrap を使ってリニューアル

(860d) 更新

FrontPage

概要

これまでこのサイトでは、pukiwiki の標準に近いスキンを使っていたのですが、 昨今の Google:モバイルファースト の流れに乗り、 Google:Bootstrap を使ってリニューアルすることにしまた。

2015-02-19 より、新しいスキンで運用を始めています。

まずは開発用環境を一式整える

php をインストール

一緒に unzip も。

LANG: console
$ sudo aptitude install libapache2-mod-php5 unzip

パーミッションの調整

自分が www-data グループに入っていることを確認した上で、 /var/www 以下を www-data グループの人間が自由にいじれるようにする

LANG: console
$ groups
 osamu ... www-data ...
$ cd /var/www
$ sudo chgrp -R www-data .
$ sudo chmod -R g+r .
$ sudo find . -type d -exec chmod g+wx {} \;

ファイルを展開

LANG: console
$ cd /var/www/html
$ unzip ~/pukiwiki-1_5_0_utf8.zip
$ mv pukiwiki-1_5_0_utf8 pukiwiki
$ cd pukiwiki
$ chgrp -R www-data .
$ chmod -R o-rwx .

ここまでで http://localhost/pukiwiki へアクセス可能

git を導入

LANG: console
$ git init
$ cat > .gitignore
 *~
 *.BAK
 
 .gitignore
 
 attach
 backup
 cache
 counter
 diff
 image
 trackback
 wiki
 ^D
$ 

古いデータを UTF-8 に変換する

古い wiki のデータは EUC で保存されていたので、 これを UTF-8 に変換する必要がありました。

古い wiki のディレクトリで以下のスクリプトを走らせることで do.sh というファイルを作成して、source コマンドでシェルに食べさせればできました。

convert.php

LANG:php(linenumber)
<?php

# 与えられた名前のディレクトリの $pattern にマッチする
# ファイルのファイル名(hex2bin でエンコード済み)を 
# EUC-JP から UTF-8 に変換してコールバック関数を呼び出す
function convert_dir(
    $dir_name, 
    $callback,    # function($src, $dest) { ... }
    $pattern = "/^(?!\.)([0-9A-F][0-9A-F]|_)*(\..*)?$/")
{
  $file_names = files_in_dir($dir_name, $pattern);
  
  print "mkdir ${dir_name}_utf8\n";
  foreach ($file_names as $index => $file_name) {
    $src = $dir_name . "/" . $file_name;
    $dest = $dir_name . "_utf8/" . convert_filename($file_name);
    $callback($src, $dest);
  }
}

# 与えられた名前のディレクトリの $pattern にマッチする
# ファイルを列挙した配列を返す
function files_in_dir($dir_name, $pattern)
{
  $file_names = [];
  
  $h_dir = opendir( $dir_name );
  while ( $file_name = readdir( $h_dir ) ) {
    if ( ! preg_match($pattern, $file_name) )
      continue;
  	$file_names[] = $file_name;
  }
  closedir( $h_dir );
  
  return $file_names;
}

# EUC-JP をエンコードしたファイル名を受け取って
# UTF-8 をエンコードしたファイル名にして返す
# ファイル名はエンコードされていない拡張子や "_" を
# 含んでいる場合があるため、それら以外の部分を変換する
function convert_filename($file_name)
{
	preg_match("/^([^\.]*?)(\..*|)$/", $file_name, $matches); # 必ずマッチする
	$base_name = $matches[1];
	$extention = $matches[2];
	return implode("_", array_map("convert_code", explode("_", $base_name))) . $extention;
}

# EUC-JP をエンコードした文字列を受け取って
# UTF-8 をエンコードした文字列にして返す
function convert_code($s)
{
  return strtoupper( bin2hex( mb_convert_encoding( hex2bin($s), "UTF-8", "EUC-JP" ) ) );
}

#
# 変換処理のバリエーション
#

function adjust_time_stamp($s, $d) 
{
  print "touch -r $s $d\n";
}

function simple_copy($s, $d) 
{
  print "cp $s $d\n";
  adjust_time_stamp($s, $d);
}

function simple_convert($s, $d) 
{
  print "nkf -w $s > $d\n";
  adjust_time_stamp($s, $d);
}

function gzip_convert($s, $d) 
{
  print "gunzip -c $s | nkf -w | gzip > $d\n";
  adjust_time_stamp($s, $d);
}

#
# 各ディレクトリに対して必要な処理を標準出力に表示する
#

convert_dir("attach", simple_copy);

convert_dir("backup", gzip_convert);

convert_dir("cache", simple_convert);
simple_convert("cache/recent.dat", "cache_utf8/recent.dat");

convert_dir("counter", simple_copy);

convert_dir("diff", simple_convert);

convert_dir("wiki", simple_convert);

?>
LANG:console
$ php5 convert.php > do.sh
$ less do.sh
$ source do.sh

こうして変換した後、????_utf8/* を新しい wiki の対応するフォルダにコピーしました。

スキンをいじる

LANG:console
$ ls skin/
 index.html       pukiwiki.css.php   tdiary.css.php
 keitai.skin.php  pukiwiki.skin.php  tdiary.skin.php
$ grep \\.skin\\. *
 INSTALL.txt:      ※スキン(外見の骨組み)に関する設定項目は skin/スキン名.skin.php の先頭に
 default.ini.php:        define('SKIN_FILE', DATA_HOME . SKIN_DIR . 'tdiary.skin.php');
 default.ini.php:        define('SKIN_FILE', DATA_HOME . SKIN_DIR . 'pukiwiki.skin.php');
 keitai.ini.php:define('SKIN_FILE', DATA_HOME . SKIN_DIR . 'keitai.skin.php');
 pukiwiki.ini.php:// Skin files (SKIN_DIR/*.skin.php) are needed at
$ jed default.ini.php
$ git diff default.ini.php
 diff --git a/default.ini.php b/default.ini.php
 index 2f5259a..3a0b3ac 100644
 --- a/default.ini.php
 +++ b/default.ini.php
 @@ -11,11 +11,7 @@
  /////////////////////////////////////////////////
  // Skin file
 
 -if (defined('TDIARY_THEME')) {
 -       define('SKIN_FILE', DATA_HOME . SKIN_DIR . 'tdiary.skin.php');
 -} else {
 -       define('SKIN_FILE', DATA_HOME . SKIN_DIR . 'pukiwiki.skin.php');
 -}
 +define('SKIN_FILE', DATA_HOME . SKIN_DIR . 'bootstrap.skin.php');
 
  /////////////////////////////////////////////////
  // 雛形とするページの読み込みを可能にする

bootstrap の theme を落としてくる

http://www.bootbundle.com/ から良さそうなのを探して・・・

これとかどうかしら?

http://ironsummitmedia.github.io/startbootstrap-sb-admin-2/pages/index.html

LANG:console  
$ cd skin/
$ wget https://github.com/IronSummitMedia/startbootstrap-sb-admin-2/archive/v1.0.5.zip
$ unzip v1.0.5.zip
$ mv startbootstrap-sb-admin-2-1.0.5/index.html sb-admin2.html
$ mv startbootstrap-sb-admin-2-1.0.5/* .
$ rm -r startbootstrap-sb-admin-2-1.0.5/ v1.0.5.zip
$ rm LICENSE README.md
$ cp pukiwiki.skin.php bootstrap.skin.php
$ cd ..
$ git add .
$ git commit -m "sb-admin-2 added"

http://localhost/pukiwiki/skin/sb-admin2.html で sb-admin2 のテーマを表示できるようになりました。

スキンの変更

pukiwiki.skin.php の

LANG:php
// ------------------------------------------------------------
// Output

以降に sb-admin2.html の内容を移していきます。

不必要な設定をごっそり削除

Output の前までを

LANG:php
<?php

// ------------------------------------------------------------
// Code start

// Prohibit direct access
if (! defined('UI_LANG')) die('UI_LANG is not set');
if (! isset($_LANG)) die('$_LANG is not set');
if (! defined('PKWK_READONLY')) die('PKWK_READONLY is not set');

$lang  = & $_LANG['skin'];
$link  = & $_LINK;
$image = & $_IMAGE['skin'];
$rw    = ! PKWK_READONLY;

まで削りました。

ヘッダー部分

  • DOCTYPE は html5
  • 文字セットは UTF-8 固定

とするため、

を参考にしながら、以下のようにしました。

LANG:php
// ------------------------------------------------------------
// Output

// HTTP headers
pkwk_common_headers();
header('Cache-control: no-cache');
header('Pragma: no-cache');
header('Content-Type: text/html; charset=UTF-8');
?>
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- meta name="description" content="" -->
  <meta name="author" content="<?php echo $modifier ?>">
  <meta name="date" content="<?php echo $lastmodified ?>">
  <?php if ($nofollow || ! $is_read)  { ?> <meta name="robots" content="NOINDEX,NOFOLLOW" /><?php } ?>

  <title><?php echo $title ?> - <?php echo $page_title ?></title>

  <link rel="SHORTCUT ICON" href="<?php echo $image['favicon'] ?>" />
  <link rel="alternate" type="application/rss+xml" title="RSS" href="<?php echo $link['rss'] ?>" /><?php // RSS auto-discovery ?>

  <link href="skin/bower_components/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
  <link href="skin/bower_components/metisMenu/dist/metisMenu.min.css" rel="stylesheet">
  <link href="skin/dist/css/timeline.css" rel="stylesheet">
  <link href="skin/dist/css/sb-admin-2.css" rel="stylesheet">
  <link href="skin/bower_components/morrisjs/morris.css" rel="stylesheet">
  <link href="skin/bower_components/font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css">
  <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
      <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
  <![endif]-->

  <?php echo $head_tag ?>
</head>

これにともない、lib/html.php の pkwk_output_dtd は必要なくなっています。

$lastmodified について

フォーマットが良くないので、変更したい。

LANG:console
$ grep lastmodified lib/*
 ...
 lib/html.php:   $lastmodified = $is_read ?  format_date(get_filetime($_page)) .
$ less lib/html.php
        $lastmodified = $is_read ?  format_date(get_filetime($_page)) .
                ' ' . get_pg_passage($_page, FALSE) : '';
$ less lib/func.php
 function format_date($val, $paren = FALSE)
 {
         global $date_format, $time_format, $weeklabels;
 
         $val += ZONETIME;
 
         $date = date($date_format, $val) .
                 ' (' . $weeklabels[date('w', $val)] . ') ' .
                 date($time_format, $val);
 
         return $paren ? '(' . $date . ')' : $date;
 }
$ grep date_format *
 pukiwiki.ini.php:$date_format = 'Y-m-d';
 ...

せっかく $date_format と $time_format を指定できるのに $weeklabels が必ず入ってしまう仕様。そして get_pg_passage も入っちゃう。

似たような関数に get_date もあって、こっちも同じ。

できれば出力を <time> タグで括って、datetime="" の属性に iso 8601 フォーマットで正確な日付を出力したい。そうしておけば後から Javascript などでいろいろできるはず(& new プラグインと被るけど・・・)。 Google:html5 time

LANG:php
function format_date_with_tag($val, $paren = FALSE)
{
  return wrap_with_time_tag(format_date($val, $paren), $val);
}

function wrap_with_time_tag($display, $time = UTIME)
{
  $datetime = date('c', $time);
       return "<time datetime=\"$datetime\">$display</time>";
}

として、

LANG:php
       $lastmodified = $is_read ? format_date_with_tag(get_filetime($_page)) .
                ' ' . get_pg_passage($_page, FALSE) : '';

とすれば、上記の meta タグを

LANG:php
 <meta name="date" content="<?php echo preg_replace('/.*\"(.*)\".*/', '\\1', $lastmodified) ?>">

とすることで ISO 8601 形式の日付を入れられます。

& new プラグインや # comment プラグインなどとも整合性がとれるようにいろいろいじる必要があるけれど・・・

navbar で囲む

LANG:html
<body>
<div id="wrapper">

    <!-- Navigation -->
    <nav class="navbar navbar-default navbar-static-top" role="navigation" style="margin-bottom: 0">
        <div class="navbar-header">
            <!-- 左上のサイト名部分 -->
        </div>
        <ul class="nav navbar-top-links navbar-right">
            <!-- 上部のバー -->
        </ul>
        <div class="navbar-default sidebar" role="navigation">
            <!-- 左部のバー -->
        </div>
    </nav>
 
    <div id="page-wrapper">
        <!-- 本文 -->
    </div>

</div>
</body>

という構造なので、本文部分を上記で囲み、

LANG:php
     <!-- 左部のバー -->
     <?php if (arg_check('read') && exist_plugin_convert('menu')) { ?>
       <div id="menubar"><?php echo do_plugin_convert('menu') ?></div>
     <?php } ?>

として、サイドメニューを左部に読み込むようにしました。

編集メニューをトップバーに移す

LANG:php
   <ul class="nav navbar-top-links navbar-right">
     <!-- 上部のバー -->
     <?php include "navbar-top.php" ?>
   </ul>

として、

LANG:php
<li class="dropdown">
    <a class="dropdown-toggle" data-toggle="dropdown" href="#">
      <i class="fa fa-edit fa-fw"></i> <i class="fa fa-caret-down"></i>
    </a>
    <ul class="dropdown-menu">
        <?php
        function _navigator($key, $value = '', $javascript = ''){
        	$lang = & $GLOBALS['_LANG']['skin'];
        	$link = & $GLOBALS['_LINK'];
        	if (! isset($lang[$key])) { echo 'LANG NOT FOUND'; return FALSE; }
        	if (! isset($link[$key])) { echo 'LINK NOT FOUND'; return FALSE; }
        	if (! PKWK_ALLOW_JAVASCRIPT) $javascript = '';

        	echo '<a href="' . $link[$key] . '" ' . $javascript . '>' .
        		(($value === '') ? $lang[$key] : $value) .
        		'</a>';

        	return TRUE;
        }
        ?>

        <?php if ($is_page) { ?>
          <?php if ($rw) { ?>
        	  <li><?php _navigator('edit') ?>
        	  <?php if ($is_read && $function_freeze) { ?>
        		  <li><?php (! $is_freeze) ? _navigator('freeze') : _navigator('unfreeze') ?>
        	  <?php } ?>
          <?php } ?>
          <li><?php _navigator('diff') ?>
          <?php if ($do_backup) { ?>
        	  <li><?php _navigator('backup') ?>
          <?php } ?>
          <?php if ($rw && (bool)ini_get('file_uploads')) { ?>
        	  <li><?php _navigator('upload') ?>
          <?php } ?>
          <li><?php _navigator('reload') ?>
          <li class="divider"></li>
        <?php } ?>

        <?php if ($rw) { ?>
          <li><?php _navigator('new') ?>
        <?php } ?>
        <li><?php _navigator('list') ?>
        <?php if (arg_check('list')) { ?>
          <li><?php _navigator('filelist') ?>
        <?php } ?>
        <li><?php _navigator('search') ?>
        <li><?php _navigator('recent') ?>
        <li><?php _navigator('help')   ?>

        <?php if ($trackback) { ?>
          <li class="divider"></li>
          <li><?php _navigator('trackback', $lang['trackback'] . '(' . tb_count($_page) . ')',
         	  ($trackback_javascript == 1) ? 'onclick="OpenTrackback(this.href); return false"' : '') ?>
        <?php } ?>
        <?php if ($referer)   { ?> &nbsp;
          <li class="divider"></li>
          <li><?php _navigator('refer') ?>
        <?php } ?>
    </ul>
</li>

highlight.js

math.inc.php

mathjax.inc.php

recaptcha - Google

lastmodified

breadscrum

編集可能・不可能での表示・非表示

heading にナビを追加

paraedit.inc.php への対応と合わせて、以下のようにすることで見出しにナビを追加しました。

lib/convert_html.php

LANG:PHP
function convert_html($lines)
{
  global $vars, $digest;
  static $contents_id = 0;

  // Set digest
  $digest = md5(join('', get_source($vars['page'])));

  if (! is_array($lines)) $lines = explode("\n", $lines);

  $body = & new Body(++$contents_id);
  $body->parse($lines);

  $headings = [];
  foreach($body->elements as $key=>$element) {
    if (is_a($element, 'Heading')) {
      $headings[] = $element;
    }
  }

  for($i=0; $i<count($headings); $i+=1){
    if (preg_match('/<a class="anchor_super" id="([^"]*)"/', $headings[$i]->elements[0]->elements[0], $match)){
      $star = 'href="#' . $match[1] . '"';
    }else{
      $star = '';
    }
    if ($i<count($headings)-1) {
      $next = 'href="#' . $headings[$i+1]->id . '"';
    } else {
      $next = '';
    }
    if ($i>0) {
      $prev = 'href="#' . $headings[$i-1]->id . '"';
    } else {
      $prev = '';
    }
    $paraedit = "?plugin=paraedit&amp;parnum=" .($i+1). "&amp;page=" . rawurlencode($vars['page']) . '&amp;refer=' . rawurlencode($vars['page']);
    $navi = "\n" . <<< EOS
      <div class="header-navi btn-group" role="group">
      <a class="btn btn-default btn-sm" $star title="anchor"><i class="fa fa-star-o"></i></a>
      <a class="btn btn-default btn-sm" $next title="next"><i class="fa fa-angle-down"></i></a>
      <a class="btn btn-default btn-sm" $prev title="prev"><i class="fa fa-angle-up"></i></a>
      <a class="btn btn-default btn-sm" href="#navigator" title="top"><i class="fa fa-angle-double-up"></i></a>
      <a class="btn btn-default btn-sm if-rw" href="$paraedit" title="edit" ><i class="fa fa-pencil"></i></a>
      </div>
EOS;
    array_splice($headings[$i]->elements, 0, 0, [new Inline($navi)]);
  }

  return $body->toString();
}

skin/bootstrap.css.php

LANG:css
/* 見出しの上に出すナビゲーション */

.header-navi {
  display: none;
}

:hover > .header-navi {
  display: block;
}

.header-navi {
  position: absolute;
  top: 0px;
  opacity: 0.4;
  transition: opacity 0.3s;
}

.header-navi:hover {
  opacity: 1;
}

/* 見出しに付けるダガー */

.anchor_super
{
  vertical-align: super;
  font-size: smaller;
  opacity: 0.4;
}

@media print {
  .anchor_super { display: none }
}

/* 書き込み権限があるときのみ表示 */

body.ro .if-rw {
  display: none;
}

Counter: 3865 (from 2010/06/03), today: 2, yesterday: 0