def replace_filtered(self, data): occurences = parseItems(self.request["form.affectedContent"]) occur_count = 0 for page_url, page_result in occurences.items(): for field, indexes in page_result.items(): occur_count += len(indexes) srutil = getUtility(ISearchReplaceUtility) repl_count = srutil.replaceFilteredOccurences( self.context, data["findWhat"], replaceWith=data["replaceWith"], occurences=occurences, searchSubFolders=data.get("searchSubfolders", False), matchCase=data["matchCase"], onlySearchableText=data["onlySearchableText"], ) IStatusMessage(self.request).addStatusMessage( _( u"Search text replaced in ${replaced} of ${items} " "instance(s).", mapping={ "replaced": repl_count, "items": occur_count }, ), type="info", )
class ISearchReplaceSettings(Interface): """Control panel settings for search and replace. """ restrict_searchable_types = schema.Bool( title=_(u'Restrict the enabled types.'), description=_( u'If checked, only the enabled types are searched, ' 'otherwise all types are searched.'), required=False, default=False, ) enabled_types = schema.List( title=_(u'List of types that are searched.'), description=_( u"When 'Restrict the enable types' is checked, " "only the selected types are searched. " "Otherwise this list is ignored."), value_type=schema.Choice( vocabulary='plone.app.vocabularies.PortalTypes', ), required=False, default=[ 'Collection', 'Document', 'Event', 'File', 'Folder', 'Image', 'News Item', ], ) maximum_text_characters = schema.Int( title=_(u'Maximum text characters'), description=_( u'The maximum number of characters to show ' 'before and after the found text.'), required=False, default=50, ) update_modified = schema.Bool( title=_(u'Update the modified datetime when replacing.'), description=_( u'If checked, the modified index/metadata of the object ' u'having text replaced will be updated.'), required=False, default=True, )
def action_replace(self, action, data): """ Replace text for all files. """ self.form_reset = False srutil = getUtility(ISearchReplaceUtility) if 'form.affectedContent' in self.request: # Do only the selected items # nitems = len(self.request['form.affectedContent']) items = srutil.parseItems(self.request['form.affectedContent']) nitems = 0 for page_url, page_result in items.items(): for field, indexes in page_result.items(): nitems += len(indexes) replaced = srutil.searchObjects( self.context, data['findWhat'], searchSubFolders=data.get('searchSubfolders', False), matchCase=data['matchCase'], replaceText=data['replaceWith'], doReplace=True, searchItems=items, onlySearchableText=data['onlySearchableText'], ) IStatusMessage(self.request).addStatusMessage( _(u'Search text replaced in ${replaced} of ${items} ' 'instance(s).', mapping={'replaced': replaced, 'items': nitems}), type='info') else: # Do everything you can find replaced = srutil.searchObjects( self.context, data['findWhat'], searchSubFolders=data.get('searchSubfolders', False), matchCase=data['matchCase'], replaceText=data['replaceWith'], onlySearchableText=data['onlySearchableText'], doReplace=True) IStatusMessage(self.request).addStatusMessage( _(u'Search text replaced in all ${items} instance(s).', mapping={'items': replaced}), type='info')
def replace_all(self, data): srutil = getUtility(ISearchReplaceUtility) repl_count = srutil.replaceAllMatches( self.context, data["findWhat"], replaceWith=data["replaceWith"], searchSubFolders=data.get("searchSubfolders", False), matchCase=data["matchCase"], onlySearchableText=data["onlySearchableText"], ) IStatusMessage(self.request).addStatusMessage( _( u"Search text replaced in all ${items} instance(s).", mapping={"items": repl_count}, ), type="info", )
def _afterReplace(self, obj, find, rtext): """Hook for doing things after a text has been replaced. - obj is the changed object - find is the found text - rtext is the replacement text By default, we will store a version in the CMFEditions repository. """ repository = getToolByName(obj, 'portal_repository', None) if repository is None: return if obj.portal_type not in repository.getVersionableContentTypes(): return comment = _(u'Replaced: ${old} -> ${new}', mapping={'old': find, 'new': rtext}) comment = translate(comment, context=obj.REQUEST) repository.save(obj, comment=comment)
from collective.searchandreplace import SearchAndReplaceMessageFactory as _ from collective.searchandreplace.interfaces import ISearchReplaceSettings from plone.app.registry.browser.controlpanel import ControlPanelFormWrapper from plone.app.registry.browser.controlpanel import RegistryEditForm from plone.z3cform import layout from z3c.form import form class SearchReplaceControlPanelForm(RegistryEditForm): form.extends(RegistryEditForm) schema = ISearchReplaceSettings SearchReplaceControlPanelView = layout.wrap_form( SearchReplaceControlPanelForm, ControlPanelFormWrapper ) SearchReplaceControlPanelView.label = _(u"Search and Replace settings")
class ISearchReplaceSettings(Interface): """Control panel settings for search and replace.""" restrict_searchable_types = schema.Bool( title=_(u"Restrict the enabled types."), description=_(u"If checked, only the enabled types are searched, " "otherwise all types are searched."), required=False, default=False, ) enabled_types = schema.List( title=_(u"List of types that are searched."), description=_(u"When 'Restrict the enable types' is checked, " "only the selected types are searched. " "Otherwise this list is ignored."), value_type=schema.Choice( vocabulary="plone.app.vocabularies.PortalTypes", ), required=False, default=[ "Collection", "Document", "Event", "File", "Folder", "Image", "News Item", ], ) maximum_text_characters = schema.Int( title=_(u"Maximum text characters"), description=_(u"The maximum number of characters to show " "before and after the found text."), required=False, default=50, ) update_modified = schema.Bool( title=_(u"Update the modified datetime when replacing."), description=_(u"If checked, the modified index/metadata of the object " u"having text replaced will be updated."), required=False, default=True, ) include_textline_fields = schema.Bool( title=_(u"Search also textline fields"), description=_( u"If checked, single line fields " u"will also be searched and replaced, " u"not only title and text fields.", ), required=False, default=True, ) include_lines_fields = schema.Bool( title=_(u"Search also lines fields"), description=_( u"If checked, multiple lines fields like subject " u"will also be searched and replaced, " u"not only title and text fields.", ), required=False, default=False, )
from collective.searchandreplace import SearchAndReplaceMessageFactory as _ from collective.searchandreplace.interfaces import ISearchReplaceSettings from plone.app.registry.browser.controlpanel import ControlPanelFormWrapper from plone.app.registry.browser.controlpanel import RegistryEditForm from plone.z3cform import layout from z3c.form import form class SearchReplaceControlPanelForm(RegistryEditForm): form.extends(RegistryEditForm) schema = ISearchReplaceSettings SearchReplaceControlPanelView = layout.wrap_form( SearchReplaceControlPanelForm, ControlPanelFormWrapper) SearchReplaceControlPanelView.label = _(u"Search and Replace settings")
class SearchReplaceForm(AddForm): """ """ @property def form_fields(self): form_fields = FormFields(ISearchReplaceForm) container = aq_parent(self.context) if not self.context.isPrincipiaFolderish and not isDefaultPage( container, self.context): form_fields = form_fields.omit("searchSubfolders") form_fields["findWhat"].custom_widget = TwoLineTextAreaWidget form_fields["replaceWith"].custom_widget = TwoLineTextAreaWidget return form_fields label = _(u"Search and Replace") description = _(u"Search and replace text found in documents.") template = ViewPageTemplateFile("pageform.pt") @action(_(u"Preview"), validator=None, name=u"Preview") def action_preview(self, action, data): """ Preview files to be changed. """ self.form_reset = False @action(_(u"Replace"), validator=validate_searchreplaceform, name=u"Replace") def action_replace(self, action, data): """ Replace text for all files. """ self.form_reset = False if "form.affectedContent" in self.request: self.replace_filtered(data) else: self.replace_all(data) def replace_filtered(self, data): occurences = parseItems(self.request["form.affectedContent"]) occur_count = 0 for page_url, page_result in occurences.items(): for field, indexes in page_result.items(): occur_count += len(indexes) srutil = getUtility(ISearchReplaceUtility) repl_count = srutil.replaceFilteredOccurences( self.context, data["findWhat"], replaceWith=data["replaceWith"], occurences=occurences, searchSubFolders=data.get("searchSubfolders", False), matchCase=data["matchCase"], onlySearchableText=data["onlySearchableText"], ) IStatusMessage(self.request).addStatusMessage( _( u"Search text replaced in ${replaced} of ${items} " "instance(s).", mapping={ "replaced": repl_count, "items": occur_count }, ), type="info", ) def replace_all(self, data): srutil = getUtility(ISearchReplaceUtility) repl_count = srutil.replaceAllMatches( self.context, data["findWhat"], replaceWith=data["replaceWith"], searchSubFolders=data.get("searchSubfolders", False), matchCase=data["matchCase"], onlySearchableText=data["onlySearchableText"], ) IStatusMessage(self.request).addStatusMessage( _( u"Search text replaced in all ${items} instance(s).", mapping={"items": repl_count}, ), type="info", ) @action(_(u"Reset"), validator=None, name=u"Reset") def action_reset(self, action, data): """ Reset the form fields to their defaults. """
class ISearchReplaceForm(Interface): """ Interface for Search and Replace form """ findWhat = Text(title=_(u"Find What"), description=_(u"Enter the text to find."), required=True) replaceWith = Text( title=_(u"Replace With"), description=_(u"Enter the text to replace the original text with."), required=False, ) maxResults = Int( title=_(u"Maximum Number of Results"), description=_( u"Maximum number of results to show. " "Warning: this has no effect on how many found texts are " "replaced when you use the Replace button directly without " "using the Preview."), default=None, required=False, ) searchSubfolders = Bool( title=_(u"Search Subfolders"), description=_(u"If checked, this will recursively search " "through any selected folders and their " "children, replacing at each level."), default=True, required=True, ) matchCase = Bool( title=_(u"Match Case"), description=_(u"Check the box for a case sensitive search."), default=False, required=True, ) onlySearchableText = Bool( title=_(u"Fast search"), description=_( u"Use the catalog to search, just like the search form does. " "This only finds keywords, not html tags. " "You might have some text fields that are not found this way, " "so if not checked, you may find more content, " "but it will be slower. " "Regardless of this setting, when at least one match is found, " "text in all text fields may be replaced."), required=False, default=True, )
class SearchReplaceForm(AddForm): """ """ @property def form_fields(self): form_fields = FormFields(ISearchReplaceForm) container = aq_parent(self.context) if not self.context.isPrincipiaFolderish and not isDefaultPage( container, self.context): form_fields = form_fields.omit('searchSubfolders') form_fields['findWhat'].custom_widget = TwoLineTextAreaWidget form_fields['replaceWith'].custom_widget = TwoLineTextAreaWidget return form_fields label = _(u'Search and Replace') description = _(u'Search and replace text found in documents.') template = ViewPageTemplateFile('pageform.pt') @action(_(u'Preview'), validator=None, name=u'Preview') def action_preview(self, action, data): """ Preview files to be changed. """ self.form_reset = False @action(_(u'Replace'), validator=validate_searchreplaceform, name=u'Replace') def action_replace(self, action, data): """ Replace text for all files. """ self.form_reset = False srutil = getUtility(ISearchReplaceUtility) if 'form.affectedContent' in self.request: # Do only the selected items # nitems = len(self.request['form.affectedContent']) items = srutil.parseItems(self.request['form.affectedContent']) nitems = 0 for page_url, page_result in items.items(): for field, indexes in page_result.items(): nitems += len(indexes) replaced = srutil.searchObjects( self.context, data['findWhat'], searchSubFolders=data.get('searchSubfolders', False), matchCase=data['matchCase'], replaceText=data['replaceWith'], doReplace=True, searchItems=items, onlySearchableText=data['onlySearchableText'], ) IStatusMessage(self.request).addStatusMessage( _(u'Search text replaced in ${replaced} of ${items} ' 'instance(s).', mapping={'replaced': replaced, 'items': nitems}), type='info') else: # Do everything you can find replaced = srutil.searchObjects( self.context, data['findWhat'], searchSubFolders=data.get('searchSubfolders', False), matchCase=data['matchCase'], replaceText=data['replaceWith'], onlySearchableText=data['onlySearchableText'], doReplace=True) IStatusMessage(self.request).addStatusMessage( _(u'Search text replaced in all ${items} instance(s).', mapping={'items': replaced}), type='info') @action(_(u'Reset'), validator=None, name=u'Reset') def action_reset(self, action, data): """ Reset the form fields to their defaults. """
class ISearchReplaceForm(Interface): """ Interface for Search and Replace form """ findWhat = Text( title=_(u'Find What'), description=_(u'Enter the text to find.'), required=True) replaceWith = Text( title=_(u'Replace With'), description=_(u'Enter the text to replace the original text with.'), required=False) maxResults = Int( title=_(u'Maximum Number of Results'), description=_( u'Maximum number of results to show. ' 'Warning: this has no effect on how many found texts are ' 'replaced when you use the Replace button directly without ' 'using the Preview.'), default=None, required=False) searchSubfolders = Bool( title=_(u'Search Subfolders'), description=_( u'If checked, this will recursively search ' 'through any selected folders and their ' 'children, replacing at each level.'), default=True, required=True) matchCase = Bool( title=_(u'Match Case'), description=_(u'Check the box for a case sensitive search.'), default=False, required=True) onlySearchableText = Bool( title=_(u'Fast search'), description=_( u'Use the catalog to search, just like the search form does. ' 'This only finds keywords, not html tags. ' 'You might have some text fields that are not found this way, ' 'so if not checked, you may find more content, ' 'but it will be slower. ' 'Regardless of this setting, when at least one match is found, ' 'text in all text fields may be replaced.'), required=False, default=True, )
def searchObjects(self, context, find, **kwargs): """ Search objects and optionally do a replace. """ # Get search parameters cpath = context.getPhysicalPath() if 'searchSubFolders' in kwargs: ssf = kwargs['searchSubFolders'] else: ssf = True if 'matchCase' in kwargs: mc = kwargs['matchCase'] else: mc = False if 'replaceText' in kwargs: rtext = kwargs['replaceText'] else: rtext = None if 'doReplace' in kwargs: replace = kwargs['doReplace'] else: replace = False if 'searchItems' in kwargs: sitems = kwargs['searchItems'] else: sitems = None # Get Regex matcher sflags = mc and searchflags or (searchflags | re.IGNORECASE) matcher = re.compile(find, sflags) # Get items to search query = {'query': '/'.join(cpath)} if context.isPrincipiaFolderish and not ssf: query['depth'] = 1 container = aq_parent(context) if isDefaultPage(container, context) and ssf: query['query'] = '/'.join(container.getPhysicalPath()) catalog = getToolByName(context, 'portal_catalog') brains = catalog( path=query, object_provides=searchinterfaces, ) memship = getToolByName(context, 'portal_membership') checkPermission = memship.checkPermission # Match objects results = [] replaced = 0 outdated_catalog = False for b in brains: ipath = b.getPath() if not sitems or ipath in sitems: obj = b.getObject() if not ISearchReplaceable.providedBy(obj): # Warn about this once. if not outdated_catalog: outdated_catalog = True msg = _( 'Item found that does not implement ' 'ISearchReplaceable. You should reindex the ' 'object_provides index of the portal_catalog.') logger.warn(msg) request = getattr(context, 'REQUEST', None) if request is not None: IStatusMessage(request).addStatusMessage( msg, type='warn') continue # Does the user have the modify permission on this object? if not checkPermission(ModifyPortalContent, obj): continue # If there is a filtered list of items, and it # is in the list, or if there is no filter # then process the item if replace and rtext: # Do a replace if sitems: sitem = sitems[ipath] else: sitem = None rep = self._replaceObject(matcher, obj, cpath, rtext, sitem) replaced += rep elif not replace: # Just find the matches and return info result = self._searchObject(matcher, obj) if result: results += result if replace: return replaced else: return results