def test_rollback_does_nothing(self): history = ZodbObjectHistory(self.adam) history.rollback(history.lastChange()) self.assertEquals(self.adam.laptop, 'ThinkPad T61') self.assertFalse(self.adam._p_changed)
def test_rollback(self): history = ZodbObjectHistory(self.adam) history.rollback(history[-2]['tid']) self.assertEquals(self.adam.laptop, 'ThinkPad T23') self.assertTrue(self.adam._p_changed)
class ZodbInfoView(VeryCarefulView): """Zodb browser view""" template = ViewPageTemplateFile('templates/zodbinfo.pt') confirmation_template = ViewPageTemplateFile( 'templates/confirm_rollback.pt') version = __version__ homepage = __homepage__ def render(self): self._started = time.time() pruneTruncations() self.obj = self.selectObjectToView() # Not using IObjectHistory(self.obj) because LP#1185175 self.history = ZodbObjectHistory(self.obj) self.latest = True if self.request.get('tid'): self.state = ZodbObjectState(self.obj, p64(int(self.request['tid'], 0)), _history=self.history) self.latest = False else: self.state = ZodbObjectState(self.obj, _history=self.history) if 'CANCEL' in self.request: self._redirectToSelf() return '' if 'ROLLBACK' in self.request: rtid = p64(int(self.request['rtid'], 0)) self.requestedState = self._tidToTimestamp(rtid) if self.request.get('confirmed') == '1': self.history.rollback(rtid) transaction.get().note(u'Rollback to old state %s' % self.requestedState) self.made_changes = True self._redirectToSelf() return '' # will show confirmation prompt return self.confirmation_template() return self.template() def renderingTime(self): return '%.3fs |' % (time.time() - self._started) def _redirectToSelf(self): self.request.response.redirect(self.getUrl()) def selectObjectToView(self): obj = None if 'oid' not in self.request: obj = self.findClosestPersistent() # Sanity check: if we're running in standalone mode, # self.context is a Folder in the just-created MappingStorage, # which we're not interested in. if obj is not None and obj._p_jar is not self.jar: obj = None if obj is None: if 'oid' in self.request: try: oid = int(self.request['oid'], 0) except ValueError: raise UserError('OID is not an integer: %r' % self.request['oid']) else: oid = self.getRootOid() try: obj = self.jar.get(p64(oid)) except POSKeyError: raise UserError('There is no object with OID 0x%x' % oid) return obj def getRequestedTid(self): if 'tid' in self.request: return self.request['tid'] else: return None def getRequestedTidNice(self): if 'tid' in self.request: return self._tidToTimestamp(p64(int(self.request['tid'], 0))) else: return None def getObjectId(self): return self.state.getObjectId() def getObjectIdHex(self): return '0x%x' % self.state.getObjectId() def getObjectType(self): return getObjectType(self.obj) def getObjectTypeShort(self): return getObjectTypeShort(self.obj) def getStateTid(self): return u64(self.state.tid) def getStateTidNice(self): return self._tidToTimestamp(self.state.tid) def getPickleSize(self): return len(self.state.pickledState) def getRootOid(self): root = self.jar.root() try: root = root[ZopePublication.root_name] except KeyError: pass return u64(root._p_oid) def locate_json(self, path): # AJAX view return json.dumps(self.locate(path)) def truncated_ajax(self, id): # AJAX view return TRUNCATIONS.get(id) def locate(self, path): not_found = object() # marker # our current position # partial -- path of the last _persistent_ object # here -- path of the last object traversed # oid -- oid of the last _persistent_ object # obj -- last object traversed partial = here = '/' oid = self.getRootOid() obj = self.jar.get(p64(oid)) steps = path.split('/') if steps and steps[0]: # 0x1234/sub/path -> start traversal at oid 0x1234 try: oid = int(steps[0], 0) except ValueError: pass else: partial = here = hex(oid) try: obj = self.jar.get(p64(oid)) except KeyError: oid = self.getRootOid() return dict(error='Not found: %s' % steps[0], partial_oid=oid, partial_path='/', partial_url=self.getUrl(oid)) steps = steps[1:] for step in steps: if not step: continue if not here.endswith('/'): here += '/' here += step try: child = obj[step] except Exception: child = getattr(obj, step, not_found) if child is not_found: return dict(error='Not found: %s' % here, partial_oid=oid, partial_path=partial, partial_url=self.getUrl(oid)) obj = child if isinstance(obj, Persistent): partial = here oid = u64(obj._p_oid) if not isinstance(obj, Persistent): return dict(error='Not persistent: %s' % here, partial_oid=oid, partial_path=partial, partial_url=self.getUrl(oid)) return dict(oid=oid, url=self.getUrl(oid)) def getUrl(self, oid=None, tid=None): if oid is None: oid = self.getObjectId() url = "@@zodbbrowser?oid=0x%x" % oid if tid is None and 'tid' in self.request: url += "&tid=" + self.request['tid'] elif tid is not None: url += "&tid=0x%x" % tid return url def getBreadcrumbs(self): breadcrumbs = [] state = self.state seen_root = False while True: url = self.getUrl(state.getObjectId()) if state.isRoot(): breadcrumbs.append(('/', url)) seen_root = True else: if breadcrumbs: breadcrumbs.append(('/', None)) if not state.getName() and state.getParentState() is None: # not using hex() because we don't want L suffixes for # 64-bit values breadcrumbs.append(('0x%x' % state.getObjectId(), url)) break breadcrumbs.append((state.getName() or '???', url)) state = state.getParentState() if state is None: if not seen_root: url = self.getUrl(self.getRootOid()) breadcrumbs.append(('/', None)) breadcrumbs.append(('...', None)) breadcrumbs.append(('/', url)) break return breadcrumbs[::-1] def getPath(self): return ''.join(name for name, url in self.getBreadcrumbs()) def getBreadcrumbsHTML(self): html = [] for name, url in self.getBreadcrumbs(): if url: html.append('<a href="%s">%s</a>' % (escape(url, True), escape(name, False))) else: html.append(escape(name, False)) return ''.join(html) def listAttributes(self): attrs = self.state.listAttributes() if attrs is None: return None return [ ZodbObjectAttribute(name, value, self.state.requestedTid) for name, value in sorted(attrs) ] def listItems(self): items = self.state.listItems() if items is None: return None return [ ZodbObjectAttribute(name, value, self.state.requestedTid) for name, value in items ] def _loadHistoricalState(self): results = [] for d in self.history: try: interp = ZodbObjectState(self.obj, d['tid'], _history=self.history) state = interp.asDict() error = interp.getError() except Exception as e: state = {} error = '%s: %s' % (e.__class__.__name__, e) results.append(dict(state=state, error=error)) results.append(dict(state={}, error=None)) return results def listHistory(self): """List transactions that modified a persistent object.""" state = self._loadHistoricalState() results = [] for n, d in enumerate(self.history): utc_timestamp = str( time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(d['time']))) local_timestamp = str( time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(d['time']))) try: user_location, user_id = d['user_name'].split() except ValueError: user_location = None user_id = d['user_name'] url = self.getUrl(tid=u64(d['tid'])) current = (d['tid'] == self.state.tid and self.state.requestedTid is not None) curState = state[n]['state'] oldState = state[n + 1]['state'] diff = compareDictsHTML(curState, oldState, d['tid']) results.append( dict(utid=u64(d['tid']), href=url, current=current, error=state[n]['error'], diff=diff, user_id=user_id, user_location=user_location, utc_timestamp=utc_timestamp, local_timestamp=local_timestamp, **d)) # number in reverse order for i in range(len(results)): results[i]['index'] = len(results) - i return results def _tidToTimestamp(self, tid): if isinstance(tid, bytes) and len(tid) == 8: return str(TimeStamp(tid)) return tid_repr(tid)
class ZodbInfoView(VeryCarefulView): """Zodb browser view""" template = ViewPageTemplateFile('templates/zodbinfo.pt') confirmation_template = ViewPageTemplateFile('templates/confirm_rollback.pt') version = __version__ homepage = __homepage__ def render(self): self._started = time.time() pruneTruncations() self.obj = self.selectObjectToView() # Not using IObjectHistory(self.obj) because LP#1185175 self.history = ZodbObjectHistory(self.obj) self.latest = True if self.request.get('tid'): self.state = ZodbObjectState(self.obj, p64(int(self.request['tid'], 0)), _history=self.history) self.latest = False else: self.state = ZodbObjectState(self.obj, _history=self.history) if 'CANCEL' in self.request: self._redirectToSelf() return '' if 'ROLLBACK' in self.request: rtid = p64(int(self.request['rtid'], 0)) self.requestedState = self._tidToTimestamp(rtid) if self.request.get('confirmed') == '1': self.history.rollback(rtid) transaction.get().note(u'Rollback to old state %s' % self.requestedState) self.made_changes = True self._redirectToSelf() return '' # will show confirmation prompt return self.confirmation_template() return self.template() def renderingTime(self): return '%.3fs |' % (time.time() - self._started) def _redirectToSelf(self): self.request.response.redirect(self.getUrl()) def selectObjectToView(self): obj = None if 'oid' not in self.request: obj = self.findClosestPersistent() # Sanity check: if we're running in standalone mode, # self.context is a Folder in the just-created MappingStorage, # which we're not interested in. if obj is not None and obj._p_jar is not self.jar: obj = None if obj is None: if 'oid' in self.request: try: oid = int(self.request['oid'], 0) except ValueError: raise UserError('OID is not an integer: %r' % self.request['oid']) else: oid = self.getRootOid() try: obj = self.jar.get(p64(oid)) except POSKeyError: raise UserError('There is no object with OID 0x%x' % oid) return obj def getRequestedTid(self): if 'tid' in self.request: return self.request['tid'] else: return None def getRequestedTidNice(self): if 'tid' in self.request: return self._tidToTimestamp(p64(int(self.request['tid'], 0))) else: return None def getObjectId(self): return self.state.getObjectId() def getObjectIdHex(self): return '0x%x' % self.state.getObjectId() def getObjectType(self): return getObjectType(self.obj) def getObjectTypeShort(self): return getObjectTypeShort(self.obj) def getStateTid(self): return u64(self.state.tid) def getStateTidNice(self): return self._tidToTimestamp(self.state.tid) def getPickleSize(self): return len(self.state.pickledState) def getRootOid(self): root = self.jar.root() try: root = root[ZopePublication.root_name] except KeyError: pass return u64(root._p_oid) def locate_json(self, path): # AJAX view return json.dumps(self.locate(path)) def truncated_ajax(self, id): # AJAX view return TRUNCATIONS.get(id) def locate(self, path): not_found = object() # marker # our current position # partial -- path of the last _persistent_ object # here -- path of the last object traversed # oid -- oid of the last _persistent_ object # obj -- last object traversed partial = here = '/' oid = self.getRootOid() obj = self.jar.get(p64(oid)) steps = path.split('/') if steps and steps[0]: # 0x1234/sub/path -> start traversal at oid 0x1234 try: oid = int(steps[0], 0) except ValueError: pass else: partial = here = hex(oid) try: obj = self.jar.get(p64(oid)) except KeyError: oid = self.getRootOid() return dict(error='Not found: %s' % steps[0], partial_oid=oid, partial_path='/', partial_url=self.getUrl(oid)) steps = steps[1:] for step in steps: if not step: continue if not here.endswith('/'): here += '/' here += step try: child = obj[step] except Exception: child = getattr(obj, step, not_found) if child is not_found: return dict(error='Not found: %s' % here, partial_oid=oid, partial_path=partial, partial_url=self.getUrl(oid)) obj = child if isinstance(obj, Persistent): partial = here oid = u64(obj._p_oid) if not isinstance(obj, Persistent): return dict(error='Not persistent: %s' % here, partial_oid=oid, partial_path=partial, partial_url=self.getUrl(oid)) return dict(oid=oid, url=self.getUrl(oid)) def getUrl(self, oid=None, tid=None): if oid is None: oid = self.getObjectId() url = "@@zodbbrowser?oid=0x%x" % oid if tid is None and 'tid' in self.request: url += "&tid=" + self.request['tid'] elif tid is not None: url += "&tid=0x%x" % tid return url def getBreadcrumbs(self): breadcrumbs = [] state = self.state seen_root = False while True: url = self.getUrl(state.getObjectId()) if state.isRoot(): breadcrumbs.append(('/', url)) seen_root = True else: if breadcrumbs: breadcrumbs.append(('/', None)) if not state.getName() and state.getParentState() is None: # not using hex() because we don't want L suffixes for # 64-bit values breadcrumbs.append(('0x%x' % state.getObjectId(), url)) break breadcrumbs.append((state.getName() or '???', url)) state = state.getParentState() if state is None: if not seen_root: url = self.getUrl(self.getRootOid()) breadcrumbs.append(('/', None)) breadcrumbs.append(('...', None)) breadcrumbs.append(('/', url)) break return breadcrumbs[::-1] def getPath(self): return ''.join(name for name, url in self.getBreadcrumbs()) def getBreadcrumbsHTML(self): html = [] for name, url in self.getBreadcrumbs(): if url: html.append('<a href="%s">%s</a>' % (escape(url, True), escape(name, False))) else: html.append(escape(name, False)) return ''.join(html) def listAttributes(self): attrs = self.state.listAttributes() if attrs is None: return None return [ZodbObjectAttribute(name, value, self.state.requestedTid) for name, value in sorted(attrs)] def listItems(self): items = self.state.listItems() if items is None: return None return [ZodbObjectAttribute(name, value, self.state.requestedTid) for name, value in items] def _loadHistoricalState(self): results = [] for d in self.history: try: interp = ZodbObjectState(self.obj, d['tid'], _history=self.history) state = interp.asDict() error = interp.getError() except Exception as e: state = {} error = '%s: %s' % (e.__class__.__name__, e) results.append(dict(state=state, error=error)) results.append(dict(state={}, error=None)) return results def listHistory(self): """List transactions that modified a persistent object.""" state = self._loadHistoricalState() results = [] for n, d in enumerate(self.history): utc_timestamp = str(time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(d['time']))) local_timestamp = str(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(d['time']))) try: user_location, user_id = d['user_name'].split() except ValueError: user_location = None user_id = d['user_name'] url = self.getUrl(tid=u64(d['tid'])) current = (d['tid'] == self.state.tid and self.state.requestedTid is not None) curState = state[n]['state'] oldState = state[n + 1]['state'] diff = compareDictsHTML(curState, oldState, d['tid']) results.append(dict(utid=u64(d['tid']), href=url, current=current, error=state[n]['error'], diff=diff, user_id=user_id, user_location=user_location, utc_timestamp=utc_timestamp, local_timestamp=local_timestamp, **d)) # number in reverse order for i in range(len(results)): results[i]['index'] = len(results) - i return results def _tidToTimestamp(self, tid): if isinstance(tid, bytes) and len(tid) == 8: return str(TimeStamp(tid)) return tid_repr(tid)
class ZodbInfoView(VeryCarefulView): """Zodb browser view""" adapts(Interface, IBrowserRequest) template = ViewPageTemplateFile('templates/zodbinfo.pt') confirmation_template = ViewPageTemplateFile('templates/confirm_rollback.pt') def render(self): self._started = time.time() pruneTruncations() self.obj = self.selectObjectToView() # Not using IObjectHistory(self.obj) because LP#1185175 self.history = ZodbObjectHistory(self.obj) self.latest = True if self.request.get('tid'): self.state = ZodbObjectState(self.obj, p64(int(self.request['tid'], 0)), _history=self.history) # We display the tid only if we find one. self.latest = self.state.tid is None else: self.state = ZodbObjectState(self.obj, _history=self.history) if 'CANCEL' in self.request: self._redirectToSelf() return '' if 'ROLLBACK' in self.request: rtid = p64(int(self.request['rtid'], 0)) self.requestedState = self._tidToTimestamp(rtid) if self.request.get('confirmed') == '1': self.history.rollback(rtid) transaction.get().note('Rollback to old state %s' % self.requestedState) self.made_changes = True self._redirectToSelf() return '' # will show confirmation prompt return self.confirmation_template() return self.template() def renderingTime(self): return '%.3fs |' % (time.time() - self._started) def _redirectToSelf(self): self.request.response.redirect(self.getUrl()) def selectObjectToView(self): obj = None if 'oid' not in self.request: obj = self.findClosestPersistent() # Sanity check: if we're running in standalone mode, # self.context is a Folder in the just-created MappingStorage, # which we're not interested in. if obj is not None and obj._p_jar is not self.jar: obj = None if obj is None: if 'oid' in self.request: try: oid = int(self.request['oid'], 0) except ValueError: raise UserError('OID is not an integer: %r' % self.request['oid']) else: oid = self.getRootOid() try: obj = self.findObjectFromOID(oid) except POSKeyError: raise UserError('There is no object with OID 0x%x' % oid) return obj def findClosestPersistent(self): obj = removeSecurityProxy(self.context) while not isinstance(obj, Persistent): try: obj = obj.__parent__ except AttributeError: return None return obj def getReferences(self): if self.references is None: return None return { 'forward': map( self.getOIDRenderedValue, self.references.getForwardReferences(self.obj._p_oid)), 'backward': map( self.getOIDRenderedValue, self.references.getBackwardReferences(self.obj._p_oid))} def getRequestedTid(self): if 'tid' in self.request: return self.request['tid'] else: return None def getRequestedTidNice(self): if 'tid' in self.request: return self._tidToTimestamp(p64(int(self.request['tid'], 0))) else: return None def getObjectId(self): return self.state.getObjectId() def getObjectIdHex(self): return '0x%x' % self.state.getObjectId() def getObjectType(self): return getObjectType(self.obj) def getObjectTypeShort(self): return getObjectTypeShort(self.obj) def getStateTid(self): return u64(self.state.tid) def getStateTidNice(self): return self._tidToTimestamp(self.state.tid) def getPickleSize(self): return len(self.state.pickledState) def getRootOid(self): root = self.jar.root() try: root = root[ZopePublication.root_name] except KeyError: pass return u64(root._p_oid) def locate_json(self, path): # AJAX view return simplejson.dumps(self.locate(path)) def truncated_ajax(self, id): # AJAX view return TRUNCATIONS.get(id) def locate(self, path): not_found = object() # marker # our current position # partial -- path of the last _persistent_ object # here -- path of the last object traversed # oid -- oid of the last _persistent_ object # obj -- last object traversed partial = here = '/' oid = self.getRootOid() obj = self.jar.get(p64(oid)) steps = path.split('/') if steps and steps[0]: # 0x1234/sub/path -> start traversal at oid 0x1234 try: oid = int(steps[0], 0) except ValueError: pass else: partial = here = hex(oid) try: obj = self.jar.get(p64(oid)) except KeyError: oid = self.getRootOid() return dict(error='Not found: %s' % steps[0], partial_oid=oid, partial_path='/', partial_url=self.getUrl(oid)) steps = steps[1:] for step in steps: if not step: continue if not here.endswith('/'): here += '/' here += step.encode('utf-8') try: child = obj[step] except Exception: child = getattr(obj, step, not_found) if child is not_found: return dict(error='Not found: %s' % here, partial_oid=oid, partial_path=partial, partial_url=self.getUrl(oid)) obj = child if isinstance(obj, Persistent): partial = here oid = u64(obj._p_oid) if not isinstance(obj, Persistent): return dict(error='Not persistent: %s' % here, partial_oid=oid, partial_path=partial, partial_url=self.getUrl(oid)) return dict(oid=oid, url=self.getUrl(oid)) def getUrl(self, oid=None, tid=None): if oid is None: oid = self.getObjectId() url = "@@zodbbrowser?oid=0x%x" % oid if self.supportsUndo: if tid is None and 'tid' in self.request: url += "&tid=" + self.request['tid'] elif tid is not None: url += "&tid=0x%x" % tid return url def getBreadcrumbs(self): breadcrumbs = [] state = self.state seen_root = False while True: url = self.getUrl(state.getObjectId()) if state.isRoot(): breadcrumbs.append(('/', url)) seen_root = True else: if breadcrumbs: breadcrumbs.append(('/', None)) if not state.getName() and state.getParentState() is None: # not using hex() because we don't want L suffixes for # 64-bit values breadcrumbs.append(('0x%x' % state.getObjectId(), url)) break breadcrumbs.append((state.getName() or '???', url)) state = state.getParentState() if state is None: if not seen_root: url = self.getUrl(self.getRootOid()) breadcrumbs.append(('/', None)) breadcrumbs.append(('...', None)) breadcrumbs.append(('/', url)) break return breadcrumbs[::-1] def getPath(self): return ''.join(name for name, url in self.getBreadcrumbs()) def getBreadcrumbsHTML(self): html = [] for name, url in self.getBreadcrumbs(): if url: html.append('<a href="%s">%s</a>' % (escape(url, True), escape(name))) else: html.append(escape(name)) return ''.join(html) def listAttributes(self): attrs = self.state.listAttributes() if attrs is None: return None return [ZodbObjectAttribute(name, value, self.state.requestedTid) for name, value in sorted(attrs)] def listItems(self): items = self.state.listItems() if items is None: return None return [ZodbObjectAttribute(name, value, self.state.requestedTid) for name, value in items] def _loadHistoricalState(self): results = [] for d in self.history: try: interp = ZodbObjectState(self.obj, d['tid'], _history=self.history) state = interp.asDict() error = interp.getError() except Exception, e: state = {} error = '%s: %s' % (e.__class__.__name__, e) results.append(dict(state=state, error=error)) results.append(dict(state={}, error=None)) return results
def test_rollback(self): history = ZodbObjectHistory(self.adam) history.rollback(history[-2]['tid']) self.assertEqual(self.adam.laptop, 'ThinkPad T23') self.assertTrue(self.adam._p_changed)
def test_rollback_does_nothing(self): history = ZodbObjectHistory(self.adam) history.rollback(history.lastChange()) self.assertEqual(self.adam.laptop, 'ThinkPad T61') self.assertFalse(self.adam._p_changed)