메아리 저널

도쿠위키 수정

저번에 도쿠위키설치했다는 글을 썼는데 최근 며칠동안 개인 위키로 쓰기 좋게 다양한 수정을 가해 보았다. 필요하신 분들을 위해 수정한 부분을 공개한다. 참고로 내가 사용하고 있는 버전은 2006년 10월 19일판(RC3)이다.

좀 긴 글이니 보기를 클릭해서 필요한 부분만 보시길 바란다. (태터는 코드 붙여 넣기가 참 불편하군... -_-;) 모든 수정된 코드는 도쿠위키 라이선스(GPL이었나?)와 동일한 조건으로 배포된다.

제목줄 문법 수정

다음 수정은 첫 단계 제목줄을 ====== asdf ====== 대신에 ## asdf 식으로 쓸 수 있도록 한다. 또한 이들이 항상 정확히 한 줄에 오도록 해서 실수를 방지한다. (현재 문법에서는 foo == bar == quux라고 해도 제목줄이 만들어진다!)

/inc/parser/handler.phpDoku_Handler 클래스에 있는 header() 메소드를 다음과 같이 수정하고 덧붙인다.

   function header($match, $state, $pos) {
        // get level and title
        $title = trim($match);
        $level = 7 - strspn($title,'=');
        if($level < 1) $level = 1;
        $title = trim($title,'=');
        $title = trim($title);

        return $this->set_header($title, $level, $pos);
    }

   function header_alt($match, $state, $pos) {
        $title = trim($match);
        $level = strspn($title,'#') - 1;
        $title = trim($title,'#');
        $title = trim($title);

        return $this->set_header($title, $level, $pos);
    }

   function set_header($title, $level, $pos) {
        global $conf;

        if ($this->status['section']) $this->_addCall('section_close',array(),$pos);

        if ($level<=$conf['maxseclevel']) {
            $this->_addCall('section_edit',array($this->status['section_edit_start'], $pos-1, $this->status['section_edit_level'], $this->status['section_edit_title']), $pos);
            $this->status['section_edit_start'] = $pos;
            $this->status['section_edit_level'] = $level;
            $this->status['section_edit_title'] = $title;
        }

        $this->_addCall('header',array($title,$level,$pos), $pos);

        $this->_addCall('section_open',array($level),$pos);
        $this->status['section'] = TRUE;
        return TRUE;
    }

그리고 /inc/parser/parser.phpDoku_Parser_Mode_header 클래스에서 preConnect() 메소드를 다음과 같이 고친다.

    function preConnect() {
        //we're not picky about the closing ones, two are enough
       $this->Lexer->addSpecialPattern(
                            '(?<=\n|^)[ \t]*={2,}[^\n]+={2,}[ \t]*(?=\n)',
                            'base',
                            'header'
                        );

       $this->Lexer->addSpecialPattern(
                            '(?<=\n|^)#{2,}\s[^\n]+(?=\n)',
                            'base',
                            'header_alt'
                        );
    }

하나 더, outdent 플러그인을 사용하면 왼쪽의 섹션 들여쓰기를 필요에 따라 줄이거나 할 수 있다. (예를 들어서 문서의 "결론" 부분이 별도의 섹션으로 들어 있지 않다면 들여쓰기를 해서는 안 될 것이다.) 이 플러그인 역시 새로 바뀐 문법에 따라 몇 군데 수정했다. /lib/plugins/outdent/syntax.php에서 connectTo()handle() 메소드를 다음과 같이 수정한다.

    function connectTo($mode) { 
        $this->Lexer->addSpecialPattern('\n[ \t]*(?:==+|##+)[ \t]*(?=\n)',$mode,'plugin_outdent'); 
    }

    function handle($match, $state, $pos, &$handler){
      if ($state == DOKU_LEXER_SPECIAL) {
        if ($match{1} == '=') {
          $tlevel = 7 - strlen(trim($match));
        } else {
          $tlevel = strlen(trim($match)) - 1;
        }

        $level = $this->_getLevel($handler->calls);
        if ($level > $tlevel) {
            $handler->_addCall('section_close', array(), $pos);
            $handler->_addCall('section_open', array($tlevel), $pos);    
        }        
      }

      return NULL;
    }

링크 문법 수정

현재 인터위키 이름에 숫자를 넣는 것은 금지되어 있다. 그러나 꼭 숫자를 안 쓰면 불안한 나같은-_- 사람을 위해 고쳐 봤다.

/inc/parser/handler.phpinternallink() 메소드에서 인터위키와 관련된 부분(아래와 비슷하게 생겼다)을 다음과 같이 고친다.

         if ( preg_match('/^[a-zA-Z0-9\.]+>{1}.*$/u',$link[0]) ) {

또한 외부 링크를 많이 쓰는 경우 http://를 맨 앞에 붙여 주는 게 상당히 귀찮기 때문에 //라고만 써도 앞의 scheme을 http:로 가정하도록 했다. (이상해 보일 수도 있지만, 사실 //로 시작하는 주소도 올바른 URL이다.) 위에서 고친 부분 약간 아래에 "external link"라고 쓰여진 주석이 있는데 그 근처를 다음과 같이 고친다.

       }elseif ( preg_match('#^(?:([a-z0-9\-\.+]+?):)?//#i',$link[0]) ) {
        // external link (accepts all protocols)
            if (substr($link[0],0,2) == '//') $link[0] = 'http:'.$link[0];

목록 문법 수정

목록 문법에는 두 가지 손질을 가했는데, 다음과 같다.

  • 정의 목록 추가 (dl 플러그인의 수정)
  • 리스트 앞에 꼭 붙어야 하는 공백을 없어도 되도록 수정 (단, 이렇게 하면 혼란을 방지하기 위해서 *- 같은 문자 뒤에 공백이 꼭 와야 한다.)

먼저 /inc/parser/parser.phpDoku_Parser_Mode_listblock 클래스에서 connectTo() 메소드를 다음과 같이 고친다.

    function connectTo($mode) {
        // - lines can be indented with 0 or more space
        // - there should be one or more whitespaces after bullet
        $this->Lexer->addEntryPattern('\n *[\-\*](?=\s)',$mode,'listblock');       $this->Lexer->addEntryPattern('\n\t{1,}[\-\*](?=\s)',$mode,'listblock');

        $this->Lexer->addPattern('\n *[\-\*](?=\s)','listblock');
        $this->Lexer->addPattern('\n\t{1,}[\-\*](?=\s)','listblock');
    }

그리고 dl 플러그인을 깐 뒤, /lib/plugins/dl/syntax.php에서 connectTo()postConnect() 메소드를 다음과 같이 수정한다.

  function connectTo($mode) {
    $this->Lexer->addEntryPattern('^ *\!', $mode, 'plugin_dl');
    $this->Lexer->addEntryPattern('^ *\?', $mode, 'plugin_dl');
  }

  function postConnect() {
    $this->Lexer->addPattern('\n *\!', 'plugin_dl');
    $this->Lexer->addPattern('\n *\?', 'plugin_dl');
    $this->Lexer->addExitPattern('\n$', 'plugin_dl');
  }

문서 목록 플러그인 수정

분류 등을 구현하기 위해 문서 목록 플러그인을 두 개 사용했는데, 하나는 현재 문서의 역링크를 보여 주는 backlinks 플러그인이고 또 하나는 특정 네임스페이스 안의 모든 문서를 보여 주는 pageindex 플러그인이다.

backlinks 플러그인은 기능은 좋지만 영 내 맘에 들지도 않고 버그-_-도 있어서 여기 저기 수정했다. (앞으로 수정할 파일은 모두 /lib/plugins/backlinks/syntax.php이다.) 먼저 역링크가 아예 없을 때 보기 흉한 php 에러가 뜨는데, handle() 메소드에서 다음을,

        ksort($backlinks);

다음으로 고치면 된다.

        if (!is_null($backlinks)) ksort($backlinks);

네임스페이스를 제대로 고려하지 않아서 링크가 깨지는-_- 문제도 있는데 renderpageList() 메소드에서 링크를 추가하는 부분(아래 코드랑 비슷하게 생겼다)을 다음과 같이 고치면 된다.

          $renderer->internalLink(':'.$page, $page);

자동으로 첫 문자에 따라 그룹화하는 기능도 있는데 전혀 쓸모가 없어서 빼 버렸다. 이건 render() 메소드에서 if (!empty($data[1]))로 시작하는 블록 안의 내용을 모두 지우고 다음으로 대체하면 된다.

                $this->renderPageList($renderer, $data[1]);

블로그 플러그인 수정

bliki라는 플러그인이 있는데 블로그를 만들어 쓰는데 딱 좋다. 이 플러그인이랑 pageindex, backlinks 플러그인을 같이 쓰면 분류라던지 뭐 그런 것도 큰 문제 없이 구현할 수 있다.

하지만 개인적으로 쓰다 보니까 이 플러그인이 생성하는 "새 글" 링크가 처음 페이지가 불려질 때 고정이 되어 버려서 나중에 링크를 열면 작성 시각이 이상하게 나오는 문제가 있었다. (사실 이건 이 플러그인의 한계이기도 하다.) 이 문제를 대강이나마 해결하기 위해 작성 시각을 "새 글" 링크가 눌렸을 때로 고치는 자바스크립트를 하나 만들어 봤다.

/lib/plugins/bliki/script.js를 새로 만들고, 그 안에 다음과 같은 코드를 넣는다. 이렇게만 하면 실시간으로 "새 글" 링크가 고쳐지기 때문에 작성 시각을 좀 더 정확하게 맞출 수 있다.

Number.prototype.formatlen = function(l) {
    var s = this.toString();
    while (s.length < l) s = '0' + s;
    return s;
};

function bliki_update_newpost() {
    try {
        var anchor = document.getElementById('blognew').firstChild;
        anchor.href = anchor.href.replace(
            /(\d{4})([-\/:]|%3A)(\d\d)([-\/:]|%3A)(\d\d)([-\/:]|%3A)(\d{6})/i,
            function (_, y, _1, m, _2, d, _3, t) {
                var t = parseInt(t, 10);
                var d = new Date(parseInt(y, 10), parseInt(m, 10) - 1, parseInt(d, 10),
                        parseInt(t / 10000), parseInt(t / 100) % 100, t % 100 + 1);
                var s = d.getFullYear().formatlen(4) + _1 +
                        (d.getMonth() + 1).formatlen(2) + _2 +
                        d.getDate().formatlen(2) + _3 +
                        d.getHours().formatlen(2) +
                        d.getMinutes().formatlen(2) +
                        d.getSeconds().formatlen(2);
                return s;
            });
    } catch (e) {}
}

setInterval('bliki_update_newpost()', 1000);

상자 플러그인 수정

boxes 플러그인은 예쁘게 생긴 상자들을 텍스트 주변에 둘러 싸는데 사용하는 플러그인이다. CSS로 상자의 모양을 마음대로 수정할 수 있다는 점이 강점인데, 길이를 백분율로만 지정할 수 있어서 고쳐 봤다.

/lib/plugins/box/syntax.php에서 _boxstyle() 메소드를 보면 preg_match를 써서 길이를 지정하는 코드가 나온다. 그 줄을 다음과 같이 고친다.

          if (preg_match('/^((\d{1,2}|100)%|\d+(\.\d+)?em|\d+px)$/', $token)) {

이렇게 하면 300px라던지 3.3em 같은 단위도 상자 크기를 정하는 데 쓸 수 있다.

삽입 플러그인 수정

include 플러그인은 한 문서의 내용을 다른 문서에 삽입하는 데 쓰인다. 그런데 이 플러그인의 작동이 상당히 이해하기 힘들고, 실제로 의도한 대로 동작하지 않는 경우가 많아서 그냥 한 가지 문법을 더 만들었다.

고친 플러그인에서는 세 가지 문법을 지원한다.

  • {{page>blah}}라고 하면 blah 문서의 내용이 현재 위치에 그대로 삽입된다. 이 때 섹션 들여쓰기가 자동으로 처리되지 않기 때문에, 뒤에 나오는 내용이 불필요하게 들여쓰기되는 문제가 발생한다.
  • {{section>blah}}라고 하면 blah 문서의 내용을 해당 문법이 들어 있는 섹션에 맞춰서 삽입한다. 여러 섹션이 들어 있는 문서를 삽입할 때는 잘 작동하지만 여전히 내가 원하는 건 아니다. -_-;
  • {{include>blah}}는 내가 이번에 추가한 문법이고, blah 문서의 내용을 어떤 수정도 가하지 않고 그대로 삽입한다.

먼저 /lib/plugins/include/syntax.phpconnectTo() 메소드 뒤에 다음과 같은 줄을 추가한다.

    $this->Lexer->addSpecialPattern("{{include>.+?}}",$mode,'plugin_include'); 

그리고 같은 파일의 _include() 메소드에서, // current section level이라는 주석 근처에 있는 코드를 다음과 같이 고친다.

      if ($include=='include') {
        $clevel = 0;
      } else {
        // current section level 
        $matches = array(); 
        preg_match_all('|<div class="level(\d)">|i', $renderer->doc, $matches, PREG_SET_ORDER); 
        $n = count($matches)-1; 
        if ($n > -1) $clevel = $matches[$n][1]; 
        else $clevel = 0; 
      }

지금이라면 이러니 저러니 주저리 주저리 쓰지 않고 DokuWiki-custom을 권할 것이다. (2010-05-06)

이 글은 본래 http://lifthrasiir.tistory.com/35에 썼던 것을 옮겨 온 것입니다.


(rev 1d46270eb038)