1 """
2 MoinMoin - Page class
3
4 Copyright (c) 2000, 2001, 2002 by Jürgen Hermann <jh@web.de>
5 All rights reserved, see COPYING for details.
6
7 $Id: FabianFranz_2fPage_2epy,v 1.2 2002/06/20 01:43:05 linuxwiki Exp $
8 """
9
10
11 import cStringIO, os, re, sys, string, urllib
12 from MoinMoin import caching, config, user, util, wikiutil, webapi
13 from MoinMoin.i18n import _
14
15
16
17
18
19 class Page:
20 """ An immutable wiki page.
21
22 To change a page's content, use the PageEditor class.
23 """
24
25 _SPLIT_RE = re.compile('([%s])([%s])' % (config.lowerletters, config.upperletters))
26
27
28 def __init__(self, page_name, **keywords):
29 """ Create page object.
30
31 Note that this is a 'lean' operation, since the text for the page
32 is loaded on demand. Thus, things like `Page(name).link_to()` are
33 efficient.
34
35 **page_name** -- WikiName of the page
36 **keywords** --
37 date: date of older revision
38 formatter: formatter instance
39 """
40 self.page_name = page_name
41 self.prev_date = keywords.get('date')
42 self.raw_body = None
43
44 if keywords.has_key('formatter'):
45 self.formatter = keywords.get('formatter')
46 self.default_formatter = 0
47 else:
48 self.default_formatter = 1
49
50
51 def split_title(self, force=0):
52 """ Return a string with the page name split by spaces, if
53 the user wants that.
54 """
55 if not force and not user.current.wikiname_add_spaces: return self.page_name
56
57
58
59 return self._SPLIT_RE.sub(r'\1 \2', self.page_name)
60
61
62 def _split_title_py16(self, force=0):
63 """ Return a string with the page name split by spaces, if
64 the user wants that.
65 """
66 if not force and not user.current.wikiname_add_spaces: return self.page_name
67
68
69
70
71 temp = [self.page_name[0]]
72 ucase = string.uppercase + config.upperletters[3:]
73 for letter in self.page_name[1:]:
74 if letter in ucase:
75 temp.append(' ')
76 temp.append(letter)
77 return string.join(temp,'')
78
79
80 def _text_filename(self):
81 """The name of the page file, possibly of an older page"""
82 if self.prev_date:
83 return os.path.join(config.backup_dir, wikiutil.quoteFilename(self.page_name) + "." + self.prev_date)
84 else:
85 return os.path.join(config.text_dir, wikiutil.quoteFilename(self.page_name))
86
87
88 def _tmp_filename(self):
89 """The name of the temporary file used while saving"""
90 return os.path.join(config.text_dir, ('#' + wikiutil.quoteFilename(self.page_name) + '.' + `os.getpid()` + '#'))
91
92
93 def exists(self):
94 """True if the page exists"""
95 return os.path.exists(self._text_filename())
96
97
98 def size(self):
99 """Return page size"""
100 if self.raw_body is not None:
101 return len(self.raw_body)
102
103 return os.path.getsize(self._text_filename())
104
105
106 def url(self, querystr=None):
107 """ Return an URL for this page.
108 """
109 url = "%s/%s" % (webapi.getScriptname(), wikiutil.quoteWikiname(self.page_name))
110 if querystr: url = "%s?%s" % (url, string.replace(querystr, '&', '&'))
111 return url
112
113
114 def link_to(self, text=None, querystr=None, anchor=None):
115 """Return HTML markup that links to this page"""
116 text = text or self.split_title()
117 fmt = getattr(self, 'formatter', None)
118 url = wikiutil.quoteWikiname(self.page_name)
119 if querystr: url = "%s?%s" % (url, string.replace(querystr, '&', '&'))
120 if anchor: url = "%s#%s" % (url, urllib.quote_plus(anchor))
121 if self.exists():
122 return wikiutil.link_tag(url, text, formatter=fmt)
123 elif user.current.show_nonexist_qm:
124 return wikiutil.link_tag(url,
125 '?', 'nonexistent', formatter=fmt) + text
126 else:
127 return wikiutil.link_tag(url, text, 'nonexistent', formatter=fmt)
128
129
130 def get_raw_body(self):
131 """Load the raw markup from the page file"""
132 if self.raw_body is not None:
133 return self.raw_body
134
135
136 try:
137 file = open(self._text_filename(), 'rb')
138 except IOError, er:
139 import errno
140 if er.errno == errno.ENOENT:
141
142
143
144 return ""
145 else:
146 raise er
147
148
149 try:
150 result = file.read()
151 finally:
152 file.close()
153
154 return result
155
156
157 def send_page(self, request, msg=None, **keywords):
158 """ Send the formatted page to stdout.
159
160 **form** -- CGI-Form
161 **msg** -- if given, display message in header area
162 **keywords** --
163 content_only: 1 to omit page header and footer
164 count_hit: add an event to the log
165 """
166 request.clock.start('send_page')
167 import cgi
168 form = request.form
169
170
171 print_mode = form.has_key('action') and form['action'].value == 'print'
172 content_only = keywords.get('content_only', 0)
173 self.hilite_re = keywords.get('hilite_re', None)
174 if msg is None: msg = ""
175
176
177 if keywords.get('count_hit', 0):
178 request.getEventLogger().add('VIEWPAGE', {'pagename': self.page_name})
179
180
181 body = self.get_raw_body()
182
183
184 if self.default_formatter:
185 from formatter.text_html import Formatter
186 self.formatter = Formatter(request, store_pagelinks=1)
187 self.formatter.setPage(self)
188
189
190 pi_format = config.default_markup or "wiki"
191 pi_redirect = None
192 pi_formtext = []
193 pi_formfields = []
194 wikiform = None
195
196
197 if body and body[:5] == '<?xml':
198 pi_format = "xslt"
199
200
201 while body and body[0] == '#':
202
203 try:
204 line, body = string.split(body, '\n', 1)
205 except ValueError:
206 line = body
207 body = ''
208
209
210 if line[1] == '#': continue
211
212
213 verb, args = string.split(line[1:]+' ', ' ', 1)
214 verb = string.lower(verb)
215 args = string.strip(args)
216
217
218 if verb == "format":
219
220 pi_format = string.lower(args)
221 elif verb == "redirect":
222
223
224
225
226 pi_redirect = args
227 if form.has_key('action') or form.has_key('redirect') or content_only: continue
228
229 webapi.http_redirect(request, '%s/%s?action=show&redirect=%s' % (
230 webapi.getScriptname(),
231 wikiutil.quoteWikiname(pi_redirect),
232 urllib.quote_plus(self.page_name, ''),))
233 return
234 elif verb == "deprecated":
235
236
237 msg = '%s<b>%s</b><br>%s' % (
238 wikiutil.getSmiley('/!\\', self.formatter),
239 _('The backupped content of this page is deprecated and will not be included in search results!'),
240 msg)
241
242 oldversions = wikiutil.getBackupList(config.backup_dir, self.page_name)
243 if oldversions:
244 oldfile = oldversions[0]
245 olddate = os.path.basename(oldfile)[len(wikiutil.quoteFilename(self.page_name))+1:]
246 oldpage = Page(self.page_name, date=olddate)
247 body = body + oldpage.get_raw_body()
248 del oldfile
249 del olddate
250 del oldpage
251 elif verb == "pragma":
252
253 try:
254 key, val = string.split(args, ' ', 1)
255 except (ValueError, TypeError):
256 pass
257 else:
258 request.setPragma(key, val)
259 elif verb == "form":
260
261 if not wikiutil.isFormPage(self.page_name):
262 continue
263
264
265 if not wikiform:
266 from MoinMoin import wikiform
267 pi_formtext.append('<table border="1" cellspacing="1" cellpadding="3">\n'
268 '<form method="POST" action="%s">\n'
269 '<input type="hidden" name="action" value="formtest">\n' % self.url())
270 pi_formtext.append(wikiform.parseDefinition(args, pi_formfields))
271 else:
272
273 break
274
275
276 doc_leader = self.formatter.startDocument(self.page_name)
277 if not content_only:
278
279 webapi.http_headers(request)
280 sys.stdout.write(doc_leader)
281
282
283 if self.default_formatter:
284 page_needle = self.page_name
285 if config.allow_subpages and string.count(page_needle, '/'):
286 page_needle = '/' + string.split(page_needle, '/')[-1]
287 link = '%s/%s?action=fullsearch&value=%s&literal=1&case=1' % (
288 webapi.getScriptname(),
289 wikiutil.quoteWikiname(self.page_name),
290 urllib.quote_plus(page_needle, ''))
291 title = self.split_title()
292 if self.prev_date:
293 msg = "<b>%s</b><br>%s" % (
294 _('Version as of %(date)s') % {'date':
295 user.current.getFormattedDateTime(os.path.getmtime(self._text_filename()))},
296 msg)
297 if form.has_key('redirect'):
298 redir = form['redirect'].value
299 msg = '%s<b>%s</b><br>%s' % (
300 wikiutil.getSmiley('/!\\', self.formatter),
301 _('Redirected from page "%(page)s"') % {'page':
302 wikiutil.link_tag(wikiutil.quoteWikiname(redir) + "?action=show", redir)},
303 msg)
304 if pi_redirect:
305 msg = '%s<b>%s</b><br>%s' % (
306 wikiutil.getSmiley('<!>', self.formatter),
307 _('This page redirects to page "%(page)s"') % {'page': pi_redirect},
308 msg)
309 wikiutil.send_title(title, link=link, msg=msg,
310 pagename=self.page_name, print_mode=print_mode,
311 allow_doubleclick=1)
312
313
314 if not print_mode and user.current.valid:
315 user.current.addTrail(self.page_name)
316 trail = user.current.getTrail()
317 if trail and user.current.show_page_trail:
318 delim = '>'
319 if string.lower(config.charset) == 'iso-8859-1':
320 delim = '»'
321 print '<font face="Verdana" size="-1">%s %s %s</font><hr>' % (
322 string.join(
323 map(lambda p: Page(p).link_to(), trail[:-1]),
324 " %s " % delim),
325 delim, cgi.escape(trail[-1]))
326
327
328 if pi_formtext:
329 pi_formtext.append('<input type="hidden" name="fieldlist" value="%s">\n' %
330 string.join(pi_formfields, "|"))
331 pi_formtext.append('</form></table>\n')
332 pi_formtext.append(_(
333 '<p><small>If you submit this form, the submitted values'
334 ' will be displayed.\nTo use this form on other pages, insert a\n'
335 '<br><br><b><tt> '
336 '[[Form("%(pagename)s")]]'
337 '</tt></b><br><br>\n'
338 'macro call.</b></small></p>\n'
339 ) % {'pagename': self.page_name[:-len(config.page_form_ending)]})
340 print string.join(pi_formtext, '')
341
342
343 Parser = util.importName("MoinMoin.parser." + pi_format, "Parser")
344 if Parser is None:
345
346 del Parser
347 from parser.plain import Parser
348
349
350 if not self.exists() and self.default_formatter and not content_only:
351
352 print wikiutil.link_tag(wikiutil.quoteWikiname(self.page_name)+'?action=edit',
353 _("Create this page"))
354
355
356 templates = filter(lambda page, u = wikiutil: u.isTemplatePage(page),
357 wikiutil.getPageList(config.text_dir))
358 if templates:
359 print self.formatter.paragraph(1)
360 print self.formatter.text(_('Alternatively, use one of these templates:'))
361 print self.formatter.paragraph(0)
362
363
364 print self.formatter.bullet_list(1)
365 for page in templates:
366 print self.formatter.listitem(1)
367 print wikiutil.link_tag("%s?action=edit&template=%s" % (
368 wikiutil.quoteWikiname(self.page_name),
369 wikiutil.quoteWikiname(page)),
370 page)
371 print self.formatter.listitem(0)
372 print self.formatter.bullet_list(0)
373
374 print self.formatter.paragraph(1)
375 print self.formatter.text(_('To create you own templates, ' +
376 'add a page with a name ending in Template.'))
377 print self.formatter.paragraph(0)
378 else:
379
380 print Parser(body, request).format(self.formatter, form, 0)
381
382
383 if getattr(request, 'footnotes', None):
384 from MoinMoin.macro.FootNote import emit_footnotes
385 print self.formatter.linebreak(0)
386
387 print emit_footnotes(request, self.formatter, 1, Parser, form)
388
389
390 doc_trailer = self.formatter.endDocument()
391 if not content_only:
392
393 if self.default_formatter and not print_mode:
394 wikiutil.send_footer(request, self.page_name, self._last_modified(),
395 print_mode=print_mode)
396
397 sys.stdout.write(doc_trailer)
398
399
400 if self.default_formatter and self.exists():
401 arena = "pagelinks"
402 key = wikiutil.quoteFilename(self.page_name)
403 cache = caching.CacheEntry(arena, key)
404 if cache.needsUpdate(self._text_filename()):
405 links = self.formatter.pagelinks
406 links.sort()
407 cache.update(string.join(links, '\n'))
408
409 request.clock.stop('send_page')
410
411
412 def _last_modified(self):
413 if not self.exists():
414 return None
415 return user.current.getFormattedDateTime(os.path.getmtime(self._text_filename()))
416
417
418 def getPageLinks(self, request):
419 """Get a list of the links on this page"""
420 if not self.exists(): return []
421
422 arena = "pagelinks"
423 key = wikiutil.quoteFilename(self.page_name)
424 cache = caching.CacheEntry(arena, key)
425 if cache.needsUpdate(self._text_filename()):
426
427
428
429 stdout = sys.stdout
430 sys.stdout = cStringIO.StringIO()
431 try:
432 try:
433 Page(self.page_name).send_page(request, content_only=1)
434 except:
435 cache.update('')
436 finally:
437 sys.stdout = stdout
438 if hasattr(request, '_fmt_hd_counters'):
439 del request._fmt_hd_counters
440
441 return filter(None, string.split(cache.content(), '\n'))
442
443
444
445 if sys.version[:3] == "1.6":
446 Page.split_title = Page._split_title_py16