index 2e1bd9b8fa5c399f5d1d8b215e14d89cd0dd59a7..dfacf2f48465a0aa76ab3800d683ff81e8194cb5 100644 (file)
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#
-#$Id: back_anydbm.py,v 1.97 2003-01-15 22:17:19 kedder Exp $
+#$Id: back_anydbm.py,v 1.106 2003-02-26 23:42:50 richard Exp $
'''
This module defines a backend that saves the hyperdatabase in a database
chosen by anydbm. It is guaranteed to always be available in python
import whichdb, anydbm, os, marshal, re, weakref, string, copy
from roundup import hyperdb, date, password, roundupdb, security
from blobfiles import FileStorage
-from sessions import Sessions
+from sessions import Sessions, OneTimeKeys
from roundup.indexer import Indexer
from roundup.backends import locking
from roundup.hyperdb import String, Password, Date, Interval, Link, \
- Multilink, DatabaseError, Boolean, Number
+ Multilink, DatabaseError, Boolean, Number, Node
#
# Now the database
self.transactions = []
self.indexer = Indexer(self.dir)
self.sessions = Sessions(self.config)
+ self.otks = OneTimeKeys(self.config)
self.security = security.Security(self)
# ensure files are group readable and writable
os.umask(0002)
if db is None:
db = self.getclassdb(classname)
if not db.has_key(nodeid):
+ # try the cache - might be a brand-new node
+ cache_dict = self.cache.setdefault(classname, {})
+ if cache_dict.has_key(nodeid):
+ if __debug__:
+ print >>hyperdb.TRACE, 'get %s %s cached'%(classname,
+ nodeid)
+ return cache_dict[nodeid]
raise IndexError, "no such %s %s"%(classname, nodeid)
# check the uncommitted, destroyed nodes
'''
if __debug__:
print >>hyperdb.DEBUG, 'commit', (self,)
- # TODO: lock the DB
# keep a handle to all the database files opened
self.databases = {}
for db in self.databases.values():
db.close()
del self.databases
- # TODO: unlock the DB
# reindex the nodes that request it
for classname, nodeid in filter(None, reindex.keys()):
These operations trigger detectors and can be vetoed. Attempts
to modify the "creation" or "activity" properties cause a KeyError.
'''
+ self.fireAuditors('create', None, propvalues)
+ newid = self.create_inner(**propvalues)
+ self.fireReactors('create', newid, None)
+ return newid
+
+ def create_inner(self, **propvalues):
+ ''' Called by create, in-between the audit and react calls.
+ '''
if propvalues.has_key('id'):
raise KeyError, '"id" is reserved'
if propvalues.has_key('creation') or propvalues.has_key('activity'):
raise KeyError, '"creation" and "activity" are reserved'
-
- self.fireAuditors('create', None, propvalues)
-
# new node's id
newid = self.db.newid(self.classname)
if self.do_journal:
self.db.addjournal(self.classname, newid, 'create', {})
- self.fireReactors('create', newid, None)
-
return newid
def export_list(self, propnames, nodeid):
elif isinstance(proptype, hyperdb.Password):
value = str(value)
l.append(repr(value))
+
+ # append retired flag
+ l.append(self.is_retired(nodeid))
+
return l
def import_list(self, propnames, proplist):
value = pwd
d[propname] = value
+ # check retired flag
+ if int(proplist[-1]):
+ d[self.db.RETIRED_FLAG] = 1
+
# add the node and journal
self.db.addnode(self.classname, newid, d)
self.fireReactors('retire', nodeid, None)
- def is_retired(self, nodeid):
+ def is_retired(self, nodeid, cldb=None):
'''Return true if the node is retired.
'''
- node = self.db.getnode(cn, nodeid, cldb)
+ node = self.db.getnode(self.classname, nodeid, cldb)
if node.has_key(self.db.RETIRED_FLAG):
return 1
return 0
The returned list contains tuples of the form
- (date, tag, action, params)
+ (nodeid, date, tag, action, params)
'date' is a Timestamp object specifying the time of the change and
'tag' is the journaltag specified when the database was opened.
# Multilink properties are sorted according to how many
# links are present.
elif isinstance(propclass, Multilink):
+ r = cmp(len(av), len(bv))
+ if r == 0:
+ # Compare contents of multilink property if lenghts is
+ # equal
+ r = cmp ('.'.join(av), '.'.join(bv))
if dir == '+':
- r = cmp(len(av), len(bv))
- if r != 0: return r
+ return r
elif dir == '-':
- r = cmp(len(bv), len(av))
- if r != 0: return r
+ return -r
elif isinstance(propclass, Number) or isinstance(propclass, Boolean):
if dir == '+':
r = cmp(av, bv)
for react in self.reactors[action]:
react(self.db, self, nodeid, oldvalues)
-class FileClass(Class):
+class FileClass(Class, hyperdb.FileClass):
'''This class defines a large chunk of data. To support this, it has a
mandatory String property "content" which is typically saved off
externally to the hyperdb.
default_mime_type = 'text/plain'
def create(self, **propvalues):
- ''' snaffle the file propvalue and store in a file
+ ''' Snarf the "content" propvalue and store in a file
'''
+ # we need to fire the auditors now, or the content property won't
+ # be in propvalues for the auditors to play with
+ self.fireAuditors('create', None, propvalues)
+
+ # now remove the content property so it's not stored in the db
content = propvalues['content']
del propvalues['content']
- newid = Class.create(self, **propvalues)
+
+ # do the database create
+ newid = Class.create_inner(self, **propvalues)
+
+ # fire reactors
+ self.fireReactors('create', newid, None)
+
+ # store off the content as a file
self.db.storefile(self.classname, newid, None, content)
return newid