def cleanupLockData(self, user_id): # This gets called on lock to avoid write-on-read, and its # possible to lock more than one object in a request so we # want to avoid doing this multiple times in a request! if self.REQUEST.get('_lock_gc_%s' % user_id, None): return mapping = getattr(self, '_locks', None) if mapping is None: return items = mapping.get(user_id, None) if items is None: return # stupid heuristic to occasionally go through and clean out # timed out and otherwise invalid lock records without taking # the hit on every lock request. if len(items) < self._lock_gc_threshold: return for key in items.keys(): try: obj = self.unrestrictedTraverse(key, None) except NotFound: obj = None if obj is None or self.locker(obj) != user_id: del items[key] if hasattr(obj, '_p_deactivate'): unproxied(obj)._p_deactivate() setattr(self.REQUEST, '_lock_gc_%s' % user_id, 1)
def updateStages2(self, obj, to_stages, message=''): """Updates objects to match the version in the source stage. """ verifyPermission(StageObjects, obj) from_stage = self.getStageOf(obj) if from_stage is None: raise StagingError("Object %s is not in any stage" % '/'.join(obj.getPhysicalPath())) if from_stage in to_stages or not to_stages: raise StagingError("Invalid to_stages parameter") if aq_base(unproxied(obj)) is not aq_base(obj): ref = obj.__dict__.get("_Proxy__reference") if ref is None: # Carefully wrap an *un*proxied version of obj in the same # context: IAW = ImplicitAcquisitionWrapper obj = IAW(unproxied(obj), aq_parent(aq_inner(obj))) proxy = None else: # obj is a proxy. Find the wrapped target and update that # instead of the reference. Note that the reference will # be updated with the container. proxy = obj obj = ref.getTarget(obj) # Decide whether the reference should be staged at the # same time. If the reference is contained in a # non-versioned container, the reference should be staged. # OTOH, if the reference is in a versioned container, # staging the container will create the reference, so the # reference should not be staged by this operation. repo = self._getVersionRepository() if repo.isUnderVersionControl(aq_parent(aq_inner(proxy))): proxy = None else: proxy = None # Check containers first. cmap = self._getObjectStages(obj, get_container=1) self._checkContainers(obj, to_stages, cmap) proxy_cmap = None if proxy is not None: # Check the containers of the reference also. proxy_cmap = self._getObjectStages(proxy, get_container=1) self._checkContainers(proxy, to_stages, proxy_cmap) # Update the stages. if self.auto_checkin: self._autoCheckin(obj, message) self._updateObjectStates(obj, cmap, to_stages) if proxy is not None: # Create and update the reference objects also. self._updateReferences(proxy, proxy_cmap, to_stages)
def getLogEntries(self, obj, only_checkins=0): """Returns the log entries for an object as a sequence of mappings. """ verifyPermission(UseVersionControl, obj) obj = unproxied(obj) repo = self._getVersionRepository() if not repo.isUnderVersionControl(obj): return [] entries = repo.getLogEntries(obj) res = [] for entry in entries: a = entry.action if a == entry.ACTION_CHECKOUT: action = 'checkout' elif a == entry.ACTION_CHECKIN: action = 'checkin' elif a == entry.ACTION_UNCHECKOUT: action = 'uncheckout' elif a == entry.ACTION_UPDATE: action = 'update' else: action = '?' if only_checkins and action != 'checkin': continue res.append({'timestamp': entry.timestamp, 'version_id': entry.version_id, 'action': action, 'message': entry.message, 'user_id': entry.user_id, 'path': entry.path, }) return res
def getHistoryId(self, obj): """Returns the version history ID of the object. """ verifyPermission(UseVersionControl, obj) obj = unproxied(obj) repo = self._getVersionRepository() return repo.getVersionInfo(obj).history_id
def checkin(self, obj, message=None): """Checks in a new version. """ verifyPermission(UseVersionControl, obj) obj = unproxied(obj) repo = self._getVersionRepository() if not repo.isUnderVersionControl(obj): repo.applyVersionControl(obj, message) else: if (not repo.isResourceUpToDate(obj, require_branch=1) and self.isCheckedOut(obj)): # This is a strange state, but it can be fixed. # Revert the object to the branch, replace the # reverted state with the new state, and check in. new_dict = obj.__dict__.copy() # Uncheckout obj = repo.uncheckoutResource(obj) info = repo.getVersionInfo(obj) obj = repo.updateResource(obj, self._getBranchName(info)) # Checkout obj = repo.checkoutResource(obj) # Restore the new state for key in obj.__dict__.keys(): if key != '__vc_info__': if not new_dict.has_key(key): del obj.__dict__[key] for key, value in new_dict.items(): if key != '__vc_info__': obj.__dict__[key] = value repo.checkinResource(obj, message or '') return None
def getLogEntries(self, obj, only_checkins=0): """Returns the log entries for an object as a sequence of mappings. """ verifyPermission(UseVersionControl, obj) obj = unproxied(obj) repo = self._getVersionRepository() if not repo.isUnderVersionControl(obj): return [] entries = repo.getLogEntries(obj) res = [] for entry in entries: a = entry.action if a == entry.ACTION_CHECKOUT: action = 'checkout' elif a == entry.ACTION_CHECKIN: action = 'checkin' elif a == entry.ACTION_UNCHECKOUT: action = 'uncheckout' elif a == entry.ACTION_UPDATE: action = 'update' else: action = '?' if only_checkins and action != 'checkin': continue res.append({ 'timestamp': entry.timestamp, 'version_id': entry.version_id, 'action': action, 'message': entry.message, 'user_id': entry.user_id, 'path': entry.path, }) return res
def isCheckedOut(self, obj): """Returns a true value if the object is checked out. """ obj = unproxied(obj) repo = self._getVersionRepository() if not repo.isUnderVersionControl(obj): return 0 info = repo.getVersionInfo(obj) return (info.status == info.CHECKED_OUT)
def getVersionIds(self, obj): """Returns the version IDs of all revisions for an object. """ verifyPermission(UseVersionControl, obj) obj = unproxied(obj) repo = self._getVersionRepository() ids = repo.getVersionIds(obj) ids = map(int, ids) ids.sort() return map(str, ids)
def _getObjectVersionIds(self, obj, include_status=0): repo = self._getVersionRepository() stages = self._getObjectStages(obj) res = {} for stage_name, obj in stages.items(): u_obj = unproxied(obj) if obj is None or not repo.isUnderVersionControl(u_obj): res[stage_name] = None else: info = repo.getVersionInfo(u_obj) v = info.version_id if include_status and info.status == info.CHECKED_OUT: v = str(v) + '+' res[stage_name] = v return res
def isStageable(self, obj): """Returns a true value if the object can be staged.""" verifyPermission(StageObjects, obj) repo = self._getVersionRepository() if not repo.isAVersionableResource(unproxied(obj)): return 0 if not getattr(obj, '_stageable', 1): return 0 # An object is stageable only if it is located in one of the stages. portal = aq_parent(aq_inner(self)) for stage_name, stage_title, path in self._stages: stage = self._getStage(portal, path) if stage is not None and obj.aq_inContextOf(stage, 1): return 1 return 0
def revertToVersion(self, obj, version_id): """Reverts the object to the given version. If make_new_revision, a new revision is created, so that the object's state can progress along a new line without making the user deal with branches, labels, etc. """ verifyPermission(UseVersionControl, obj) obj = unproxied(obj) repo = self._getVersionRepository() # Verify the object is under version control. repo.getVersionInfo(obj) if self.isCheckedOut(obj): # Save the current data. self.checkin(obj, 'Auto-saved') repo.updateResource(obj, version_id)
def getVersionId(self, obj, plus=0): """Returns the version ID of the current revision. If the 'plus' flag is set and the object is checked out, the version ID will include a plus sign to indicate that when the object is checked in, it will have a higher version number. """ obj = unproxied(obj) repo = self._getVersionRepository() if repo.isUnderVersionControl(obj): info = repo.getVersionInfo(obj) res = info.version_id if plus and info.status == info.CHECKED_OUT: res += '+' return res else: return ''
def checkout(self, obj): """Opens the object for development. Returns the object, which might be different from what was passed to the method if the object was replaced. """ verifyPermission(UseVersionControl, obj) obj = unproxied(obj) repo = self._getVersionRepository() old_state = None if not repo.isUnderVersionControl(obj): repo.applyVersionControl(obj) elif self.auto_copy_forward: if not repo.isResourceUpToDate(obj, require_branch=1): # The object is not at the latest revision or has a # sticky tag. Get it unstuck by copying the old state # forward after the object has been checked out. info = repo.getVersionInfo(obj) old_state = repo.getVersionOfResource(info.history_id, info.version_id) # Momentarily revert to the branch. obj = repo.updateResource(obj, self._getBranchName(info)) obj = repo.checkoutResource(obj) # Copy the old state into the object, minus __vc_info__. # XXX There ought to be some way to do this more cleanly. obj._p_changed = 1 for key in obj.__dict__.keys(): if key != '__vc_info__': if not old_state.__dict__.has_key(key): del obj.__dict__[key] for key, value in old_state.__dict__.items(): if key != '__vc_info__': obj.__dict__[key] = value # Check in as a copy. obj = repo.checkinResource( obj, 'Copied from revision %s' % info.version_id) repo.checkoutResource(obj) return None
def checkout(self, obj): """Opens the object for development. Returns the object, which might be different from what was passed to the method if the object was replaced. """ verifyPermission(UseVersionControl, obj) obj = unproxied(obj) repo = self._getVersionRepository() old_state = None if not repo.isUnderVersionControl(obj): repo.applyVersionControl(obj) elif self.auto_copy_forward: if not repo.isResourceUpToDate(obj, require_branch=1): # The object is not at the latest revision or has a # sticky tag. Get it unstuck by copying the old state # forward after the object has been checked out. info = repo.getVersionInfo(obj) old_state = repo.getVersionOfResource( info.history_id, info.version_id) # Momentarily revert to the branch. obj = repo.updateResource(obj, self._getBranchName(info)) obj = repo.checkoutResource(obj) # Copy the old state into the object, minus __vc_info__. # XXX There ought to be some way to do this more cleanly. obj._p_changed = 1 for key in obj.__dict__.keys(): if key != '__vc_info__': if not old_state.__dict__.has_key(key): del obj.__dict__[key] for key, value in old_state.__dict__.items(): if key != '__vc_info__': obj.__dict__[key] = value # Check in as a copy. obj = repo.checkinResource( obj, 'Copied from revision %s' % info.version_id) repo.checkoutResource(obj) return None
def _updateObjectStates(self, source_object, container_map, to_stages): """Internal: updates the state of an object in specified stages. Uses version control to do the propagation. """ repo = self._getVersionRepository() object_map = self._getObjectStages(source_object) version_info = repo.getVersionInfo(source_object) version_id = version_info.version_id history_id = version_info.history_id # Update and/or copy the object to the different stages. for stage_name, ob in object_map.items(): if stage_name in to_stages: if ob is None: # The object has not yet been created in the stage. # Copy from the repository to the given stage. ob = repo.getVersionOfResource(history_id, version_id) container = container_map[stage_name] # Make a copy and put it in the new place. id = source_object.getId() container._setObject(id, ob) else: if not repo.isUnderVersionControl(ob): p = '/'.join(ob.getPhysicalPath()) raise StagingError( 'The object "%s", not under version control, ' 'is in the way.' % p) if repo.getVersionInfo(ob).history_id != history_id: p = '/'.join(ob.getPhysicalPath()) p2 = '/'.join(source_object.getPhysicalPath()) raise StagingError( 'The object "%s", backed by a different ' 'version history than "%s", ' 'is in the way.' % (p, p2)) repo.updateResource(unproxied(ob), version_id)
def isUnderVersionControl(self, obj): """Returns a true value if the object is under version control. """ obj = unproxied(obj) repo = self._getVersionRepository() return repo.isUnderVersionControl(obj)
def isResourceUpToDate(self, obj, require_branch=0): """Return true if a version-controlled resource is up to date. """ obj = unproxied(obj) repo = self._getVersionRepository() return repo.isResourceUpToDate(obj, require_branch)