--- a/server/session.py Wed Mar 10 16:07:24 2010 +0100
+++ b/server/session.py Mon Mar 01 11:26:14 2010 +0100
@@ -12,12 +12,13 @@
import sys
import threading
from time import time
+from uuid import uuid4
from logilab.common.deprecation import deprecated
from rql.nodes import VariableRef, Function, ETYPE_PYOBJ_MAP, etype_from_pyobj
from yams import BASE_TYPES
-from cubicweb import Binary, UnknownEid
+from cubicweb import Binary, UnknownEid, schema
from cubicweb.req import RequestSessionBase
from cubicweb.dbapi import ConnectionProperties
from cubicweb.utils import make_uid
@@ -25,6 +26,10 @@
ETYPE_PYOBJ_MAP[Binary] = 'Bytes'
+NO_UNDO_TYPES = schema.SCHEMA_TYPES.copy()
+NO_UNDO_TYPES.add('CWCache')
+# XXX rememberme,forgotpwd,apycot,vcsfile
+
def is_final(rqlst, variable, args):
# try to find if this is a final var or not
for select in rqlst.children:
@@ -110,6 +115,7 @@
"""tie session id, user, connections pool and other session data all
together
"""
+ is_internal_session = False
def __init__(self, user, repo, cnxprops=None, _id=None):
super(Session, self).__init__(repo.vreg)
@@ -120,8 +126,14 @@
self.cnxtype = cnxprops.cnxtype
self.creation = time()
self.timestamp = self.creation
- self.is_internal_session = False
self.default_mode = 'read'
+ # support undo for Create Update Delete entity / Add Remove relation
+ if repo.config.creating or repo.config.repairing or self.is_internal_session:
+ self.undo_actions = ()
+ else:
+ self.undo_actions = set(repo.config['undo-support'].upper())
+ if self.undo_actions - set('CUDAR'):
+ raise Exception('bad undo-support string in configuration')
# short cut to querier .execute method
self._execute = repo.querier.execute
# shared data, used to communicate extra information between the client
@@ -334,7 +346,10 @@
# so we can't rely on simply checking session.read_security, but
# recalling the first transition from DEFAULT_SECURITY to something
# else (False actually) is not perfect but should be enough
- self._threaddata.dbapi_query = oldmode is self.DEFAULT_SECURITY
+ #
+ # also reset dbapi_query to true when we go back to DEFAULT_SECURITY
+ self._threaddata.dbapi_query = (oldmode is self.DEFAULT_SECURITY
+ or activated is self.DEFAULT_SECURITY)
return oldmode
@property
@@ -689,6 +704,7 @@
self.critical('error while %sing', trstate,
exc_info=sys.exc_info())
self.info('%s session %s done', trstate, self.id)
+ return self.transaction_uuid(set=False)
finally:
self._clear_thread_data()
self._touch()
@@ -769,6 +785,27 @@
else:
self.pending_operations.insert(index, operation)
+ # undo support ############################################################
+
+ def undoable_action(self, action, ertype):
+ return action in self.undo_actions and not ertype in NO_UNDO_TYPES
+ # XXX elif transaction on mark it partial
+
+ def transaction_uuid(self, set=True):
+ try:
+ return self.transaction_data['tx_uuid']
+ except KeyError:
+ if not set:
+ return
+ self.transaction_data['tx_uuid'] = uuid = uuid4().hex
+ self.repo.system_source.start_undoable_transaction(self, uuid)
+ return uuid
+
+ def transaction_inc_action_counter(self):
+ num = self.transaction_data.setdefault('tx_action_count', 0) + 1
+ self.transaction_data['tx_action_count'] = num
+ return num
+
# querier helpers #########################################################
@property
@@ -890,13 +927,13 @@
class InternalSession(Session):
"""special session created internaly by the repository"""
+ is_internal_session = True
def __init__(self, repo, cnxprops=None):
super(InternalSession, self).__init__(InternalManager(), repo, cnxprops,
_id='internal')
self.user.req = self # XXX remove when "vreg = user.req.vreg" hack in entity.py is gone
self.cnxtype = 'inmemory'
- self.is_internal_session = True
self.disable_hook_categories('integrity')