Attachment 'page-refactor.patch'
Download 1 * looking for arch@arch.thinkmo.de--2003-archives/moin--main--1.3--patch-460 to compare with
2 * comparing to arch@arch.thinkmo.de--2003-archives/moin--main--1.3--patch-460
3 M MoinMoin/logfile/eventlog.py
4 M MoinMoin/logfile/editlog.py
5 M MoinMoin/server/standalone.py
6 M MoinMoin/theme/__init__.py
7 M MoinMoin/Page.py
8 M MoinMoin/PageEditor.py
9 M MoinMoin/action/AttachFile.py
10 M MoinMoin/caching.py
11 M MoinMoin/request.py
12 M MoinMoin/wikiutil.py
13
14 * modified files
15
16 --- orig/MoinMoin/Page.py
17 +++ mod/MoinMoin/Page.py
18 @@ -7,7 +7,7 @@
19 """
20
21 import StringIO, os, re, urllib, os.path, random, codecs
22 -from MoinMoin import config, caching, user, util, wikiutil
23 +from MoinMoin import config, caching, user, util, wikiutil, error
24 from MoinMoin.logfile import eventlog
25 from MoinMoin.util import filesys, web
26
27 @@ -20,11 +20,20 @@
28 header_re = re.compile(r'(^#+.*(?:\n\s*)+)+', re.UNICODE | re.MULTILINE)
29
30
31 -class Page:
32 +# Page factory - return pages from request._pages dict
33 +def Page(request, page_name, **kw):
34 + # Note - ignore kw on page creation, its broken design
35 + ## return _Page(request, page_name)
36 + if not page_name in request._pages:
37 + page = _Page(request, page_name)
38 + request._pages[page_name] = page
39 + return request._pages[page_name]
40 +
41 +class _Page:
42 """Page - Manage an (immutable) page associated with a WikiName.
43 To change a page's content, use the PageEditor class.
44 """
45 -
46 +
47 def __init__(self, request, page_name, **keywords):
48 """
49 Create page object.
50 @@ -45,13 +54,29 @@
51 self._raw_body_modified = 0
52 self.hilite_re = None
53 self.language = None
54 -
55 +
56 if keywords.has_key('formatter'):
57 self.formatter = keywords.get('formatter')
58 self.default_formatter = 0
59 else:
60 self.default_formatter = 1
61
62 + # Page info is filled with data as data is requested
63 + self._urlName = None
64 + self._storageName = None
65 + self._pageinfo = None
66 +
67 + ## request.log('Created page %s' % repr(page_name))
68 +
69 + def storageName(self):
70 + if self._storageName is None:
71 + self._storageName = wikiutil.quoteWikinameFS(self.page_name)
72 + return self._storageName
73 +
74 + def urlName(self):
75 + if self._urlName is None:
76 + self._urlName = wikiutil.quoteWikinameURL(self.page_name)
77 + return self._urlName
78
79 def split_title(self, request, force=0):
80 """
81 @@ -74,37 +99,9 @@
82 splitted = SPLIT_RE.sub(r'\1 \2', self.page_name)
83 return splitted
84
85 - def get_rev(self, pagedir=None, rev=0):
86 - """
87 - get a revision of the page in this path
88 - @param pagedir: the path to the pagedir
89 - @param rev: int revision to get (default is 0 and means the current
90 - revision (in this case, the real revint is returned)
91 - @return: (str pagefilename, int realrevint, bool exists)
92 - """
93 - if pagedir is None:
94 - pagedir = self.getPagePath(check_create=0)
95 -
96 - if rev == 0:
97 - revfilename = os.path.join(pagedir, 'current')
98 - try:
99 - revfile = open(revfilename)
100 - revstr = revfile.read().strip()
101 - revfile.close()
102 - except:
103 - revstr = '99999999' # XXX
104 - rev = int(revstr)
105 - else:
106 - revstr = '%08d' % rev
107 -
108 - pagefile = os.path.join(pagedir, 'revisions', revstr)
109 - exists = os.path.exists(pagefile)
110 - return pagefile, rev, exists
111 -
112 def current_rev(self):
113 - pagefile, rev, exists = self.get_rev()
114 - return rev
115 -
116 + return self.getInfo().get('revision')
117 +
118 def get_real_rev(self):
119 """Returns the real revision number of this page. A rev=0 is
120 translated to the current revision.
121 @@ -116,85 +113,199 @@
122 return self.current_rev()
123 return self.rev
124
125 - def getPagePath(self, *args, **kw):
126 - """
127 - Get full path to a page-specific storage area. `args` can
128 - contain additional path components that are added to the base path.
129 + def hasRevision(self, revision):
130 + """ Does page has revision number? """
131 + info = self.getInfo()
132 + current = info['revision']
133 + if revision == current:
134 + return info.get('textfile') is not None
135 + elif revision < current:
136 + filename = '%08d' % revision
137 + path = os.path.join(info['path'], 'revisions', filename)
138 + return os.path.exists(path)
139
140 - @param args: additional path components
141 - @keyword force_pagedir: force using a specific pagedir, default 'auto'
142 - 'auto' = automatically choose page dir
143 - 'underlay' = use underlay page dir
144 - 'standard' = use standard page dir
145 - @keyword check_create: if true, ensures that the path requested really exists
146 - (if it doesn't, create all directories automatically).
147 - (default true)
148 - @keyword isfile: is the last component in args a filename? (default is false)
149 - @rtype: string
150 - @return: the full path to the storage area
151 - """
152 - force_pagedir = kw.get('force_pagedir', 'auto')
153 - check_create = kw.get('check_create', 1)
154 - qpagename = wikiutil.quoteWikinameFS(self.page_name)
155 - data_dir = self.cfg.data_dir
156 - # underlay is used to store system and help pages in a separate place
157 - underlay_dir = self.cfg.data_underlay_dir
158 - if underlay_dir:
159 - underlaypath = os.path.join(underlay_dir, "pages", qpagename)
160 - else:
161 - force_pagedir = 'standard'
162 + # No such revision
163 + return False
164
165 - # self is a NORMAL page
166 - if not self is self.request.rootpage:
167 - kw['check_create'] = 0 # we don't want to create empty page dirs:
168 - path = self.request.rootpage.getPagePath("pages", qpagename, **kw)
169 - if force_pagedir == 'auto':
170 - pagefile, rev, exists = self.get_rev(path)
171 - if not exists:
172 - pagefile, rev, exists = self.get_rev(underlaypath)
173 - if exists:
174 - path = underlaypath
175 - elif force_pagedir == 'underlay':
176 - path = underlaypath
177 - # no need to check 'standard' case, we just use path in that case!
178 + def getRevision(self, revision):
179 + """ Return page text as of revision, or None """
180 + if self.hasRevision(revision):
181 + info = self.getInfo()
182 + textfile = info.get('textfile')
183 + if textfile is None:
184 + textfile = '%08d' % revision
185 + textfile = os.path.join(info['path'], 'revisions', textfile)
186 + return self.readText(textfile)
187 + # No such revision or revision file missing
188 + return None
189 +
190 + def readTextFile(self, path):
191 + """ Read text file at path """
192 + try:
193 + file = codecs.open(path, 'rb', config.charset)
194 + except IOError, err:
195 + import errno
196 + if er.errno == errno.ENOENT:
197 + # just doesn't exist, return empty text (note that we
198 + # never store empty pages, so this is detectable and also
199 + # safe when passed to a function expecting a string)
200 + return ""
201 + else:
202 + msg = '''
203 +Could not read text file in %(path)s because of unexpected error: %(class)s: %(error)s
204 +
205 +Make sure page directories have correct ownership and permissions.
206 +''' % {'path': path, 'class': err.__class__.__name__, 'error': str(err),}
207 + raise error.InternalError(msg)
208 +
209 + # read file content and make sure it is closed properly
210 + try:
211 + text = file.read()
212 + text = self.decodeTextMimeType(text)
213 + finally:
214 + file.close()
215 +
216 + return text
217
218 - # self is rootpage
219 - else:
220 - # our current rootpage is not a toplevel, but under another page
221 - if self.page_name:
222 - # this assumes flat storage of pages and sub pages on same level
223 - path = os.path.join(data_dir, "pages", qpagename)
224 - if force_pagedir == 'auto':
225 - pagefile, rev, exists = self.get_rev(path)
226 - if not exists:
227 - pagefile, rev, exists = self.get_rev(underlaypath)
228 - if exists:
229 - path = underlaypath
230 - elif force_pagedir == 'underlay':
231 - path = underlaypath
232 - # no need to check 'standard' case, we just use path in that case!
233 -
234 - # our current rootpage is THE virtual rootpage, really at top of all
235 + def getInfo(self):
236 + """ Return a dictionary with page information """
237 + if self._pageinfo is None:
238 + if self.page_name == u'':
239 + # The root page of the wiki, create virtual info
240 + info = {'path': self.cfg.data_dir,}
241 else:
242 - # this is the location of the virtual root page
243 - path = data_dir
244 - # 'auto' doesn't make sense here. maybe not even 'underlay':
245 - if force_pagedir == 'underlay':
246 - path = underlay_dir
247 - # no need to check 'standard' case, we just use path in that case!
248 + # Check where page live. First try at the data dir
249 + name = self.storageName()
250 + info = self.getInfoForDomain(domain='standard',
251 + name=name)
252 + if not info.get('textfile'):
253 + # Try the underlay directory
254 + info = self.getInfoForDomain(domain='underlay',
255 + name=name)
256 + self._pageinfo = info
257 +
258 + return self._pageinfo
259
260 - fullpath = os.path.join(*((path,) + args))
261 + def getInfoForDomain(self, domain, name):
262 + """ Return information at specific domain """
263 + if domain == 'standard':
264 + dir = self.cfg.data_dir
265 + elif domain == 'underlay' and self.cfg.data_underlay_dir:
266 + dir = self.cfg.data_underlay_dir
267 + else:
268 + return {}
269 +
270 + info = {}
271 + # Check if page directory exists
272 + path = os.path.join(dir, 'pages', name)
273 + if os.path.exists(path):
274 + info['domain'] = domain
275 + info['path'] = path
276 + # Try to get the current revision
277 + current = os.path.join(path, 'current')
278 + try:
279 + f = file(current)
280 + revision = f.read().strip()
281 + f.close()
282 + info['revision'] = int(revision)
283 + # Check if revision exists, or its a deleted page
284 + textfile = os.path.join(path, 'revisions', revision)
285 + if os.path.exists(textfile):
286 + info['textfile'] = textfile
287 + except:
288 + # Page is deleted or broken
289 + pass
290 +
291 + return info
292 +
293 + def _makeDirectory(self, path):
294 + """ Wraper around mkdir that raise fatal errors
295 +
296 + This method should be used only by Page, as its immutable.
297 +
298 + @param path: path to create
299 + """
300 + try:
301 + os.mkdir(path, 0777 & config.umask)
302 + except (IOError, OSError), err:
303 + # If the directory already exists, ignore the error
304 + if not os.path.isdir(path):
305 + msg = '''
306 +Could not create page sub directory at %(path)s because of %(class)s:
307 +%(error)s.
308 +
309 +Try to set permissions on the wiki directory again. The directory and
310 +all its subdirectories and files should be readable, writable and
311 +executable by the web server user and group.
312 +''' % {'path': childpath, 'class': err.__class__.__name__, 'error': str(err),}
313 +
314 + raise error.InternalError(msg)
315 +
316 + def _getChildPath(self, child, repair=0):
317 + """ The central point to get page childern paths
318 +
319 + This method should not be used out of Page. Use the convinience
320 + methods: getAttachmentPath, getCachePath etc.
321
322 - if check_create:
323 - if kw.get('isfile', 0):
324 - dirname, filename = os.path.split(fullpath)
325 + @param child: one of page children: pages, revisions,
326 + attachments etc (string).
327 + @param repair: if true, create missing directories
328 + @rtype: unicode
329 + @return: path to child or None if there is not page dir
330 +
331 + """
332 + info = self.getInfo()
333 + # Return none for non existing page directory
334 + pagepath = info.get('path')
335 + if not pagepath:
336 + return None
337 +
338 + # Get value for child key on first call
339 + if not info.has_key(child):
340 + # Check if child exists
341 + childpath = os.path.join(pagepath, child)
342 + if os.path.exists(childpath):
343 + info[child] = childpath
344 + elif repair:
345 + # Repair missing directories
346 + self._makeDirectory(childpath)
347 + info[child] = childpath
348 else:
349 - dirname = fullpath
350 - if not os.path.exists(dirname):
351 - filesys.makeDirs(dirname)
352 + info[child] = None
353 +
354 + return info[child]
355 +
356 + # Get methods ------------------------------------------------------
357 +
358 + # Public get methods that hides the internal structure of the
359 + # page. The caller does not have to know the names of the children.
360 +
361 + # TODO: all these methods are wrong. Clients should not know the
362 + # page path at all. They should get objects, for example,
363 + # getEventLogPath should be called getEventLog and return the page
364 + # event log object. This will require larger refactoring.
365 +
366 + def getEditLockPath(self):
367 + return self._getChildPath('edit-lock')
368 +
369 + def getEventLogPath(self):
370 + return self._getChildPath('event-log')
371 +
372 + def getEditLogPath(self):
373 + return self._getChildPath('edit-log')
374 +
375 + def getAttachmentPath(self, repair=0):
376 + return self._getChildPath('attachments', repair=repair)
377 +
378 + def getPagesPath(self, repair=0):
379 + return self._getChildPath('pages', repair=repair)
380
381 - return fullpath
382 + def getCachePath(self, repair=1):
383 + return self._getChildPath('cache', repair=repair)
384
385 + def getTempPath(self, repair=1):
386 + return self._getChildPath('temp', repair=repair)
387 +
388 def _text_filename(self, **kw):
389 """
390 The name of the page file, possibly of an older page.
391 @@ -205,12 +316,8 @@
392 """
393 if hasattr(self, '_text_filename_force'):
394 return self._text_filename_force
395 - rev = kw.get('rev', 0)
396 - if not rev and self.rev:
397 - rev = self.rev
398 - fname, rev, exists = self.get_rev(None, rev)
399 - return fname
400 -
401 + return self.getInfo().get('textfile')
402 +
403 def _tmp_filename(self):
404 """
405 The name of the temporary file used while saving.
406 @@ -218,20 +325,27 @@
407 @rtype: string
408 @return: temporary filename (complete path + filename)
409 """
410 - rnd = random.randint(0,1000000000)
411 - tmpname = os.path.join(self.cfg.data_dir, ('#%s.%d#' % (wikiutil.quoteWikinameFS(self.page_name), rnd)))
412 - return tmpname
413 + tempdir = self.getTempPath()
414 + if not tempdir:
415 + return None
416 +
417 + name = str(long(time.time() * (10**6)))
418 + name = os.path.join(tempdir, name)
419 + return name
420
421 # XXX TODO clean up the mess, rewrite _last_edited, last_edit, lastEditInfo for new logs,
422 # XXX TODO do not use mtime() calls any more
423 def _last_edited(self, request):
424 from MoinMoin.logfile import editlog
425 + log = None
426 try:
427 - logfile = editlog.EditLog(request, self.getPagePath('edit-log', check_create=0, isfile=1))
428 - logfile.to_end()
429 - log = logfile.previous()
430 + logpath = self.getEditLogPath()
431 + if logpath is not None:
432 + logfile = editlog.EditLog(request, logpath)
433 + logfile.to_end()
434 + log = logfile.previous()
435 except StopIteration:
436 - log = None
437 + pass
438 return log
439
440 def last_edit(self, request):
441 @@ -301,38 +415,34 @@
442 @rtype: bool
443 @return: true, if this page is writable or does not exist
444 """
445 - return os.access(self._text_filename(), os.W_OK) or not self.exists()
446 + info = self.getInfo()
447 + if info.get('textfile'):
448 + # If we have a text file, check if its writeable
449 + return os.access(info['textfile'], os.W_OK)
450 + # We have not text file, so we can write new one
451 + return True
452
453 - def isUnderlayPage(self, includeDeleted=True):
454 + def isUnderlayPage(self):
455 """ Does this page live in the underlay dir?
456
457 - Return true even if the data dir has a copy of this page. To
458 - check for underlay only page, use ifUnderlayPage() and not
459 - isStandardPage()
460 -
461 - @param includeDeleted: include deleted pages
462 @rtype: bool
463 @return: true if page lives in the underlay dir
464 """
465 - return self.exists(domain='underlay', includeDeleted=includeDeleted)
466 -
467 - def isStandardPage(self, includeDeleted=True):
468 + return self.getInfo().get('domain') == 'underlay'
469 +
470 + def isStandardPage(self):
471 """ Does this page live in the data dir?
472
473 - Return true even if this is a copy of an underlay page. To check
474 - for data only page, use isStandardPage() and not isUnderlayPage().
475 -
476 - @param includeDeleted: include deleted pages
477 @rtype: bool
478 @return: true if page lives in the data dir
479 """
480 - return self.exists(domain='standard', includeDeleted=includeDeleted)
481 -
482 - def exists(self, rev=0, domain=None, includeDeleted=False):
483 + return self.getInfo().get('domain') == 'standard'
484 +
485 + def exists(self, domain=None, includeDeleted=False):
486 """ Does this page exist?
487
488 This is the lower level method for checking page existence. Use
489 - the higher level methods isUnderlayPagea and isStandardPage for
490 + the convinience methods isUnderlayPage and isStandardPage for
491 cleaner code.
492
493 @param rev: revision to look for. Default check current
494 @@ -346,26 +456,18 @@
495 if domain == 'underlay' and not self.request.cfg.data_underlay_dir:
496 return False
497
498 + # Get info for page, either using the default domain search
499 + # method, or for specific domain.
500 + if domain == None:
501 + info = self.getInfo()
502 + else:
503 + info = self.getInfoForDomain(domain)
504 +
505 if includeDeleted:
506 - # Look for page directory, ignore page state
507 - if domain is None:
508 - domains = ['underlay', 'standard']
509 - else:
510 - domains = [domain]
511 - for domain in domains:
512 - pagedir = self.getPagePath(force_pagedir=domain, check_create=0)
513 - if os.path.exists(pagedir):
514 - return True
515 - return False
516 + return info.get('path') is not None
517 else:
518 - # Look for non-deleted pages only, using get_rev
519 - if not rev and self.rev:
520 - rev = self.rev
521 - if domain is not None:
522 - domain = self.getPagePath(force_pagedir=domain, check_create=0)
523 - d, d, exists = self.get_rev(domain, rev)
524 - return exists
525 -
526 + return info.get('textfile') is not None
527 +
528 def size(self, rev=0):
529 """ Get Page size.
530
531 @@ -376,12 +478,15 @@
532 if self._raw_body is not None:
533 return len(self._raw_body)
534
535 - try:
536 - return os.path.getsize(self._text_filename(rev=rev))
537 - except EnvironmentError, e:
538 - import errno
539 - if e.errno == errno.ENOENT: return 0
540 - raise
541 + textfile = self.getInfo().get('textfile')
542 + if textfile is not None:
543 + try:
544 + return os.path.getsize(self._text_filename(rev=rev))
545 + except EnvironmentError, e:
546 + import errno
547 + if e.errno == errno.ENOENT: return 0
548 + raise
549 + return 0
550
551 def mtime_usecs(self):
552 """
553 @@ -451,7 +556,7 @@
554 rootpage = self.request.rootpage
555
556 # Get pages in pages directory
557 - dir = rootpage.getPagePath('pages')
558 + dir = rootpage.getPagesPath()
559 pages = self.listPages(dir, user)
560
561 # Merge with underlay pages
562 @@ -492,26 +597,10 @@
563 @return: raw page contents of this page
564 """
565 if self._raw_body is None:
566 - # try to open file
567 - try:
568 - file = codecs.open(self._text_filename(), 'rb', config.charset)
569 - except IOError, er:
570 - import errno
571 - if er.errno == errno.ENOENT:
572 - # just doesn't exist, return empty text (note that we
573 - # never store empty pages, so this is detectable and also
574 - # safe when passed to a function expecting a string)
575 - return ""
576 - else:
577 - raise er
578 -
579 - # read file content and make sure it is closed properly
580 - try:
581 - text = file.read()
582 - text = self.decodeTextMimeType(text)
583 + textfile = self.getInfo().get('textfile')
584 + if textfile:
585 + text = self.readTextFile(textfile)
586 self.set_raw_body(text)
587 - finally:
588 - file.close()
589
590 return self._raw_body
591
592 @@ -527,7 +616,7 @@
593 """
594 self._raw_body = body
595 self._raw_body_modified = modified
596 -
597 +
598 def url(self, request, querystr=None, escape=1):
599 """ Return complete URL for this page, including scriptname
600
601 @@ -539,8 +628,7 @@
602 @rtype: str
603 @return: complete url of this page, including scriptname
604 """
605 - url = '%s/%s' % (request.getScriptname(),
606 - wikiutil.quoteWikinameURL(self.page_name))
607 + url = '%s/%s' % (request.getScriptname(), self.urlName())
608
609 if querystr:
610 querystr = web.makeQueryString(querystr)
611 @@ -577,7 +665,7 @@
612 text = self.split_title(request)
613
614 # Create url, excluding scriptname
615 - url = wikiutil.quoteWikinameURL(self.page_name)
616 + url = self.urlName()
617 if querystr:
618 querystr = web.makeQueryString(querystr)
619 # makeQueryString does not escape any more
620 @@ -690,9 +778,10 @@
621 # count hit?
622 if keywords.get('count_hit', 0):
623 eventlog.EventLog(request).add(request, 'VIEWPAGE', {'pagename': self.page_name})
624 -
625 + request.clock.start('load_page_body')
626 # load the text
627 body = self.get_raw_body()
628 + request.clock.stop('load_page_body')
629
630 # if necessary, load the default formatter
631 if self.default_formatter:
632 @@ -701,7 +790,8 @@
633 self.formatter.setPage(self)
634 if self.hilite_re: self.formatter.set_highlight_re(self.hilite_re)
635 request.formatter = self.formatter
636 -
637 +
638 + request.clock.start('process_pi')
639 # default is wiki markup
640 pi_format = self.cfg.default_markup or "wiki"
641 pi_formatargs = ''
642 @@ -826,6 +916,8 @@
643
644 # Save values for later use
645 self.pi_format = pi_format
646 + request.clock.stop('process_pi')
647 +
648
649 # start document output
650 doc_leader = self.formatter.startDocument(self.page_name)
651 @@ -854,7 +946,7 @@
652
653 link = '%s/%s?action=fullsearch&value=%s&literal=1&case=1&context=180' % (
654 request.getScriptname(),
655 - wikiutil.quoteWikinameURL(self.page_name),
656 + self.urlName(),
657 urllib.quote_plus(page_needle.encode(config.charset), ''))
658 title = self.split_title(request)
659 if self.rev:
660 @@ -922,7 +1014,10 @@
661 request.write("<strong>%s</strong><br>" % _("You are not allowed to view this page."))
662 else:
663 # parse the text and send the page content
664 - self.send_page_content(request, Parser, body, format_args=pi_formatargs, do_cache=do_cache)
665 + request.clock.start('send_page_content')
666 + self.send_page_content(request, Parser, body,
667 + format_args=pi_formatargs, do_cache=do_cache)
668 + request.clock.stop('send_page_content')
669
670 # check for pending footnotes
671 if getattr(request, 'footnotes', None):
672 @@ -1001,7 +1096,6 @@
673 @param body: text of the wiki page
674 @param needsupdate: if 1, force update of the cached compiled page
675 """
676 - request.clock.start('send_page_content')
677 formatter_name = self.getFormatterName()
678
679 # if we should not or can not use caching
680 @@ -1018,8 +1112,7 @@
681 cache = caching.CacheEntry(request, arena, key)
682 code = None
683
684 - if cache.needsUpdate(self._text_filename(),
685 - self.getPagePath('attachments', check_create=0)):
686 + if cache.needsUpdate(self._text_filename(), self.getAttachmentPath()):
687 needsupdate = 1
688
689 # load cache
690 @@ -1094,14 +1187,12 @@
691 self.cache_mtime = cache.mtime()
692
693 # TODO: move this into theme (currently used only by classic)
694 - qpage = wikiutil.quoteWikinameURL(self.page_name)
695 + qpage = self.urlName()
696 url = "%s?action=refresh&arena=Page.py&key=%s" % (qpage, key)
697 link = wikiutil.link_tag(request, url, _("RefreshCache", formatted=False))
698 date = self.request.user.getFormattedDateTime(cache.mtime())
699 fragment = link + ' ' + _('(cached %s)') % date
700 self.request.add2footer('RefreshCache', fragment)
701 -
702 - request.clock.stop('send_page_content')
703
704 def _emptyPageText(self, request):
705 """
706 @@ -1125,17 +1216,20 @@
707 @return: page revisions
708 """
709 revisions = []
710 - if self.page_name:
711 - rev_dir = self.getPagePath('revisions', check_create=0)
712 - if os.path.isdir(rev_dir):
713 - for rev in os.listdir(rev_dir):
714 + info = self.getInfo()
715 + path = info.get('path')
716 + if path:
717 + path = os.path.join(path, 'revisions')
718 + if os.path.isdir(path):
719 + for name in os.listdir(path):
720 try:
721 - revint = int(rev)
722 - revisions.append(revint)
723 + number = int(name)
724 + revisions.append(number)
725 except ValueError:
726 pass
727 revisions.sort()
728 revisions.reverse()
729 +
730 return revisions
731
732
733 @@ -1261,9 +1355,9 @@
734 import wikiacl
735 return wikiacl.AccessControlList(request)
736 # mtime check for forked long running processes
737 - fn = self._text_filename()
738 + fn = self.getInfo().get('textfile')
739 acl = None
740 - if os.path.exists(fn):
741 + if fn is not None:
742 mtime = os.path.getmtime(fn)
743 else:
744 mtime = 0
745 @@ -1325,19 +1419,17 @@
746 if name.startswith('.') or name.startswith('#') or name == 'CVS':
747 continue
748
749 - # Filter deleted pages
750 - pagedir = os.path.join(dir, name)
751 - d, d, exists = self.get_rev(pagedir)
752 - if not exists:
753 - continue
754 -
755 # Unquote - from this point name is Unicode
756 name = wikiutil.unquoteWikiname(name)
757 -
758 +
759 # Filter meta-pages like editor backups
760 if name.endswith(u'/MoinEditorBackup'):
761 continue
762 -
763 +
764 + # Filter deleted pages
765 + if not Page(self.request, name).exists():
766 + continue
767 +
768 # Filter out page user may not read
769 if user and not user.may.read(name):
770 continue
771
772
773 --- orig/MoinMoin/PageEditor.py
774 +++ mod/MoinMoin/PageEditor.py
775 @@ -8,7 +8,7 @@
776
777 import os, time, codecs
778 from MoinMoin import caching, config, user, util, wikiutil, error
779 -from MoinMoin.Page import Page
780 +from MoinMoin.Page import Page, _Page
781 from MoinMoin.widget import html
782 from MoinMoin.widget.dialog import Status
783 from MoinMoin.logfile import editlog, eventlog
784 @@ -78,7 +78,7 @@
785 #############################################################################
786 ### PageEditor - Edit pages
787 #############################################################################
788 -class PageEditor(Page):
789 +class PageEditor(_Page):
790 """Editor for a wiki page."""
791
792 # exceptions for .saveText()
793 @@ -112,7 +112,7 @@
794 self._ = request.getText
795 self.cfg = request.cfg
796
797 - Page.__init__(self, request, page_name, **keywords)
798 + _Page.__init__(self, request, page_name, **keywords)
799
800 self.do_revision_backup = keywords.get('do_revision_backup', 1)
801 self.do_editor_backup = keywords.get('do_editor_backup', 1)
802 @@ -311,7 +311,7 @@
803 # send form
804 self.request.write('<form id="editor" method="post" action="%s/%s#preview">' % (
805 self.request.getScriptname(),
806 - wikiutil.quoteWikinameURL(self.page_name),
807 + self.urlName() ,
808 ))
809
810 # yet another weird workaround for broken IE6 (it expands the text
811 @@ -709,9 +709,11 @@
812 """
813 Copy a page from underlay directory to page directory
814 """
815 - src = self.getPagePath(force_pagedir='underlay', check_create=0)
816 - dst = self.getPagePath(force_pagedir='standard', check_create=0)
817 - if src and dst and src != dst and os.path.exists(src):
818 + info = self.getInfo()
819 + if info.get('domain') == 'underlay':
820 + src = info('path')
821 + dst = os.path.join(self.request.cfg.data_dir, 'pages',
822 + self.getStorageName())
823 try:
824 os.rmdir(dst) # simply remove empty dst dirs
825 # XXX in fact, we should better remove anything we regard as an
826 @@ -721,20 +723,23 @@
827 pass
828 if not os.path.exists(dst):
829 filesys.copytree(src, dst)
830 + # Invalidate page info, it will be re created when needed.
831 + self._pageinfo = None
832
833 def _get_pragmas(self, text):
834 pragmas = {}
835 - for line in text.split('\n'):
836 - if not line or line[0] != '#':
837 - # end of pragmas
838 - break
839 -
840 - if len(line) > 1 and line[1] == '#':
841 - # a comment within pragmas
842 - continue
843 -
844 - verb, args = (line[1:]+' ').split(' ', 1)
845 - pragmas[verb.lower()] = args.strip()
846 + if text is not None:
847 + for line in text.split('\n'):
848 + if not line or line[0] != '#':
849 + # end of pragmas
850 + break
851 +
852 + if len(line) > 1 and line[1] == '#':
853 + # a comment within pragmas
854 + continue
855 +
856 + verb, args = (line[1:]+' ').split(' ', 1)
857 + pragmas[verb.lower()] = args.strip()
858
859 return pragmas
860
861 @@ -749,8 +754,7 @@
862 was_deprecated = self._get_pragmas(self.get_raw_body()).has_key("deprecated")
863
864 self.copypage()
865 -
866 - pagedir = self.getPagePath(check_create=0)
867 + pagedir = os.path.join(self.cfg.data_dir, 'pages', self.getStorageName())
868 revdir = os.path.join(pagedir, 'revisions')
869 cfn = os.path.join(pagedir,'current')
870 clfn = os.path.join(pagedir,'current-locked')
871 @@ -1032,7 +1036,7 @@
872
873 def _filename(self):
874 """get path and filename for edit-lock file"""
875 - return self.pageobj.getPagePath('edit-lock', isfile=1)
876 + return self.pageobj.getEditLockPath()
877
878
879 def _readLockFile(self):
880 @@ -1043,30 +1047,35 @@
881 self.timestamp = 0
882
883 if self.locktype:
884 - try:
885 - entry = editlog.EditLog(self.request, filename=self._filename()).next()
886 - except StopIteration:
887 - entry = None
888 -
889 - if entry:
890 - self.owner = entry.userid or entry.addr
891 - self.owner_html = entry.getEditor(self.request)
892 - self.timestamp = wikiutil.version2timestamp(entry.ed_time_usecs)
893 -
894 + filename = self._filename()
895 + if filename is not None:
896 + try:
897 + entry = editlog.EditLog(self.request, filename=self._filename()).next()
898 + except StopIteration:
899 + entry = None
900 +
901 + if entry:
902 + self.owner = entry.userid or entry.addr
903 + self.owner_html = entry.getEditor(self.request)
904 + self.timestamp = wikiutil.version2timestamp(entry.ed_time_usecs)
905
906 def _writeLockFile(self):
907 """Write new lock file."""
908 self._deleteLockFile()
909 - try:
910 - editlog.EditLog(self.request, filename=self._filename()).add(
911 - self.request, wikiutil.timestamp2version(self.now), 0, "LOCK", self.page_name)
912 - except IOError:
913 - pass
914 + filename = self._filename()
915 + if filename is not None:
916 + try:
917 + editlog.EditLog(self.request, filename=self._filename()).add(
918 + self.request, wikiutil.timestamp2version(self.now), 0, "LOCK", self.page_name)
919 + except IOError:
920 + pass
921
922 def _deleteLockFile(self):
923 """Delete the lock file unconditionally."""
924 - try:
925 - os.remove(self._filename())
926 - except OSError:
927 - pass
928 + filename = self._filename()
929 + if filename is not None:
930 + try:
931 + os.remove(filename)
932 + except OSError:
933 + pass
934
935
936
937 --- orig/MoinMoin/action/AttachFile.py
938 +++ mod/MoinMoin/action/AttachFile.py
939 @@ -58,7 +58,7 @@
940 filesys.makeDirs(attach_dir)
941 else:
942 # send file via CGI, from page storage area
943 - attach_dir = Page(request, pagename).getPagePath("attachments", check_create=create)
944 + attach_dir = Page(request, pagename).getAttachmentPath(repair=create)
945
946 return attach_dir
947
948 @@ -97,7 +97,8 @@
949 """
950 _ = request.getText
951 attach_dir = getAttachDir(request, pagename)
952 - if not os.path.exists(attach_dir): return ''
953 + if attach_dir is None:
954 + return ''
955
956 files = os.listdir(attach_dir)
957 if not files: return ''
958 @@ -122,7 +123,7 @@
959
960 attach_dir = getAttachDir(request, pagename)
961 files = []
962 - if os.path.isdir(attach_dir):
963 + if attach_dir and os.path.isdir(attach_dir):
964 files = os.listdir(attach_dir)
965 page = Page(request, pagename)
966 # TODO: remove escape=0 in 1.4
967 @@ -247,7 +248,7 @@
968
969 def _get_files(request, pagename):
970 attach_dir = getAttachDir(request, pagename)
971 - if os.path.isdir(attach_dir):
972 + if attach_dir and os.path.isdir(attach_dir):
973 files = map(lambda a: a.decode(config.charset), os.listdir(attach_dir))
974 files.sort()
975 return files
976
977
978 --- orig/MoinMoin/caching.py
979 +++ mod/MoinMoin/caching.py
980 @@ -28,7 +28,7 @@
981 os.chmod(self.arena_dir, 0777 & config.umask)
982 else: # arena is in fact a page object
983 cache_dir = None
984 - self.arena_dir = arena.getPagePath('cache', check_create=1)
985 + self.arena_dir = arena.getCachePath()
986 self.key = key
987
988 def _filename(self):
989
990
991 --- orig/MoinMoin/logfile/editlog.py
992 +++ mod/MoinMoin/logfile/editlog.py
993 @@ -75,9 +75,9 @@
994 if filename == None:
995 rootpagename = kw.get('rootpagename', None)
996 if rootpagename:
997 - filename = Page(request, rootpagename).getPagePath('edit-log', isfile=1)
998 + filename = Page(request, rootpagename).getEditLogPath()
999 else:
1000 - filename = request.rootpage.getPagePath('edit-log', isfile=1)
1001 + filename = request.rootpage.getEditLogPath()
1002 LogFile.__init__(self, filename, buffer_size)
1003 self._NUM_FIELDS = 9
1004 self._usercache = {}
1005
1006
1007 --- orig/MoinMoin/logfile/eventlog.py
1008 +++ mod/MoinMoin/logfile/eventlog.py
1009 @@ -15,9 +15,9 @@
1010 rootpagename = kw.get('rootpagename', None)
1011 if rootpagename:
1012 from MoinMoin.Page import Page
1013 - filename = Page(request, rootpagename).getPagePath('event-log', isfile=1)
1014 + filename = Page(request, rootpagename).getEventLogPath()
1015 else:
1016 - filename = request.rootpage.getPagePath('event-log', isfile=1)
1017 + filename = request.rootpage.getEventLogPath()
1018 LogFile.__init__(self, filename, buffer_size)
1019
1020 def add(self, request, eventtype, values=None, add_http_info=1, mtime_usecs=None):
1021
1022
1023 --- orig/MoinMoin/request.py
1024 +++ mod/MoinMoin/request.py
1025 @@ -67,6 +67,7 @@
1026 self._known_actions = None
1027 self.sent_headers = 0
1028 self.user_headers = []
1029 + self._pages = {}
1030
1031 # check for some asses trying to use us as a proxy:
1032 self.forbidden = False
1033 @@ -969,7 +970,7 @@
1034 # Set Pragma for http 1.0 caches
1035 # See http://www.cse.ohio-state.edu/cgi-bin/rfc/rfc2068.html#sec-14.32
1036 self.setHttpHeader('Pragma: no-cache')
1037 -
1038 +
1039 def setCookie(self):
1040 """ Set cookie for the current user
1041
1042 @@ -1056,6 +1057,7 @@
1043 del self.user
1044 del self.theme
1045 del self.dicts
1046 + del self._pages
1047 except:
1048 pass
1049
1050
1051
1052 --- orig/MoinMoin/server/standalone.py
1053 +++ mod/MoinMoin/server/standalone.py
1054 @@ -148,12 +148,14 @@
1055 if config.memoryProfile:
1056 config.memoryProfile.addRequest()
1057 try:
1058 - req = RequestStandAlone(self)
1059 +
1060 if config.hotshotProfile and moin_requests_done > 0:
1061 # Don't profile the first request, its not interesting
1062 # for long running process, and its very expensive.
1063 + req = config.hotshotProfile.runcall(RequestStandAlone, self)
1064 config.hotshotProfile.runcall(req.run)
1065 else:
1066 + req = RequestStandAlone(self)
1067 req.run()
1068 except socket.error, err:
1069 # Ignore certain errors
1070
1071
1072 --- orig/MoinMoin/theme/__init__.py
1073 +++ mod/MoinMoin/theme/__init__.py
1074 @@ -287,6 +287,7 @@
1075 @return: navibar html
1076 """
1077 request = self.request
1078 + request.clock.start('navibar')
1079 found = {} # pages we found. prevent duplicates
1080 items = [] # navibar items
1081 item = u'<li class="%s">%s</li>'
1082 @@ -329,6 +330,7 @@
1083 %s
1084 </ul>
1085 ''' % items
1086 + request.clock.stop('navibar')
1087 return html
1088
1089 def get_icon(self, icon):
1090 @@ -929,6 +931,7 @@
1091
1092 # Make new edit bar
1093 request = self.request
1094 + request.clock.start('theme_editbar')
1095 _ = self.request.getText
1096 link = wikiutil.link_tag
1097 quotedname = wikiutil.quoteWikinameURL(page.page_name)
1098 @@ -963,6 +966,7 @@
1099
1100 # cache for next call
1101 self._cache[cacheKey] = html
1102 + request.clock.stop('theme_editbar')
1103 return html
1104
1105 def startPage(self):
1106
1107
1108 --- orig/MoinMoin/wikiutil.py
1109 +++ mod/MoinMoin/wikiutil.py
1110 @@ -926,6 +926,7 @@
1111 @keyword body_attr: additional <body> attributes
1112 @keyword body_onload: additional "onload" JavaScript code
1113 """
1114 + request.clock.start('send_title')
1115 from MoinMoin.Page import Page
1116 _ = request.getText
1117 pagename = keywords.get('pagename', '')
1118 @@ -1121,13 +1122,15 @@
1119 request.themedict = d
1120
1121 # now call the theming code to do the rendering
1122 + request.clock.start('theme_header')
1123 output.append(theme.header(d))
1124 + request.clock.stop('theme_header')
1125
1126 # emit it
1127 request.write(''.join(output))
1128 output = []
1129 request.flush()
1130 -
1131 + request.clock.stop('send_title')
1132
1133 def send_footer(request, pagename, **keywords):
1134 """
1135 @@ -1139,6 +1142,7 @@
1136 @keyword showpage: true, when link back to page is wanted (default: false)
1137 @keyword print_mode: true, when page is displayed in Print mode
1138 """
1139 + request.clock.start('send_footer')
1140 d = request.themedict
1141 theme = request.theme
1142
1143 @@ -1150,6 +1154,7 @@
1144 # This is used only by classic now, kill soon
1145 d['footer_fragments'] = request._footer_fragments
1146 request.write(theme.footer(d, **keywords))
1147 + request.clock.stop('send_footer')
1148
1149
1150 ########################################################################
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.You are not allowed to attach a file to this page.