def plonegroup_contact_transition(contact, event): """ React when a IPloneGroupContact transition is done """ if event.transition and event.transition.id == 'deactivate': # check if the transition is selected registry = getUtility(IRegistry) pp = api.portal.get_tool('portal_properties') errors = [] if contact.UID() in registry[ORGANIZATIONS_REGISTRY]: errors.append(_('This contact is selected in configuration')) elif pp.site_properties.enable_link_integrity_checks: search_value_in_objects(contact, contact.UID(), p_types=[], type_fields={}) storage = ILinkIntegrityInfo(contact.REQUEST) breaches = storage.getIntegrityBreaches() if contact in breaches: errors.append(_("This contact is used in following content: ${items}", mapping={'items': ', '.join(['<a href="%s" target="_blank">%s</a>' % (i.absolute_url(), i.Title()) for i in breaches[contact]])})) if errors: smi = IStatusMessage(contact.REQUEST) smi.addStatusMessage(_('You cannot deactivate this item !'), type='error') smi.addStatusMessage(errors[0], type='error') view_url = getMultiAdapter((contact, contact.REQUEST), name=u'plone_context_state').view_url() # contact.REQUEST['RESPONSE'].redirect(view_url) raise Redirect(view_url)
def on_removed_resource(resource, event): request = getattr(resource, 'REQUEST', None) if request is None: return info = ILinkIntegrityInfo(request) if info.integrityCheckingEnabled(): if info.getIntegrityBreaches(): return # info.isConfirmedItem simply does not work # it is really awful to have to deal with these internals if 'form.submitted' not in request: return if 'form.button.Cancel' in request: return if getattr(request, 'link_integrity_events_counter', 0) != 2: return log.info('extinguising resource {}'.format(resource.uuid())) db.extinguish_resource(resource.uuid())
def referenceRemoved(obj, event, toInterface=IContactContent): """Store information about the removed link integrity reference. """ # inspired from z3c/relationfield/event.py:breakRelations # and plone/app/linkintegrity/handlers.py:referenceRemoved # if the object the event was fired on doesn't have a `REQUEST` attribute # we can safely assume no direct user action was involved and therefore # never raise a link integrity exception... request = aq_get(obj, 'REQUEST', None) if not request: return storage = ILinkIntegrityInfo(request) catalog = component.queryUtility(ICatalog) intids = component.queryUtility(IIntIds) if catalog is None or intids is None: return # find all relations that point to us obj_id = intids.queryId(obj) if obj_id is None: return rels = list(catalog.findRelations({'to_id': obj_id})) for rel in rels: if toInterface.providedBy(rel.to_object): storage.addBreach(rel.from_object, rel.to_object)
def isLinked(obj): """ check if the given content object is linked from another one WARNING: don't use this function in your code!! it is a helper for the link integrity code and will potentially abort the ongoing transaction, giving you unexpected results... """ # first check to see if link integrity handling has been enabled at all # and if so, if the removal of the object was already confirmed, i.e. # while replaying the request; unfortunately this makes it necessary # to import from plone.app.linkintegrity here, hence the try block... try: from plone.app.linkintegrity.interfaces import ILinkIntegrityInfo info = ILinkIntegrityInfo(obj.REQUEST) except (ImportError, TypeError): # if p.a.li isn't installed the following check can be cut short... return False if not info.integrityCheckingEnabled(): return False if info.isConfirmedItem(obj): return True # otherwise, when not replaying the request already, it is tried to # delete the object, making it possible to find out if it was referenced, # i.e. in case a link integrity exception was raised linked = False parent = obj.aq_inner.aq_parent try: parent.manage_delObjects(obj.getId()) except OFS.ObjectManager.BeforeDeleteException, e: linked = True
def is_relevant(self): request = self.obj.REQUEST if getattr(request, '_activity_reported', False): return False else: setattr(request, '_activity_reported', True) info = ILinkIntegrityInfo(request) if not info.integrityCheckingEnabled(): return True elif info.isConfirmedItem(self.obj): return True if request.URL.endswith('/sl_delete_object'): return True if request.has_key('form.submitted') and \ request.URL.endswith('/delete_confirmation'): return True if request.URL.endswith('/folder_delete'): return True if request.has_key('form.button.Cancel'): return True return False
def delete(self): uids = self.request['uids'] ctool = getToolByName(self.context, 'portal_catalog') mtool = getToolByName(self.context, 'portal_membership') brains = ctool(UID=uids) fails = [] success = 0 integrity_info = ILinkIntegrityInfo(self.request) for b in brains: obj = b.getObject() integrity_info.addDeletedItem(obj) if not mtool.checkPermission('Delete objects', obj): fails.append(translate(_(u"Unauthorized: ${path}", mapping={'path': b.getPath()}), context=self.request)) else: try: parent = obj.getParentNode() parent.manage_delObjects([obj.getId()]) except LinkIntegrityNotificationException: pass finally: success += 1 IStatusMessage(self.request).add(_("msg_objects_deleted", default="${num} object(s) deleted", mapping={'num': success})) if fails: IStatusMessage(self.request).add( _("msg_objects_delete_failed", default="${num} object(s) were not deleted : ${fails}", mapping={'num': len(fails), 'fails': ", ".join(fails)}), 'error')
def test_plonegroupOrganizationRemoved_1(self): """ We cannot remove an organization selected in settings and used in an object """ view = self.portal.restrictedTraverse( '{0}/{1}/department1/delete_confirmation'.format(DEFAULT_DIRECTORY_ID, PLONEGROUP_ORG)) self.assertRaises(LinkIntegrityNotificationException, view.render) storage = ILinkIntegrityInfo(view.REQUEST) breaches = storage.getIntegrityBreaches() self.assertIn(self.contacts[0], breaches) self.assertSetEqual(breaches[self.contacts[0]], set([self.portal['acontent1']]))
def test_plonegroupOrganizationRemoved_1(self): """ We cannot remove an organization selected in settings and used in an object """ view = self.portal.restrictedTraverse( '{0}/{1}/department1/delete_confirmation'.format( DEFAULT_DIRECTORY_ID, PLONEGROUP_ORG)) self.assertRaises(LinkIntegrityNotificationException, view.render) storage = ILinkIntegrityInfo(view.REQUEST) breaches = storage.getIntegrityBreaches() self.assertIn(self.contacts[0], breaches) self.assertSetEqual(breaches[self.contacts[0]], set([self.portal['acontent1']]))
def integrityBreaches(self): info = ILinkIntegrityInfo(self.request).getIntegrityBreaches() byTitle = lambda a, b: cmp(a.Title(), b.Title()) breaches = [] for target, sources in info.items(): breaches.append({ 'title': target.Title(), 'type': target.getPortalTypeName(), 'url': target.absolute_url(), 'sources': sorted(sources, byTitle), }) return sorted(breaches, lambda a, b: cmp(a['title'], b['title']))
def test_delete_content_succeeds_with_link_integrity_breach(self): doc2 = self.portal[self.portal.invokeFactory("Document", id="doc2", title="My Document")] from plone.app.linkintegrity.interfaces import ILinkIntegrityInfo info = ILinkIntegrityInfo(self.layer["request"]) info.addBreach(doc2, self.doc1) service = self.traverse("/plone/doc1", method="DELETE") service() self.assertEqual(204, info.context.response.status) self.assertNotIn("doc1", self.portal.objectIds())
def test_delete_content_succeeds_with_link_integrity_breach(self): doc2 = self.portal[self.portal.invokeFactory( 'Document', id='doc2', title='My Document', )] from plone.app.linkintegrity.interfaces import ILinkIntegrityInfo info = ILinkIntegrityInfo(self.layer['request']) info.addBreach(doc2, self.doc1) service = self.traverse('/plone/doc1', method='DELETE') service() self.assertEqual(204, info.context.response.status) self.assertNotIn('doc1', self.portal.objectIds())
def integrityBreaches(self): info = ILinkIntegrityInfo(self.request).getIntegrityBreaches() byTitle = lambda a, b: cmp((a.Title(), a.getId()), (b.Title(), b.getId())) breaches = [] for target, sources in info.items(): breaches.append({ 'title': target.Title(), 'type': target.getPortalTypeName(), 'type_title': self.getPortalTypeTitle(target), 'url': target.absolute_url(), 'sources': sorted(sources, byTitle), }) return sorted(breaches, lambda a, b: cmp(a['title'], b['title']))
def referenceRemoved(obj, event): """ store information about the removed link integrity reference """ assert IReference.providedBy(obj) assert obj is event.object # just making sure... if not obj.relationship in get_protected_relationships(): return # skip for other removed references # if the object the event was fired on doesn't have a `REQUEST` attribute # we can safely assume no direct user action was involved and therefore # never raise a link integrity exception... request = aq_get(obj, 'REQUEST', None) if not request: return storage = ILinkIntegrityInfo(request) storage.addBreach(obj.getSourceObject(), obj.getTargetObject())
def delete_integrity(context, event): request = getRequest() if request is not None: path = context.getPhysicalPath() for base in _ignorepaths(request): if tuple(path[:len(base)]) == base: # allow deletion of Plone site marked by before_site_delete() return used_by = InUseBy(context, request) if len(used_by) > 0: info = ILinkIntegrityInfo(request) for brain in used_by._brainmap.values(): info.addBreach(brain._unrestrictedGetObject(), context) raise LinkIntegrityNotificationException(context)
def integrityBreaches(self): info = ILinkIntegrityInfo(self.request).getIntegrityBreaches() byTitle = lambda a, b: cmp((a.Title(), a.getId()), (b.Title(), b.getId())) breaches = [] for target, sources in info.items(): breaches.append( { "title": target.Title(), "type": target.getPortalTypeName(), "type_title": self.getPortalTypeTitle(target), "url": target.absolute_url(), "sources": sorted(sources, byTitle), } ) return sorted(breaches, lambda a, b: cmp(a["title"], b["title"]))
def __call__(self): payload = self.request.get('data', None) if not payload: raise BadRequest('No data given') # TODO validate payload contains blocks and confirmed flag. self.link_integrity = ILinkIntegrityInfo(self.request) data = json.loads(payload) self.block = uuidToObject(data['block']) if self.request.get('form.submitted', False): if self.link_integrity: # Always allow deletion of block, regardless of the integrity # check. self.request.environ[self.link_integrity.marker] = 'all' self.context.manage_delObjects([self.block.id]) transaction_note('Deleted %s' % self.block.absolute_url()) return json_response(self.request, proceed=True) else: return json_response(self.request, content=self.confirm_template(), proceed=False)
def createCommitteesFolder(portal): # New in profile version 1 (software version 1.0.3) if 'committees' in portal.objectIds(): committees = portal['committees'] if committees.portal_type == 'Committee Folder': return request = getattr(portal, 'REQUEST') getattr(request, 'environ')[ILinkIntegrityInfo(request).getEnvMarker()] = 'all' # WTF: bug in plone.app.linkintegrity-1.0.12 line 43 AttributeError: 'NoneType' object has no attribute 'UID' try: portal.manage_delObjects('committees') except AttributeError: # OK, that was #fail. Try it without events. try: portal._delObject('committees', suppress_events=True) except AttributeError: pass committees = portal[portal.invokeFactory('Committee Folder', 'committees')] committees.setTitle(u'Committees') committees.setDescription( u'The following describes the committees, subcommittees, and other components of EDRN.' ) committees.rdfDataSource = u'http://edrn-dev.jpl.nasa.gov/cancerdataexpo/rdf-data/committees/rdf' committees.setExcludeFromNav(True) committees.reindexObject()
def test_enable_link_integrity_checks_active(self): self.browser.open("%s/@@editing-controlpanel" % self.portal_url) self.browser.getControl("Enable link integrity checks")\ .selected = True self.browser.getControl('Save').click() self.assertTrue( ILinkIntegrityInfo(self.request).integrityCheckingEnabled())
def personnel_contact_removed(del_obj, event): """ Check if a personnel held_position is used as sender. """ # only interested by held_position user if del_obj.portal_type == 'person': return try: portal = api.portal.get() pp = portal.portal_properties catalog = portal.portal_catalog except api.portal.CannotGetPortalError: # When deleting site, the portal is no more found... return if pp.site_properties.enable_link_integrity_checks: storage = ILinkIntegrityInfo(aq_get(del_obj, 'REQUEST', None)) for brain in catalog.unrestrictedSearchResults(portal_type=['dmsoutgoingmail'], sender_index=[del_obj.UID()]): storage.addBreach(brain._unrestrictedGetObject(), del_obj)
def isLinked(obj): """ check if the given content object is linked from another one WARNING: this function can be time consuming !! It deletes the object in a subtransaction that is rollbacked. In other words, the object is kept safe. Nevertheless, this implies that it also deletes recursively all object's subobjects and references, which can be very expensive. """ # first check to see if link integrity handling has been enabled at all # and if so, if the removal of the object was already confirmed, i.e. # while replaying the request; unfortunately this makes it necessary # to import from plone.app.linkintegrity here, hence the try block... try: from plone.app.linkintegrity.interfaces import ILinkIntegrityInfo info = ILinkIntegrityInfo(obj.REQUEST) except (ImportError, TypeError): # if p.a.li isn't installed the following check can be cut short... return False if not info.integrityCheckingEnabled(): return False if info.isConfirmedItem(obj): return True # otherwise, when not replaying the request already, it is tried to # delete the object, making it possible to find out if it was referenced, # i.e. in case a link integrity exception was raised linked = False parent = obj.aq_inner.aq_parent try: savepoint = transaction.savepoint() parent.manage_delObjects(obj.getId()) except OFS.ObjectManager.BeforeDeleteException: linked = True except: # ignore other exceptions, not useful to us at this point pass finally: savepoint.rollback() return linked
def handle_remove_event(obj, event): """If an object will be removed on the senders instance, we need to create a publisher delete job. """ # the event is notified for every subobject, but we only want to check # the top object which the users tries to delete if obj is not event.object: return workflow = get_workflow_name(obj) if not workflow or workflow not in config.PUBLISHING_WORKFLOWS: # we don't have a workflow or the workflow does not publish ever - so we # don't need to delete anything on the receiver. return # the event handler is fired twice (once from link integrity check), but # we just want to do our stuff once. And we should only do it if the user # already did confirm. do_delete = False request = getattr(obj, 'REQUEST', None) if request is None: do_delete = True else: info = ILinkIntegrityInfo(request) if not info.integrityCheckingEnabled(): do_delete = True elif info.isConfirmedItem(obj): do_delete = True if request.URL.endswith('/sl_delete_object'): do_delete = True if request.has_key('form.submitted') and \ request.URL.endswith('/delete_confirmation'): do_delete = True if request.URL.endswith('/folder_delete'): do_delete = True if request.has_key('form.button.Cancel'): do_delete = True # register the job if do_delete: obj.restrictedTraverse('@@publisher.delete')()
def referenceRemoved(obj, event): """ store information about the removed link integrity reference """ assert IReference.providedBy(obj) assert obj is event.object # just making sure... if not obj.relationship == referencedRelationship: return # skip for other removed references # if the object the event was fired on doesn't have a `REQUEST` attribute # we can safely assume no direct user action was involved and therefore # never raise a link integrity exception... request = aq_get(obj, 'REQUEST', None) if not request: return storage = ILinkIntegrityInfo(request) source = obj.getSourceObject() if not IBaseObject.providedBy(source) and hasattr(source, 'context'): source = source.context target = obj.getTargetObject() if not IBaseObject.providedBy(target) and hasattr(target, 'context'): target = target.context if source is not None and target is not None: storage.addBreach(source, target)
def reference_document_removed(obj, event): """ Check if there is a relation with another Document. Like collective.contact.core.subscribers.referenceRemoved. Where referenceObjectRemoved is also used """ request = aq_get(obj, 'REQUEST', None) if not request: return # if '_link_integrity_check_' not in request: # request.set('_link_integrity_check_', True) storage = ILinkIntegrityInfo(request) # confirmed = request.get('HTTP_REFERER').endswith('delete_confirmation?') catalog = queryUtility(ICatalog) intids = queryUtility(IIntIds) if catalog is None or intids is None: return obj_id = intids.queryId(obj) # find all relations that point to us for rel in catalog.findRelations({'to_id': obj_id, 'from_attribute': 'reply_to'}): storage.addBreach(rel.from_object, rel.to_object) # find relations we point for rel in catalog.findRelations({'from_id': obj_id, 'from_attribute': 'reply_to'}): storage.addBreach(rel.to_object, rel.from_object)
def plonegroup_contact_transition(contact, event): """ React when a IPloneGroupContact transition is done """ if event.transition and event.transition.id == 'deactivate': # check if the transition is selected pp = api.portal.get_tool('portal_properties') errors = [] if contact.UID() in get_registry_organizations(): errors.append(_('This contact is selected in configuration')) elif pp.site_properties.enable_link_integrity_checks: search_value_in_objects(contact, contact.UID(), p_types=[], type_fields={}) storage = ILinkIntegrityInfo(contact.REQUEST) breaches = storage.getIntegrityBreaches() if contact in breaches: errors.append( _("This contact is used in following content: ${items}", mapping={ 'items': ', '.join([ '<a href="%s" target="_blank">%s</a>' % (i.absolute_url(), i.Title()) for i in breaches[contact] ]) })) if errors: smi = IStatusMessage(contact.REQUEST) smi.addStatusMessage(_('You cannot deactivate this item !'), type='error') smi.addStatusMessage(errors[0], type='error') view_url = getMultiAdapter((contact, contact.REQUEST), name=u'plone_context_state').view_url() # contact.REQUEST['RESPONSE'].redirect(view_url) raise Redirect(view_url)
def referencedObjectRemoved(obj, event): """ check if the removal was already confirmed or redirect to the form """ # if the object the event was fired on doesn't have a `REQUEST` attribute # we can safely assume no direct user action was involved and therefore # never raise a link integrity exception... request = aq_get(obj, 'REQUEST', None) if not request: return info = ILinkIntegrityInfo(request) # first we check if link integrity checking was enabled if not info.integrityCheckingEnabled(): return # since the event gets called for every subobject before it's # called for the item deleted directly via _delObject (event.object) # itself, but we do not want to present the user with a confirmation # form for every (referred) subobject, so we remember and skip them... info.addDeletedItem(obj) if obj is not event.object: return # if the number of expected events has been stored to help us prevent # multiple forms (i.e. in folder_delete), we wait for the next event # if we know there will be another... if info.moreEventsToExpect(): return # at this point all subobjects have been removed already, so all # link integrity breaches caused by that have been collected as well; # if there aren't any (after things have been cleaned up), # we keep lurking in the shadows... if not info.getIntegrityBreaches(): return # if the user has confirmed to remove the currently handled item in a # previous confirmation form we won't need it anymore this time around... if info.isConfirmedItem(obj): return # otherwise we raise an exception and pass the object that is supposed # to be removed as the exception value so we can use it as the context # for the view triggered by the exception; this is needed since the # view is an adapter for the exception and a request, so it gets the # exception object as the context, which is not very useful... raise LinkIntegrityNotificationException(obj)
def __call__(self): # this view is intended to provide an action called by the # confirmation form; all it does is prepare the request for # the retry exception and raise it... request = aq_inner(self.request) clicked = request.form.has_key if clicked('delete') or clicked('delete_all'): # the user choose to actually delete the referred to object, # so we reconstruct the original request which we interrupted # before, store the so far confirmed items and retry it... body, env = decodeRequestData(request.get('original_request')) marker = ILinkIntegrityInfo(request).getEnvMarker() if clicked('delete_all'): env[marker] = 'all' else: env[marker] = request.get('confirmed_items') auth = request._authUserPW() if auth is not None: authtoken = b64encode('%s:%s' % auth) env['HTTP_AUTHORIZATION'] = 'Basic %s' % authtoken env['HTTP_COOKIE'] = request.get('HTTP_COOKIE', '') # Update the original environment with the new one. In a WSGI # context, we want to update the dict, not overwrite it, because # we actually want to modify the WSGI environ. We also need to # make sure we don't touch keys that are not strings request._orig_env.update(env) # Set the stdin for the request new_stdin = StringIO(body) if 'wsgi.input' in request._orig_env: request._orig_env['wsgi.input'] = new_stdin setattr(request, 'stdin', new_stdin) raise Retry else: # the user choose to cancel the removal, in which case we # redirect back to the original HTTP_REFERER url... msg = _(u'Removal cancelled.') IStatusMessage(request).addStatusMessage(msg, type='info') request.RESPONSE.redirect(request.get('cancel_url'))
def search_value_in_objects(s_obj, ref, p_types=[], type_fields={}): """ Searching a value (reference to an object like id or uid) in fields of objects. Parameters: * s_obj : the object that is maybe referenced in another objects fields * ref : the value to search in field * p_types : portal_types that will be only searched * type_fields : dict containing as key portal_type and as value a list of fields that must be searched. If a portal_type is not given, all fields will be searched """ # we check all dexterity objects fields to see if ref is used in # we can't check only fields using plonegroup vocabulary because maybe another vocabulary name is used # this can be long but this operation is not made so often request = aq_get(s_obj, 'REQUEST', None) if not request: return try: catalog = api.portal.get_tool('portal_catalog') except api.portal.CannotGetPortalError: # When deleting site, the portal is no more found... return storage = ILinkIntegrityInfo(request) def list_fields(ptype, filter_interfaces=(IText, ICollection, IChoice)): """ return for the portal_type the selected fields """ if ptype not in type_fields: type_fields[ptype] = [] fti = getUtility(IDexterityFTI, name=ptype) for name, fld in getFieldsInOrder(fti.lookupSchema()): for iface in filter_interfaces: if iface.providedBy(fld): type_fields[ptype].append(name) break # also lookup behaviors for behavior_id in fti.behaviors: behavior = getUtility(IBehavior, behavior_id).interface for name, fld in getFieldsInOrder(behavior): for iface in filter_interfaces: if iface.providedBy(fld): type_fields[ptype].append(name) break return type_fields[ptype] def check_value(val): if isinstance(val, basestring) and val == ref: return True return False def check_attribute(val): """ check the attribute value and walk in it """ if isinstance(val, dict): for v in val.values(): res = check_attribute(v) if res: return res elif base_hasattr(val, '__iter__'): for v in val: res = check_attribute(v) if res: return res elif check_value(val): res = [val] return res return [] for brain in catalog.unrestrictedSearchResults(portal_types=p_types, object_provides=IDexterityContent.__identifier__): obj = brain._unrestrictedGetObject() ptype = obj.portal_type for attr in list_fields(ptype): if base_hasattr(obj, attr): res = check_attribute(getattr(obj, attr)) if res: storage.addBreach(obj, s_obj) break
class DeleteBlocks(BrowserView): confirm_template = ViewPageTemplateFile( 'templates/block_delete_confirmation.pt') def __init__(self, context, request): super(DeleteBlocks, self).__init__(context, request) self.block = None self.link_integrity = None def __call__(self): payload = self.request.get('data', None) if not payload: raise BadRequest('No data given') # TODO validate payload contains blocks and confirmed flag. self.link_integrity = ILinkIntegrityInfo(self.request) data = json.loads(payload) self.block = uuidToObject(data['block']) if self.request.get('form.submitted', False): if self.link_integrity: # Always allow deletion of block, regardless of the integrity # check. self.request.environ[self.link_integrity.marker] = 'all' self.context.manage_delObjects([self.block.id]) transaction_note('Deleted %s' % self.block.absolute_url()) return json_response(self.request, proceed=True) else: return json_response(self.request, content=self.confirm_template(), proceed=False) def get_link_integrity_breaches(self): if isLinked(self.block): breaches = self.link_integrity.getIntegrityBreaches() breaches_info = [] sources = breaches.values() sources = len(sources) and sources[0] or sources for source in sources: breaches_info.append({'title': source.title_or_id(), 'url': source.absolute_url()}) return breaches_info else: return None def is_locked_for_current_user(self): locking_info = self.block.restrictedTraverse('@@plone_lock_info', None) if locking_info: return locking_info.is_locked_for_current_user() else: return False @property def context_state(self): return self.block.restrictedTraverse('@@plone_context_state') def block_payload(self): block = IUUID(self.block) return json.dumps({'block': block})
def clean_folder(self): """ find folders that are empty and delete them the loop run recursively until there is no more folders to cancel """ catalog = self.portal_catalog total = 0 transaction_threshold = 20 # var for infinite loop empty_count = 42 # Start information log info("START") out = StringIO() forced_delete = [] while empty_count != 0: # Secure infinite loop empty_count = 0 # Get all folders folders = catalog.unrestrictedSearchResults({ 'portal_type': ('ATFolder', 'Folder') }) # Find empty folders and delete them for folder in folders: obj = folder._unrestrictedGetObject() if len(obj.getFolderContents()) == 0: empty_count += 1 total += 1 refs = obj.getBRefs(relationship='isReferencing') try: obj.aq_parent.manage_delObjects([folder.id]) except LinkIntegrityNotificationException: li = ILinkIntegrityInfo(self.REQUEST) self.REQUEST.environ['link_integrity_info'] = \ li.encodeConfirmedItems([obj]) forced_delete.append((obj.absolute_url(), refs)) info(obj.absolute_url()) # Commiting transaction if empty_count % transaction_threshold == 0: info("Commit: delete %s folders", transaction_threshold) transaction.savepoint() # End information log info("COMPLETE, %s folders deleted", total) print >> out, ("The following linkintegrity conflicts were encountered:") print >> out, ("The conflicting objects have been deleted, " "but the referencing pages should be updated.") for failed, referencing in forced_delete: print >> out, "This object failed reference integrity: ", failed print >> out, "It was referenced by:" for r in referencing: print >> out, r.absolute_url() print >> out, "Total objects deleted %s" % total out.seek(0) return out.read()
def confirmedItems(self): info = ILinkIntegrityInfo(self.request) targets = info.getIntegrityBreaches().keys() return info.encodeConfirmedItems(additions=targets)
def clean_folder(self): """ find folders that are empty and delete them the loop run recursively until there is no more folders to cancel """ catalog = self.portal_catalog total = 0 transaction_threshold = 20 # var for infinite loop empty_count = 42 # Start information log info("START") out = StringIO() forced_delete = [] while empty_count != 0: # Secure infinite loop empty_count = 0 # Get all folders folders = catalog.unrestrictedSearchResults( {'portal_type': ('ATFolder', 'Folder')}) # Find empty folders and delete them for folder in folders: obj = folder._unrestrictedGetObject() if len(obj.getFolderContents()) == 0: empty_count += 1 total += 1 refs = obj.getBRefs(relationship='isReferencing') try: obj.aq_parent.manage_delObjects([folder.id]) except LinkIntegrityNotificationException: li = ILinkIntegrityInfo(self.REQUEST) self.REQUEST.environ['link_integrity_info'] = \ li.encodeConfirmedItems([obj]) forced_delete.append((obj.absolute_url(), refs)) info(obj.absolute_url()) # Commiting transaction if empty_count % transaction_threshold == 0: info("Commit: delete %s folders", transaction_threshold) transaction.savepoint() # End information log info("COMPLETE, %s folders deleted", total) print >> out, ("The following linkintegrity conflicts were encountered:") print >> out, ("The conflicting objects have been deleted, " "but the referencing pages should be updated.") for failed, referencing in forced_delete: print >> out, "This object failed reference integrity: ", failed print >> out, "It was referenced by:" for r in referencing: print >> out, r.absolute_url() print >> out, "Total objects deleted %s" % total out.seek(0) return out.read()
def __call__(self): _logger.info('INGEST EVERYTHING FULLY') # Turn OFF content rules during ingest contentRules = getUtility(IRuleStorage) contentRulesState = contentRules.active contentRules.active = False # Find out what paths to ingest context = aq_inner(self.context) portalURL = getToolByName(context, 'portal_url') portal = portalURL.getPortalObject() reportEmail = portal.getProperty('ingestReportEmail', _defaultIngestReportEmail) paths = portal.getProperty('edrnIngestPaths', []) doNotPublish = portal.getProperty('nonPublishedIngestPaths', _doNotPublish) doNotDelete = portal.getProperty('nonClearedIngestPaths', _doNotDelete) report = [ u'The following is a report of the RDF ingest for the EDRN portal at {}:' .format(portalURL()) ] try: # No paths? No need to continue. if len(paths) == 0: _logger.info( "There are no ingest paths, so there's nothing to ingest.") report.append( u'There are no ingest paths configured for the portal, so no ingest' ) return # We'll need the workflow tool later on. wfTool = getToolByName(context, 'portal_workflow') # Ignore link integrity checks request = aq_inner(self.request) getattr( request, 'environ')[ILinkIntegrityInfo(request).getEnvMarker()] = 'all' # Ingest and publish each path for path in paths: try: _logger.info('Starting ingest of "%s"', path) report.append(u'Starting ingest of "{}"'.format(path)) obj = portal.restrictedTraverse(path.split('/')) if path not in doNotDelete: obj.manage_delObjects(obj.objectIds()) ingestor = getMultiAdapter((obj, self.request), name=u'ingest') ingestor.render = False ingestor() transaction.commit() _logger.info('Ingest of "%s" completed', path) report.append(u'Ingest of "{}" completed'.format(path)) # Some paths don't need publication if path not in doNotPublish: self._publish(wfTool, obj) _logger.info('And published all of "%s" too', path) report.append( u'Published everything in "{}"'.format(path)) else: _logger.info( 'Skipping publishing of "%s" since it takes care of its own publication state', path) report.append( u'Skipping publishing of "{}"; it takes care of its own publication' .format(path)) transaction.commit() except: _logger.exception('Ingest failed for "%s"', path) report.append(u'Ingest failed for "{}"'.format(path)) # And re-index _logger.info('Clearing and rebuilding the catalog') report.append(u'Clearing and rebuilding the catalog.') catalog = getToolByName(context, 'portal_catalog') catalog.clearFindAndRebuild() transaction.commit() # OK, now we can restore whatever the content rule state was contentRules.active = contentRulesState _logger.info('All ingestion completed') report.append(u'All ingestion completed') finally: date = unicode(datetime.date.today().isoformat()) plone.api.portal.send_email( recipient=reportEmail, subject=u'EDRN Portal Ingest {} for {}'.format( date, portalURL()), body=u'\n'.join(report))
def search_value_in_objects(s_obj, ref, p_types=[], type_fields={}): """ Searching a value (reference to an object like id or uid) in fields of objects. Parameters: * s_obj : the object that is maybe referenced in another objects fields * ref : the value to search in field * p_types : portal_types that will be only searched * type_fields : dict containing as key portal_type and as value a list of fields that must be searched. If a portal_type is not given, all fields will be searched """ # we check all dexterity objects fields to see if ref is used in # we can't check only fields using plonegroup vocabulary because maybe another vocabulary name is used # this can be long but this operation is not made so often request = aq_get(s_obj, 'REQUEST', None) if not request: return try: catalog = api.portal.get_tool('portal_catalog') except api.portal.CannotGetPortalError: # When deleting site, the portal is no more found... return storage = ILinkIntegrityInfo(request) def list_fields(ptype, filter_interfaces=(IText, ICollection, IChoice)): """ return for the portal_type the selected fields """ if ptype not in type_fields: type_fields[ptype] = [] fti = getUtility(IDexterityFTI, name=ptype) for name, fld in getFieldsInOrder(fti.lookupSchema()): for iface in filter_interfaces: if iface.providedBy(fld): type_fields[ptype].append(name) break # also lookup behaviors for behavior_id in fti.behaviors: behavior = getUtility(IBehavior, behavior_id).interface for name, fld in getFieldsInOrder(behavior): for iface in filter_interfaces: if iface.providedBy(fld): type_fields[ptype].append(name) break return type_fields[ptype] def check_value(val): if isinstance(val, basestring) and val == ref: return True return False def check_attribute(val): """ check the attribute value and walk in it """ if isinstance(val, dict): for v in val.values(): res = check_attribute(v) if res: return res elif base_hasattr(val, '__iter__'): for v in val: res = check_attribute(v) if res: return res elif check_value(val): res = [val] return res return [] for brain in catalog.unrestrictedSearchResults( portal_types=p_types, object_provides=IDexterityContent.__identifier__): obj = brain._unrestrictedGetObject() ptype = obj.portal_type for attr in list_fields(ptype): if base_hasattr(obj, attr): res = check_attribute(getattr(obj, attr)) if res: storage.addBreach(obj, s_obj) break