yjcho       2006/08/29 22:05:52

  Modified:    .        config.py
  Log:
   * front_page외에 페이지 이름은 더이상 config.py에 두지 않고, wiki상에서 결정하도록 함
   * 한글화를 하면서 show_page, edit_page 부분을 제거하고 wplus.py에 아예 한글로 집어 넣음.
  
  Revision  Changes    Path
  1.7       +1 -6      wikiplus/config.py
  
  Index: config.py
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/config.py,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- config.py	25 Jun 2005 07:29:07 -0000	1.6
  +++ config.py	29 Aug 2006 13:05:52 -0000	1.7
  @@ -34,13 +34,8 @@
   
   denied_ips = ()
   
  -# page names and button names
  +# default page name
   front_page = '대문'
  -recent_changes = '바뀐글'
  -find_page = '찾기'
  -title_index = '목록'
  -edit_page = '고치기'
  -show_page = '보기'
   
   
   # for uploading feature
  
  
  


yjcho       2006/08/29 22:07:57

  Modified:    .        wplus.cgi
  Log:
   * 가장 많이 쓰이는 path가 /usr/bin/python인거 같아서 수정
  
  Revision  Changes    Path
  1.6       +1 -1      wikiplus/wplus.cgi
  
  Index: wplus.cgi
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/wplus.cgi,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- wplus.cgi	24 Jun 2005 03:25:32 -0000	1.5
  +++ wplus.cgi	29 Aug 2006 13:07:57 -0000	1.6
  @@ -1,4 +1,4 @@
  -#!/usr/local/bin/python -OO
  +#!/usr/bin/python -OO
   # -*- coding: utf-8 -*-
   """
      Wiki+ Driver Script
  
  
  


yjcho       2006/08/29 22:10:46

  Modified:    .        wplus.css wplus.py
  Log:
   * 스타일을 최대한 간단하게 하면서 css파일에서 거의 대부분을 삭제
   * wplus.py에서도 스타일 간단화가 적용
   * 문법에서 title이 heading 1단계가 되도록 함, 원래 heading은 한단계식 밀려남
  
  Revision  Changes    Path
  1.8       +4 -76     wikiplus/wplus.css
  
  Index: wplus.css
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/wplus.css,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- wplus.css	24 Jun 2005 02:25:42 -0000	1.7
  +++ wplus.css	29 Aug 2006 13:10:46 -0000	1.8
  @@ -1,88 +1,16 @@
   /* Wiki+ CSS */
   
  -body {
  -	font-family:Verdana,굴림; font-size:0.75em;
  -	line-height:140%;
  -}
  -a {
  -	color:#205DA8;
  -	text-decoration:none;
  -}
  -a:visited {
  -	color:#205DA8;
  -	text-decoration:none;
  -}
  -
   img.logo {
  -	float:left;
  -	padding-right:10px;
  -	border:0px;
  -}
  -div.title {
  -	font-family:Tahoma,굴림;
  -	font-size:2.3em; font-weight:bold;
  -	padding:15px 0px 10px 0px;
  -}
  -div.goto {
   	float:right;
  -	padding-top:15px;
  +	padding-left:10px;
  +	border:0px;
   }
   
  -div.body {
  -	font-family:굴림,Times;
  -	line-height:160%;
  -}
  -div.footmenu {
  +div.info {
   	float:left;
   }
  +
   div.power {
   	text-align:right;
   	float:right;
   }
  -
  -
  -/* formatting style */
  -div.body a {
  -	text-decoration:underline;
  -}
  -div.body a:visited {
  -	color:#143864;
  -	text-decoration:underline;
  -}
  -
  -div.body h1, h2, h3, h4, h5, h6 {
  -	border-bottom:1px solid #EEEEEE;
  -}
  -div.body h1 {font-size:1.6em;}
  -div.body h2 {font-size:1.4em;}
  -div.body h3 {font-size:1.2em;}
  -div.body h4 {font-size:1.0em;}
  -div.body h5 {font-size:0.8em;}
  -div.body h6 {font-size:0.6em;}
  -
  -div.body hr {
  -	height:1px;
  -	border:1px solid #BBBBBB;
  -}
  -
  -div.body table {
  -	border:1px solid #EEEEEE;
  -}
  -div.body th {
  -	background-color:#EEEEEE;
  -	padding:3px 8px 2px 8px;
  -}
  -div.body td {
  -	background-color:#FFFFFF;
  -	padding:3px 8px 2px 8px;
  -}
  -
  -div.body code {
  -	font-size:1.1em;
  -}
  -div.body pre {
  -	font-size:1.1em;
  -	background-color:#EEEEEE;
  -	border-left:5px solid #5593DF;
  -	padding:5px 10px 5px 10px;
  -}
  
  
  
  1.15      +24 -38    wikiplus/wplus.py
  
  Index: wplus.py
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/wplus.py,v
  retrieving revision 1.14
  retrieving revision 1.15
  diff -u -r1.14 -r1.15
  --- wplus.py	30 Jun 2005 16:22:20 -0000	1.14
  +++ wplus.py	29 Aug 2006 13:10:46 -0000	1.15
  @@ -41,10 +41,8 @@
           data/ --+-- text/ ---+-- pageid1
                   |            `-- pageid2
                   |
  -                +-- backup/ -+-- pageid1            --> editlog
  +                +-- backup/ -+-- pageid1.timestamp
                   |            +-- pageid1.timestamp
  -                |            +-- pageid1.timestamp
  -                |            +-- pageid2
                   |            `-- pageid2.timestamp
                   |
                   `-- editlog
  @@ -55,10 +53,10 @@
   
          Editlog File Structure
          ----------------------
  -        +-------------------------------------------------------+
  -        |page id<TAB>modified time<TAB>host<NEWLINE>|
  -        |    .             .             .         .            |
  -        |                                                       |
  +        +-----------------------------------------+
  +        |page id<TAB>modified time<TAB>ip<NEWLINE>|
  +        |    .             .            .         |
  +        |                                         |
       """
   
       def __init__(self, name, timestamp=None):
  @@ -272,7 +270,7 @@
   class Formatter:
       def __init__(self):
           self._block_re = re.compile(r'^(?:' \
  -            r'(?P<heading>(?P<hmarker>={1,6})\s.*\s(?P=hmarker))|' \
  +            r'(?P<heading>(?P<hmarker>={1,5})\s.*\s(?P=hmarker))|' \
               r'(?P<rule>-{4,})|' \
               r'(?P<list>(?P<indent>\s+)(?:(?P<ol>\d+\.\s)|(?P<ul>\*\s)).*)|' \
               r'(?P<paragraph>.+)|' \
  @@ -328,9 +326,9 @@
   
   
       def _heading_block(self, match):
  -        level = len(match.group('hmarker'))
  +        level = len(match.group('hmarker')) + 1
           print '<h%d>' % level
  -        print self._inline(match.group(0)[level + 1: -(level + 1)])
  +        print self._inline(match.group(0)[level: -level])
           print '</h%d>' % level
   
       def _rule_block(self, match):
  @@ -743,8 +741,8 @@
   def send_title(title, link=None, msg=None):
       splited_title = re.sub(r'([a-z])([A-Z])', r'\1 \2', title)
   
  -    print '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" ' \
  -          '"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
  +    print '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" ' \
  +          '"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
       print '<html><head><title>%s</title>' % splited_title
       print '<meta http-equiv="content-type" content="text/html;' \
             'charset=%s" />' % config.encoding
  @@ -752,40 +750,28 @@
             '<body>' % config.css_url
       print link_tag(urlencode(config.front_page),
                     '<img class="logo" src="%s" alt="logo" />' % config.logo_url)
  -    # goto form
  -    print '<div class="goto">' \
  -          '<form action="%s" method="get">' % get_scriptname()
  -    print 'GO <input type="hidden" name="action" value="goto" />' \
  -          '<input type="text" name="value" size="10" /></form></div>' \
  -          '<div class="title">'
  -    if link:
  -        print link_tag(link, splited_title)
  -    else:
  -        print title
  -    print '</div>'
  -    print link_tag(urlencode(config.recent_changes), config.recent_changes)
  -    print ' | '
  -    print link_tag(urlencode(config.find_page), config.find_page)
  -    print ' | '
  -    print link_tag(urlencode(config.title_index), config.title_index)
  -    print '<hr /><div class="body">'
  +    print '<h1>%s</h1>' % splited_title
       if msg:
           print '%s<hr />' % msg
   
   
   def send_footer(page_name, editable=True, mod_string=None):
  -    print '</div><hr /><div class="footmenu">'
  -    if editable:
  -        print link_tag(urlencode(page_name) + '?action=edit', config.edit_page)
  -    else:
  -        print link_tag(urlencode(page_name), config.show_page)
  -    if mod_string:
  -        print ' (last modified %s)' % mod_string
  -    print '</div><div class="power">Powered by ' \
  +    print '<hr />'
  +    print '<div class="power">Powered by ' \
             '<a href="http://wikiplus.kldp.net">Wiki+</a><br />' \
             '<a href="http://validator.w3.org/check/referer">XHTML 1.0</a> | ' \
             '<a href="http://jigsaw.w3.org/css-validator/check/referer">' \
  -          'CSS 2.0</a></div></body></html>'
  +          'CSS 2.0</a></div>'
  +    if mod_string:
  +        print '이 문서는 %s에 마지막으로 수정되었습니다.<br />' % mod_string
  +    print '이 문서에 사용 가능한 명령은 다음과 같습니다.<br />'
  +    if editable:
  +        print link_tag(urlencode(page_name) + '?action=edit', '고치기')
  +    else:
  +        print link_tag(urlencode(page_name), '보기')
  +    print '</body></html>'
  +
  +
   
   
   
  
  
  


yjcho       2006/08/30 14:12:42

  Modified:    .        wplus.css wplus.py
  Log:
   * formatter에서 block처리시의 복잡성을 약간 줄임
   * table 규칙 추가
   * pre시에 기존 block을 닫지 않던 문제 해결
   * XHTML strict가 되도록 수정
  
  Revision  Changes    Path
  1.9       +3 -2      wikiplus/wplus.css
  
  Index: wplus.css
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/wplus.css,v
  retrieving revision 1.8
  retrieving revision 1.9
  diff -u -r1.8 -r1.9
  --- wplus.css	29 Aug 2006 13:10:46 -0000	1.8
  +++ wplus.css	30 Aug 2006 05:12:42 -0000	1.9
  @@ -1,8 +1,9 @@
   /* Wiki+ CSS */
   
  -img.logo {
  +div.logo {
   	float:right;
  -	padding-left:10px;
  +}
  +img.logo {
   	border:0px;
   }
   
  
  
  
  1.16      +80 -45    wikiplus/wplus.py
  
  Index: wplus.py
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/wplus.py,v
  retrieving revision 1.15
  retrieving revision 1.16
  diff -u -r1.15 -r1.16
  --- wplus.py	29 Aug 2006 13:10:46 -0000	1.15
  +++ wplus.py	30 Aug 2006 05:12:42 -0000	1.16
  @@ -273,12 +273,12 @@
               r'(?P<heading>(?P<hmarker>={1,5})\s.*\s(?P=hmarker))|' \
               r'(?P<rule>-{4,})|' \
               r'(?P<list>(?P<indent>\s+)(?:(?P<ol>\d+\.\s)|(?P<ul>\*\s)).*)|' \
  -            r'(?P<paragraph>.+)|' \
  -            r'(?P<blank>)' \
  +            r'(?P<table>\|\|(?:.+\|\|)+)|' \
  +            r'(?P<blank>\s*)|' \
  +            r'(?P<paragraph>.+)' \
               r')$')
  -        self._cur_block = 'blank'
  -        self._list_stack = [(0, None)]      # (level, order)
  -
  +        self._block_stack = [(None, None)] # (block_type, block_data)
  +        
           self._inline_re = re.compile(
               r"(?P<emph>'{2,3})|" \
               r'(?P<word>\b(?:[A-Z][a-z]+){2,}\b)|' \
  @@ -294,36 +294,39 @@
   
   
       def format(self, raw):
  -        raw_lines = raw.expandtabs().split('\n')
  +        raw_lines = raw.split('\n')
           for raw_line in raw_lines:
               if not self._in_pre:
                   match = self._block_re.match(raw_line)
  -                for block, s in match.groupdict().items():
  -                    if s != None and hasattr(self, '_' + block + '_block'):
  -                        self._change_block(block)
  -                        apply(getattr(self, '_' + block + '_block'), (match,))
  +                for btype, s in match.groupdict().items():
  +                    if s != None and hasattr(self, '_' + btype + '_block'):
  +                        self._close_block(btype)
  +                        apply(getattr(self, '_' + btype + '_block'), (match,))
                           break
               else:
                   print self._inline(raw_line)
   
  -        self._change_block('blank')
  +        self._close_block('blank')
   
   
       # block formatting part
  -    def _change_block(self, new_block):
  -        old_block = self._cur_block
  -        self._cur_block = new_block
  -        if new_block != old_block:
  -            if old_block == 'list':
  -                while self._list_stack[-1][0]:
  -                    prev_order = self._list_stack.pop()[1]
  -                    print '</li></%s>' % prev_order
  -            elif old_block == 'paragraph':
  -                print '</p>'
  -
  -            if new_block == 'paragraph':
  -                print '<p>'
  -
  +    def _close_block(self, new_btype):
  +        """
  +        멀티라인 블럭의 경우는 다른 블록으로 넘어갈 때,
  +        해당 블럭을 닫아 주어야 한다.
  +        """
  +        cur_btype = self._block_stack[-1][0]
  +        if cur_btype == 'list' and new_btype != 'list':
  +            while cur_btype == 'list':
  +                cur_level, cur_order = self._block_stack.pop()[1]
  +                print '</li></%s>' % cur_order
  +                cur_btype = self._block_stack[-1][0]
  +        elif cur_btype == 'table' and new_btype != 'table':
  +            self._block_stack.pop()
  +            print '</tr></table>'
  +        elif cur_btype == 'paragraph' and new_btype != 'paragraph':
  +            self._block_stack.pop()
  +            print '</p>'
   
       def _heading_block(self, match):
           level = len(match.group('hmarker')) + 1
  @@ -338,31 +341,62 @@
               print '<hr size="%d" />' % (len(match.group(0)) - 2)
   
       def _list_block(self, match):
  +        """
  +        XXX: 좀더 간단하게 수정이 필요
  +        현재 block_data는 (level, order)로 저장됨
  +        """
           if match.group('ul'):
  -            order = 'ul'
  +            new_order = 'ul'
           else:
  -            order = 'ol'
  -        level = len(match.group('indent'))
  -        prev_level = self._list_stack[-1][0]
  +            new_order = 'ol'
  +        new_level = len(match.group('indent'))
  +        cur_btype = self._block_stack[-1][0]
  +        if cur_btype == 'list':
  +            cur_level = self._block_stack[-1][1][0]
  +        else:
  +            cur_level = 0
   
  -        if level == prev_level:
  +        if new_level == cur_level:
               print '</li><li>'
  -        elif level > prev_level:
  -            print '<%s><li>' % order
  -            self._list_stack.append((level, order))
  -        else:
  -            while level < prev_level:
  -                prev_order = self._list_stack.pop()[1]
  -                print '</li></%s>' % prev_order
  -                prev_level = self._list_stack[-1][0]
  +        elif new_level > cur_level:
  +            print '<%s><li>' % new_order
  +            self._block_stack.append(('list', (new_level, new_order)))
  +        else:
  +            while new_level < cur_level:
  +                cur_order = self._block_stack.pop()[1][1]
  +                print '</li></%s>' % cur_order
  +                cur_btype = self._block_stack[-1][0]
  +                if cur_btype == 'list':
  +                    cur_level = self._block_stack[-1][1][0]
  +                else:
  +                    cur_level = 0
               print '</li><li>'
   
  -        print self._inline(match.group(0)[level + len(match.group(order)):])
  +        print self._inline(match.group(0)[new_level + 2:])
  +
  +    def _table_block(self, match):
  +        cur_btype, cur_bdata = self._block_stack[-1]
  +        new_bdata = match.group(0).count('||')
  +        if cur_btype != 'table':
  +            print '<table><tr>'
  +            self._block_stack.append(('table', new_bdata))
  +        else:
  +            if new_bdata == cur_bdata:
  +                print '</tr><tr>'
  +            else:
  +                print '</tr></table><table><tr>'
  +                self._block_stack.append(('table', new_bdata))
  +
  +        cols = match.group(0)[2:-2].split('||')
  +        for col in cols:
  +            print '<td>%s</td>' % self._inline(col)
   
       def _paragraph_block(self, match):
  +        cur_btype = self._block_stack[-1][0]
  +        if cur_btype != 'paragraph':
  +            print '<p>'
  +            self._block_stack.append(('paragraph', None))
           print self._inline(match.group(0))
  -        if config.auto_linebreak and not self._in_pre:
  -            print '<br />'
   
       def _blank_block(self, match):
           pass
  @@ -440,6 +474,7 @@
       def _pre_repl(self, word):
           if not self._in_pre and word == '{{{':
               self._in_pre = True
  +            self._close_block('blank')  # pre에 들어가기전 block을 닫아줘야한다
               return '<pre>'
           elif self._in_pre and word == '}}}':
               self._in_pre = False
  @@ -747,10 +782,10 @@
       print '<meta http-equiv="content-type" content="text/html;' \
             'charset=%s" />' % config.encoding
       print '<link rel="stylesheet" type="text/css" href="%s" /></head>' \
  -          '<body>' % config.css_url
  +          '<body><div class="logo">' % config.css_url
       print link_tag(urlencode(config.front_page),
                     '<img class="logo" src="%s" alt="logo" />' % config.logo_url)
  -    print '<h1>%s</h1>' % splited_title
  +    print '</div><h1>%s</h1>' % splited_title
       if msg:
           print '%s<hr />' % msg
   
  @@ -763,13 +798,13 @@
             '<a href="http://jigsaw.w3.org/css-validator/check/referer">' \
             'CSS 2.0</a></div>'
       if mod_string:
  -        print '이 문서는 %s에 마지막으로 수정되었습니다.<br />' % mod_string
  +        print '<div>이 문서는 %s에 마지막으로 수정되었습니다.<br />' % mod_string
       print '이 문서에 사용 가능한 명령은 다음과 같습니다.<br />'
       if editable:
           print link_tag(urlencode(page_name) + '?action=edit', '고치기')
       else:
           print link_tag(urlencode(page_name), '보기')
  -    print '</body></html>'
  +    print '</div></body></html>'
   
   
   
  
  
  


yjcho       2006/08/30 16:04:32

  Modified:    .        config.py wplus.py
  Log:
   * editlog 파일의 구조를 현재 버젼의 레코드에 이전 버젼의 레코드를 가리키는 포인터를 추가.
   * host를 ip로 변경
  
  Revision  Changes    Path
  1.8       +1 -1      wikiplus/config.py
  
  Index: config.py
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/config.py,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- config.py	29 Aug 2006 13:05:52 -0000	1.7
  +++ config.py	30 Aug 2006 07:04:32 -0000	1.8
  @@ -26,7 +26,7 @@
   
   #
   encoding = 'utf-8'
  -show_hosts = True
  +show_ips = False
   date_fmt = '%Y.%m.%d'
   datetime_fmt = '%Y-%m-%d %I:%M:%S'
   changed_time_fmt = '[%I:%M %p]'
  
  
  
  1.17      +20 -27    wikiplus/wplus.py
  
  Index: wplus.py
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/wplus.py,v
  retrieving revision 1.16
  retrieving revision 1.17
  diff -u -r1.16 -r1.17
  --- wplus.py	30 Aug 2006 05:12:42 -0000	1.16
  +++ wplus.py	30 Aug 2006 07:04:32 -0000	1.17
  @@ -53,10 +53,10 @@
   
          Editlog File Structure
          ----------------------
  -        +-----------------------------------------+
  -        |page id<TAB>modified time<TAB>ip<NEWLINE>|
  -        |    .             .            .         |
  -        |                                         |
  +        +------------------------------------------------------+
  +        |page id<TAB>mtime<TAB>previously mtime<TAB>ip<NEWLINE>|
  +        |    .         .              .              .         |
  +        |                                                      |
       """
   
       def __init__(self, name, timestamp=None):
  @@ -73,8 +73,6 @@
               return os.path.join(config.backup_dir,
                                           self.file_name + '.' + self.timestamp)
   
  -    def _log_path(self):
  -        return os.path.join(config.backup_dir, self.file_name)
   
       def _tmp_path(self):
           return os.path.join(config.data_dir,
  @@ -138,8 +136,8 @@
       def update_text(self, timestamp, newtext):
           if not self.is_current():
               raise PageError
  -        host = os.environ.get('REMOTE_ADDR', '')
  -        if host in config.denied_ips:
  +        ip = os.environ.get('REMOTE_ADDR', '')
  +        if ip in config.denied_ips:
               raise DeniedIP
           newtext = newtext.replace('\r', '')
           if not newtext:
  @@ -159,21 +157,20 @@
                   raise UnchangedText
               os.rename(text_path, os.path.join(config.backup_dir,
                                               self.file_name + '.' + str(mtime)))
  +            prev_timestamp = timestamp
  +        else:
  +            prev_timestamp = '0'
   
           os.chmod(tmp_path, 0666)
           os.rename(tmp_path, text_path)
   
           timestamp = str(self.get_mtime())
  -        editlog_add(self._log_path(), self.file_name, timestamp, host)
  -        os.chmod(self._log_path(), 0666)
  -        editlog_add(config.editlog_path,
  -                                      self.file_name, timestamp, host)
  +        editlog_add(self.file_name, timestamp, prev_timestamp, ip)
   
   
       def get_backup_list(self):
           tmp_list = os.listdir(config.backup_dir)
  -        prefix = self.file_name + '.'
  -        tmp_list = filter(lambda x: x.startswith(prefix), tmp_list)
  +        tmp_list = filter(lambda x: x.startswith(self.file_name), tmp_list)
           return map(lambda x: x.split('.')[1], tmp_list)
   
   
  @@ -242,9 +239,9 @@
   # The editlog is stored with one record per line, as tab-separated
   # words: pageid, timestamp, host, comment
   
  -def editlog_add(log_path, pageid, timestamp, host):
  -    entry = string.join((pageid, timestamp, host), "\t") + "\n"
  -    editlog = open(log_path, 'ab')
  +def editlog_add(pageid, timestamp, prev_timestamp, host):
  +    entry = string.join((pageid, timestamp, prev_timestamp, host), "\t") + "\n"
  +    editlog = open(config.editlog_path, 'ab')
       try: 
           fcntl.flock(editlog.fileno(), fcntl.LOCK_EX)
           editlog.seek(0, 2)                  # to end
  @@ -254,8 +251,8 @@
           editlog.close()
   
   
  -def editlog_raw_lines(log_path):
  -    editlog = open(log_path, 'rb')
  +def editlog_raw_lines():
  +    editlog = open(config.editlog_path, 'rb')
       try:
           fcntl.flock(editlog.fileno(), fcntl.LOCK_SH)
           return editlog.readlines()
  @@ -515,14 +512,14 @@
       def _macro_RecentChanges(self, arg):
           import cStringIO
   
  -        lines = editlog_raw_lines(config.editlog_path)
  +        lines = editlog_raw_lines()
           lines.reverse()
       
           buf = cStringIO.StringIO()
           ratchet_day = None
           done_names = {}
           for line in lines:
  -            page_name, mtime, host = line[:-1].split('\t')
  +            page_name, mtime, pmtime, ip = line[:-1].split('\t')
               page_name = unquote_name(page_name)
   
               if done_names.has_key(page_name):
  @@ -541,12 +538,8 @@
               buf.write('<li>')
               buf.write(Page(page_name).link())
               buf.write(' . . . . ')
  -            if config.show_hosts:
  -                try:
  -                    buf.write(socket.gethostbyaddr(host)[0])
  -                except:
  -                    buf.write(host)
  -                buf.write(' ')
  +            if config.show_ips:
  +                buf.write(ip + ' ')
   
               if config.changed_time_fmt:
                   buf.write(time.strftime(config.changed_time_fmt, time_tuple))
  
  
  


yjcho       2006/09/04 16:17:43

  Modified:    .        config.py
  Log:
   * 날짜와 시간을 한글로 표현 하도록 수정
   * denied_ips 변수에 튜플을 쓸 경우 멤버가 한 개일 때 마지막에 콤마를 찍는 것을 깜빡할 수 있다.
     따라서 간단한 리스트로 수정
  
  Revision  Changes    Path
  1.9       +3 -3      wikiplus/config.py
  
  Index: config.py
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/config.py,v
  retrieving revision 1.8
  retrieving revision 1.9
  diff -u -r1.8 -r1.9
  --- config.py	30 Aug 2006 07:04:32 -0000	1.8
  +++ config.py	4 Sep 2006 07:17:43 -0000	1.9
  @@ -27,12 +27,12 @@
   #
   encoding = 'utf-8'
   show_ips = False
  -date_fmt = '%Y.%m.%d'
  -datetime_fmt = '%Y-%m-%d %I:%M:%S'
  +date_fmt = '%Y년 %m월 %d일'
  +datetime_fmt = '%Y년 %m월 %d일 %I시 %M분 %S초'
   changed_time_fmt = '[%I:%M %p]'
   auto_linebreak = True
   
  -denied_ips = ()
  +denied_ips = []
   
   # default page name
   front_page = '대문'
  
  
  


yjcho       2006/09/04 21:46:39

  Modified:    .        wplus.py
  Log:
   * editlog 파일을 이전 개정판에 대한 포인터가 없도록 수정
     - 포인터가 없어도 백업파일 리스트를 보면 충분히 트랙킹 할 수 있고,
       binary search가 가능함
   * Page class를 특정 개정판에 대한 추상화가 아니라 페이지 자체에 대해서 나타냄
     - 모든 개정판을 추상화 함
     - 페이지 보여줄 때 메시지도 같이 보여주는 방식 제거 -> 에러전송 방식은 별도로
     - history 보내기 기능을 클래스 안으로 넣음
   * info 명령을 history로 변경
  
  Revision  Changes    Path
  1.18      +218 -201  wikiplus/wplus.py
  
  Index: wplus.py
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/wplus.py,v
  retrieving revision 1.17
  retrieving revision 1.18
  diff -u -r1.17 -r1.18
  --- wplus.py	30 Aug 2006 07:04:32 -0000	1.17
  +++ wplus.py	4 Sep 2006 12:46:39 -0000	1.18
  @@ -19,8 +19,10 @@
   # Exceptions --------------------------------------------------------
   
   # for page class
  -PageError = 'PageError'
  -NoPage = 'NoPage'
  +NoPage = 'NoPage'                       # 페이지 자체가 없음
  +NoRevision = 'NoRevision'               # 페이지의 특정 개정판이 없음
  +DeletedPage = 'DeletedPage'             # 페이지의 현재의 개정판이 없음
  +NoText = 'NoText'
   EmptyText = 'EmptyText'
   UnchangedText = 'UnchangedText'
   UpdateConflict = 'UpdateConflict'
  @@ -53,77 +55,47 @@
   
          Editlog File Structure
          ----------------------
  -        +------------------------------------------------------+
  -        |page id<TAB>mtime<TAB>previously mtime<TAB>ip<NEWLINE>|
  -        |    .         .              .              .         |
  -        |                                                      |
  +        +-------------------------------------+
  +        |page id<TAB>timestamp<TAB>ip<NEWLINE>|
  +        |    .           .          .         |
  +        |                                     |
       """
   
  -    def __init__(self, name, timestamp=None):
  +    def __init__(self, name):
           self.name = name
           self.url_name = urlencode(name)
           self.file_name = self.url_name.replace('%', '_')
  -        self.timestamp = timestamp
  -
  -
  -    def _text_path(self):
  -        if not self.timestamp:
  -            return os.path.join(config.text_dir, self.file_name)
  -        else:
  -            return os.path.join(config.backup_dir,
  -                                        self.file_name + '.' + self.timestamp)
  -
  -
  -    def _tmp_path(self):
  -        return os.path.join(config.data_dir,
  -                        ('#' + self.file_name + '.' + str(os.getpid()) + '#'))
  -
  -    def is_current(self):
  -        if not self.timestamp:
  -            return True
  -        else:
  -            return False
  -
   
       def exists(self):
  +        return self.has_current() or self.has_backup()
  +
  +    def has_current(self):
           return os.path.exists(self._text_path())
   
  +    def has_backup(self):
  +        return len(self.backup_list()) > 0
   
       def link(self):
  -        if not self.is_current():
  -            raise PageError
  -        if self.exists():
  +        if self.has_current():
               return link_tag(self.url_name, self.name)
           else:
               return link_tag(self.url_name, '?') + self.name
   
  -
  -    def get_mtime(self):
  -        try:
  -            return os.path.getmtime(self._text_path())
  -        except OSError, er:
  -            if er.errno == errno.ENOENT:
  -                raise NoPage
  -            else:
  -                raise er
  -
  -
  -    def get_size(self):
  +    def timestamp(self):
           try:
  -            return os.path.getsize(self._text_path())
  +            return str(os.path.getmtime(self._text_path()))
           except OSError, er:
               if er.errno == errno.ENOENT:
  -                raise NoPage
  +                raise NotExists
               else:
                   raise er
  -
  -
  -    def get_text(self):
  +        
  +    def text(self, timestamp=''):
           try:
  -            text_file = open(self._text_path(), 'rb')
  +            text_file = open(self._text_path(timestamp), 'rb')
           except IOError, er:
               if er.errno == errno.ENOENT:
  -                raise NoPage
  +                raise NoText
               else:
                   raise er
           try:
  @@ -132,10 +104,8 @@
               text_file.close()
           return text
   
  -
  -    def update_text(self, timestamp, newtext):
  -        if not self.is_current():
  -            raise PageError
  +    def update(self, newtext, timestamp):
  +        # 사용자로 부터 넘겨받은 데이터를 정리
           ip = os.environ.get('REMOTE_ADDR', '')
           if ip in config.denied_ips:
               raise DeniedIP
  @@ -143,88 +113,134 @@
           if not newtext:
               raise EmptyText
   
  -        tmp_path = self._tmp_path()
  -        tmp_file = open(tmp_path, 'wb')
  -        tmp_file.write(newtext)
  -        tmp_file.close()
  -
  -        text_path = self._text_path()
  +        # 현재 텍스트가 있는 경우의 처리
           if self.exists():
  -            mtime = self.get_mtime()
  -            if timestamp != str(mtime):
  +            if timestamp != self.timestamp():
                   raise UpdateConflict
  -            elif newtext == self.get_text():
  +            elif newtext == self.text():
                   raise UnchangedText
  -            os.rename(text_path, os.path.join(config.backup_dir,
  -                                            self.file_name + '.' + str(mtime)))
  -            prev_timestamp = timestamp
  -        else:
  -            prev_timestamp = '0'
  +            os.rename(self._text_path(), os.path.join(config.backup_dir,
  +                                            self.file_name + '.' + timestamp))
   
  -        os.chmod(tmp_path, 0666)
  -        os.rename(tmp_path, text_path)
  +        # 새 텍스트를 기록
  +        tmp_path = os.path.join(config.data_dir, ('#' + self.file_name + '.' +
  +                                                  str(os.getpid()) + '#'))
  +        text_file = open(self._text_path(), 'wb')
  +        text_file.write(newtext)
  +        text_file.close()
  +        os.chmod(self._text_path(), 0666)
  +
  +        # 로그 파일에 추가
  +        timestamp = self.timestamp()
  +        editlog_add(self.file_name, timestamp, ip)
   
  -        timestamp = str(self.get_mtime())
  -        editlog_add(self.file_name, timestamp, prev_timestamp, ip)
  -
  -
  -    def get_backup_list(self):
  +    def backup_list(self):
           tmp_list = os.listdir(config.backup_dir)
           tmp_list = filter(lambda x: x.startswith(self.file_name), tmp_list)
           return map(lambda x: x.split('.')[1], tmp_list)
   
  -
  -    def send_page(self, msg=None):
  -        link = '%s?action=search&amp;method=full&amp;value=%s' % \
  -                                                (self.url_name, self.url_name)
  -        send_title(self.name, link, msg)
  - 
  +    def send_page(self, timestamp=''):
           try:
  -            text = self.get_text()
  -        except NoPage:
  -            print link_tag(self.url_name + '?action=edit', 'Create this page')
  -            last_modified = None
  -        else:
  -            Formatter().format(text)
  -            mtime = self.get_mtime()
  -            mod_str = time.strftime(config.datetime_fmt,
  -                                            time.localtime(self.get_mtime()))
  -            last_modified = link_tag(self.url_name + '?action=info', mod_str)
  - 
  -        send_footer(self.name, True, last_modified)
  -
  -
  -    def send_editor(self, preview=False, **kw):
  -        if not self.is_current():
  -            raise PageError
  +            text = self.text(timestamp)
  +        except NoText:
  +            if not timestamp:
  +                if self.has_backup():
  +                    raise DeletedPage
  +                else:
  +                    raise NoPage
  +            else:
  +                if self.exists():
  +                    raise NoRevision
  +                else:
  +                    raise NoPage
  +
  +        title = re.sub(r'([a-z])([A-Z])', r'\1 \2', self.name)
  +        if not timestamp:
  +            timestamp = self.timestamp()
  +            avail_act = ('edit', 'history')
  +        else:
  +            title = '"%s"의 개정판' % title
  +            avail_act = ('history',)
  +
  +        send_title(title)
  +        Formatter().format(text)
  +        send_footer(self.name, timestamp, avail_act)
  +
  +    def send_editor(self, preview=False, newtext='', timestamp=''):
           if not preview:
  -            send_title('Edit "%s"' % self.name)
               if self.exists():
  -                timestamp = str(self.get_mtime())
  -                newtext = self.get_text()
  +                title = '"%s" 고치기' % self.name
  +                timestamp = self.timestamp()
  +                newtext = self.text()
               else:
  +                title = '"%s" 새로 만들기' % self.name
                   timestamp = ''
  -                newtext = 'Describe %s here.' % self.name
  +                newtext = ''
           else:
  -            send_title('Preview of "%s"' % self.name)
  -            timestamp = kw['timestamp']
  -            newtext = kw['newtext'].replace('\r', '')
  +            title = '"%s" 미리보기' % self.name
   
  -        print '<form method="post" action="%s/%s#preview">' % \
  +        send_title(title)
  +        print '<form method="post" action="%s/%s#preview"><p>' % \
                 (get_scriptname(), self.url_name)
           print '<input type="hidden" name="action" value="update" />'
           print '<input type="hidden" name="timestamp" value="%s" />' % timestamp
           print '<textarea name="newtext" rows="18" cols="90" ' \
  -              'style="width:95%%;word-wrap:virtual;">%s</textarea><br />' % newtext
  -        print '<input type="submit" name="save" value="Save" /> ' \
  -              '<input type="submit" name="preview" value="Preview" /></form>'
  -
  +              'style="width:95%%;word-wrap:virtual;">%s</textarea>' % newtext
  +        print '<input type="submit" name="save" value="저장하기" /> ' \
  +              '<input type="submit" name="preview" value="미리보기" />'
  +        print link_tag(self.url_name, '취소하기')
  +        print '</p></form>'
           if preview:
               print '<a name="preview"></a><hr />'
               Formatter().format(newtext)
  +        send_footer(self.name)
   
  -        send_footer(self.name, False)
  +    def send_history(self):
  +        def print_row(timestamp):
  +            if not timestamp:
  +                timestamp = self.timestamp()
  +                size = self._size()
  +                suffix = ''
  +            else:
  +                suffix = '&timestamp=' + timestamp
  +                size = self._size(timestamp)
  +            print '<tr><td>%s</td><td>%s</td><td>' % \
  +                  (str_datetime(timestamp), str_size(size))
  +            print link_tag(self.url_name + '?action=show' + suffix, '보기')
  +            print link_tag(self.url_name + '?action=text' + suffix, '텍스트')
  +            print '</td></tr>'
  +
  +        blist = self.backup_list()
  +        if not blist and not self.has_current():
  +            raise NoPage
  +        blist.sort()
  +        blist.reverse()
  +
  +        send_title('"%s"의 개정판 정보' % self.name)
  +        print '<table><tr><th>날짜</th><th>크기</th><th>명령</th></tr>'
  +        if self.has_current():
  +            print_row('')
  +        for timestamp in blist:
  +            print_row(timestamp)
  +        print '</table>'
  +        send_footer(self.name)
   
  +    def _text_path(self, timestamp=''):
  +        if not timestamp:
  +            return os.path.join(config.text_dir, self.file_name)
  +        else:
  +            return os.path.join(config.backup_dir,
  +                                self.file_name + '.' + timestamp)
  +
  +    def _size(self, timestamp=''):
  +        try:
  +            return os.path.getsize(self._text_path(timestamp))
  +        except OSError, er:
  +            if er.errno == errno.ENOENT:
  +                raise NoPage
  +            else:
  +                raise er
  +            
   
   def get_page_list():
       tmp_list = map(lambda x:x.replace('_', '%'), os.listdir(config.text_dir))
  @@ -239,8 +255,8 @@
   # The editlog is stored with one record per line, as tab-separated
   # words: pageid, timestamp, host, comment
   
  -def editlog_add(pageid, timestamp, prev_timestamp, host):
  -    entry = string.join((pageid, timestamp, prev_timestamp, host), "\t") + "\n"
  +def editlog_add(pageid, timestamp, host):
  +    entry = string.join((pageid, timestamp, host), "\t") + "\n"
       editlog = open(config.editlog_path, 'ab')
       try: 
           fcntl.flock(editlog.fileno(), fcntl.LOCK_EX)
  @@ -760,64 +776,87 @@
           return str(int(msize)) + 'M'
   
   
  +def str_datetime(timestamp):
  +    return time.strftime(config.datetime_fmt, time.localtime(int(timestamp)))
  +
  +
  +def str_date(timestamp):
  +    return time.strftime(config.date_fmt, time.localtime(int(timestamp)))
  +
  +
   def link_tag(link, text=None):
       if not text:
           text = link
       return '<a href="%s/%s">%s</a>' % (get_scriptname(), link, text)
   
   
  -def send_title(title, link=None, msg=None):
  -    splited_title = re.sub(r'([a-z])([A-Z])', r'\1 \2', title)
  -
  +def send_title(title):
       print '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" ' \
             '"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
  -    print '<html><head><title>%s</title>' % splited_title
  +    print '<html><head><title>%s</title>' % title
       print '<meta http-equiv="content-type" content="text/html;' \
             'charset=%s" />' % config.encoding
       print '<link rel="stylesheet" type="text/css" href="%s" /></head>' \
             '<body><div class="logo">' % config.css_url
       print link_tag(urlencode(config.front_page),
                     '<img class="logo" src="%s" alt="logo" />' % config.logo_url)
  -    print '</div><h1>%s</h1>' % splited_title
  -    if msg:
  -        print '%s<hr />' % msg
  +    print '</div><h1>%s</h1>' % title
   
   
  -def send_footer(page_name, editable=True, mod_string=None):
  +def send_footer(page_name, timestamp=None, avail_act=None):
       print '<hr />'
       print '<div class="power">Powered by ' \
             '<a href="http://wikiplus.kldp.net">Wiki+</a><br />' \
             '<a href="http://validator.w3.org/check/referer">XHTML 1.0</a> | ' \
             '<a href="http://jigsaw.w3.org/css-validator/check/referer">' \
  -          'CSS 2.0</a></div>'
  -    if mod_string:
  -        print '<div>이 문서는 %s에 마지막으로 수정되었습니다.<br />' % mod_string
  -    print '이 문서에 사용 가능한 명령은 다음과 같습니다.<br />'
  -    if editable:
  -        print link_tag(urlencode(page_name) + '?action=edit', '고치기')
  -    else:
  -        print link_tag(urlencode(page_name), '보기')
  +          'CSS 2.0</a></div><div>'
  +    if timestamp:
  +        print '이 문서는 %s에 수정되었습니다.<br />' % \
  +              str_datetime(timestamp)
  +    if avail_act:
  +        print '이 문서에 사용 가능한 명령은 다음과 같습니다.<br />'
  +        if 'show' in avail_act:
  +            print link_tag(urlencode(page_name), '보기')
  +        if 'edit' in avail_act:
  +            print link_tag(urlencode(page_name) + '?action=edit', '고치기')
  +        if 'history' in avail_act:
  +            print link_tag(urlencode(page_name) + '?action=history', '개정정보')
       print '</div></body></html>'
   
   
  +def send_error(title, msg):
  +    send_title(title)
  +    print '<p>%s</p>' % msg
  +    send_footer('')
   
   
   
   # Action handlers ---------------------------------------------------
   
   def do_show(page_name, form):
  -    pg = Page(page_name, form.getvalue('timestamp'))
  -    if pg.is_current():
  -        msg = None
  -    else:
  -        try:
  -            mtime = pg.get_mtime()
  -        except NoPage:
  -            msg = '<b>No revision exists.</b>'
  -        else:
  -            date = time.strftime(config.datetime_fmt, time.localtime(mtime))
  -            msg = '<b>Revision as of %s</b>' % date
  -    pg.send_page(msg)
  +    pg = Page(page_name)
  +    timestamp = form.getvalue('timestamp', '')
  +    try:
  +        pg.send_page(timestamp)
  +    except NoPage:
  +        send_error('문서가 없습니다.',
  +                   '새로 생성하기 위해서는 ' + \
  +                   link_tag(page_name + '?action=edit', '여기') + \
  +                   '를 클릭해주세요.')
  +    except DeletedPage:
  +        send_error('문서가 삭제되었습니다.',
  +                   '그렇지만 아직 예전 개정판이 남아있습니다. ' + \
  +                   '예전 개정판 정보를 보시려면 ' + \
  +                   link_tag(urlencode(page_name) + '?action=history',
  +                            '여기') + \
  +                   '를 클릭해주시고, 완전히 새로 작성하시려면 ' + \
  +                   link_tag(urlencode(page_name) +
  +                            '?action=edit', '여기') + \
  +                   '를 클릭해 주세요.')
  +    except NoRevision:
  +        send_error('개정판이 존재하지 않습니다.',
  +                   '%s 문서에 대한 %s의 개정판은 존재하지 않습니다.' % \
  +                   (page_name, str_datetime(timestamp)))
   
   
   def do_edit(page_name, form):
  @@ -825,85 +864,63 @@
   
   
   def do_update(page_name, form):
  -    timestamp = form.getvalue('timestamp')
  -    newtext = form.getvalue('newtext', '')
  -
       pg = Page(page_name)
  +    newtext = form.getvalue('newtext', '').replace('\r', '')
  +    timestamp = form.getvalue('timestamp', '')
       if form.getvalue('save'):
           try:
  -            pg.update_text(timestamp, newtext)
  +            pg.update(newtext, timestamp)
           except EmptyText:
  -            msg = '<b>You cannot save empty pages.</b>'
  +            send_error('내용이 없습니다.',
  +                       '빈 문서는 저장할 수 없습니다.')
           except UnchangedText:
  -            msg = '<b>You cannot save unchanged pages.</b>'
  +            send_error('내용이 변경되지 않았습니다.',
  +                       '변경되지 않은 내용은 저장할 수 없습니다.')
           except UpdateConflict:
  -            msg = '<p><b>Sorry, someone else saved the page while you edited' \
  -                  ' it.</b></p><p>Please do the following: Cut&amp;paste you' \
  -                  'r changes from the following box. Then click EditPage aga' \
  -                  'in(or shift-click it to open in a new window). Now re-add' \
  -                  ' your changes to the current page contents.</p><p><em>Do ' \
  -                  'not just replace the content editbox with your version of' \
  -                  ' the page, because that would delete the changes of the o' \
  -                  'ther person, which is excessively rude!</em></p><p>Follow' \
  -                  'ing is the page you edited.</p>'
  -            msg += '<textarea wrap="virtual" rows="17" cols="80">%s' \
  -                   '</textarea>' % newtext
  +            send_error('문서 수정에 충돌이 발생했습니다.',
  +                       '이 페이지를 수정하는 동안 다른 사용자가 수정해서 ' + \
  +                       '먼저 저장했습니다.<b><br />작업하시던 내용은 아' + \
  +                       '래 텍스트 박스에 그대로 있습니다. 이 내용을 지금 ' + \
  +                       '바로 덮어쓰신다면 다른 사용자가 먼저 작업한 내용을' + \
  +                       '덮어쓰게 됩니다. 따라서 수정된 부분만을 추가해주시' + \
  +                       '면 감사하겠습니다.<textarea wrap="virtual" ' + \
  +                       'rows="17" cols="80">%s</textarea>' % newtext)
           except DeniedIP:
  -            msg = '<b>Sorry. Your IP address is blocked by administrator.</b>'
  +            send_error('금지된 IP 주소 입니다.',
  +                        '해당 IP 주소는 관리자에 의해서 위키를 변경하는 작' + \
  +                        '업을 하지 못하도록 막혔습니다.')
           else:
  -            msg = '<b>Thankyou for your changes. Your attention to detail is' \
  -                  ' appreciated.</b>'
  -        pg.send_page(msg)
  -
  +            pg.send_page()
       else:   # if preview
  -        pg.send_editor(True, timestamp=timestamp, newtext=newtext)
  +        pg.send_editor(True, newtext, timestamp)
  +
   
  +def do_history(page_name, form):
  +    pg = Page(page_name)
  +    try:
  +        pg.send_history()
  +    except NoPage: 
  +        send_error('문서가 없습니다.',
  +                   '새로 생성하기 위해서는 ' + \
  +                   link_tag(page_name + '?action=edit', '여기') + \
  +                   '를 클릭해주세요.')
  +       
   
   def do_text(page_name, form):
  -    pg = Page(page_name, form.getvalue('timestamp'))
  +    pg = Page(page_name)
       try:
  -        text = pg.get_text()
  +        text = pg.text(form.getvalue('timestamp', ''))
       except NoPage:
  -        pg.seng_page('<b>No revision exists.</b>')
  +        send_error('문서가 없습니다.',
  +                   '새로 생성하기 위해서는 ' + \
  +                   link_tag(page_name + '?action=edit', '여기') + \
  +                   '를 클릭해주세요.')
       else:
           print '<pre>'
           print text
           print '</pre>'
   
   
  -def do_info(page_name, form):
  -    def print_info_row(pg):
  -        mtime = pg.get_mtime()
  -        print '<tr><td>%s</td>' % \
  -                    time.strftime(config.datetime_fmt, time.localtime(mtime))
  -        print '<td align="right">%s</td><td>' % str_size(pg.get_size())
  -        if pg.is_current():
  -            url_suffix = ''
  -        else:
  -            url_suffix = '&timestamp=' + pg.timestamp
  -        print link_tag(pg.url_name + '?action=show' + url_suffix, 'show')
  -        print link_tag(pg.url_name + '?action=text' + url_suffix, 'text')
  -        print '</td></tr>'
  -
  -    pg = Page(page_name)
  -    backup_list = pg.get_backup_list()
  -    backup_list.sort()
  -    backup_list.reverse()
  -
  -    if len(backup_list):
  -        send_title('Info of "' + page_name + '"')
  -        print '<table><tr><th>Date</th><th>Size</th><th>Actions</th></tr>'
  -        if pg.exists():
  -            print_info_row(pg)
  -        for timestamp in backup_list:
  -            pg = Page(page_name, timestamp)
  -            print_info_row(pg)
  -        print '</table>'
  -        send_footer(page_name, False)
  -    else:
  -        pg.send_page('<b>No older revision exists.</b>')
  -
  -
   def do_goto(page_name, form):
       page_name = form.getvalue('value')
       init_macro(page_name, form)         # reset global variables for macro
  @@ -997,8 +1014,8 @@
           'show':     do_show,
           'edit':     do_edit,
           'update':   do_update,
  +        'history':  do_history,
           'text':     do_text,
  -        'info':     do_info,
           'goto':     do_goto,
           'search':   do_search,
           'upload':   do_upload
  
  
  


yjcho       2006/09/06 11:00:41

  Modified:    .        wplus.py
  Log:
   * error 처리 방식을 class 기반으로 변경
     - 에러 메시지를 한 곳에 몰아 놓아서 관리를 쉽게 함
   * timestamp를 revise time의 의미인 rtime으로 변경
     - 의미상 revise time이 좀 더 맞는 표현
  
  Revision  Changes    Path
  1.19      +193 -163  wikiplus/wplus.py
  
  Index: wplus.py
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/wplus.py,v
  retrieving revision 1.18
  retrieving revision 1.19
  diff -u -r1.18 -r1.19
  --- wplus.py	4 Sep 2006 12:46:39 -0000	1.18
  +++ wplus.py	6 Sep 2006 02:00:41 -0000	1.19
  @@ -18,17 +18,92 @@
   
   # Exceptions --------------------------------------------------------
   
  -# for page class
  -NoPage = 'NoPage'                       # 페이지 자체가 없음
  -NoRevision = 'NoRevision'               # 페이지의 특정 개정판이 없음
  -DeletedPage = 'DeletedPage'             # 페이지의 현재의 개정판이 없음
  +class WikiError(Exception):
  +    def __init__(self):
  +        self.title = ''
  +        self.contents = ''
  +
  +    def send_error(self):
  +        send_title(self.title)
  +        print '<p>'
  +        print self.contents
  +        print '</p>'
  +        send_footer()
  +
  +
  +class NoPage(WikiError):               # 문서 자체가 없음
  +    def __init__(self, page_name):
  +        self.title = '없는 문서입니다.'
  +        self.contents = \
  +            '요청 하신 %s는 없는 문서 입니다. ' \
  +            '이 문서를 작성 하시려면 %s를 이용해 주세요.' % \
  +            (page_name,
  +             link_tag(urlencode(page_name) + '?action=edit', '문서 생성'))
  +
  +
  +class DeletedPage(WikiError):         # 문서의 현재의 개정판이 없음
  +    def __init__(self, page_name):
  +        self.title = '삭제된 문서입니다.'
  +        self.contents = \
  +            '요청하신 %는 삭제된 문서입니다. ' \
  +            '그렇지만 아직 예전 개정판이 남아있습니다. ' \
  +            '예전 개정판 정보를 보시려면 %s를 이용해 주시고, ' \
  +            '새로 작성하시려면 %s를 이용해 주세요.' % \
  +            (page_name,
  +             link_tag(urlencode(page_name) + '?action=history', '개정 정보'),
  +             link_tag(urlencode(page_name) + '?action=edit', '문서 작성'))
  +
  +
  +class NoRevision(WikiError):           # 페이지의 특정 개정판이 없음
  +    def __init__(self, page_name, rtime):
  +        self.title = '없는 개정판입니다.'
  +        self.contents = \
  +            '%s 문서에 대한 %s의 개정판은 존재하지 않습니다. ' \
  +            '최근 개정판을 보시려면 %s를 이용해 주시고, ' \
  +            '다른 개정판을 보시려면 %s를 이용해 주세요.' % \
  +            (page_name, str_datetime(rtime),
  +             link_tag(urlencode(page_name), '문서 보기'),
  +             link_tag(urlencode(page_name) + '?action=history', '개정 정보'))
  +
  +
  +class EmptyText(WikiError):
  +    def __init__(self):
  +        self.title = '내용이 없습니다.',
  +        self.contents = '내용이 없는 문서는 저장할 수 없습니다.'
  +
  +
  +class UnchangedText(WikiError):
  +    def __init__(self):
  +        self.title = '내용이 같습니다.'
  +        self.contents = '같은 내용의 문서는 저장할 수 없습니다.'
  +
  +
  +class UpdateConflict(WikiError):
  +    def __init__(self, newtext):
  +        self.title = '문서 수정 충돌'
  +        self.contents = \
  +            '이 페이지를 수정하는 동안 다른 사용자가 수정해서 ' + \
  +            '먼저 저장했습니다. 작업하시던 내용은 아' + \
  +            '래 텍스트 박스에 그대로 있습니다. 이 내용을 지금 ' + \
  +            '바로 덮어쓰신다면 다른 사용자가 먼저 작업한 내용을' + \
  +            '덮어쓰게 됩니다. 따라서 수정된 부분만을 추가해주시' + \
  +            '면 감사하겠습니다.<textarea wrap="virtual" ' + \
  +            'rows="17" cols="80">%s</textarea>' % newtext
  +
  +
  +class DeniedIP(WikiError):
  +    def __init__(self):
  +        self.title = '금지된 IP 주소 입니다.'
  +        self.contents = \
  +            '해당 IP 주소는 관리자에 의해서 위키를 변경하는 작' + \
  +            '업을 하지 못하도록 막혔습니다.'
  +
  +
  +# for internal use in Page class
   NoText = 'NoText'
  -EmptyText = 'EmptyText'
  -UnchangedText = 'UnchangedText'
  -UpdateConflict = 'UpdateConflict'
  -DeniedIP = 'DeniedIP'
   
  -# for action handler
  +
  +# for internal use in action handler
   UnknownAction = 'UnknownAction'
   
   
  @@ -43,9 +118,9 @@
           data/ --+-- text/ ---+-- pageid1
                   |            `-- pageid2
                   |
  -                +-- backup/ -+-- pageid1.timestamp
  -                |            +-- pageid1.timestamp
  -                |            `-- pageid2.timestamp
  +                +-- backup/ -+-- pageid1.rtime
  +                |            +-- pageid1.rtime
  +                |            `-- pageid2.rtime
                   |
                   `-- editlog
   
  @@ -56,7 +131,7 @@
          Editlog File Structure
          ----------------------
           +-------------------------------------+
  -        |page id<TAB>timestamp<TAB>ip<NEWLINE>|
  +        |page id<TAB>rtime<TAB>ip<NEWLINE>|
           |    .           .          .         |
           |                                     |
       """
  @@ -66,45 +141,13 @@
           self.url_name = urlencode(name)
           self.file_name = self.url_name.replace('%', '_')
   
  -    def exists(self):
  -        return self.has_current() or self.has_backup()
  -
  -    def has_current(self):
  -        return os.path.exists(self._text_path())
  -
  -    def has_backup(self):
  -        return len(self.backup_list()) > 0
  -
       def link(self):
  -        if self.has_current():
  +        if self._has_current():
               return link_tag(self.url_name, self.name)
           else:
               return link_tag(self.url_name, '?') + self.name
   
  -    def timestamp(self):
  -        try:
  -            return str(os.path.getmtime(self._text_path()))
  -        except OSError, er:
  -            if er.errno == errno.ENOENT:
  -                raise NotExists
  -            else:
  -                raise er
  -        
  -    def text(self, timestamp=''):
  -        try:
  -            text_file = open(self._text_path(timestamp), 'rb')
  -        except IOError, er:
  -            if er.errno == errno.ENOENT:
  -                raise NoText
  -            else:
  -                raise er
  -        try:
  -            text = text_file.read()
  -        finally:
  -            text_file.close()
  -        return text
  -
  -    def update(self, newtext, timestamp):
  +    def update(self, newtext, rtime):
           # 사용자로 부터 넘겨받은 데이터를 정리
           ip = os.environ.get('REMOTE_ADDR', '')
           if ip in config.denied_ips:
  @@ -114,49 +157,42 @@
               raise EmptyText
   
           # 현재 텍스트가 있는 경우의 처리
  -        if self.exists():
  -            if timestamp != self.timestamp():
  -                raise UpdateConflict
  -            elif newtext == self.text():
  +        if self._has_current():
  +            if rtime != self._current_rtime():
  +                raise UpdateConflict(newtext)
  +            elif newtext == self._text():
                   raise UnchangedText
               os.rename(self._text_path(), os.path.join(config.backup_dir,
  -                                            self.file_name + '.' + timestamp))
  +                                        self.file_name + '.' + rtime))
   
           # 새 텍스트를 기록
  -        tmp_path = os.path.join(config.data_dir, ('#' + self.file_name + '.' +
  -                                                  str(os.getpid()) + '#'))
           text_file = open(self._text_path(), 'wb')
           text_file.write(newtext)
           text_file.close()
           os.chmod(self._text_path(), 0666)
   
           # 로그 파일에 추가
  -        timestamp = self.timestamp()
  -        editlog_add(self.file_name, timestamp, ip)
  +        rtime = self._current_rtime()
  +        editlog_add(self.file_name, rtime, ip)
   
  -    def backup_list(self):
  -        tmp_list = os.listdir(config.backup_dir)
  -        tmp_list = filter(lambda x: x.startswith(self.file_name), tmp_list)
  -        return map(lambda x: x.split('.')[1], tmp_list)
  -
  -    def send_page(self, timestamp=''):
  +    def send_page(self, rtime=''):
           try:
  -            text = self.text(timestamp)
  +            text = self._text(rtime)
           except NoText:
  -            if not timestamp:
  -                if self.has_backup():
  -                    raise DeletedPage
  +            if not rtime:
  +                if self._has_backup():
  +                    raise DeletedPage(self.name)
                   else:
  -                    raise NoPage
  +                    raise NoPage(self.name)
               else:
  -                if self.exists():
  -                    raise NoRevision
  +                if self._exists():
  +                    raise NoRevision(self.name, rtime)
                   else:
  -                    raise NoPage
  +                    raise NoPage(self.name)
   
           title = re.sub(r'([a-z])([A-Z])', r'\1 \2', self.name)
  -        if not timestamp:
  -            timestamp = self.timestamp()
  +        if not rtime:
  +            rtime = self._current_rtime()
               avail_act = ('edit', 'history')
           else:
               title = '"%s"의 개정판' % title
  @@ -164,17 +200,17 @@
   
           send_title(title)
           Formatter().format(text)
  -        send_footer(self.name, timestamp, avail_act)
  +        send_footer(self.name, rtime, avail_act)
   
  -    def send_editor(self, preview=False, newtext='', timestamp=''):
  +    def send_editor(self, preview=False, newtext='', rtime=''):
           if not preview:
  -            if self.exists():
  +            if self._exists():
                   title = '"%s" 고치기' % self.name
  -                timestamp = self.timestamp()
  -                newtext = self.text()
  +                rtime = self._current_rtime()
  +                newtext = self._text()
               else:
                   title = '"%s" 새로 만들기' % self.name
  -                timestamp = ''
  +                rtime = ''
                   newtext = ''
           else:
               title = '"%s" 미리보기' % self.name
  @@ -183,7 +219,7 @@
           print '<form method="post" action="%s/%s#preview"><p>' % \
                 (get_scriptname(), self.url_name)
           print '<input type="hidden" name="action" value="update" />'
  -        print '<input type="hidden" name="timestamp" value="%s" />' % timestamp
  +        print '<input type="hidden" name="rtime" value="%s" />' % rtime
           print '<textarea name="newtext" rows="18" cols="90" ' \
                 'style="width:95%%;word-wrap:virtual;">%s</textarea>' % newtext
           print '<input type="submit" name="save" value="저장하기" /> ' \
  @@ -196,51 +232,88 @@
           send_footer(self.name)
   
       def send_history(self):
  -        def print_row(timestamp):
  -            if not timestamp:
  -                timestamp = self.timestamp()
  -                size = self._size()
  +        def print_row(rtime):
  +            if not rtime:
  +                rtime = self._current_rtime()
  +                size = self._text_size()
                   suffix = ''
               else:
  -                suffix = '&timestamp=' + timestamp
  -                size = self._size(timestamp)
  +                suffix = '&rtime=' + rtime
  +                size = self._text_size(rtime)
               print '<tr><td>%s</td><td>%s</td><td>' % \
  -                  (str_datetime(timestamp), str_size(size))
  +                  (str_datetime(rtime), str_size(size))
               print link_tag(self.url_name + '?action=show' + suffix, '보기')
               print link_tag(self.url_name + '?action=text' + suffix, '텍스트')
               print '</td></tr>'
   
  -        blist = self.backup_list()
  -        if not blist and not self.has_current():
  -            raise NoPage
  +        blist = self._backup_list()
  +        if not blist and not self._has_current():
  +            raise NoPage(self.name)
           blist.sort()
           blist.reverse()
   
           send_title('"%s"의 개정판 정보' % self.name)
           print '<table><tr><th>날짜</th><th>크기</th><th>명령</th></tr>'
  -        if self.has_current():
  +        if self._has_current():
               print_row('')
  -        for timestamp in blist:
  -            print_row(timestamp)
  +        for rtime in blist:
  +            print_row(rtime)
           print '</table>'
           send_footer(self.name)
   
  -    def _text_path(self, timestamp=''):
  -        if not timestamp:
  +    def _exists(self):
  +        return self._has_current() or self._has_backup()
  +
  +    def _has_current(self):
  +        return os.path.exists(self._text_path())
  +
  +    def _has_backup(self):
  +        return len(self._backup_list()) > 0
  +
  +    def _text_path(self, rtime=''):
  +        if not rtime:
               return os.path.join(config.text_dir, self.file_name)
           else:
               return os.path.join(config.backup_dir,
  -                                self.file_name + '.' + timestamp)
  +                                self.file_name + '.' + rtime)
   
  -    def _size(self, timestamp=''):
  +    def _text_size(self, rtime=''):
           try:
  -            return os.path.getsize(self._text_path(timestamp))
  +            return os.path.getsize(self._text_path(rtime))
           except OSError, er:
               if er.errno == errno.ENOENT:
  -                raise NoPage
  +                raise NoText
               else:
                   raise er
  +    def _text(self, rtime=''):
  +        try:
  +            text_file = open(self._text_path(rtime), 'rb')
  +        except IOError, er:
  +            if er.errno == errno.ENOENT:
  +                raise NoText
  +            else:
  +                raise er
  +        try:
  +            text = text_file.read()
  +        finally:
  +            text_file.close()
  +        return text
               
  +    def _backup_list(self):
  +        tmp_list = os.listdir(config.backup_dir)
  +        tmp_list = filter(lambda x: x.startswith(self.file_name), tmp_list)
  +        return map(lambda x: x.split('.')[1], tmp_list)
  +
  +    def _current_rtime(self):
  +        try:
  +            return str(os.path.getmtime(self._text_path()))
  +        except OSError, er:
  +            if er.errno == errno.ENOENT:
  +                raise NoText
  +            else:
  +                raise er
  +        
  +
   
   def get_page_list():
       tmp_list = map(lambda x:x.replace('_', '%'), os.listdir(config.text_dir))
  @@ -253,10 +326,10 @@
   # Functions to keep track of when people have changed pages, so we can
   # do the recent changes page and so on.
   # The editlog is stored with one record per line, as tab-separated
  -# words: pageid, timestamp, host, comment
  +# words: pageid, rtime, host, comment
   
  -def editlog_add(pageid, timestamp, host):
  -    entry = string.join((pageid, timestamp, host), "\t") + "\n"
  +def editlog_add(pageid, rtime, host):
  +    entry = string.join((pageid, rtime, host), "\t") + "\n"
       editlog = open(config.editlog_path, 'ab')
       try: 
           fcntl.flock(editlog.fileno(), fcntl.LOCK_EX)
  @@ -776,12 +849,12 @@
           return str(int(msize)) + 'M'
   
   
  -def str_datetime(timestamp):
  -    return time.strftime(config.datetime_fmt, time.localtime(int(timestamp)))
  +def str_datetime(rtime):
  +    return time.strftime(config.datetime_fmt, time.localtime(int(rtime)))
   
   
  -def str_date(timestamp):
  -    return time.strftime(config.date_fmt, time.localtime(int(timestamp)))
  +def str_date(rtime):
  +    return time.strftime(config.date_fmt, time.localtime(int(rtime)))
   
   
   def link_tag(link, text=None):
  @@ -803,16 +876,16 @@
       print '</div><h1>%s</h1>' % title
   
   
  -def send_footer(page_name, timestamp=None, avail_act=None):
  +def send_footer(page_name='', rtime=None, avail_act=None):
       print '<hr />'
       print '<div class="power">Powered by ' \
             '<a href="http://wikiplus.kldp.net">Wiki+</a><br />' \
             '<a href="http://validator.w3.org/check/referer">XHTML 1.0</a> | ' \
             '<a href="http://jigsaw.w3.org/css-validator/check/referer">' \
             'CSS 2.0</a></div><div>'
  -    if timestamp:
  +    if rtime:
           print '이 문서는 %s에 수정되었습니다.<br />' % \
  -              str_datetime(timestamp)
  +              str_datetime(rtime)
       if avail_act:
           print '이 문서에 사용 가능한 명령은 다음과 같습니다.<br />'
           if 'show' in avail_act:
  @@ -824,39 +897,16 @@
       print '</div></body></html>'
   
   
  -def send_error(title, msg):
  -    send_title(title)
  -    print '<p>%s</p>' % msg
  -    send_footer('')
  -
  -
   
   # Action handlers ---------------------------------------------------
   
   def do_show(page_name, form):
       pg = Page(page_name)
  -    timestamp = form.getvalue('timestamp', '')
  +    rtime = form.getvalue('rtime', '')
       try:
  -        pg.send_page(timestamp)
  -    except NoPage:
  -        send_error('문서가 없습니다.',
  -                   '새로 생성하기 위해서는 ' + \
  -                   link_tag(page_name + '?action=edit', '여기') + \
  -                   '를 클릭해주세요.')
  -    except DeletedPage:
  -        send_error('문서가 삭제되었습니다.',
  -                   '그렇지만 아직 예전 개정판이 남아있습니다. ' + \
  -                   '예전 개정판 정보를 보시려면 ' + \
  -                   link_tag(urlencode(page_name) + '?action=history',
  -                            '여기') + \
  -                   '를 클릭해주시고, 완전히 새로 작성하시려면 ' + \
  -                   link_tag(urlencode(page_name) +
  -                            '?action=edit', '여기') + \
  -                   '를 클릭해 주세요.')
  -    except NoRevision:
  -        send_error('개정판이 존재하지 않습니다.',
  -                   '%s 문서에 대한 %s의 개정판은 존재하지 않습니다.' % \
  -                   (page_name, str_datetime(timestamp)))
  +        pg.send_page(rtime)
  +    except (NoPage, DeletedPage, NoRevision), e:
  +        e.send_error()
   
   
   def do_edit(page_name, form):
  @@ -866,50 +916,30 @@
   def do_update(page_name, form):
       pg = Page(page_name)
       newtext = form.getvalue('newtext', '').replace('\r', '')
  -    timestamp = form.getvalue('timestamp', '')
  +    rtime = form.getvalue('rtime', '')
       if form.getvalue('save'):
           try:
  -            pg.update(newtext, timestamp)
  -        except EmptyText:
  -            send_error('내용이 없습니다.',
  -                       '빈 문서는 저장할 수 없습니다.')
  -        except UnchangedText:
  -            send_error('내용이 변경되지 않았습니다.',
  -                       '변경되지 않은 내용은 저장할 수 없습니다.')
  -        except UpdateConflict:
  -            send_error('문서 수정에 충돌이 발생했습니다.',
  -                       '이 페이지를 수정하는 동안 다른 사용자가 수정해서 ' + \
  -                       '먼저 저장했습니다.<b><br />작업하시던 내용은 아' + \
  -                       '래 텍스트 박스에 그대로 있습니다. 이 내용을 지금 ' + \
  -                       '바로 덮어쓰신다면 다른 사용자가 먼저 작업한 내용을' + \
  -                       '덮어쓰게 됩니다. 따라서 수정된 부분만을 추가해주시' + \
  -                       '면 감사하겠습니다.<textarea wrap="virtual" ' + \
  -                       'rows="17" cols="80">%s</textarea>' % newtext)
  -        except DeniedIP:
  -            send_error('금지된 IP 주소 입니다.',
  -                        '해당 IP 주소는 관리자에 의해서 위키를 변경하는 작' + \
  -                        '업을 하지 못하도록 막혔습니다.')
  +            pg.update(newtext, rtime)
  +        except (EmptyText, UnchangedText, UpdateConflict, DeniedIP), e:
  +            e.send_error()
           else:
               pg.send_page()
       else:   # if preview
  -        pg.send_editor(True, newtext, timestamp)
  +        pg.send_editor(True, newtext, rtime)
   
   
   def do_history(page_name, form):
       pg = Page(page_name)
       try:
           pg.send_history()
  -    except NoPage: 
  -        send_error('문서가 없습니다.',
  -                   '새로 생성하기 위해서는 ' + \
  -                   link_tag(page_name + '?action=edit', '여기') + \
  -                   '를 클릭해주세요.')
  +    except NoPage, e:
  +        e.send_error()
          
   
   def do_text(page_name, form):
       pg = Page(page_name)
       try:
  -        text = pg.text(form.getvalue('timestamp', ''))
  +        text = pg.text(form.getvalue('rtime', ''))
       except NoPage:
           send_error('문서가 없습니다.',
                      '새로 생성하기 위해서는 ' + \
  
  
  


yjcho       2006/09/09 14:24:28

  Modified:    .        wplus.css
  Log:
   * 글자크기를 조금 작게 조절, 글자체 굴림으로 변경
  
  Revision  Changes    Path
  1.10      +5 -1      wikiplus/wplus.css
  
  Index: wplus.css
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/wplus.css,v
  retrieving revision 1.9
  retrieving revision 1.10
  diff -u -r1.9 -r1.10
  --- wplus.css	30 Aug 2006 05:12:42 -0000	1.9
  +++ wplus.css	9 Sep 2006 05:24:28 -0000	1.10
  @@ -1,5 +1,10 @@
   /* Wiki+ CSS */
   
  +body {
  +	font-family:굴림;
  +	font-size:small;
  +}
  +
   div.logo {
   	float:right;
   }
  @@ -10,7 +15,6 @@
   div.info {
   	float:left;
   }
  -
   div.power {
   	text-align:right;
   	float:right;
  
  
  


yjcho       2006/09/09 14:27:49

  Modified:    .        wplus.py
  Log:
   * Formatter class에 macro를 다 넣음
     - macro에서 formatter의 context를 이용할 필요가 있음
     - 원래 formatter안에 잇어야 할 것 같음
   * Page class 변경 후 macro 작동 문제 해결
   * search를 fullsearch와 titlesearch둘로 나눔
  
  Revision  Changes    Path
  1.20      +373 -281  wikiplus/wplus.py
  
  Index: wplus.py
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/wplus.py,v
  retrieving revision 1.19
  retrieving revision 1.20
  diff -u -r1.19 -r1.20
  --- wplus.py	6 Sep 2006 02:00:41 -0000	1.19
  +++ wplus.py	9 Sep 2006 05:27:49 -0000	1.20
  @@ -104,7 +104,10 @@
   
   
   # for internal use in action handler
  -UnknownAction = 'UnknownAction'
  +class UnknownAction(WikiError):
  +    def __init__(self, action):
  +        self.title = '알 수 없는 명령입니다.'
  +        self.contents = 'action의 전달 값 %s을 확인해주세요.' % action
   
   
   
  @@ -163,7 +166,7 @@
               elif newtext == self._text():
                   raise UnchangedText
               os.rename(self._text_path(), os.path.join(config.backup_dir,
  -                                        self.file_name + '.' + rtime))
  +                                                self.file_name + '.' + rtime))
   
           # 새 텍스트를 기록
           text_file = open(self._text_path(), 'wb')
  @@ -176,6 +179,7 @@
           editlog_add(self.file_name, rtime, ip)
   
       def send_page(self, rtime=''):
  +        # text를 읽어옴
           try:
               text = self._text(rtime)
           except NoText:
  @@ -190,6 +194,7 @@
                   else:
                       raise NoPage(self.name)
   
  +        # title등 출력에 필요한 각종 정보를 생성
           title = re.sub(r'([a-z])([A-Z])', r'\1 \2', self.name)
           if not rtime:
               rtime = self._current_rtime()
  @@ -198,8 +203,9 @@
               title = '"%s"의 개정판' % title
               avail_act = ('history',)
   
  +        # 문서 출력
           send_title(title)
  -        Formatter().format(text)
  +        send_body(self.name, text)
           send_footer(self.name, rtime, avail_act)
   
       def send_editor(self, preview=False, newtext='', rtime=''):
  @@ -228,7 +234,7 @@
           print '</p></form>'
           if preview:
               print '<a name="preview"></a><hr />'
  -            Formatter().format(newtext)
  +            send_body(self.name, newtext)
           send_footer(self.name)
   
       def send_history(self):
  @@ -261,6 +267,16 @@
           print '</table>'
           send_footer(self.name)
   
  +    def send_text(self, rtime):
  +        try:
  +            text = self._text(rtime)
  +        except NoText:
  +            raise NoPage(self.name)
  +
  +        print '<pre>'
  +        print text
  +        print '</pre>'
  +
       def _exists(self):
           return self._has_current() or self._has_backup()
   
  @@ -354,253 +370,332 @@
   # Formatter ---------------------------------------------------------
   
   class Formatter:
  -    def __init__(self):
  -        self._block_re = re.compile(r'^(?:' \
  -            r'(?P<heading>(?P<hmarker>={1,5})\s.*\s(?P=hmarker))|' \
  -            r'(?P<rule>-{4,})|' \
  -            r'(?P<list>(?P<indent>\s+)(?:(?P<ol>\d+\.\s)|(?P<ul>\*\s)).*)|' \
  -            r'(?P<table>\|\|(?:.+\|\|)+)|' \
  -            r'(?P<blank>\s*)|' \
  -            r'(?P<paragraph>.+)' \
  -            r')$')
  -        self._block_stack = [(None, None)] # (block_type, block_data)
  -        
  -        self._inline_re = re.compile(
  -            r"(?P<emph>'{2,3})|" \
  -            r'(?P<word>\b(?:[A-Z][a-z]+){2,}\b)|' \
  -            r'(?P<eword>\[[^\[\]]+\])|' \
  -            r'(?P<url>(?:http|ftp|nntp|news|mailto)\:[^\s\'"]+\S)|' \
  -            r'(?P<email>[-\w._+]+\@[\w.-]+)|' \
  -            r'(?P<macro>\[\[[A-Za-z][A-Za-z0-9]*(?:\(.*\))?\]\])|' \
  -            r'(?P<code>\{\{\{[^(?:\}\}\})]+\}\}\})|' \
  -            r'(?P<pre>(?:\{\{\{|\}\}\}))|' \
  -            r'(?P<ent>[<>&])')
  -        self._in_pre = False
  -        self._is_em = self._is_b = False
  -
  -
  -    def format(self, raw):
  -        raw_lines = raw.split('\n')
  -        for raw_line in raw_lines:
  -            if not self._in_pre:
  -                match = self._block_re.match(raw_line)
  -                for btype, s in match.groupdict().items():
  -                    if s != None and hasattr(self, '_' + btype + '_block'):
  -                        self._close_block(btype)
  -                        apply(getattr(self, '_' + btype + '_block'), (match,))
  -                        break
  +    BLOCK_PAT = r'^(?:' \
  +        r'(?P<h2>=\s.*\s=)|' \
  +        r'(?P<h3>==\s.*\s==)|' \
  +        r'(?P<h4>===\s.*\s===)|' \
  +        r'(?P<h5>====\s.*\s====)|' \
  +        r'(?P<h6>=====\s.*\s=====)|' \
  +        r'(?P<hr>-{4,})|' \
  +        r'(?P<ul>\s+\*\s.*)|' \
  +        r'(?P<ol>\s+\d+\.\s.*)|' \
  +        r'(?P<dl>\s+[^(?:::)]*::.*)|' \
  +        r'(?P<table>\|\|(?:.*\|\|)+)|' \
  +        r'(?P<blank>\s*)|' \
  +        r'(?P<p>.+)' \
  +        r')$'
  +
  +    URL_PAT = r'(?:http|https|ftp|news|mailto)\:[^\s\'"]+\S'
  +    EMAIL_PAT = r'[-\w._+]+\@[\w.-]+'
  +    INLINE_PAT = \
  +        r"(?P<emph>'{2,3})|" \
  +        r'(?P<word>\b(?:[A-Z][a-z]+){2,}\b)|' \
  +        r'(?P<eword>\[[^\[\]]+\])|' \
  +        r'(?P<url>' + URL_PAT + r')|' \
  +        r'(?P<email>' + EMAIL_PAT + r')|' \
  +        r'(?P<macro>\[\[[A-Za-z][A-Za-z0-9]*(?:\([^\)]*\))?\]\])|' \
  +        r'(?P<code>\{\{\{.*\}\}\})|' \
  +        r'(?P<pre>\{\{\{|\}\}\})|' \
  +        r'(?P<ent>[<>&])|' \
  +        r'(?P<tag><i>|</i>|<b>|</b>|<tt>|</tt>|<big>|</big>|<small>|</small>|' \
  +        r'<sub>|</sub>|<sup>|</sup>|<ins>|</ins>|<del>|</del>)|' \
  +        r'(?P<br><br\s*/?>)'
  +
  +    def __init__(self, page_name):
  +        self.page_name = page_name
  +
  +        self._block_re = re.compile(Formatter.BLOCK_PAT)
  +        self._inline_re = re.compile(Formatter.INLINE_PAT)
  +        self._indent_re = re.compile(r'^\s+')
  +        self._stack = [(None, None)] # (tag, data)
  +
  +    def print_xhtml(self, text):
  +        lines = text.split('\n')
  +        for line in lines:
  +            if not self._in('pre'):
  +                self._block(line)
               else:
  -                print self._inline(raw_line)
  +                self._inline(line)
  +        self._close_block('blank')
   
  +    def _close_all(self):
  +        self._close_inline()
           self._close_block('blank')
   
  +    # stack operations --------
  +    def _in(self, tag):
  +        if self._stack[-1][0] == tag:
  +            return True
  +        else:
  +            return False
  +
  +    def _push(self, tag, data=None):
  +        self._stack.append((tag, data))
  +
  +    def _pop(self):
  +        return self._stack.pop()
  +
  +    def _pop_and_push(self, tag, data=None):
  +        tmp = self._stack.pop()
  +        self._stack.append((tag, data))
  +        return tmp
  +
  +    def _peek(self):
  +        return self._stack[-1]
  +
  +    def _peek_tag(self):
  +        return self._stack[-1][0]
  +
  +    # block-level --------
  +    def _block(self, s):
  +        m = self._block_re.search(s)
  +        tag = m.lastgroup
  +        self._close_block(tag)
  +        apply(getattr(self, '_' + tag), (m.group(),))
  +
  +    def _close_block(self, tag):
  +        list_tags = ('ul', 'ol', 'dl')
  +
  +        upper_tag = self._peek_tag()
  +        if upper_tag in list_tags and tag not in list_tags:
  +            while upper_tag in list_tags:
  +                upper_tag, upper_level = self._pop()
  +                if upper_tag == 'dl':
  +                    print '</dd></dl>'
  +                else:
  +                    print '</li></%s>' % upper_tag
  +                upper_tag = self._peek_tag()
   
  -    # block formatting part
  -    def _close_block(self, new_btype):
  -        """
  -        멀티라인 블럭의 경우는 다른 블록으로 넘어갈 때,
  -        해당 블럭을 닫아 주어야 한다.
  -        """
  -        cur_btype = self._block_stack[-1][0]
  -        if cur_btype == 'list' and new_btype != 'list':
  -            while cur_btype == 'list':
  -                cur_level, cur_order = self._block_stack.pop()[1]
  -                print '</li></%s>' % cur_order
  -                cur_btype = self._block_stack[-1][0]
  -        elif cur_btype == 'table' and new_btype != 'table':
  -            self._block_stack.pop()
  +        elif upper_tag == 'table' and tag != 'table':
  +            self._pop()
               print '</tr></table>'
  -        elif cur_btype == 'paragraph' and new_btype != 'paragraph':
  -            self._block_stack.pop()
  +
  +        elif upper_tag == 'p' and tag != 'p':
  +            self._pop()
               print '</p>'
   
  -    def _heading_block(self, match):
  -        level = len(match.group('hmarker')) + 1
  +    def _h2(self, s):
  +        self._h(s, 2)
  +    def _h3(self, s):
  +        self._h(s, 3)
  +    def _h4(self, s):
  +        self._h(s, 4)
  +    def _h5(self, s):
  +        self._h(s, 5)
  +    def _h6(self, s):
  +        self._h(s, 6)
  +    def _h(self, s, level):
           print '<h%d>' % level
  -        print self._inline(match.group(0)[level: -level])
  +        self._inline(s[level:-level])
           print '</h%d>' % level
  +        
  +    def _hr(self, s):
  +        print '<hr />'
   
  -    def _rule_block(self, match):
  -        if len(match.group(0)) <= 4:
  -            print '<hr />'
  -        else:
  -            print '<hr size="%d" />' % (len(match.group(0)) - 2)
  -
  -    def _list_block(self, match):
  -        """
  -        XXX: 좀더 간단하게 수정이 필요
  -        현재 block_data는 (level, order)로 저장됨
  -        """
  -        if match.group('ul'):
  -            new_order = 'ul'
  -        else:
  -            new_order = 'ol'
  -        new_level = len(match.group('indent'))
  -        cur_btype = self._block_stack[-1][0]
  -        if cur_btype == 'list':
  -            cur_level = self._block_stack[-1][1][0]
  -        else:
  -            cur_level = 0
  -
  -        if new_level == cur_level:
  -            print '</li><li>'
  -        elif new_level > cur_level:
  -            print '<%s><li>' % new_order
  -            self._block_stack.append(('list', (new_level, new_order)))
  -        else:
  -            while new_level < cur_level:
  -                cur_order = self._block_stack.pop()[1][1]
  -                print '</li></%s>' % cur_order
  -                cur_btype = self._block_stack[-1][0]
  -                if cur_btype == 'list':
  -                    cur_level = self._block_stack[-1][1][0]
  -                else:
  -                    cur_level = 0
  -            print '</li><li>'
  -
  -        print self._inline(match.group(0)[new_level + 2:])
  -
  -    def _table_block(self, match):
  -        cur_btype, cur_bdata = self._block_stack[-1]
  -        new_bdata = match.group(0).count('||')
  -        if cur_btype != 'table':
  +    def _ul(self, s):
  +        self._l(s, 'ul')
  +    def _ol(self, s):
  +        self._l(s, 'ol')
  +    def _dl(self, s):
  +        self._l(s, 'dl')
  +    def _l(self, s, tag):
  +        indent = self._indent_re.match(s).end()
  +        upper_tag, upper_indent = self._peek()
  +        if upper_tag not in ('ul', 'ol', 'dl'):
  +            upper_indent = 0
  +
  +        while indent < upper_indent:
  +            upper_tag, upper_indent = self._pop()
  +            if upper_tag == 'dl':
  +                print '</dd></%s>' % upper_tag
  +            else:
  +                print '</li></%s>' % upper_tag
  +            upper_tag, upper_indent = self._peek()
  +            if upper_tag not in ('ul', 'ol', 'dl'):
  +                upper_indent = 0
  +        if indent == upper_indent:
  +            if upper_tag == 'dl':
  +                print '</dd>'
  +                if tag in ('ul', 'ol'):
  +                    print '</dl><%s>' % tag
  +                    self._pop_and_push(tag, indent)
  +            else:
  +                print '</li>'
  +                if tag == 'dl':
  +                    print '</%s><dl>' % upper_tag
  +                    self._pop_and_push(tag, indent)
  +        elif indent > upper_indent:
  +            print '<%s>' % tag
  +            self._push(tag, indent)
  +
  +        if tag == 'ul':
  +            print '<li>'
  +            self._inline(s[indent + 2:])
  +        elif tag == 'ol':
  +            pos = s.find('.')
  +            print '<li>'
  +            self._inline(s[pos + 2:])
  +        else:
  +            start = self._indent_re.match(s).end()
  +            end = s.find('::')
  +            print '<dt>'
  +            self._inline(s[start:end])
  +            print '</dt>'
  +            print '<dd>'
  +            self._inline(s[end + 2:])
  +
  +    def _table(self, s):
  +        upper_tag, upper_ncols = self._peek()
  +        ncols = s.count('||')
  +        if upper_tag != 'table':
               print '<table><tr>'
  -            self._block_stack.append(('table', new_bdata))
  +            self._push('table', ncols)
           else:
  -            if new_bdata == cur_bdata:
  +            if ncols == upper_ncols:
                   print '</tr><tr>'
               else:
                   print '</tr></table><table><tr>'
  -                self._block_stack.append(('table', new_bdata))
  +                self._push('table', ncols)
   
  -        cols = match.group(0)[2:-2].split('||')
  +        cols = s[2:-2].split('||')
           for col in cols:
  -            print '<td>%s</td>' % self._inline(col)
  +            print '<td>'
  +            self._inline(col)
  +            print '</td>'
   
  -    def _paragraph_block(self, match):
  -        cur_btype = self._block_stack[-1][0]
  -        if cur_btype != 'paragraph':
  -            print '<p>'
  -            self._block_stack.append(('paragraph', None))
  -        print self._inline(match.group(0))
  -
  -    def _blank_block(self, match):
  +    def _blank(self, s):
           pass
   
  +    def _p(self, s):
  +        upper_tag = self._peek_tag()
  +        if upper_tag != 'p':
  +            print '<p>'
  +            self._push('p')
  +        self._inline(s)
   
  -    # inline replacement part
  -    def _inline(self, line):
  -        return self._inline_re.sub(self._replace, line)
  -
  -
  -    def _replace(self, match):
  -        for type, hit in match.groupdict().items():
  -            if hit:
  -                return apply(getattr(self, '_' + type + '_repl'), (hit,))
  -
  -
  -    def _emph_repl(self, word):
  -        if not self._in_pre:
  -            if len(word) == 3:
  -                self._is_b = not self._is_b
  -                return ['</b>', '<b>'][self._is_b]
  +    # inline --------
  +    def _inline(self, s):
  +        print self._inline_re.sub(self._replace, s)
  +        self._close_inline()
  +
  +    def _close_inline(self):
  +        tags = ('i', 'b', 'tt', 'big', 'small', 'sub', 'sup', 'ins', 'del')
  +        upper_tag = self._peek_tag()
  +        while upper_tag in tags:
  +            self._pop()
  +            print '</%s>' % upper_tag            
  +            upper_tag = self._peek_tag()
  +
  +    def _replace(self, m):
  +        return apply(getattr(self, '_' + m.lastgroup + '_repl'), (m.group(),))
  +
  +    def _emph_repl(self, s):
  +        if not self._in('pre'):
  +            if len(s) == 3:
  +                if self._in('b'):
  +                    self._pop()
  +                    return '</b>'
  +                else:
  +                    self._push('b')
  +                    return '<b>'
               else:
  -                self._is_em = not self._is_em
  -                return ['</em>', '<em>'][self._is_em]
  +                if self._in('i'):
  +                    self._pop()
  +                    return '</i>'
  +                else:
  +                    self._push('i')
  +                    return '<i>'
           else:
  -            return word
  +            return s
   
  -    def _word_repl(self, word):
  -        if not self._in_pre:
  -            return Page(word).link()
  +    def _word_repl(self, s):
  +        if not self._in('pre'):
  +            return Page(s).link()
           else:
  -            return word
  +            return s
   
  -    def _eword_repl(self, word):
  -        if not self._in_pre:
  -            return Page(word[1:-1]).link()
  +    def _eword_repl(self, s):
  +        if not self._in('pre'):
  +            return Page(s[1:-1]).link()
           else:
  -            return word
  +            return s
   
  -    def _url_repl(self, word):
  -        if not self._in_pre:
  -            ext = word[word.rfind('.') + 1:]
  +    def _url_repl(self, s):
  +        if not self._in('pre'):
  +            ext = s[s.rfind('.') + 1:]
               if ext in ('gif', 'jpg', 'bmp', 'png'):
  -                return '<img src="%s" alt="%s" />' % (word, word)
  +                return '<img src="%s" alt="%s" />' % (s, s)
               else:
  -                return '<a href="%s">%s</a>' % (word, word)
  +                return '<a href="%s">%s</a>' % (s, s)
           else:
  -            return word
  +            return s
   
  -    def _email_repl(self, word):
  -        if not self._in_pre:
  -            return '<a href="mailto:%s">%s</a>' % (word, word)
  +    def _email_repl(self, s):
  +        if not self._in('pre'):
  +            return '<a href="mailto:%s">%s</a>' % (s, s)
           else:
  -            return word
  +            return s
   
  -    def _macro_repl(self, word):
  -        if not self._in_pre:
  -            pos = word.find('(')
  +    def _macro_repl(self, s):
  +        if not self._in('pre'):
  +            pos = s.find('(')
               if pos == -1:
  -                name = word[2:-2]
  +                name = s[2:-2]
                   arg = ''
               else:
  -                name = word[2:pos]
  -                arg = word[pos+1:-3]
  -            return expand_macro(name, arg)
  -        else:
  -            return word
  -
  -    def _code_repl(self, word):
  -        if not self._in_pre:
  -            return '<code>%s</code>' % word[3:-3]
  -        else:
  -            return word
  -
  -    def _pre_repl(self, word):
  -        if not self._in_pre and word == '{{{':
  -            self._in_pre = True
  +                name = s[2:pos]
  +                arg = s[pos+1:-3]
  +            method = getattr(self, '_macro_' + name)
  +            if method:
  +                return method(arg)
  +        else:
  +            return s
  +
  +    def _code_repl(self, s):
  +        if not self._in('pre'):
  +            return '<tt>%s</tt>' % s[3:-3]
  +        else:
  +            return s
  +
  +    def _pre_repl(self, s):
  +        if not self._in('pre') and s == '{{{':
  +            self._close_inline()
               self._close_block('blank')  # pre에 들어가기전 block을 닫아줘야한다
               return '<pre>'
  -        elif self._in_pre and word == '}}}':
  -            self._in_pre = False
  +            self._push('pre')
  +        elif self._in('pre') and s == '}}}':
  +            self._pop()
               return '</pre>'
           else:
  -            return word
  +            return s
   
       def _ent_repl(self, s):
           return {'&': '&amp;', '<': '&lt;', '>': '&gt;'}[s]
   
   
  -
  -# Macro functions ---------------------------------------------------
  -
  -def init_macro(page_name, form):
  -    global _macro
  -    _macro = Macro(page_name, form)
  -
  -
  -def expand_macro(name, arg):
  -    global _macro
  -    return _macro.expand(name, arg)
  -
  -
  -class Macro:
  -    def __init__(self, page_name, form):
  -        self.page_name = page_name
  -        self.form = form
  -
  -
  -    def expand(self, name, arg):
  -        if hasattr(self, '_macro_' + name):
  -            return apply(getattr(self, '_macro_' + name), (arg,))
  -        else:
  -            return '<i>"%s" macro does not exist.</i>' % name
  -
  -
  +    def _br_repl(self, s):
  +        if not self._in('pre'):
  +            return '<br />'
  +        else:
  +            return '&lt;br /&gt;'
  +
  +    def _tag_repl(self, s):
  +        is_open = (True, False)[s[1] != '/']
  +        if is_open:
  +            return s
  +        else:
  +            upper_tag = self._peek_tag()
  +            tag = s[2:-1]
  +            if tag == upper_tag:
  +                return s
  +            else:
  +                return ''
  +        
  +    # macros --------
       def _macro_RecentChanges(self, arg):
           import cStringIO
   
  +        self._close_all()
           lines = editlog_raw_lines()
           lines.reverse()
       
  @@ -608,7 +703,7 @@
           ratchet_day = None
           done_names = {}
           for line in lines:
  -            page_name, mtime, pmtime, ip = line[:-1].split('\t')
  +            page_name, rtime, ip = line[:-1].split('\t')
               page_name = unquote_name(page_name)
   
               if done_names.has_key(page_name):
  @@ -616,15 +711,15 @@
               done_names[page_name] = 1
   
               # year, month, day, DoW
  -            time_tuple = time.localtime(float(mtime))
  +            time_tuple = time.localtime(float(rtime))
               day = tuple(time_tuple[0:3])
               if day != ratchet_day:
                   if ratchet_day:
  -                    buf.write('</ul>')
  -                buf.write('<h3>%s</h3><ul>' % time.strftime(config.date_fmt, time_tuple))
  +                    buf.write('</dl>')
  +                buf.write('<dl><dt><b>%s</b></dt>' % time.strftime(config.date_fmt, time_tuple))
                   ratchet_day = day
   
  -            buf.write('<li>')
  +            buf.write('<dd>')
               buf.write(Page(page_name).link())
               buf.write(' . . . . ')
               if config.show_ips:
  @@ -633,27 +728,27 @@
               if config.changed_time_fmt:
                   buf.write(time.strftime(config.changed_time_fmt, time_tuple))
   
  -            buf.write('</li>')
  -        buf.write('</ul>')
  +            buf.write('</dd>')
  +        buf.write('</dl>')
   
           return buf.getvalue()
   
       
       def _macro_FullSearch(self, arg):
  +        self._close_all()
           url_name = urlencode(self.page_name)
           return '<form action="%s/%s" method="get">' \
  -               '<input type="hidden" name="action" value="search" />' \
  -               '<input type="hidden" name="method" value="full" />' \
  +               '<input type="hidden" name="action" value="fullsearch" />' \
                  '<input type="text" name="value" size="30" /> ' \
                  '<input type="submit" value="Search" />' \
                  '</form>' % (get_scriptname(), url_name)
   
   
       def _macro_TitleSearch(self, arg):
  +        self._close_all()
           url_name = urlencode(self.page_name)
           return '<form action="%s/%s" method="get">' \
  -               '<input type="hidden" name="action" value="search" />' \
  -               '<input type="hidden" name="method" value="title" />' \
  +               '<input type="hidden" name="action" value="titlesearch" />' \
                  '<input type="text" name="value" size="30" /> ' \
                  '<input type="submit" value="Search" />' \
                  '</form>' % (get_scriptname(), url_name)
  @@ -662,6 +757,8 @@
       def _macro_TitleIndex(self, arg):
           import cStringIO
   
  +        self._close_all()
  +
           page_list = get_page_list()
           tmp_list = map(lambda x: x.decode(config.encoding), page_list)
           tmp_list = map(lambda x: (x.upper(), x), tmp_list)
  @@ -679,20 +776,20 @@
   
               if letter != current_letter:
                   if current_letter:
  -                    buf.write('</ul>')
  +                    buf.write('</dl>')
                   encoded_letter = letter.encode(config.encoding)
  -                buf.write('<h2><a name="%s" href="#top">%s</a></h2><ul>' %
  +                buf.write('<dl><dt><b><a name="%s" href="#top">%s</a></b></dt>' %
                                                   (encoded_letter, encoded_letter))
                   current_letter = letter
   
  -            buf.write('<li>%s</li>' % Page(name.encode(config.encoding)).link())
  -        buf.write('</ul>')
  +            buf.write('<dd>%s</dd>' % Page(name.encode(config.encoding)).link())
  +        buf.write('</dl>')
   
           index = map(lambda x: x.encode(config.encoding), index_letters)
           index = map(lambda x: '<a href="#%s">%s</a> ' % (x, x), index)
           top = '<a name="top"></a>'
   
  -        return top + ' | '.join(index) + buf.getvalue()
  +        return '<p>' + top + ' | '.join(index) + '</p>' + buf.getvalue()
   
   
       def _macro_PageCount(self, arg):
  @@ -715,11 +812,12 @@
   
   
       def _macro_UploadFile(self, arg):
  +        self._close_all()
           return '<form action="%s/%s" method="post" enctype="multipart/form-data">' \
                  '<input type="hidden" name="action" value="upload" />' \
                  '<input type="file" name="file" size="40" /><br />' \
  -               'Rename (optional): <input type="text" name="rename" size="30" />' \
  -               '<br /><input type="submit" value="Upload" />' \
  +               '파일이름(옵션): <input type="text" name="rename" size="30" />' \
  +               '<br /><input type="submit" value="업로드" />' \
                  '</form>' % (get_scriptname(), self.page_name)
   
   
  @@ -736,17 +834,18 @@
               else:
                   return '<a href="%s">%s</a>' % (url, name)
           else:
  -            return '<i>"%s" file does not exist.</i>' % name
  +            return '<i>"%s" 파일이 없습니다.</i>' % name
   
   
       def _macro_UploadedFiles(self, arg):
           import cStringIO
   
  +        self._close_all()
           buf = cStringIO.StringIO()
   
           file_list = os.listdir(config.upload_dir)
  -        buf.write('<table><tr align="center">')
  -        buf.write('<th>Name</th><th>Size</th><th>Date</th></tr>')
  +        buf.write('<table><tr>')
  +        buf.write('<th>파일 이름</th><th>크기</th><th>날짜</th></tr>')
           for file_name in file_list:
               file_path = os.path.join(config.upload_dir, file_name)
   
  @@ -760,7 +859,7 @@
               buf.write('<td>%s</td></tr>' % \
                         time.strftime(config.datetime_fmt, time.localtime(mtime)))
           buf.write('</table>')
  -        buf.write('<b>Total %d files are uploaded.</b>' % len(file_list))
  +        buf.write('<p>총 %d개 파일이 업로드되었습니다.</p>' % len(file_list))
   
           return buf.getvalue()
   
  @@ -781,6 +880,7 @@
   
   
   
  +
   # Utility functions -------------------------------------------------
   
   def emit_header():
  @@ -876,6 +976,9 @@
       print '</div><h1>%s</h1>' % title
   
   
  +def send_body(page_name, text):
  +    Formatter(page_name).print_xhtml(text)
  +
   def send_footer(page_name='', rtime=None, avail_act=None):
       print '<hr />'
       print '<div class="power">Powered by ' \
  @@ -902,9 +1005,8 @@
   
   def do_show(page_name, form):
       pg = Page(page_name)
  -    rtime = form.getvalue('rtime', '')
       try:
  -        pg.send_page(rtime)
  +        pg.send_page(form.getvalue('rtime', ''))
       except (NoPage, DeletedPage, NoRevision), e:
           e.send_error()
   
  @@ -939,16 +1041,9 @@
   def do_text(page_name, form):
       pg = Page(page_name)
       try:
  -        text = pg.text(form.getvalue('rtime', ''))
  -    except NoPage:
  -        send_error('문서가 없습니다.',
  -                   '새로 생성하기 위해서는 ' + \
  -                   link_tag(page_name + '?action=edit', '여기') + \
  -                   '를 클릭해주세요.')
  -    else:
  -        print '<pre>'
  -        print text
  -        print '</pre>'
  +        pg.send_text(form.getvalue('rtime', ''))
  +    except NoPage, e:
  +        e.send_error()
   
   
   def do_goto(page_name, form):
  @@ -957,49 +1052,48 @@
       Page(page_name).send_page()
   
   
  -def do_search(page_name, form):
  -    method = form.getvalue('method')
  -    if method not in ('title', 'full'):
  -        method = 'title'
  +def do_fullsearch(page_name, form):
  +    _do_search(page_name, form, 'full')
  +
  +def do_titlesearch(page_name, form):
  +    _do_search(page_name, form, 'title')
   
  +def _do_search(page_name, form, method):
       needle = form.getvalue('value', '')
       if method == 'title':
  -        send_title("Title search for \"" + needle + '"')
  +        send_title('"%s"로 제목만 검색한 결과' % needle)
       else:
  -        send_title('Full search for "%s"' % (needle))
  +        send_title('"%s"로 검색한 결과' % (needle))
   
       needle_re = re.compile(needle, re.IGNORECASE)
       page_list = get_page_list()
  -    if method == 'title':
  -        hits = filter(needle_re.search, page_list)
  -    else:
  -        hits = []
  +    title_hits = filter(needle_re.search, page_list)
  +
  +    contents_hits = []
  +    if method != 'title':
           for page in page_list:
  -            body = Page(page).get_text()
  +            body = Page(page)._text()
               match = needle_re.findall(body)
               count = len(needle_re.findall(body))
               if count:
  -                hits.append((count, page))
  -
  -        # The default comparison for tuples compares elements in order,
  -        # so this sorts by number of hits
  -        hits.sort()
  -        hits.reverse()
  -
  -    print '<ul>'
  -    if method == 'title':
  -        for page in hits:
  -            print '<li>' + Page(page).link() + '</li>'
  -    else:
  -        for (count, page) in hits:
  -            print '<li>' + Page(page).link()
  -            print ' . . . . ' + `count`
  -            print ['match', 'matches'][count != 1] + '</li>'
  -    print '</ul>'
  -
  -    print "<p>%d hits " % len(hits)
  -    print " out of %d pages searched." % len(page_list)
  +                contents_hits.append((count, page))
  +        contents_hits.sort()
  +        contents_hits.reverse()
  +
  +    print '<dl>'
  +    print '<dt><b>제목 검색 결과: %d건</b></dt>' % len(title_hits)
  +    for page in title_hits:
  +        print '<dd>' + Page(page).link() + '</dd>'
  +    print '</dl>'
  +    if method != 'title':
  +        print '<dl>'
  +        print '<dt><b>본문 검색 결과: %d건</b></dt>' % len(contents_hits)
  +        for (count, page) in contents_hits:
  +            print '<dd>' + Page(page).link()
  +            print ' . . . . %d번' % count
  +        print '</dl>'
   
  +    print '<p>총 %d의 문서를 검색하였습니다.</p>' % len(page_list)
       send_footer(page_name, 1)
   
   
  @@ -1036,22 +1130,23 @@
           else:
               msg = '<b>"%s" is not allowed extension.</b>' % ext
       
  -    Page(page_name).send_page(msg)
  +    Page(page_name).send_page()
   
   
   def get_handler(action):
       handlers = {
  -        'show':     do_show,
  -        'edit':     do_edit,
  -        'update':   do_update,
  -        'history':  do_history,
  -        'text':     do_text,
  -        'goto':     do_goto,
  -        'search':   do_search,
  -        'upload':   do_upload
  +        'show':         do_show,
  +        'edit':         do_edit,
  +        'update':       do_update,
  +        'history':      do_history,
  +        'text':         do_text,
  +        'goto':         do_goto,
  +        'fullsearch':   do_fullsearch,
  +        'titlesearch':  do_titlesearch,
  +        'upload':       do_upload
       }
       if not handlers.has_key(action):
  -        raise UnknownAction, action
  +        raise UnknownAction(action)
       return handlers[action]
   
   
  @@ -1071,15 +1166,12 @@
           form = cgi.FieldStorage()
           action = form.getvalue('action', 'show')
   
  -        # inititalize macro
  -        init_macro(page_name, form)
  -
           # handle request
           handler = get_handler(action)
           handler(page_name, form)
   
  -    except UnknownAction, action:
  -        print 'Unknown action: ' + action
  +    except UnknownAction, e:
  +        e.send_error()
   
       except:
           cgi.print_exception()
  
  
  


yjcho       2006/09/09 14:54:45

  Modified:    .        wplus.css wplus.py
  Log:
   * line-height를 늘이면서 title이 잘리던 문제를 해결
  
  Revision  Changes    Path
  1.11      +5 -1      wikiplus/wplus.css
  
  Index: wplus.css
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/wplus.css,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -u -r1.10 -r1.11
  --- wplus.css	9 Sep 2006 05:24:28 -0000	1.10
  +++ wplus.css	9 Sep 2006 05:54:45 -0000	1.11
  @@ -3,13 +3,17 @@
   body {
   	font-family:굴림;
   	font-size:small;
  +	padding-top:10px;
  +	line-height:150%;
   }
   
  -div.logo {
  +span.logo {
   	float:right;
   }
   img.logo {
  +	float:right;
   	border:0px;
  +	text-align:right;
   }
   
   div.info {
  
  
  
  1.21      +1 -1      wikiplus/wplus.py
  
  Index: wplus.py
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/wplus.py,v
  retrieving revision 1.20
  retrieving revision 1.21
  diff -u -r1.20 -r1.21
  --- wplus.py	9 Sep 2006 05:27:49 -0000	1.20
  +++ wplus.py	9 Sep 2006 05:54:45 -0000	1.21
  @@ -972,7 +972,7 @@
       print '<link rel="stylesheet" type="text/css" href="%s" /></head>' \
             '<body><div class="logo">' % config.css_url
       print link_tag(urlencode(config.front_page),
  -                  '<img class="logo" src="%s" alt="logo" />' % config.logo_url)
  +               '<img class="logo" src="%s" alt="[HOME]" />' % config.logo_url)
       print '</div><h1>%s</h1>' % title
   
   
  
  
  


yjcho       2006/09/09 17:48:43

  Modified:    .        wplus.css
  Log:
   * pre tag에서 글자가 너무 작게 보이던 문제 해결
   * 글자체를 Comic Sans MS로 변경
   * css encoding을 나타내는 지시어 추가
  
  Revision  Changes    Path
  1.12      +6 -3      wikiplus/wplus.css
  
  Index: wplus.css
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/wplus.css,v
  retrieving revision 1.11
  retrieving revision 1.12
  diff -u -r1.11 -r1.12
  --- wplus.css	9 Sep 2006 05:54:45 -0000	1.11
  +++ wplus.css	9 Sep 2006 08:48:43 -0000	1.12
  @@ -1,19 +1,22 @@
  +@charset "UTF-8";
   /* Wiki+ CSS */
   
   body {
  -	font-family:굴림;
  +	font-family:"Comic Sans MS";
   	font-size:small;
   	padding-top:10px;
   	line-height:150%;
   }
   
  -span.logo {
  -	float:right;
  +pre {
  +	font-size:small;
   }
  +
   img.logo {
   	float:right;
   	border:0px;
   	text-align:right;
  +	margin-top:-10px;
   }
   
   div.info {
  
  
  


yjcho       2006/09/09 17:50:08

  Modified:    .        wplus.py
  Log:
   * pre에서 <pre>로 열어줄때 return호출후 stack에 push하던 거를 순서를 바꿔줌
  
  Revision  Changes    Path
  1.22      +4 -4      wikiplus/wplus.py
  
  Index: wplus.py
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/wplus.py,v
  retrieving revision 1.21
  retrieving revision 1.22
  diff -u -r1.21 -r1.22
  --- wplus.py	9 Sep 2006 05:54:45 -0000	1.21
  +++ wplus.py	9 Sep 2006 08:50:08 -0000	1.22
  @@ -396,10 +396,10 @@
           r'(?P<macro>\[\[[A-Za-z][A-Za-z0-9]*(?:\([^\)]*\))?\]\])|' \
           r'(?P<code>\{\{\{.*\}\}\})|' \
           r'(?P<pre>\{\{\{|\}\}\})|' \
  -        r'(?P<ent>[<>&])|' \
           r'(?P<tag><i>|</i>|<b>|</b>|<tt>|</tt>|<big>|</big>|<small>|</small>|' \
           r'<sub>|</sub>|<sup>|</sup>|<ins>|</ins>|<del>|</del>)|' \
  -        r'(?P<br><br\s*/?>)'
  +        r'(?P<br><br\s*/?>)|' \
  +        r'(?P<ent>[<>&])'
   
       def __init__(self, page_name):
           self.page_name = page_name
  @@ -661,8 +661,8 @@
           if not self._in('pre') and s == '{{{':
               self._close_inline()
               self._close_block('blank')  # pre에 들어가기전 block을 닫아줘야한다
  -            return '<pre>'
               self._push('pre')
  +            return '<pre>'
           elif self._in('pre') and s == '}}}':
               self._pop()
               return '</pre>'
  @@ -970,7 +970,7 @@
       print '<meta http-equiv="content-type" content="text/html;' \
             'charset=%s" />' % config.encoding
       print '<link rel="stylesheet" type="text/css" href="%s" /></head>' \
  -          '<body><div class="logo">' % config.css_url
  +          '<body><div>' % config.css_url
       print link_tag(urlencode(config.front_page),
                  '<img class="logo" src="%s" alt="[HOME]" />' % config.logo_url)
       print '</div><h1>%s</h1>' % title
  
  
  


yjcho       2006/09/09 22:17:31

  Modified:    .        wplus.py
  Log:
   * do_text에서 NoPage만 except에서 잡아내서 NoRevision, DeletedPage에러는
     처리하지 못하던 문제 해결
   * 한글 메시지 일관성 처리
  
  Revision  Changes    Path
  1.23      +44 -28    wikiplus/wplus.py
  
  Index: wplus.py
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/wplus.py,v
  retrieving revision 1.22
  retrieving revision 1.23
  diff -u -r1.22 -r1.23
  --- wplus.py	9 Sep 2006 08:50:08 -0000	1.22
  +++ wplus.py	9 Sep 2006 13:17:31 -0000	1.23
  @@ -35,10 +35,10 @@
       def __init__(self, page_name):
           self.title = '없는 문서입니다.'
           self.contents = \
  -            '요청 하신 %s는 없는 문서 입니다. ' \
  +            '요청 하신 <b>%s</b>는 없는 문서 입니다. ' \
               '이 문서를 작성 하시려면 %s를 이용해 주세요.' % \
               (page_name,
  -             link_tag(urlencode(page_name) + '?action=edit', '문서 생성'))
  +             link_tag(urlencode(page_name) + '?action=edit', '새로 만들기'))
   
   
   class DeletedPage(WikiError):         # 문서의 현재의 개정판이 없음
  @@ -50,7 +50,7 @@
               '예전 개정판 정보를 보시려면 %s를 이용해 주시고, ' \
               '새로 작성하시려면 %s를 이용해 주세요.' % \
               (page_name,
  -             link_tag(urlencode(page_name) + '?action=history', '개정 정보'),
  +             link_tag(urlencode(page_name) + '?action=history', '개정정보'),
                link_tag(urlencode(page_name) + '?action=edit', '문서 작성'))
   
   
  @@ -62,8 +62,8 @@
               '최근 개정판을 보시려면 %s를 이용해 주시고, ' \
               '다른 개정판을 보시려면 %s를 이용해 주세요.' % \
               (page_name, str_datetime(rtime),
  -             link_tag(urlencode(page_name), '문서 보기'),
  -             link_tag(urlencode(page_name) + '?action=history', '개정 정보'))
  +             link_tag(urlencode(page_name), '보기'),
  +             link_tag(urlencode(page_name) + '?action=history', '개정정보 보기'))
   
   
   class EmptyText(WikiError):
  @@ -79,16 +79,21 @@
   
   
   class UpdateConflict(WikiError):
  -    def __init__(self, newtext):
  -        self.title = '문서 수정 충돌'
  +    def __init__(self, page_name, newtext):
  +        self.title = '문서 수정 충돌!!!'
           self.contents = \
  -            '이 페이지를 수정하는 동안 다른 사용자가 수정해서 ' + \
  -            '먼저 저장했습니다. 작업하시던 내용은 아' + \
  -            '래 텍스트 박스에 그대로 있습니다. 이 내용을 지금 ' + \
  -            '바로 덮어쓰신다면 다른 사용자가 먼저 작업한 내용을' + \
  -            '덮어쓰게 됩니다. 따라서 수정된 부분만을 추가해주시' + \
  -            '면 감사하겠습니다.<textarea wrap="virtual" ' + \
  -            'rows="17" cols="80">%s</textarea>' % newtext
  +            '<b>아래 내용을 주의 깊게 읽어주십시요.</b><br />' \
  +            '이 페이지를 수정하는 동안 다른 사용자가 수정해서 ' \
  +            '먼저 저장했습니다. 작업하시던 내용은 아래 ' \
  +            '텍스트 박스에 그대로 있습니다. 이 내용을 지금 ' \
  +            '바로 덮어쓰신다면 다른 사용자가 먼저 작업한 내용을 ' \
  +            '덮어쓰게 됩니다. 따라서 이 내용을 메모장 같은 곳에 ' \
  +            '일단 복사하신 후 수정된 부분만을 추가해주시면 ' \
  +            '감사하겠습니다.<br /><br /><textarea wrap="virtual" ' \
  +            'rows="17" cols="80">%s</textarea><br /><br />' \
  +            '위의 내용을 메모장에 복사해 두셨다면 %s를 눌러서 ' \
  +            '다른 사용자가 수정한 것을 먼저 보시기 바랍니다.' % \
  +            (newtext, link_tag(urlencode(page_name), '여기'))
   
   
   class DeniedIP(WikiError):
  @@ -99,17 +104,16 @@
               '업을 하지 못하도록 막혔습니다.'
   
   
  -# for internal use in Page class
  -NoText = 'NoText'
  -
  -
  -# for internal use in action handler
   class UnknownAction(WikiError):
       def __init__(self, action):
           self.title = '알 수 없는 명령입니다.'
           self.contents = 'action의 전달 값 %s을 확인해주세요.' % action
   
   
  +# for internal use in Page class
  +NoText = 'NoText'
  +
  +
   
   # Page class --------------------------------------------------------
   
  @@ -162,7 +166,7 @@
           # 현재 텍스트가 있는 경우의 처리
           if self._has_current():
               if rtime != self._current_rtime():
  -                raise UpdateConflict(newtext)
  +                raise UpdateConflict(self.name, newtext)
               elif newtext == self._text():
                   raise UnchangedText
               os.rename(self._text_path(), os.path.join(config.backup_dir,
  @@ -198,7 +202,7 @@
           title = re.sub(r'([a-z])([A-Z])', r'\1 \2', self.name)
           if not rtime:
               rtime = self._current_rtime()
  -            avail_act = ('edit', 'history')
  +            avail_act = ('reload', 'edit', 'history')
           else:
               title = '"%s"의 개정판' % title
               avail_act = ('history',)
  @@ -267,11 +271,20 @@
           print '</table>'
           send_footer(self.name)
   
  -    def send_text(self, rtime):
  +    def send_text(self, rtime=''):
           try:
               text = self._text(rtime)
           except NoText:
  -            raise NoPage(self.name)
  +            if not rtime:
  +                if self._has_backup():
  +                    raise DeletedPage(self.name)
  +                else:
  +                    raise NoPage(self.name)
  +            else:
  +                if self._exists():
  +                    raise NoRevision(self.name, rtime)
  +                else:
  +                    raise NoPage(self.name)
   
           print '<pre>'
           print text
  @@ -990,13 +1003,16 @@
           print '이 문서는 %s에 수정되었습니다.<br />' % \
                 str_datetime(rtime)
       if avail_act:
  -        print '이 문서에 사용 가능한 명령은 다음과 같습니다.<br />'
  +#        print '이 문서에 사용 가능한 명령은 다음과 같습니다.<br />'
  +        url_name = urlencode(page_name)
  +        if 'reload' in avail_act:
  +            print link_tag(url_name, '새로 고침')
           if 'show' in avail_act:
  -            print link_tag(urlencode(page_name), '보기')
  +            print link_tag(url_name, '보기')
           if 'edit' in avail_act:
  -            print link_tag(urlencode(page_name) + '?action=edit', '고치기')
  +            print link_tag(url_name + '?action=edit', '고치기')
           if 'history' in avail_act:
  -            print link_tag(urlencode(page_name) + '?action=history', '개정정보')
  +            print link_tag(url_name + '?action=history', '개정정보 보기')
       print '</div></body></html>'
   
   
  @@ -1042,7 +1058,7 @@
       pg = Page(page_name)
       try:
           pg.send_text(form.getvalue('rtime', ''))
  -    except NoPage, e:
  +    except (NoPage, DeletedPage, NoRevision), e:
           e.send_error()
   
   
  
  
  


yjcho       2006/09/10 10:08:44

  Modified:    .        wplus.py
  Log:
  FIX: code 포매팅에서 한줄에 두개가 있을 경우 두개 양끝의 {{{ }}}를 매칭시키던 문제 해결
  ADD: [] 링크에서 url, email추가하고 link에서 이름을 다르게 지정가능
  FIX: HTML tag를 직접쓰는 경우 제대로 처리되지 않던 문제 해결
  
  Revision  Changes    Path
  1.24      +30 -3     wikiplus/wplus.py
  
  Index: wplus.py
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/wplus.py,v
  retrieving revision 1.23
  retrieving revision 1.24
  diff -u -r1.23 -r1.24
  --- wplus.py	9 Sep 2006 13:17:31 -0000	1.23
  +++ wplus.py	10 Sep 2006 01:08:44 -0000	1.24
  @@ -407,7 +407,7 @@
           r'(?P<url>' + URL_PAT + r')|' \
           r'(?P<email>' + EMAIL_PAT + r')|' \
           r'(?P<macro>\[\[[A-Za-z][A-Za-z0-9]*(?:\([^\)]*\))?\]\])|' \
  -        r'(?P<code>\{\{\{.*\}\}\})|' \
  +        r'(?P<code>\{\{\{[^(?:\{\{\{)(?:\}\}\})]*\}\}\})|' \
           r'(?P<pre>\{\{\{|\}\}\})|' \
           r'(?P<tag><i>|</i>|<b>|</b>|<tt>|</tt>|<big>|</big>|<small>|</small>|' \
           r'<sub>|</sub>|<sup>|</sup>|<ins>|</ins>|<del>|</del>)|' \
  @@ -419,6 +419,8 @@
   
           self._block_re = re.compile(Formatter.BLOCK_PAT)
           self._inline_re = re.compile(Formatter.INLINE_PAT)
  +        self._url_re = re.compile(Formatter.URL_PAT)
  +        self._email_re = re.compile(Formatter.EMAIL_PAT)
           self._indent_re = re.compile(r'^\s+')
           self._stack = [(None, None)] # (tag, data)
   
  @@ -629,7 +631,28 @@
   
       def _eword_repl(self, s):
           if not self._in('pre'):
  -            return Page(s[1:-1]).link()
  +            s = s[1:-1]
  +            m = self._url_re.match(s)
  +            if m:
  +                url = m.group()
  +                name = s[m.end():].strip()
  +                if not name:
  +                    name = url
  +                ext = url[url.rfind('.') + 1:]
  +                if ext in ('gif', 'jpg', 'bmp', 'png'):
  +                    return '<img src="%s" alt="%s" />' % (url, name)
  +                else:
  +                    return '<a href="%s">%s</a>' % (url, name)
  +
  +            m = self._email_re.match(s)
  +            if m:
  +                email = m.group()
  +                name = s[m.end():].strip()
  +                if not name:
  +                    name = email
  +                return '<a href="mailto:%s">%s</a>' % (email, name)
  +
  +            return Page(s).link()
           else:
               return s
   
  @@ -666,6 +689,8 @@
   
       def _code_repl(self, s):
           if not self._in('pre'):
  +            s = s.replace('&', '&amp;').replace('<', '&lt;')
  +            s = s.replace('>', '&gt;')
               return '<tt>%s</tt>' % s[3:-3]
           else:
               return s
  @@ -693,13 +718,15 @@
               return '&lt;br /&gt;'
   
       def _tag_repl(self, s):
  -        is_open = (True, False)[s[1] != '/']
  +        is_open = (True, False)[s[1] == '/']
           if is_open:
  +            self._push(s[1:-1])
               return s
           else:
               upper_tag = self._peek_tag()
               tag = s[2:-1]
               if tag == upper_tag:
  +                self._pop()
                   return s
               else:
                   return ''
  
  
  


yjcho       2006/09/10 11:05:17

  Modified:    .        wplus.py
  Log:
   * ADD: Echo 매크로 추가
   * FIX: 매크로 없는 경우 exception 핸들링
  
  Revision  Changes    Path
  1.25      +15 -7     wikiplus/wplus.py
  
  Index: wplus.py
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/wplus.py,v
  retrieving revision 1.24
  retrieving revision 1.25
  diff -u -r1.24 -r1.25
  --- wplus.py	10 Sep 2006 01:08:44 -0000	1.24
  +++ wplus.py	10 Sep 2006 02:05:17 -0000	1.25
  @@ -407,11 +407,11 @@
           r'(?P<url>' + URL_PAT + r')|' \
           r'(?P<email>' + EMAIL_PAT + r')|' \
           r'(?P<macro>\[\[[A-Za-z][A-Za-z0-9]*(?:\([^\)]*\))?\]\])|' \
  -        r'(?P<code>\{\{\{[^(?:\{\{\{)(?:\}\}\})]*\}\}\})|' \
  -        r'(?P<pre>\{\{\{|\}\}\})|' \
  -        r'(?P<tag><i>|</i>|<b>|</b>|<tt>|</tt>|<big>|</big>|<small>|</small>|' \
  -        r'<sub>|</sub>|<sup>|</sup>|<ins>|</ins>|<del>|</del>)|' \
  +        r'(?P<tag><i>|</i>|<b>|</b>|<tt>|</tt>|<big>|</big>|<small>|' \
  +        r'</small>|<sub>|</sub>|<sup>|</sup>|<ins>|</ins>|<del>|</del>)|' \
           r'(?P<br><br\s*/?>)|' \
  +        r'(?P<code>\{\{\{[^(?:\}\}\})]*\}\}\})|' \
  +        r'(?P<pre>\{\{\{|\}\}\})|' \
           r'(?P<ent>[<>&])'
   
       def __init__(self, page_name):
  @@ -681,9 +681,11 @@
               else:
                   name = s[2:pos]
                   arg = s[pos+1:-3]
  -            method = getattr(self, '_macro_' + name)
  -            if method:
  -                return method(arg)
  +            try:
  +                method = getattr(self, '_macro_' + name)
  +            except:
  +                return '<i>%s 매크로는 없습니다.</i>' % name
  +            return method(arg)
           else:
               return s
   
  @@ -732,6 +734,12 @@
                   return ''
           
       # macros --------
  +    def _macro_Echo(self, arg):
  +        arg = arg.replace('&', '&amp;').replace('<', '&lt;')
  +        arg = arg.replace('>', '&gt;')
  +        return arg
  +        
  +        
       def _macro_RecentChanges(self, arg):
           import cStringIO
   
  
  
  


yjcho       2006/09/10 14:05:32

  Modified:    .        wplus.py
  Log:
  FIX: DeletedPage에러에서 %s로 쓰지않고 %만 써서 에러가 나던 문제해결
  FIX: backup_list에서 무조건 시작하는 문자로 찾던 문제 해결
  MOD: front_page에서 wiki_name으로 변경하면서 html title에도 반영
  
  Revision  Changes    Path
  1.26      +7 -6      wikiplus/wplus.py
  
  Index: wplus.py
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/wplus.py,v
  retrieving revision 1.25
  retrieving revision 1.26
  diff -u -r1.25 -r1.26
  --- wplus.py	10 Sep 2006 02:05:17 -0000	1.25
  +++ wplus.py	10 Sep 2006 05:05:32 -0000	1.26
  @@ -45,7 +45,7 @@
       def __init__(self, page_name):
           self.title = '삭제된 문서입니다.'
           self.contents = \
  -            '요청하신 %는 삭제된 문서입니다. ' \
  +            '요청하신 %s는 삭제된 문서입니다. ' \
               '그렇지만 아직 예전 개정판이 남아있습니다. ' \
               '예전 개정판 정보를 보시려면 %s를 이용해 주시고, ' \
               '새로 작성하시려면 %s를 이용해 주세요.' % \
  @@ -330,7 +330,8 @@
               
       def _backup_list(self):
           tmp_list = os.listdir(config.backup_dir)
  -        tmp_list = filter(lambda x: x.startswith(self.file_name), tmp_list)
  +        tmp_list = filter(lambda x: x.startswith(self.file_name + '.'),
  +                          tmp_list)
           return map(lambda x: x.split('.')[1], tmp_list)
   
       def _current_rtime(self):
  @@ -1013,13 +1014,13 @@
   
   def send_title(title):
       print '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" ' \
  -          '"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
  -    print '<html><head><title>%s</title>' % title
  +          '"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html>'
  +    print '<head><title>%s</title>' % ' - '.join((title, config.wiki_name))
       print '<meta http-equiv="content-type" content="text/html;' \
             'charset=%s" />' % config.encoding
       print '<link rel="stylesheet" type="text/css" href="%s" /></head>' \
             '<body><div>' % config.css_url
  -    print link_tag(urlencode(config.front_page),
  +    print link_tag(urlencode(config.wiki_name),
                  '<img class="logo" src="%s" alt="[HOME]" />' % config.logo_url)
       print '</div><h1>%s</h1>' % title
   
  @@ -1213,7 +1214,7 @@
           if len(path_info) > 1:
               page_name = path_info[1:]
           else:
  -            page_name = config.front_page
  +            page_name = config.wiki_name
           form = cgi.FieldStorage()
           action = form.getvalue('action', 'show')
   
  
  
  


yjcho       2006/09/10 14:06:56

  Modified:    .        config.py
  Log:
  MOD: front_page에서 wiki_name으로 변경
  DEL: autolinebreak를 없앰
  
  Revision  Changes    Path
  1.10      +2 -5      wikiplus/config.py
  
  Index: config.py
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/config.py,v
  retrieving revision 1.9
  retrieving revision 1.10
  diff -u -r1.9 -r1.10
  --- config.py	4 Sep 2006 07:17:43 -0000	1.9
  +++ config.py	10 Sep 2006 05:06:56 -0000	1.10
  @@ -9,6 +9,8 @@
   """
   import os
   
  +# wiki name
  +wiki_name = '위키'
   
   # path to wikiplus
   dir_prefix = '.'
  @@ -30,13 +32,8 @@
   date_fmt = '%Y년 %m월 %d일'
   datetime_fmt = '%Y년 %m월 %d일 %I시 %M분 %S초'
   changed_time_fmt = '[%I:%M %p]'
  -auto_linebreak = True
  -
   denied_ips = []
   
  -# default page name
  -front_page = '대문'
  -
   
   # for uploading feature
   upload_dir = os.path.join(data_dir, 'upload')
  
  
  


yjcho       2006/09/10 14:11:01

  Modified:    .        wplus.css
  Log:
  FIX: tt에서 글자가 너무 작던 문제 해결
  FIX: small에서 글자가 작게 보이지 않던 문제 해결
  ADD: h1, h2에서 줄 간격이 너무 좁아서 추가
  ADD: table, td, pre에 border를 주어서 구분하기 쉽게 함
  
  Revision  Changes    Path
  1.13      +33 -1     wikiplus/wplus.css
  
  Index: wplus.css
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/wplus.css,v
  retrieving revision 1.12
  retrieving revision 1.13
  diff -u -r1.12 -r1.13
  --- wplus.css	9 Sep 2006 08:48:43 -0000	1.12
  +++ wplus.css	10 Sep 2006 05:11:01 -0000	1.13
  @@ -1,6 +1,6 @@
   @charset "UTF-8";
  -/* Wiki+ CSS */
   
  +/* 문서 포매팅에 쓰이는 CSS */
   body {
   	font-family:"Comic Sans MS";
   	font-size:small;
  @@ -8,10 +8,42 @@
   	line-height:150%;
   }
   
  +tt {
  +	font-size:small;
  +}
  +
  +small {
  +	font-size:xx-small;
  +}
  +
  +h1 {
  +	margin-bottom:4%;
  +}
  +
  +h2 {
  +	margin-top:4%;
  +}
  +
  +
  +table {
  +	border-style:solid;
  +	border-width:1px;
  +}
  +
  +td {
  +	border-style:solid;
  +	border-width:1px;
  +}
  +
   pre {
  +	border-style:solid;
  +	border-width:1px;
  +	border-color:gray;
   	font-size:small;
   }
   
  +
  +/* 기타 CSS */
   img.logo {
   	float:right;
   	border:0px;
  
  
  


yjcho       2006/09/10 14:41:34

  Modified:    .        wplus.py
  Log:
  FIX: modification of the link to the piki site
  
  Revision  Changes    Path
  1.27      +1 -1      wikiplus/wplus.py
  
  Index: wplus.py
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/wplus.py,v
  retrieving revision 1.26
  retrieving revision 1.27
  diff -u -r1.26 -r1.27
  --- wplus.py	10 Sep 2006 05:05:32 -0000	1.26
  +++ wplus.py	10 Sep 2006 05:41:34 -0000	1.27
  @@ -6,7 +6,7 @@
      Wiki+ is distributed under GNU GPL. See COPYING for details.
   
      This program is based on PikiPiki which is a simple wiki engine.
  -   PikiPiki project homepage is http://sourcefrog.net/projects/piki.
  +   PikiPiki project homepage is http://sf.net/projects/piki.
   
      For more information, visit http://wikiplus.kldp.net.
   """
  
  
  


yjcho       2006/09/11 19:14:12

  Modified:    .        wplus.py
  Log:
  ADD: blockquote markup ("")
  
  Revision  Changes    Path
  1.28      +12 -0     wikiplus/wplus.py
  
  Index: wplus.py
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/wplus.py,v
  retrieving revision 1.27
  retrieving revision 1.28
  diff -u -r1.27 -r1.28
  --- wplus.py	10 Sep 2006 05:41:34 -0000	1.27
  +++ wplus.py	11 Sep 2006 10:14:12 -0000	1.28
  @@ -395,6 +395,7 @@
           r'(?P<ol>\s+\d+\.\s.*)|' \
           r'(?P<dl>\s+[^(?:::)]*::.*)|' \
           r'(?P<table>\|\|(?:.*\|\|)+)|' \
  +        r'(?P<blockquote>"".*)|' \
           r'(?P<blank>\s*)|' \
           r'(?P<p>.+)' \
           r')$'
  @@ -490,6 +491,10 @@
               self._pop()
               print '</p>'
   
  +        elif upper_tag == 'blockquote' and tag != 'blockquote':
  +            self._pop()
  +            print '</blockquote>'
  +
       def _h2(self, s):
           self._h(s, 2)
       def _h3(self, s):
  @@ -579,6 +584,13 @@
               self._inline(col)
               print '</td>'
   
  +    def _blockquote(self, s):
  +        upper_tag = self._peek_tag()
  +        if upper_tag != 'blockquote':
  +            print '<blockquote>'
  +            self._push('blockquote')
  +        self._inline(s[2:])
  +        
       def _blank(self, s):
           pass
   
  
  
  


yjcho       2006/09/11 19:29:14

  Modified:    .        wplus.py
  Log:
  FIX: blockquote안에 p 태그를 안써서 XHTML에 맞지 않던 문제 고침
  
  Revision  Changes    Path
  1.29      +2 -2      wikiplus/wplus.py
  
  Index: wplus.py
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/wplus.py,v
  retrieving revision 1.28
  retrieving revision 1.29
  diff -u -r1.28 -r1.29
  --- wplus.py	11 Sep 2006 10:14:12 -0000	1.28
  +++ wplus.py	11 Sep 2006 10:29:14 -0000	1.29
  @@ -493,7 +493,7 @@
   
           elif upper_tag == 'blockquote' and tag != 'blockquote':
               self._pop()
  -            print '</blockquote>'
  +            print '</p></blockquote>'
   
       def _h2(self, s):
           self._h(s, 2)
  @@ -587,7 +587,7 @@
       def _blockquote(self, s):
           upper_tag = self._peek_tag()
           if upper_tag != 'blockquote':
  -            print '<blockquote>'
  +            print '<blockquote><p>'
               self._push('blockquote')
           self._inline(s[2:])
           
  
  
  


yjcho       2006/09/15 09:32:07

  Modified:    .        wplus.cgi wplus.py
  Log:
  ETC: chagne my name
  
  Revision  Changes    Path
  1.7       +1 -1      wikiplus/wplus.cgi
  
  Index: wplus.cgi
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/wplus.cgi,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- wplus.cgi	29 Aug 2006 13:07:57 -0000	1.6
  +++ wplus.cgi	15 Sep 2006 00:32:07 -0000	1.7
  @@ -3,7 +3,7 @@
   """
      Wiki+ Driver Script
   
  -   Copyright (C) 2005 Yong-Jin Cho <yongjin.cho@gmail.com>
  +   Copyright (C) 2005 Yongjin Cho <yongjin.cho@gmail.com>
      Wiki+ is distributed under GNU GPL. See COPYING for details.
   
      For more information, visit http://wikiplus.kldp.net.
  
  
  
  1.30      +1 -1      wikiplus/wplus.py
  
  Index: wplus.py
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/wplus.py,v
  retrieving revision 1.29
  retrieving revision 1.30
  diff -u -r1.29 -r1.30
  --- wplus.py	11 Sep 2006 10:29:14 -0000	1.29
  +++ wplus.py	15 Sep 2006 00:32:07 -0000	1.30
  @@ -2,7 +2,7 @@
   """
      Wiki+
   
  -   Copyright (C) 2005 Yong-Jin Cho <yongjin.cho@gmail.com>
  +   Copyright (C) 2005 Yongjin Cho <yongjin.cho@gmail.com>
      Wiki+ is distributed under GNU GPL. See COPYING for details.
   
      This program is based on PikiPiki which is a simple wiki engine.
  
  
  


yjcho       2006/09/29 21:29:12

  Modified:    .        wplus.css wplus.py
  Log:
  MOD: 스타일을 좀더 심플하게 변경, 파폭에서도 원만하게 보이도록 변경
   - 나중에 로그인 기능을 만들 때 필요할 것들에 대해서 미리 변경한 것임
  
  Revision  Changes    Path
  1.14      +9 -45     wikiplus/wplus.css
  
  Index: wplus.css
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/wplus.css,v
  retrieving revision 1.13
  retrieving revision 1.14
  diff -u -r1.13 -r1.14
  --- wplus.css	10 Sep 2006 05:11:01 -0000	1.13
  +++ wplus.css	29 Sep 2006 12:29:12 -0000	1.14
  @@ -1,60 +1,24 @@
   @charset "UTF-8";
   
  +
   /* 문서 포매팅에 쓰이는 CSS */
   body {
  -	font-family:"Comic Sans MS";
  -	font-size:small;
  -	padding-top:10px;
  -	line-height:150%;
  -}
  -
  -tt {
  -	font-size:small;
  -}
  -
  -small {
  -	font-size:xx-small;
  -}
  -
  -h1 {
  -	margin-bottom:4%;
  -}
  -
  -h2 {
  -	margin-top:4%;
  -}
  -
  -
  -table {
  -	border-style:solid;
  -	border-width:1px;
  -}
  -
  -td {
  -	border-style:solid;
  -	border-width:1px;
  +	font-family:굴림;
  +	line-height:120%;
   }
   
   pre {
  -	border-style:solid;
  -	border-width:1px;
  -	border-color:gray;
  -	font-size:small;
  +	background-color:lightgray;
  +	padding:3px;
   }
   
  -
  -/* 기타 CSS */
  -img.logo {
  -	float:right;
  +img {
   	border:0px;
  -	text-align:right;
  -	margin-top:-10px;
   }
   
  -div.info {
  -	float:left;
  -}
  -div.power {
  +
  +/* 기타 CSS */
  +div.wikiplus {
   	text-align:right;
   	float:right;
   }
  
  
  
  1.31      +10 -7     wikiplus/wplus.py
  
  Index: wplus.py
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/wplus.py,v
  retrieving revision 1.30
  retrieving revision 1.31
  diff -u -r1.30 -r1.31
  --- wplus.py	15 Sep 2006 00:32:07 -0000	1.30
  +++ wplus.py	29 Sep 2006 12:29:12 -0000	1.31
  @@ -232,6 +232,7 @@
           print '<input type="hidden" name="rtime" value="%s" />' % rtime
           print '<textarea name="newtext" rows="18" cols="90" ' \
                 'style="width:95%%;word-wrap:virtual;">%s</textarea>' % newtext
  +        print '<br />필명: <input type="text" name="name" /> ' 
           print '<input type="submit" name="save" value="저장하기" /> ' \
                 '<input type="submit" name="preview" value="미리보기" />'
           print link_tag(self.url_name, '취소하기')
  @@ -263,7 +264,8 @@
           blist.reverse()
   
           send_title('"%s"의 개정판 정보' % self.name)
  -        print '<table><tr><th>날짜</th><th>크기</th><th>명령</th></tr>'
  +        print '<table border="1" cellspacing="0" cellpadding="3"><tr>' \
  +              '<th>날짜</th><th>크기</th><th>명령</th></tr>'
           if self._has_current():
               print_row('')
           for rtime in blist:
  @@ -569,13 +571,14 @@
           upper_tag, upper_ncols = self._peek()
           ncols = s.count('||')
           if upper_tag != 'table':
  -            print '<table><tr>'
  +            print '<table border="1" cellspacing="0" cellpadding="3"><tr>'
               self._push('table', ncols)
           else:
               if ncols == upper_ncols:
                   print '</tr><tr>'
               else:
  -                print '</tr></table><table><tr>'
  +                print '</tr></table>' \
  +                      '<table border="1" cellspacing="0" cellpadding="3"><tr>'
                   self._push('table', ncols)
   
           cols = s[2:-2].split('||')
  @@ -905,7 +908,7 @@
           buf = cStringIO.StringIO()
   
           file_list = os.listdir(config.upload_dir)
  -        buf.write('<table><tr>')
  +        buf.write('<table border="1" cellspacing="0" cellpadding="3"><tr>')
           buf.write('<th>파일 이름</th><th>크기</th><th>날짜</th></tr>')
           for file_name in file_list:
               file_path = os.path.join(config.upload_dir, file_name)
  @@ -1031,10 +1034,10 @@
       print '<meta http-equiv="content-type" content="text/html;' \
             'charset=%s" />' % config.encoding
       print '<link rel="stylesheet" type="text/css" href="%s" /></head>' \
  -          '<body><div>' % config.css_url
  +          '<body><h1>' % config.css_url
       print link_tag(urlencode(config.wiki_name),
                  '<img class="logo" src="%s" alt="[HOME]" />' % config.logo_url)
  -    print '</div><h1>%s</h1>' % title
  +    print '%s</h1>' % title
   
   
   def send_body(page_name, text):
  @@ -1042,7 +1045,7 @@
   
   def send_footer(page_name='', rtime=None, avail_act=None):
       print '<hr />'
  -    print '<div class="power">Powered by ' \
  +    print '<div class="wikiplus">Powered by ' \
             '<a href="http://wikiplus.kldp.net">Wiki+</a><br />' \
             '<a href="http://validator.w3.org/check/referer">XHTML 1.0</a> | ' \
             '<a href="http://jigsaw.w3.org/css-validator/check/referer">' \
  
  
  


yjcho       2007/02/17 00:47:42

  Modified:    .        config.py wplus.css wplus.py
  Log:
   enable to login and logout of administrator
  
  Revision  Changes    Path
  1.11      +4 -0      wikiplus/config.py
  
  Index: config.py
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/config.py,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -u -r1.10 -r1.11
  --- config.py	10 Sep 2006 05:06:56 -0000	1.10
  +++ config.py	16 Feb 2007 15:47:37 -0000	1.11
  @@ -11,6 +11,9 @@
   
   # wiki name
   wiki_name = '위키'
  +menu_bar = '메뉴'
  +password = '1111'
  +
   
   # path to wikiplus
   dir_prefix = '.'
  @@ -18,6 +21,7 @@
   text_dir = os.path.join(data_dir, 'text')
   backup_dir = os.path.join(data_dir, 'backup')
   editlog_path = os.path.join(data_dir, 'editlog')
  +session_path = os.path.join(data_dir, 'session')
   
   
   # url path to wikiplus
  
  
  
  1.15      +15 -1     wikiplus/wplus.css
  
  Index: wplus.css
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/wplus.css,v
  retrieving revision 1.14
  retrieving revision 1.15
  diff -u -r1.14 -r1.15
  --- wplus.css	29 Sep 2006 12:29:12 -0000	1.14
  +++ wplus.css	16 Feb 2007 15:47:37 -0000	1.15
  @@ -8,7 +8,8 @@
   }
   
   pre {
  -	background-color:lightgray;
  +	font-size:small;
  +	background-color:gray;
   	padding:3px;
   }
   
  @@ -18,6 +19,19 @@
   
   
   /* 기타 CSS */
  +body {
  +	margin-top:5px;
  +	font-size:small;
  +}
  +
  +div.menu {
  +	text-align:center;
  +}
  +
  +img.logo {
  +	float:right;
  +}
  +
   div.wikiplus {
   	text-align:right;
   	float:right;
  
  
  
  1.32      +142 -6    wikiplus/wplus.py
  
  Index: wplus.py
  ===================================================================
  RCS file: /cvsroot/wikiplus/wikiplus/wplus.py,v
  retrieving revision 1.31
  retrieving revision 1.32
  diff -u -r1.31 -r1.32
  --- wplus.py	29 Sep 2006 12:29:12 -0000	1.31
  +++ wplus.py	16 Feb 2007 15:47:37 -0000	1.32
  @@ -11,7 +11,7 @@
      For more information, visit http://wikiplus.kldp.net.
   """
   # Imports -----------------------------------------------------------
  -import cgi, sys, string, os, fcntl, re, errno, time, urllib
  +import cgi, sys, string, os, fcntl, re, errno, time, urllib, Cookie
   import config
   
   
  @@ -273,6 +273,9 @@
           print '</table>'
           send_footer(self.name)
   
  +    def send_diff(self, rtime1, rtime2=''):
  +        pass
  +        
       def send_text(self, rtime=''):
           try:
               text = self._text(rtime)
  @@ -353,6 +356,21 @@
   
   
   
  +# User --------------------------------------------------------------
  +
  +# The class to manage user infomation, login and logout
  +
  +class User:
  +    def __init__(self, name):
  +        self.name = name
  +
  +    def login(self, password):
  +        pass
  +
  +    def logout(self):
  +        pass
  +
  +
   # Editlog -----------------------------------------------------------
   
   # Functions to keep track of when people have changed pages, so we can
  @@ -1027,6 +1045,34 @@
       return '<a href="%s/%s">%s</a>' % (get_scriptname(), link, text)
   
   
  +_is_admin = None
  +def is_admin():
  +    global _is_admin
  +
  +    if _is_admin == None:
  +        if os.environ.has_key('HTTP_COOKIE'):
  +            c = Cookie.SimpleCookie()
  +            c.load(os.environ['HTTP_COOKIE'])
  +            if c.has_key('key'):
  +                try:
  +                    f = open(config.session_path, 'r')
  +                except IOError:
  +                    _is_admin = False
  +                else:
  +                    key = f.read()
  +                    f.close()
  +                    if c['key'].value == key:
  +                        _is_admin = True
  +                    else:
  +                        _is_admin = False
  +            else:
  +                _is_admin = False
  +        else:
  +            _is_admin = False
  +
  +    return _is_admin
  +
  +
   def send_title(title):
       print '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" ' \
             '"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html>'
  @@ -1034,10 +1080,12 @@
       print '<meta http-equiv="content-type" content="text/html;' \
             'charset=%s" />' % config.encoding
       print '<link rel="stylesheet" type="text/css" href="%s" /></head>' \
  -          '<body><h1>' % config.css_url
  +          '<body>' % config.css_url
  +    print '<div class="menu">%s</div>' % config.menu_bar
  +    print '<div>'
       print link_tag(urlencode(config.wiki_name),
                  '<img class="logo" src="%s" alt="[HOME]" />' % config.logo_url)
  -    print '%s</h1>' % title
  +    print '</div><h1>%s</h1><div><br /></div>' % title
   
   
   def send_body(page_name, text):
  @@ -1064,13 +1112,28 @@
               print link_tag(url_name + '?action=edit', '고치기')
           if 'history' in avail_act:
               print link_tag(url_name + '?action=history', '개정정보 보기')
  +        if is_admin():
  +            print link_tag(url_name + '?action=logout', '관리자 로그아웃')
  +        else:
  +            print link_tag(url_name + '?action=loginform', '관리자 로그인')
       print '</div></body></html>'
   
   
  +def send_refresh(page_name):
  +    emit_header()
  +    print """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  +<html>
  +<head><meta http-equiv="refresh" content="0;url=%s/%s"></head>
  +<body></body>
  +</html>""" % (get_scriptname(), urlencode(page_name))
  +
  +
   
   # Action handlers ---------------------------------------------------
   
   def do_show(page_name, form):
  +    emit_header()
  +
       pg = Page(page_name)
       try:
           pg.send_page(form.getvalue('rtime', ''))
  @@ -1079,10 +1142,14 @@
   
   
   def do_edit(page_name, form):
  +    emit_header()
  +
       Page(page_name).send_editor()
   
   
   def do_update(page_name, form):
  +    emit_header()
  +
       pg = Page(page_name)
       newtext = form.getvalue('newtext', '').replace('\r', '')
       rtime = form.getvalue('rtime', '')
  @@ -1098,6 +1165,8 @@
   
   
   def do_history(page_name, form):
  +    emit_header()
  +
       pg = Page(page_name)
       try:
           pg.send_history()
  @@ -1105,7 +1174,19 @@
           e.send_error()
          
   
  +def do_diff(page_name, form):
  +    emit_header()
  +
  +    pg = Page(page_name)
  +    try:
  +        pg.send_diff()
  +    except (NoPage, NoRevision), e:
  +        e.send_error()
  +
  +        
   def do_text(page_name, form):
  +    emit_header()
  +
       pg = Page(page_name)
       try:
           pg.send_text(form.getvalue('rtime', ''))
  @@ -1114,15 +1195,21 @@
   
   
   def do_goto(page_name, form):
  +    emit_header()
  +
       page_name = form.getvalue('value')
       init_macro(page_name, form)         # reset global variables for macro
       Page(page_name).send_page()
   
   
   def do_fullsearch(page_name, form):
  +    emit_header()
  +
       _do_search(page_name, form, 'full')
   
   def do_titlesearch(page_name, form):
  +    emit_header()
  +
       _do_search(page_name, form, 'title')
   
   def _do_search(page_name, form, method):
  @@ -1167,6 +1254,8 @@
   def do_upload(page_name, form):
       import shutil
   
  +    emit_header()
  +
       if not form.getvalue('file') or not form['file'].file:
           msg = '<b>File was not trasferred.</b>'
       else:
  @@ -1200,17 +1289,66 @@
       Page(page_name).send_page()
   
   
  +def do_loginform(page_name, form):
  +    emit_header()
  +
  +    url_name = urlencode(page_name)
  +    print '<form action="%s/%s" method="get">' \
  +          '<input type="hidden" name="action" value="login" />' \
  +          '관리자 암호: <input type="password" name="password" size="30" />' \
  +          '<input type="submit" value="로그인" />' \
  +          '</form>' % (get_scriptname(), url_name)
  +
  +
  +def do_login(page_name, form):
  +    if form['password'].value == config.password:
  +        import md5
  +
  +        m = md5.new()
  +        m.update(`time.time()`)
  +        key = m.digest()
  +
  +        f = open(config.session_path, 'w+')
  +        f.write(key)
  +        f.close()
  +
  +        c = Cookie.SimpleCookie()
  +        c['key'] = key
  +        c['key']['domain'] = '.yongd.com'
  +        c['key']['path'] = '/'
  +        c['key']['expires'] = Cookie._getdate(3600)
  +        print c
  +
  +    send_refresh(page_name)
  +
  +
  +def do_logout(page_name, form):
  +    f = open(config.session_path, 'w+')
  +    f.close()
  +
  +    c = Cookie.SimpleCookie()
  +    c['key'] = ''
  +    c['key']['max-age'] = 0
  +    print c
  +
  +    send_refresh(page_name)
  +
  +
   def get_handler(action):
       handlers = {
           'show':         do_show,
           'edit':         do_edit,
           'update':       do_update,
           'history':      do_history,
  +        'diff':         do_diff,
           'text':         do_text,
           'goto':         do_goto,
           'fullsearch':   do_fullsearch,
           'titlesearch':  do_titlesearch,
  -        'upload':       do_upload
  +        'upload':       do_upload,
  +        'loginform':    do_loginform,
  +        'login':        do_login,
  +        'logout':       do_logout
       }
       if not handlers.has_key(action):
           raise UnknownAction(action)
  @@ -1222,8 +1360,6 @@
   
   def main():
       try:
  -        emit_header()
  -
           # get request
           path_info = os.environ.get('PATH_INFO', '')
           if len(path_info) > 1:
  
  
  


