1.1 --- a/ItemSupport.py Wed Oct 30 15:38:24 2013 +0100
1.2 +++ b/ItemSupport.py Wed Nov 06 00:24:17 2013 +0100
1.3 @@ -6,11 +6,95 @@
1.4 @license: GNU GPL (v2 or later), see COPYING.txt for details.
1.5 """
1.6
1.7 +from MoinMoin.Page import RootPage
1.8 from MoinMoin.util import lock
1.9 import os
1.10
1.11 # Content storage support.
1.12
1.13 +class ItemStoreBase:
1.14 +
1.15 + "Common item store functionality."
1.16 +
1.17 + def __init__(self, lock_dir):
1.18 +
1.19 + "Initialise an item store with the given 'lock_dir' guarding access."
1.20 +
1.21 + self.lock_dir = lock_dir
1.22 + self.writelock = lock.WriteLock(lock_dir)
1.23 + self.readlock = lock.ReadLock(lock_dir)
1.24 +
1.25 + def deduce_next(self):
1.26 +
1.27 + "Deduce the next item number from the existing item files."
1.28 +
1.29 + return max(self.get_keys() or [-1]) + 1
1.30 +
1.31 + # High-level methods.
1.32 +
1.33 + def __len__(self):
1.34 +
1.35 + """
1.36 + Return the number of items.
1.37 + """
1.38 +
1.39 + return len(self.keys())
1.40 +
1.41 + def __iter__(self):
1.42 +
1.43 + "Return an iterator over the items in the store."
1.44 +
1.45 + return ItemIterator(self)
1.46 +
1.47 + def keys(self):
1.48 +
1.49 + "Return a list of keys for items in the store."
1.50 +
1.51 + self.readlock.acquire()
1.52 + try:
1.53 + return self.get_keys()
1.54 + finally:
1.55 + self.readlock.release()
1.56 +
1.57 + def __getitem__(self, number):
1.58 +
1.59 + "Return the item with the given 'number'."
1.60 +
1.61 + self.readlock.acquire()
1.62 + try:
1.63 + try:
1.64 + return self.read_item(number)
1.65 + except (IOError, OSError):
1.66 + raise IndexError, number
1.67 + finally:
1.68 + self.readlock.release()
1.69 +
1.70 + def __delitem__(self, number):
1.71 +
1.72 + "Remove the item with the given 'number' from the store."
1.73 +
1.74 + self.writelock.acquire()
1.75 + try:
1.76 + try:
1.77 + self.remove_item(number)
1.78 + except (IOError, OSError):
1.79 + raise IndexError, number
1.80 + finally:
1.81 + self.writelock.release()
1.82 +
1.83 + def next(self):
1.84 +
1.85 + """
1.86 + Return the number of the next item (which should also be the number of
1.87 + items if none have been deleted).
1.88 + """
1.89 +
1.90 + self.writelock.acquire()
1.91 + try:
1.92 + return self.get_next()
1.93 + finally:
1.94 + self.writelock.release()
1.95 +
1.96 class ItemDirectoryStore:
1.97
1.98 "A directory-based item store."
1.99 @@ -47,12 +131,6 @@
1.100
1.101 return [int(filename) for filename in os.listdir(self.path) if filename.isdigit()]
1.102
1.103 - def deduce_next(self):
1.104 -
1.105 - "Deduce the next item number from the existing item files."
1.106 -
1.107 - return max(self.get_keys() or [-1]) + 1
1.108 -
1.109 def read_next(self):
1.110
1.111 "Read the next item number from a special file."
1.112 @@ -131,69 +209,6 @@
1.113 finally:
1.114 self.writelock.release()
1.115
1.116 - def __len__(self):
1.117 -
1.118 - """
1.119 - Return the number of items.
1.120 - """
1.121 -
1.122 - return len(self.keys())
1.123 -
1.124 - def __iter__(self):
1.125 -
1.126 - "Return an iterator over the items in the store."
1.127 -
1.128 - return ItemIterator(self)
1.129 -
1.130 - def keys(self):
1.131 -
1.132 - "Return a list of keys for items in the store."
1.133 -
1.134 - self.readlock.acquire()
1.135 - try:
1.136 - return self.get_keys()
1.137 - finally:
1.138 - self.readlock.release()
1.139 -
1.140 - def __getitem__(self, number):
1.141 -
1.142 - "Return the item with the given 'number'."
1.143 -
1.144 - self.readlock.acquire()
1.145 - try:
1.146 - try:
1.147 - return self.read_item(number)
1.148 - except (IOError, OSError):
1.149 - raise IndexError, number
1.150 - finally:
1.151 - self.readlock.release()
1.152 -
1.153 - def __delitem__(self, number):
1.154 -
1.155 - "Remove the item with the given 'number' from the store."
1.156 -
1.157 - self.writelock.acquire()
1.158 - try:
1.159 - try:
1.160 - self.remove_item(number)
1.161 - except (IOError, OSError):
1.162 - raise IndexError, number
1.163 - finally:
1.164 - self.writelock.release()
1.165 -
1.166 - def next(self):
1.167 -
1.168 - """
1.169 - Return the number of the next item (which should also be the number of
1.170 - items if none have been deleted).
1.171 - """
1.172 -
1.173 - self.writelock.acquire()
1.174 - try:
1.175 - return self.get_next()
1.176 - finally:
1.177 - self.writelock.release()
1.178 -
1.179 class ItemIterator:
1.180
1.181 "An iterator over items in a store."
2.1 --- a/MoinSupport.py Wed Oct 30 15:38:24 2013 +0100
2.2 +++ b/MoinSupport.py Wed Nov 06 00:24:17 2013 +0100
2.3 @@ -13,9 +13,10 @@
2.4 """
2.5
2.6 from DateSupport import *
2.7 -from ItemSupport import ItemDirectoryStore
2.8 +from ItemSupport import ItemDirectoryStore, ItemStoreBase
2.9 from MoinMoin.parser import text_moin_wiki
2.10 from MoinMoin.Page import Page
2.11 +from MoinMoin.PageEditor import PageEditor
2.12 from MoinMoin.util import lock
2.13 from MoinMoin import config, search, wikiutil
2.14 from shlex import shlex
2.15 @@ -1092,18 +1093,18 @@
2.16
2.17 # Content storage support.
2.18
2.19 -class ItemStore(ItemDirectoryStore):
2.20 +class ItemStore:
2.21
2.22 "A page-specific item store."
2.23
2.24 - def __init__(self, page, item_dir="items", lock_dir="item_locks"):
2.25 + def __init__(self, page, item_dir="items", lock_dir="item_locks", store=None):
2.26
2.27 "Initialise an item store for the given 'page'."
2.28
2.29 item_dir_path = tuple(item_dir.split("/"))
2.30 lock_dir_path = tuple(lock_dir.split("/"))
2.31 - ItemDirectoryStore.__init__(self, page.getPagePath(*item_dir_path), page.getPagePath(*lock_dir_path))
2.32 self.page = page
2.33 + self.store = store or ItemDirectoryStore(page.getPagePath(*item_dir_path), page.getPagePath(*lock_dir_path))
2.34
2.35 def can_write(self):
2.36
2.37 @@ -1135,8 +1136,22 @@
2.38 user = self.page.request.user
2.39 return user and user.may.delete(self.page.page_name)
2.40
2.41 + # Store-specific methods.
2.42 +
2.43 + def mtime(self):
2.44 + return self.store.mtime()
2.45 +
2.46 # High-level methods.
2.47
2.48 + def keys(self):
2.49 +
2.50 + "Return a list of keys for items in the store."
2.51 +
2.52 + if not self.can_read():
2.53 + return 0
2.54 +
2.55 + return self.store.keys()
2.56 +
2.57 def append(self, item):
2.58
2.59 "Append the given 'item' to the store."
2.60 @@ -1144,7 +1159,7 @@
2.61 if not self.can_write():
2.62 return
2.63
2.64 - ItemDirectoryStore.append(self, item)
2.65 + self.store.append(item)
2.66
2.67 def __len__(self):
2.68
2.69 @@ -1153,7 +1168,7 @@
2.70 if not self.can_read():
2.71 return 0
2.72
2.73 - return ItemDirectoryStore.__len__(self)
2.74 + return len(self.store)
2.75
2.76 def __getitem__(self, number):
2.77
2.78 @@ -1162,7 +1177,7 @@
2.79 if not self.can_read():
2.80 raise IndexError, number
2.81
2.82 - return ItemDirectoryStore.__getitem__(self, number)
2.83 + return self.store.__getitem__(number)
2.84
2.85 def __delitem__(self, number):
2.86
2.87 @@ -1171,6 +1186,105 @@
2.88 if not self.can_delete():
2.89 return
2.90
2.91 - return ItemDirectoryStore.__delitem__(self, number)
2.92 + return self.store.__delitem__(number)
2.93 +
2.94 + def __iter__(self):
2.95 + return self.store.__iter__()
2.96 +
2.97 + def next(self):
2.98 + return self.store.next()
2.99 +
2.100 +class ItemSubpageStore(ItemStoreBase):
2.101 +
2.102 + "A subpage-based item store."
2.103 +
2.104 + def __init__(self, page, lock_dir="item_locks"):
2.105 +
2.106 + "Initialise an item store for subpages under the given 'page'."
2.107 +
2.108 + lock_dir_path = tuple(lock_dir.split("/"))
2.109 + ItemStoreBase.__init__(self, page.getPagePath(*lock_dir_path))
2.110 + self.page = page
2.111 +
2.112 + def mtime(self):
2.113 +
2.114 + "Return the last modified time of the item store."
2.115 +
2.116 + keys = self.get_keys()
2.117 + if not keys:
2.118 + page = self.page
2.119 + else:
2.120 + page = Page(self.page.request, self.get_item_path(max(keys)))
2.121 +
2.122 + return wikiutil.version2timestamp(
2.123 + getMetadata(page)["last-modified"]
2.124 + )
2.125 +
2.126 + def get_next(self):
2.127 +
2.128 + "Return the next item number."
2.129 +
2.130 + return self.deduce_next()
2.131 +
2.132 + def get_keys(self):
2.133 +
2.134 + "Return the item keys."
2.135 +
2.136 + is_subpage = re.compile(u"^%s/" % re.escape(self.page.page_name), re.UNICODE).match
2.137 +
2.138 + # Collect the strict subpages of the parent page.
2.139 +
2.140 + leafnames = []
2.141 + parentname = self.page.page_name
2.142 +
2.143 + for pagename in RootPage(self.page.request).getPageList(filter=is_subpage):
2.144 + parts = pagename[len(parentname)+1:].split("/")
2.145 +
2.146 + # Only collect numbered pages immediately below the parent.
2.147 +
2.148 + if len(parts) == 1 and parts[0].isdigit():
2.149 + leafnames.append(int(parts[0]))
2.150 +
2.151 + return leafnames
2.152 +
2.153 + def write_item(self, item, next):
2.154 +
2.155 + "Write the given 'item' to a file with the given 'next' item number."
2.156 +
2.157 + page = PageEditor(self.page.request, self.get_item_path(next))
2.158 + page.saveText(item, 0)
2.159 +
2.160 + def read_item(self, number):
2.161 +
2.162 + "Read the item with the given item 'number'."
2.163 +
2.164 + page = Page(self.page.request, self.get_item_path(number))
2.165 + return page.get_raw_body()
2.166 +
2.167 + def remove_item(self, number):
2.168 +
2.169 + "Remove the item with the given item 'number'."
2.170 +
2.171 + page = PageEditor(self.page.request, self.get_item_path(number))
2.172 + page.deletePage()
2.173 +
2.174 + def get_item_path(self, number):
2.175 +
2.176 + "Get the path for the given item 'number'."
2.177 +
2.178 + return "%s/%s" % (self.page.page_name, number)
2.179 +
2.180 + # High-level methods.
2.181 +
2.182 + def append(self, item):
2.183 +
2.184 + "Append the given 'item' to the store."
2.185 +
2.186 + self.writelock.acquire()
2.187 + try:
2.188 + next = self.get_next()
2.189 + self.write_item(item, next)
2.190 + finally:
2.191 + self.writelock.release()
2.192
2.193 # vim: tabstop=4 expandtab shiftwidth=4