# ReplaceSupport 1.0.2
# (c) 2000-2004, Stefan H. Holek, stefan@epy.co.at
# http://zope.org/Members/shh/ReplaceSupport
# License: ZPL
# Zope: 2.3-2.7

__doc__ = 'Replace Support'
__version__ = '1.0.2'

import sys, os, string, time, Globals, ExtensionClass
from DocumentTemplate.DT_Util import Eval
from AccessControl.Permission import name_trans
from AccessControl import getSecurityManager
from Globals import HTMLFile
from DocumentTemplate.DT_Util import InstanceDict, TemplateDict
from DateTime import DateTime
from string import find, replace

# 2.4 compatibility hack
try: 
    from DocumentTemplate.DT_Util import expr_globals
except ImportError: 
    expr_globals=None

# 2.4 compatibility hack
try: 
    from AccessControl.DTML import RestrictedDTML
    class td(RestrictedDTML, TemplateDict):
        pass
except ImportError: 
    from AccessControl import getSecurityManager
    class td(TemplateDict):
        def validate(self, inst, parent, name, value, md):
            return getSecurityManager().validate(inst, parent, name, value)


class ReplaceSupport(ExtensionClass.Base):
    """Replace support for Zope Folders"""

    manage_replaceForm=HTMLFile('dtml/replaceForm', globals(), management_view='Replace', 
                                 help_product='ReplaceSupport', help_topic='Replace.stx')
    manage_replaceResult=HTMLFile('dtml/replaceResult', globals(), management_view='Replace',
                                   help_product='ReplaceSupport', help_topic='Replace.stx')

    __ac_permissions__=(
        ('View management screens',
         ('manage_replaceForm', 
          'manage_replaceResult',
          'getReplaceHandlerMetaTypes',)),
        ('Search & replace in objects',
         ('manage_replaceForm', 
          'manage_replaceResult', 
          'getReplaceHandlerMetaTypes',
          'ZopeReplace', 'PrincipiaReplace',)),
        )

    manage_options=(
        {'label':'Replace', 'action':'manage_replaceForm',
         'help':('ReplaceSupport','Replace.stx')},   
        ) 


    def getReplaceHandlerMetaTypes(self):
        """Return list of handler meta types"""
        return self._ReplaceHandlerRegistry.getHandlerMetaTypes()
   

    def ZopeReplace(self, obj, obj_ids=None, obj_metatypes=None,
                    obj_searchterm=None, obj_replaceterm=None, obj_expr=None,
                    obj_mtime=None, obj_mspec=None,
                    obj_permission=None, obj_roles=None,
                    search_sub=0,
                    REQUEST=None, result=None, pre=''):
        """Zope Replace interface"""
        return self._ZopeReplace(obj, obj_ids, obj_metatypes,
                                 obj_searchterm, obj_replaceterm, obj_expr,
                                 obj_mtime, obj_mspec,
                                 obj_permission, obj_roles,
                                 search_sub,
                                 REQUEST, result, pre,
                                 check_change_permission=1)

    PrincipiaReplace = ZopeReplace


    def _ZopeReplace(self, obj, obj_ids=None, obj_metatypes=None,
                     obj_searchterm=None, obj_replaceterm=None, obj_expr=None,
                     obj_mtime=None, obj_mspec=None,
                     obj_permission=None, obj_roles=None,
                     search_sub=0,
                     REQUEST=None, result=None, pre='',
                     check_change_permission=0):
        """Zope Replace interface; unrestricted"""
	
        if result is None:
            result=[]

            if obj_metatypes and 'all' in obj_metatypes:
                obj_metatypes=None
                
            if obj_mtime and type(obj_mtime)==type('s'):
                obj_mtime=DateTime(obj_mtime).timeTime()

            if obj_permission:
                obj_permission=p_name(obj_permission)

            if obj_roles and type(obj_roles) is type('s'):
                obj_roles=[obj_roles]
                
            if obj_expr:
                # Setup expr machinations
                md=td()
                if hasattr(REQUEST, 'AUTHENTICATED_USER'):
                    md.AUTHENTICATED_USER=REQUEST.AUTHENTICATED_USER
                obj_expr=(Eval(obj_expr, expr_globals), md, md._push, md._pop)

        base=obj
        if hasattr(obj, 'aq_base'):
            base=obj.aq_base

        if hasattr(base, 'objectItems'):
	    try:    items=obj.objectItems()
	    except: return result
        else:
	    if getattr(base, 'meta_type', None) == 'Z Class':
	        try:    items=obj.propertysheets.methods.objectItems()
		except: return result
	    else:
		return result

        try: add_result=result.append
        except:
            raise AttributeError, `result`

        for id, ob in items:
            if pre: p="%s/%s" % (pre, id)
            else:   p=id
            
            dflag=0
            if hasattr(ob, '_p_changed') and (ob._p_changed == None):
                dflag=1

            if hasattr(ob, 'aq_base'):
                bs=ob.aq_base
            else: bs=ob

            if (
                (not obj_ids or absattr(bs.getId()) in obj_ids)
                and
                (not obj_metatypes or (hasattr(bs, 'meta_type') and
                 bs.meta_type in obj_metatypes))
                and
                (not obj_searchterm or
                 (hasattr(ob, 'PrincipiaSearchSource') and
                  find(ob.PrincipiaSearchSource(), obj_searchterm) >= 0
                  ))
                and
                (not obj_expr or expr_match(ob, obj_expr))
                and
                (not obj_mtime or mtime_match(ob, obj_mtime, obj_mspec))
                and
                ( (not obj_permission or not obj_roles) or \
                   role_match(ob, obj_permission, obj_roles)
                )
                ):
		# Find and apply a handler for this object
		handler = self._ReplaceHandlerRegistry.getHandler(ob.meta_type)
		if handler and (not check_change_permission or \
                    getSecurityManager().checkPermission(handler.getChangePermission(), ob)):
                    body = replace(handler.getBody(ob), obj_searchterm, obj_replaceterm)
                    params = replace(handler.getParams(ob), obj_searchterm, obj_replaceterm)	
                    handler.manageEdit(ob, body, params)
                    add_result((p, ob))
                    dflag = 0

            is_zclass = getattr(bs, 'meta_type', None) == 'Z Class'
            if search_sub and (hasattr(bs, 'objectItems') or is_zclass):
	        if is_zclass:
		    subob = ob.propertysheets.methods
		    sub_p = '%s/propertysheets/methods' % p
		else:
		    subob = ob
		    sub_p = p
		self._ZopeReplace(subob, obj_ids, obj_metatypes,
                                  obj_searchterm, obj_replaceterm, obj_expr,
                                  obj_mtime, obj_mspec,
                                  obj_permission, obj_roles,
                                  search_sub,
                                  REQUEST, result, sub_p,
                                  check_change_permission)

            if dflag: ob._p_deactivate()

        return result

    _PrincipiaReplace = _ZopeReplace
 

    # Unused and unlikely to change 'cause I do not seem to figure out
    # what to do with it...
if 0:
    def ZopeReplaceAndApply(self, obj, obj_ids=None, obj_metatypes=None,
                         obj_searchterm=None, obj_replaceterm=None, obj_expr=None,
                         obj_mtime=None, obj_mspec=None,
                         obj_permission=None, obj_roles=None,
                         search_sub=0,
                         REQUEST=None, result=None, pre='',
                         apply_func=None, apply_path=''):
        """Zope Replace interface and apply"""

        if result is None:
            result=[]

            if obj_metatypes and 'all' in obj_metatypes:
                obj_metatypes=None
                
            if obj_mtime and type(obj_mtime)==type('s'):
                obj_mtime=DateTime(obj_mtime).timeTime()

            if obj_permission:
                obj_permission=p_name(obj_permission)

            if obj_roles and type(obj_roles) is type('s'):
                obj_roles=[obj_roles]
                
            if obj_expr:
                # Setup expr machinations
                md=td()
                if hasattr(REQUEST, 'AUTHENTICATED_USER'):
                    md.AUTHENTICATED_USER=REQUEST.AUTHENTICATED_USER
                obj_expr=(Eval(obj_expr, expr_globals), md, md._push, md._pop)

        base=obj
        if hasattr(obj, 'aq_base'):
            base=obj.aq_base

        if not hasattr(base, 'objectItems'):
            return result
        try:    items=base.objectItems()
        except: return result

        try: add_result=result.append
        except:
            raise AttributeError, `result`

        for id, ob in items:
            if pre: p="%s/%s" % (pre, id)
            else:   p=id
            
            dflag=0
            if hasattr(ob, '_p_changed') and (ob._p_changed == None):
                dflag=1

            if hasattr(ob, 'aq_base'):
                bs=ob.aq_base
            else: bs=ob

            if (
                (not obj_ids or absattr(bs.getId()) in obj_ids)
                and
                (not obj_metatypes or (hasattr(bs, 'meta_type') and
                 bs.meta_type in obj_metatypes))
                and
                (not obj_searchterm or
                 (hasattr(ob, 'PrincipiaSearchSource') and
                  find(ob.PrincipiaSearchSource(), obj_searchterm) >= 0
                  ))
                and
                (not obj_expr or expr_match(ob, obj_expr))
                and
                (not obj_mtime or mtime_match(ob, obj_mtime, obj_mspec))
                and
                ( (not obj_permission or not obj_roles) or \
                   role_match(ob, obj_permission, obj_roles)
                )
                ):
                if apply_func:
                    apply_func(ob, (apply_path+'/'+p))
                else:
                    add_result((p, ob))
                    dflag=0
                    
            if search_sub and hasattr(bs, 'objectItems'):
                self.ZopeReplaceAndApply(ob, obj_ids, obj_metatypes,
                                      obj_searchterm, obj_replaceterm, obj_expr,
                                      obj_mtime, obj_mspec,
                                      obj_permission, obj_roles,
                                      search_sub,
                                      REQUEST, result, p,
                                      apply_func, apply_path)
            if dflag: ob._p_deactivate()

        return result



def expr_match(ob, ed, c=InstanceDict, r=0):
    e, md, push, pop=ed
    push(c(ob, md))
    try: r=e.eval(md)
    finally:
        pop()
        return r


def mtime_match(ob, t, q, fn=hasattr):
    if not fn(ob, '_p_mtime'):
        return 0    
    return q=='<' and (ob._p_mtime < t) or (ob._p_mtime > t)


def role_match(ob, permission, roles, lt=type([]), tt=type(())):
    pr=[]
    fn=pr.append
    
    while 1:
        if hasattr(ob, permission):
            p=getattr(ob, permission)
            if type(p) is lt:
                map(fn, p)
                if hasattr(ob, 'aq_parent'):
                    ob=ob.aq_parent
                    continue
                break
            if type(p) is tt:
                map(fn, p)
                break
            if p is None:
                map(fn, ('Manager', 'Anonymous'))
                break

        if hasattr(ob, 'aq_parent'):
            ob=ob.aq_parent
            continue
        break

    for role in roles:
        if not (role in pr):
            return 0
    return 1


Globals.default__class_init__(ReplaceSupport)

# Helper functions

def absattr(attr):
    if callable(attr): return attr()
    return attr


def p_name(name):
    return '_' + string.translate(name, name_trans) + '_Permission'


