Back to index

plone3  3.1.7
txtfilter.py
Go to the documentation of this file.
00001 ##########################################################
00002 #
00003 # Licensed under the terms of the GNU Public License
00004 # (see docs/LICENSE.GPL)
00005 #
00006 # Copyright (c) 2005:
00007 #   - The Open Planning Project (http://www.openplans.org/)
00008 #   - Whit Morriss <whit@kalistra.com>
00009 #   - Rob Miller <rob@kalistra.com> (RaFromBRC)
00010 #   - and contributors
00011 #
00012 ##########################################################
00013 from interfaces import IAmWickedField, IAmWicked, IFieldEvent 
00014 from interfaces import ICacheManager, IValueToString, IScope
00015 from interfaces import IWickedFilter, IWickedQuery, IBacklinkManager
00016 from normalize import titleToNormalizedId as normalize
00017 from wicked import utils
00018 from wicked.fieldevent.interfaces import EndFiltrationException
00019 from wicked.fieldevent.interfaces import ITxtFilterList, IFieldRenderEvent
00020 from wicked.fieldevent.interfaces import IFieldValueSetter, IFieldStorageEvent
00021 from wicked.fieldevent.interfaces import IFieldEvent
00022 from zope.component.interfaces import ComponentLookupError
00023 from wicked.fieldevent.txtfilter import TxtFilter
00024 from zope.component import getMultiAdapter, adapts, adapter
00025 from zope.interface import implements, implementer, Interface, alsoProvides
00026 
00027 import re
00028 
00029 _marker = object()
00030 
00031 pattern1 = re.compile(r'\(\(([\w\W]+?)\)\)') # matches ((Some Text To link 123))
00032 pattern2 = re.compile(r'\[\[([\w\W]+?)\]\]') # matches [[Some Text To link 123]]
00033 
00034 def removeParens(wikilink):
00035     wikilink.replace('((', '')
00036     wikilink.replace('))', '')
00037     wikilink.replace('[[', '')
00038     wikilink.replace(']]', '')
00039     return wikilink
00040 
00041 class WickedFilter(TxtFilter):
00042     implements(IWickedFilter)
00043     adapts(IAmWickedField, IAmWicked, IFieldEvent)
00044 
00045     name = 'Wicked Filter'
00046 
00047     #pattern = [pattern1, pattern2]
00048     query_iface = IWickedQuery
00049     _encoding = 'UTF8'
00050 
00051     def __init__(self, field, instance, event):
00052         super(WickedFilter, self).__init__(field, instance, event)
00053         self.section = field.__name__
00054         self.pattern = None
00055 
00056     @utils.memoizedproperty
00057     def scope(self):
00058         try:
00059             return getMultiAdapter((self.field, self.context), IScope)
00060         except ComponentLookupError:
00061             return ''
00062 
00063     # avoid global lookup
00064     getMatch = staticmethod(utils.getMatch)
00065     _normalize = staticmethod(normalize)
00066 
00067     # optimization
00068     @utils.memoize
00069     def normalize(self, value):
00070         return self._normalize(value)
00071 
00072     @utils.memoizedproperty
00073     def encoding(self):
00074         """AT hack"""
00075         try:
00076             encoding = self.context.getCharset()
00077         except AttributeError:
00078             encoding = self._encoding
00079         return encoding
00080     
00081     def _filterCore(self, chunk, **kwargs):
00082         normalled = self.normalize(chunk)
00083         links=self.getLinks(chunk, normalled)
00084         self.renderer.load(links, chunk)
00085         return self.renderer().encode(self.encoding)
00086 
00087     @property
00088     def filtered_text(self):
00089         """syntax preprocessing"""
00090         return super(WickedFilter, self).filtered_text
00091     
00092     @utils. memoize
00093     @utils.linkcache
00094     def getLinks(self, chunk, normalled):
00095         self.resolver.configure(chunk, normalled, self.scope)
00096         brains = self.resolver.search
00097         if not brains:
00098             brains = self.resolver.scopedSearch
00099         links = [utils.packBrain(b) for b in brains if b]
00100         return links
00101 
00102     @utils.memoizedproperty
00103     def resolver(self):
00104         """
00105         @return query object
00106         """
00107         return self.query_iface(self.context)
00108 
00109     @utils.memoizedproperty    
00110     def backlinker(self):
00111         return getMultiAdapter((self, self.context), IBacklinkManager)
00112 
00113     def manageLink(self, obj, link):
00114         self.backlinker.manageLink(obj, link)
00115 
00116     def unlink(self, uid):
00117         self.backlinker.unlink(uid)
00118         
00119     def manageLinks(self, links):
00120         self.backlinker.manageLinks(links)
00121         
00122     @utils.memoizedproperty
00123     def cache(self):
00124         return getMultiAdapter((self, self.context), ICacheManager)
00125     
00126     @utils.memoizedproperty
00127     def renderer(self):
00128         # @@ better way to get request? maybe a txtfilter should be a view?
00129         renderer = getMultiAdapter((self.context, self.context.REQUEST), Interface, 'link_renderer')
00130         renderer.section = self.section
00131         # hook for zope2 aq wrapper
00132         if hasattr(renderer, '__of__'):
00133             return renderer.__of__(self.context)
00134         return renderer
00135 
00136     def __call__(self):
00137         if self.event.kwargs.get('raw', False):
00138             raise EndFiltrationException('Kwargs flag for raw return')
00139         super(WickedFilter, self).__call__()
00140 
00141 ##     def removeParens(wikilink):
00142 ##         wikilink.replace('((', '')
00143 ##         wikilink.replace('))', '')
00144 ##         return wikilink
00145     removeParens=staticmethod(removeParens)
00146 
00147 
00148 class BrackettedWickedFilter(WickedFilter):
00149     """media wiki style bracket matching"""
00150     pattern=pattern2
00151     def removeParens(wikilink):
00152         wikilink.replace('[[', '')
00153         wikilink.replace(']]', '')
00154         return wikilink
00155     removeParens=staticmethod(removeParens)
00156 
00157 NAME = WickedFilter.name
00158 
00159 
00160 ## event handlers ##
00161 
00162 class WickedListener(object):
00163 
00164     def __init__(self, pattern):
00165         self.pattern = pattern
00166         
00167     def render(self, field, instance, event):
00168         """standalone wicked filter (ie not as a txtfilter). Optimal if
00169         not using txtfilters"""
00170 
00171         if event.kwargs.get('raw', False):
00172             return 
00173 
00174         wicked = getMultiAdapter((field, instance, event), IWickedFilter)
00175         wicked.pattern = self.pattern
00176         try:
00177             wicked()
00178         except EndFiltrationException:
00179             pass
00180 
00181     def store(self, field, event):
00182         try:
00183             wicked = utils.getWicked(field, event.instance, event)
00184         except ComponentLookupError:
00185             # no adapter registered for this type currently
00186             # @@ This might be handle better by redispatch
00187             return
00188         
00189         wicked.pattern = self.pattern
00190         if not event.value:
00191             return
00192 
00193         value = event.value
00194         value_str = value
00195 
00196         try:
00197             # this block handle conversions for file uploads and
00198             # atapi.BaseUnit or any other not quite plain text "value objects"
00199             value_str = getMultiAdapter((value, field), IValueToString)
00200         except ComponentLookupError:
00201             pass
00202 
00203         found = wicked.findall(value_str)
00204 
00205         if not len(found):
00206             return
00207 
00208         new_links = [wicked.removeParens(link) for link in found]
00209         wicked.manageLinks(new_links)
00210 
00211 from zope.interface import classImplements
00212 
00213 @implementer(IFieldValueSetter)
00214 @adapter(IAmWickedField, IFieldStorageEvent)
00215 def backlink_handler(field, event):
00216     try:
00217         wicked = utils.getWicked(field, event.instance, event)
00218     except ComponentLookupError:
00219         # no adapter registered for this type currently
00220         # @@ This might be handle better by redispatch
00221         return
00222         
00223     if not event.value:
00224         return
00225 
00226     value = event.value
00227     value_str = value
00228 
00229     try:
00230         # this block handle conversions for file uploads and
00231         # atapi.BaseUnit or any other not quite plain text "value objects"
00232         value_str = getMultiAdapter((value, field), IValueToString)
00233     except ComponentLookupError:
00234         pass
00235 
00236 
00237     found = wicked.findall(value_str)
00238 
00239     if not len(found):
00240         return
00241 
00242     new_links = [wicked.removeParens(link) for link in found]
00243     wicked.manageLinks(new_links)
00244 
00245 pattern1_listeners = WickedListener(pattern1)
00246 pattern2_listeners = WickedListener(pattern2)
00247 
00248 ## hack around fact that functions can not be pickled ##
00249 
00250 class wicked_listener(object):
00251     __init__=staticmethod(pattern1_listeners.render)
00252 
00253 class bracketted_wicked_listener(object):
00254     __init__=staticmethod(pattern2_listeners.render)
00255     
00256 class backlink(object):
00257     implements(IFieldValueSetter)
00258     adapts(IAmWickedField, IFieldStorageEvent)
00259     
00260     __init__ = staticmethod(pattern1_listeners.store)
00261 
00262 BacklinkRegistrationProxy = backlink
00263 
00264 class brackettedbacklink(object):
00265     implements(IFieldValueSetter)
00266     adapts(IAmWickedField, IFieldStorageEvent)
00267     
00268     __init__ = staticmethod(pattern2_listeners.store)    
00269 
00270 
00271 ## toy example code ##
00272     
00273 @adapter(IAmWickedField, IAmWicked, IFieldRenderEvent)
00274 @implementer(ITxtFilterList)
00275 def filter_list(field, context, event):
00276     """example adapter for a one item list for ordering a txtfilter
00277     pipeline involving wicked only.  Practically useless, for example
00278     only"""
00279     return [NAME]