Attachment 'diamond.py'

Download

   1 #! /usr/bin/env python2.2
   2 
   3 """Quick-quick implementation of WikiWikiWeb in Python
   4 """
   5 
   6 # Copyright (C) 1999, 2000 Martin Pool <mbp@humbug.org.au>
   7 # Copyright (C) 2003 Kimberley Burchett http://www.kimbly.com/
   8 
   9 # This program is free software; you can redistribute it and/or modify
  10 # it under the terms of the GNU General Public License as published by
  11 # the Free Software Foundation; either version 2 of the License, or
  12 # (at your option) any later version.
  13 
  14 # This program is distributed in the hope that it will be useful, but
  15 # WITHOUT ANY WARRANTY; without even the implied warranty of
  16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 # General Public License for more details.
  18 
  19 # You should have received a copy of the GNU General Public License
  20 # along with this program; if not, write to the Free Software
  21 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  22 # USA
  23 
  24 __version__ = '0.2';
  25 
  26 
  27 import cgi, sys, string, os, re, errno, time, stat, urllib
  28 from cgi import log
  29 from os import path, environ
  30 from socket import gethostbyaddr
  31 from time import localtime, strftime
  32 from cStringIO import StringIO
  33 
  34 True = 1
  35 False = 0
  36 
  37 def emit_header():
  38     print "Content-type: text/html"
  39     print
  40 
  41 
  42 # Regular expression defining a WikiWord (but this definition
  43 # is also assumed in other places.
  44 word_re_str = r"\b([A-Z][a-z]+){2,}\b"
  45 word_anchored_re = re.compile('^' + word_re_str + '$')
  46 command_re_str = "(search|edit|fullsearch|titlesearch)\=(.*)"
  47 
  48 # Editlog -----------------------------------------------------------
  49 
  50 # Functions to keep track of when people have changed pages, so we can
  51 # do the recent changes page and so on.
  52 # The editlog is stored with one record per line, as tab-separated
  53 # words: page_name, host, time
  54 
  55 # TODO: Check values written in are reasonable
  56 
  57 def editlog_add(page_name, host):
  58     editlog = open(editlog_name, 'a+')
  59     try: 
  60         # fcntl.flock(editlog.fileno(), fcntl.LOCK_EX)
  61         editlog.seek(0, 2)                  # to end
  62         editlog.write(string.join((page_name,host,`time.time()`), "\t") + "\n")
  63     finally:
  64         # fcntl.flock(editlog.fileno(), fcntl.LOCK_UN)
  65         editlog.close()
  66 
  67 
  68 def editlog_raw_lines():
  69     editlog = open(editlog_name, 'rt')
  70     try:
  71         # fcntl.flock(editlog.fileno(), fcntl.LOCK_SH)
  72         return editlog.readlines()
  73     finally:
  74         # fcntl.flock(editlog.fileno(), fcntl.LOCK_UN)
  75         editlog.close()
  76 
  77 
  78 
  79 
  80 # Formatting stuff --------------------------------------------------
  81 
  82 
  83 def get_scriptname():
  84     return environ.get('SCRIPT_NAME', '')
  85 
  86 
  87 def send_title(text, link=None, msg=None):
  88     print "<head><title>%s</title>" % text
  89     print """<style type="text/css">
  90     .body {
  91         font: 10pt/15pt arial;
  92         background-color: #FFFFFF; 
  93         color: #000000;
  94     }
  95     a {
  96         font-weight: bold; 
  97         color: #7050c0;
  98         text-decoration: none;
  99     }
 100     a:hover, a:active {
 101         text-decoration: underline;
 102         color: #5080a0;
 103     }
 104     a.nonexistent {
 105         vertical-align: top;
 106     }
 107     .title, .title a, .title a:hover, .title a:active {
 108         font: normal 24pt georgia; 
 109         letter-spacing: 1px;
 110         color: #000000;
 111     }
 112     .metabox {
 113         background-color: #d8d8f4;
 114     }
 115     .metakey {
 116         background-color: #e8e8f4;
 117         font: normal 10pt/11pt arial;
 118     }
 119     .metaval {
 120         background-color: #ffffff;
 121         font: normal 10pt/11pt arial;
 122     }
 123     </style>"""
 124     print "</head>"
 125     print '<body class="body" bgcolor="#ffffff" text="#000000" link="#4040ff vlink="#4040ff>'
 126     print '<table><tr><td valign=absmiddle>'
 127     print '<a href="%s">%s</a>' % (get_scriptname()+"/BrowseFacets/", logo_string)
 128     print '</td>'
 129     print '<td width=10></td>'
 130     print '<td><h1 class="title">'
 131     if link:
 132         print '<a href="%s">%s</a>' % (link, text)
 133     else:
 134         print text
 135     print '</h1></td></table><p>'
 136     if msg: print msg, "<hr>"
 137 
 138 
 139 
 140 def link_tag(params, text=None, ss_class=None):
 141     if text is None:
 142         text = params                   # default
 143     if ss_class:
 144         classattr = 'class="%s" ' % ss_class
 145     else:
 146         classattr = ''
 147     return '<a %s href="%s/%s">%s</a>' % (classattr, get_scriptname(),
 148                                          params, text)
 149 
 150 
 151 def send_unordered_list(list):
 152     print "<UL>"
 153     for item in list:
 154         print '<LI>' + item
 155     print "</UL>"
 156 
 157 
 158 
 159 # Search ---------------------------------------------------
 160 
 161 def do_fullsearch(needle):
 162     send_title('Full text search for "%s"' % (needle))
 163 
 164     needle_re = re.compile(needle, re.IGNORECASE)
 165     hits = []
 166     all_pages = page_list()
 167     for page_name in all_pages:
 168         body = Page(page_name).get_body()
 169         count = len(needle_re.findall(body))
 170         if count:
 171             hits.append((count, page_name))
 172 
 173     # The default comparison for tuples compares elements in order,
 174     # so this sorts by number of hits
 175     hits.sort()
 176     hits.reverse()
 177 
 178     print "<span class='body nav'>"
 179     print "<UL>"
 180     for (count, page_name) in hits:
 181         print '<LI>' + Page(page_name).link_to()
 182         print ' . . . . ' + `count`
 183         print ['match', 'matches'][count != 1]
 184     print "</UL>"
 185     print "</span>"
 186 
 187     print_search_stats(len(hits), len(all_pages))
 188 
 189 
 190 def do_browse(view):
 191     if (view.breadcrumb()):
 192         send_title("Browsing "+view.breadcrumb())
 193     else:
 194         send_title("Browsing All Pages")
 195 
 196     all_pages = page_list()
 197     hits = view.pages()
 198 
 199     refinements = view.refinements(hits)
 200     groups = group_refinements(refinements)
 201 
 202     print "<table class='body nav'><tr><td valign=top>"
 203 
 204     group_names = groups.keys()
 205     group_names.sort()
 206     first = True
 207     for group_name in group_names:
 208         if not first: print "<br>"
 209         first = False
 210         print "<b>%s</b><br>" % group_name
 211         for kv in groups[group_name]:
 212             subview = View().copy(view).narrow(kv)
 213             print "&nbsp;&nbsp;%s <small>(%s)</small><br>" % \
 214                 (subview.link_to(kv.val), kv.count)
 215         none_view = View().copy(view).narrow(KeyVal(group_name, ""))
 216         none_view_pages = none_view.pages()
 217         if none_view_pages:
 218             print "&nbsp;&nbsp;%s <small>(%s)</small><br>" % \
 219                 (none_view.link_to("&lt;none&gt;"), len(none_view_pages))
 220 
 221     print "</td><td width=30>"
 222     print "</td><td valign='top'>"
 223 
 224     print "<p><b>%d pages" % len(hits)
 225     if len(all_pages) == len(hits):
 226         print "</b>"
 227     else:
 228         print " (out of %s)</b>\n" % View().link_to(len(all_pages))
 229 
 230     send_unordered_list([p.link_to() for p in hits])
 231 
 232     print "</td></tr></table>"
 233 
 234 
 235 def do_titlesearch(needle):
 236     # TODO: check needle is legal -- but probably we can just accept any RE
 237 
 238     send_title("Title search for \"" + needle + '"')
 239     
 240     needle_re = re.compile(needle, re.IGNORECASE)
 241     all_pages = page_list()
 242     hits = filter(needle_re.search, all_pages)
 243 
 244     print "<span class='body nav'>"
 245     send_unordered_list([Page(file).link_to() for file in hits])
 246     print "</span>"
 247 
 248     print_search_stats(len(hits), len(all_pages))
 249 
 250 
 251 def print_search_stats(hits, searched):
 252     print "<p>%d hits " % hits
 253     print " out of %d pages searched." % searched
 254 
 255 
 256 def do_edit(pagename):
 257     Page(pagename).send_editor()
 258 
 259 
 260 def do_savepage(pagename):
 261     global form
 262     pg = Page(pagename)
 263     if form.has_key('savetext'):
 264         pg.save_text(form['savetext'].value)
 265     else:
 266         pg.save_text("")
 267     if form.has_key('savemeta'):
 268         pg.save_metadata(form['savemeta'].value)
 269     else:
 270         pg.save_metadata("");
 271     msg = """<i>Thank you for your changes.  Your attention to
 272     detail is appreciated.</i>"""
 273     
 274     pg.send_page(msg=msg)
 275 
 276 
 277 def make_index_key():
 278     s = '<p><center>'
 279     links = map(lambda ch: '<a href="#%s">%s</a>' % (ch, ch),
 280                 string.lowercase)
 281     s = s + string.join(links, ' | ')
 282     s = s + '</center><p>'
 283     return s
 284 
 285 
 286 def page_list():
 287     files = filter(word_anchored_re.match, os.listdir(text_dir))
 288     files.sort()
 289     return files
 290 
 291 
 292 def print_footer(name, editable=True, mod_string=None):
 293     base = get_scriptname()
 294     print '<hr noshade size=1>'
 295     if editable:
 296         print link_tag('?edit='+name, 'EditText')
 297         #if mod_string:
 298             #print "(last modified %s)" % mod_string
 299     print "|", link_tag('BrowseFacets', 'BrowseFacets')
 300     print "|", link_tag('RecentChanges', 'RecentChanges')
 301     print "|", link_tag('FindPage?value='+name, 'FindPage')
 302 
 303 
 304 def group_refinements(refinements):
 305     result = {}
 306     for kv in refinements:
 307         if result.has_key(kv.key):
 308             result[kv.key].append(kv)
 309         else:
 310             result[kv.key] = [kv]
 311     for key in result.keys():
 312         result[key].sort()
 313     return result
 314 
 315 
 316 # raises ValueError on failure
 317 def str_to_pair(str, separator):
 318     i = string.index(str, separator)
 319     return (str[0:i], str[i+len(separator):])
 320     
 321 
 322 
 323 # ----------------------------------------------------------
 324 # Macros
 325 def _macro_TitleSearch():
 326     return _macro_search("titlesearch")
 327 
 328 def _macro_FullSearch():
 329     return _macro_search("fullsearch")
 330 
 331 def _macro_search(type):
 332     if form.has_key('value'):
 333         default = form["value"].value
 334     else:
 335         default = ''
 336     return """<form method=get>
 337     <input name=%s size=30 value="%s"> 
 338     <input type=submit value="Go">
 339     </form>""" % (type, default)
 340 
 341 def _macro_GoTo():
 342     return """<form method=get><input name=goto size=30>
 343     <input type=submit value="Go">
 344     </form>"""
 345     # isindex is deprecated, but it gives the right result here
 346 
 347 def _macro_WordIndex():
 348     s = make_index_key()
 349     pages = list(page_list())
 350     map = {}
 351     word_re = re.compile('[A-Z][a-z]+')
 352     for name in pages:
 353         for word in word_re.findall(name):
 354             try:
 355                 map[word].append(name)
 356             except KeyError:
 357                 map[word] = [name]
 358 
 359     all_words = map.keys()
 360     all_words.sort()
 361     last_letter = None
 362     for word in all_words:
 363         letter = string.lower(word[0])
 364         if letter != last_letter:
 365             s = s + '<a name="%s"></a><h3>%s</h3>\n' % (letter, letter)
 366             last_letter = letter
 367             
 368         s = s + '<b>%s</b><br>\n' % word
 369         links = map[word]
 370         links.sort()
 371         last_page = None
 372         for name in links:
 373             if name == last_page: continue
 374             s = s + '&nbsp;'*5 + Page(name).link_to() + "<br>\n"
 375     return s
 376 
 377 
 378 def _macro_TitleIndex():
 379     s = make_index_key()
 380     pages = list(page_list())
 381     pages.sort()
 382     current_letter = None
 383     for name in pages:
 384         letter = string.lower(name[0])
 385         if letter != current_letter:
 386             s = s + '<a name="%s"></a><h3>%s</h3>\n' % (letter, letter)
 387             current_letter = letter
 388         else:
 389             s = s + '<br>\n'
 390         s = s + Page(name).link_to()
 391     return s
 392 
 393 
 394 def _macro_RecentChanges():
 395     lines = editlog_raw_lines()
 396     lines.reverse()
 397     
 398     ratchet_day = None
 399     done_words = {}
 400     buf = StringIO()
 401     for line in lines:
 402         page_name, addr, ed_time = line.split('\t')
 403         # year, month, day, DoW
 404         time_tuple = localtime(float(ed_time) - 4*60*60)
 405         day = tuple(time_tuple[0:3])
 406 
 407         if done_words.has_key(page_name):
 408             continue
 409 
 410         if day != ratchet_day:
 411             buf.write('</table>\n')
 412             buf.write('\n<h3>%s</h3>\n\n' % strftime(date_fmt, time_tuple))
 413             buf.write('<table class="body nav" width="100%">\n')
 414             ratchet_day = day
 415 
 416         done_words[page_name] = True
 417         buf.write('<tr><td width="35%">')
 418         buf.write(Page(page_name).link_to())
 419         if changed_time_fmt:
 420             buf.write('</td><td>')
 421             buf.write(time.strftime(changed_time_fmt, time_tuple))
 422         buf.write('</td></tr>\n')
 423     buf.write('</table>\n')
 424 
 425     return buf.getvalue()
 426 
 427 
 428 
 429 # ----------------------------------------------------------
 430 class KeyVal:
 431     """A key-value pair.  This class is used to represent the metadata
 432     for individual pages, as well as refinements when browsing."""
 433 
 434     def __init__(self, key, val):
 435         self.key = key
 436         self.val = val
 437         self.count = 0
 438 
 439     def __cmp__(self, other):
 440         key_lower = self.key.lower()
 441         other_key_lower = other.key.lower()
 442         if key_lower < other_key_lower: return -1
 443         if key_lower > other_key_lower: return 1
 444 
 445         if self.count and other.count:
 446             if self.count < other.count: return 1
 447             if self.count > other.count: return -1
 448 
 449         val_lower = self.val.lower()
 450         other_val_lower = other.val.lower()
 451         if val_lower < other_val_lower: return -1
 452         if val_lower > other_val_lower: return 1
 453 
 454         return 0
 455 
 456 
 457     def describe_val(self):
 458         return self.val or "No "+self.key
 459 
 460     def url_piece(self):
 461         return "/%s=%s" % (urllib.quote(self.key), urllib.quote(self.val))
 462 
 463 
 464 # ----------------------------------------------------------
 465 class View:
 466     """A view is the subset of pages that match particular metadata 
 467     key-value pairs.  In fact, it's just the key/value pairs, and the 
 468     pages are handled separately"""
 469 
 470     def __init__(self):
 471         # note that keyvals is a list, not a dictionary.  This is because
 472         # we want to support multi-assignment (e.g. "Subject=foo" and 
 473         # "Subject=bar").  Also, we want to preserve order for breadcrumbs.
 474         self.keyvals = []
 475 
 476 
 477     def copy(self, other_view):
 478         self.keyvals = [keyval for keyval in other_view.keyvals]
 479         return self
 480 
 481     def from_url(self, url):
 482         self.keyvals = []
 483         for chunk in url.split("/"):
 484             try:
 485 		(key, val) = str_to_pair(chunk, "=")
 486                 self.narrow(KeyVal(key, val))
 487             except ValueError, er:
 488                 pass
 489         return self
 490     
 491 
 492     # narrow the view to only pages that have the given key/value pair.
 493     def narrow(self, keyval):
 494         self.keyvals.append(keyval)
 495         return self
 496 
 497     def contains(self, keyval):
 498         return keyval in self.keyvals
 499 
 500 
 501     def pages(self):
 502         all_pages = [Page(page_name) for page_name in page_list()]
 503         return [p for p in all_pages if self.includes(p.metadata())]
 504 
 505 
 506     def breadcrumb(self):
 507         crumbs = [kv.describe_val() for kv in self.keyvals]
 508         return string.join(crumbs, ", ")
 509 
 510 
 511     def includes(self, other_view):
 512         for kv in self.keyvals:
 513             if kv.val == "":
 514                 # if we have no value, then make sure the other view has no 
 515                 # value for this key
 516                 if len([okv for okv in other_view.keyvals if okv.key==kv.key]):
 517                     return False
 518             else:
 519                 # if we do have a value, then make sure it's the same as the 
 520                 # other view's value for this key
 521                 if kv not in other_view.keyvals:
 522                     return False
 523         return True
 524 
 525     def is_empty(self):
 526         return len(self.keyvals) == 0
 527 
 528 
 529     # returns [(String group, String val)]
 530     def refinements(self, pages):
 531         potential_refinements = []
 532         for page in pages:
 533             for kv in page.metadata().keyvals:
 534                 # ignore refinements that are already contained by this view
 535                 if not self.contains(kv):
 536                     already_seen = False
 537                     for ref in potential_refinements:
 538                         if ref == kv:
 539                             already_seen = True
 540                             ref.count = ref.count + 1
 541                     if not already_seen:
 542                         kv.count = 1
 543                         potential_refinements.append(kv)
 544     
 545         # Only include refinements that aren't shared by all pages in the view.
 546         # Otherwise we sometimes get boring, redundant refinements that "don't 
 547         # add any information"
 548         result = []
 549         for refinement in potential_refinements:
 550             restricts_view = False
 551             for page in pages:
 552                 if not page.metadata().contains(refinement):
 553                     restricts_view = True
 554             if restricts_view:
 555                 result.append(refinement)
 556 
 557         return result
 558 
 559 
 560     def link_to(self, title):
 561         url = ""
 562         for kv in self.keyvals:
 563             url += kv.url_piece()
 564         return "<A HREF='%s/BrowseFacets%s'>%s</A>" % \
 565                 (get_scriptname(), url, title)
 566 
 567 
 568     def to_string(self):
 569         result = ""
 570         for kv in self.keyvals:
 571             if len(result): result += ", "
 572             result += kv.key + ":" + kv.val
 573         return result
 574 
 575 
 576 # ----------------------------------------------------------
 577 class PageFormatter:
 578     """Object that turns Wiki markup into HTML.
 579 
 580     All formatting commands can be parsed one line at a time, though
 581     some state is carried over between lines.
 582     """
 583     def __init__(self, raw_body):
 584         self.raw_body = raw_body
 585         self.is_em = self.is_b = 0
 586         self.list_indents = []
 587         self.in_pre = 0
 588 
 589 
 590     def _emph_repl(self, word):
 591         if len(word) == 3:
 592             self.is_b = not self.is_b
 593             return ['</b>', '<b>'][self.is_b]
 594         else:
 595             self.is_em = not self.is_em
 596             return ['</em>', '<em>'][self.is_em]
 597 
 598     def _rule_repl(self, word):
 599         s = self._undent()
 600         width = (len(word) - 3) * 25;
 601         if width < 0: width = 25
 602         if width > 100: width = 100
 603         s = s + "\n<hr noshade size=1 width='%s%%'>\n" % (width)
 604         return s
 605 
 606     def _word_repl(self, word):
 607         return Page(word).link_to()
 608 
 609 
 610     def _url_repl(self, word):
 611         return '<a href="%s">%s</a>' % (word, word)
 612 
 613 
 614     def _email_repl(self, word):
 615         return '<a href="mailto:%s">%s</a>' % (word, word)
 616 
 617 
 618     def _ent_repl(self, s):
 619         return {'&': '&amp;',
 620                 '<': '&lt;',
 621                 '>': '&gt;'}[s]
 622     
 623 
 624     def _li_repl(self, match):
 625         return '<li>'
 626 
 627 
 628     def _pre_repl(self, word):
 629         if word == '{{{' and not self.in_pre:
 630             self.in_pre = True
 631             return '<pre>'
 632         elif self.in_pre:
 633             self.in_pre = False
 634             return '</pre>'
 635         else:
 636             return ''
 637 
 638     def _macro_repl(self, word):
 639         macro_name = word[2:-2]
 640         # TODO: Somehow get the default value into the search field
 641         return apply(globals()['_macro_' + macro_name], ())
 642 
 643 
 644     def _indent_level(self):
 645         return len(self.list_indents) and self.list_indents[-1]
 646 
 647     def _indent_to(self, new_level):
 648         s = ''
 649         while self._indent_level() > new_level:
 650             del(self.list_indents[-1])
 651             s = s + '</ul>\n'
 652         while self._indent_level() < new_level:
 653             self.list_indents.append(new_level)
 654             s = s + '<ul>\n'
 655         return s
 656 
 657     def _undent(self):
 658         res = '</ul>' * len(self.list_indents)
 659         self.list_indents = []
 660         return res
 661 
 662 
 663     def replace(self, match):
 664         for type, hit in match.groupdict().items():
 665             if hit:
 666                 return apply(getattr(self, '_' + type + '_repl'), (hit,))
 667         else:
 668             raise "Can't handle match " + `match`
 669         
 670 
 671     def print_html(self):
 672         # For each line, we scan through looking for magic
 673         # strings, outputting verbatim any intervening text
 674         scan_re = re.compile(
 675             r"(?:(?P<emph>'{2,3})"
 676             + r"|(?P<ent>[<>&])"
 677             + r"|(?P<word>\b(?:[A-Z][a-z]+){2,}\b)"
 678             + r"|(?P<rule>-{4,})"
 679             + r"|(?P<url>(http|ftp|nntp|news|mailto)\:[^\s'\"]+\S)"
 680             + r"|(?P<email>[-\w._+]+\@[\w.-]+)"
 681             + r"|(?P<li>^\s+\*)"
 682             + r"|(?P<pre>(\{\{\{|\}\}\}))"
 683             + r"|(?P<macro>\[\[(TitleSearch|FullSearch|WordIndex"
 684                             + r"|TitleIndex|RecentChanges|GoTo)\]\])"
 685             + r")")
 686         blank_re = re.compile("^\s*$")
 687         bullet_re = re.compile("^\s+\*")
 688         indent_re = re.compile("^\s*")
 689         eol_re = re.compile(r'\r?\n')
 690         raw_body = string.expandtabs(self.raw_body)
 691         for line in eol_re.split(raw_body):
 692             if not self.in_pre:
 693                 # XXX: Should we check these conditions in this order?
 694                 if blank_re.match(line):
 695                     print '<p>'
 696                     continue
 697                 indent = indent_re.match(line)
 698                 print self._indent_to(len(indent.group(0)))
 699             print re.sub(scan_re, self.replace, line)
 700         if self.in_pre: print '</pre>'
 701         print self._undent()
 702         
 703 
 704 # ----------------------------------------------------------
 705 class Page:
 706     def __init__(self, page_name):
 707         self.page_name = page_name
 708 
 709     def split_title(self):
 710         # look for the end of words and the start of a new word,
 711         # and insert a space there
 712         return re.sub('([a-z])([A-Z])', r'\1 \2', self.page_name)
 713 
 714 
 715     def _body_filename(self):
 716         return path.join(text_dir, self.page_name)
 717 
 718     def _metadata_filename(self):
 719         return path.join(text_dir, self.page_name + ".meta")
 720 
 721 
 722     def _tmp_filename(self):
 723         return path.join(text_dir, ('#' + self.page_name + '.' + `os.getpid()` + '#'))
 724 
 725 
 726     def exists(self):
 727         try:
 728             os.stat(self._body_filename())
 729             return True
 730         except OSError, er:
 731             if er.errno == errno.ENOENT:
 732                 return False
 733             else:
 734                 raise er
 735         
 736 
 737     def link_to(self):
 738         word = self.page_name
 739         if self.exists():
 740             return link_tag(word)
 741         else:
 742             return word + link_tag(word, '*', 'nonexistent')
 743 
 744 
 745     def raw_metadata(self):
 746         try:
 747             metatext = open(self._metadata_filename(), 'rt').read()
 748 	    return metatext or 'Enter meta data here.'
 749         except IOError, er:
 750             if er.errno == errno.ENOENT:
 751                 # just doesn't exist, use default
 752                 return 'Enter meta data here.'
 753             else:
 754                 raise er
 755 
 756     def metadata(self):
 757         metatext = self.raw_metadata()
 758         metatext = string.replace(metatext, "\r\n", "\n")
 759         view = View()
 760         for line in metatext.splitlines():
 761             try:
 762                 (key, val) = str_to_pair(line, ":")
 763                 view.narrow(KeyVal(key.strip(), val.strip()))
 764             except ValueError, er:
 765                 # ignore invalid metatext lines
 766                 pass
 767         # add automatic metadata
 768         return view
 769 
 770 
 771     def get_body(self):
 772         try:
 773             return open(self._body_filename(), 'rt').read()
 774         except IOError, er:
 775             if er.errno == errno.ENOENT:
 776                 # just doesn't exist, use default
 777                 return 'Describe %s here.' % self.page_name
 778             else:
 779                 raise er
 780     
 781 
 782     def send_page(self, msg=None):
 783         link = get_scriptname() + '?fullsearch=' + self.page_name
 784         send_title(self.split_title(), link, msg)
 785         PageFormatter(self.get_body()).print_html()
 786 
 787         # kbkb -- move this to a method somewhere
 788         if not self.metadata().is_empty():
 789             print "<p><table class='body metabox'>\n"
 790             
 791         for kv in self.metadata().keyvals:
 792             print "<tr>"
 793             print "<td class='metakey'>%s: </td> " % (kv.key)
 794             val_link = View().narrow(kv).link_to(kv.val)
 795             print "<td class='metaval'> %s</td>" % (val_link)
 796             print "</tr>\n"
 797 
 798         if not self.metadata().is_empty():
 799             print "</table>\n"
 800 
 801         print_footer(self.page_name, True, self._last_modified())
 802 
 803 
 804     def _last_modified(self):
 805         if not self.exists():
 806             return None
 807         modtime = localtime(os.stat(self._body_filename())[stat.ST_MTIME])
 808         return strftime(datetime_fmt, modtime)
 809 
 810 
 811     def send_editor(self):
 812         send_title('Edit ' + self.split_title())
 813         print '<form method="post" action="%s">' % (get_scriptname())
 814         print '<input type=hidden name="savepage" value="%s">' % (self.page_name)
 815         body = string.replace(self.get_body(), '\r\n', '\n')
 816         meta = string.replace(self.raw_metadata(), '\r\n', '\n')
 817         print """<textarea wrap="virtual" name="savetext" rows="17"
 818                  cols="80">%s</textarea><br>""" % body
 819         print """<textarea wrap="virtual" name="savemeta" rows="4"
 820                  cols="80">%s</textarea>""" % meta
 821         print """<br><input type=submit value="Save">
 822                  <input type=reset value="Reset">
 823                  """
 824         print "</form>"
 825         print "<p>" + Page('EditingTips').link_to()
 826                  
 827 
 828     def _write_file(self, text, filename):
 829         tmp_filename = self._tmp_filename()
 830         open(tmp_filename, 'wt').write(text)
 831         if os.name == 'nt':
 832             # Bad Bill!  POSIX rename ought to replace. :-(
 833             try:
 834                 os.remove(filename)
 835             except OSError, er:
 836                 if er.errno != errno.ENOENT: raise er
 837         os.rename(tmp_filename, filename)
 838 
 839 
 840     def save_text(self, newtext):
 841         self._write_file(newtext, self._body_filename())
 842         remote_name = environ.get('REMOTE_ADDR', '')
 843         editlog_add(self.page_name, remote_name)
 844         
 845     def save_metadata(self, newmetatext):
 846         self._write_file(newmetatext, self._metadata_filename())
 847         remote_name = environ.get('REMOTE_ADDR', '')
 848         editlog_add(self.page_name, remote_name)
 849         
 850 
 851 emit_header()
 852 
 853 # Configurable parts ------------------------------------------
 854 data_dir = '/users/web/kimbly/web/piki-data/'
 855 text_dir = path.join(data_dir, 'text')
 856 editlog_name = path.join(data_dir, 'editlog')
 857 cgi.logfile = path.join(data_dir, 'cgi_log')
 858 logo_string = """<img style="vertical-align: middle" 
 859                       src="/piki-data/diamond.jpg" 
 860                       border=0 
 861                       alt="diamond logo">"""
 862 changed_time_fmt = '%I:%M&nbsp;%P'
 863 date_fmt = '%A, %d %B %Y'
 864 datetime_fmt = '%a %d %b %Y %I:%M %p'
 865 css_url = '/piki-data/piki.css'         # stylesheet link, or ''
 866 
 867 try:
 868     form = cgi.FieldStorage()
 869 
 870     handlers = { 'fullsearch':  do_fullsearch,
 871                  'titlesearch': do_titlesearch,
 872                  'edit':        do_edit,
 873                  'savepage':    do_savepage }
 874 
 875     for cmd in handlers.keys():
 876         if form.has_key(cmd):
 877             apply(handlers[cmd], (form[cmd].value,))
 878             break
 879     else:
 880         path_info = environ.get('PATH_INFO', '')
 881 
 882         if form.has_key('goto'):
 883             query = form['goto'].value
 884         elif len(path_info) and path_info[0] == '/':
 885             query = path_info[1:] or 'FrontPage'
 886         else:       
 887             query = environ.get('QUERY_STRING', '') or 'FrontPage'
 888 
 889         word_match = re.match(word_re_str, query)
 890         if path_info.startswith("/BrowseFacets/") or path_info=="/BrowseFacets":
 891             do_browse(View().from_url(path_info[13:]))
 892         elif word_match:
 893             word = word_match.group(0)
 894             Page(word).send_page()
 895         else:
 896             print "<p>Can't work out query \"<pre>" + query + "</pre>\""
 897 
 898 except:
 899     cgi.print_exception()
 900 
 901 sys.stdout.flush()

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.
  • [get | view] (2005-06-09 12:47:46, 26.5 KB) [[attachment:diamond.py]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.