# Zope Navigator Product
# Copyright (C) 1999  Jonas Juselius
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
# This product includes software developed by Digital Creations
# for use in the Z Object Publishing Environment
# (http://www.zope.org/).
#
_doc__="""ZNavigator 
$Id: ZNavigator.py,v 1.6 2000/09/29 10:37:23 jonas Exp $ """
__version__="2.02"


from Globals import Persistent, HTMLFile, MessageDialog
from OFS import SimpleItem 
from OFS.Folder import Folder
from OFS.ObjectManager import ObjectManager
from OFS.PropertyManager import PropertyManager
from AccessControl.Role import RoleManager
import Acquisition
from DateTime import DateTime
from OFS.Document import Document
import OFS.DTMLMethod 
import string, re, os, types

addNavigatorForm=HTMLFile('addNavigator', globals())
manage_navigator=HTMLFile('manageNavigator', globals())
addNavItemForm=HTMLFile('addNavItem', globals())
manage_navItemForm=HTMLFile('manageNavItem', globals())
miscPropertyForm=HTMLFile('miscPropertyForm', globals())
helpNavigatorForm=HTMLFile('helpNavigator', globals())
helpNavItemForm=HTMLFile('helpNavItem', globals())


def manage_addNavigator(self, id, title='', navlink='', navicon='', 
                        REQUEST=None):
	"""Add a navigator object"""
	top=1
	nav=ZNavigator(id, title, navlink, navicon, top, REQUEST)
	self._setObject(id, nav)
	if REQUEST: 
		return self.manage_main(self, REQUEST)

class ZNavigator(ObjectManager, PropertyManager, RoleManager, 
		SimpleItem.Item):
	"""ZNavigator class: Container of NavItems and ZNavigators"""

	meta_type = 'ZNavigator'
	id	  = 'ZNavigator'
	icon      = 'misc_/ZNavigator/navigator'
	meta_types=(
		{'name': 'NavItem', 'action' : 'manage_addNavItemForm'},
		{'name': 'ZNavigator', 'action' : 'manage_addNavigatorForm'},
		)

	# Specify definitions for tabs:
	manage_options=(
		{'label' : 'Contents', 'action' : 'manage_main'},
		{'label' : 'Misc', 'action' : 'manage_miscProperties'},
		{'label' : 'Properties', 'action' : 'manage_propertiesForm'},
		{'label' : 'Security', 'action' : 'manage_access'},
		{'label' : 'Help', 'action' : 'help_Navigator'},
		)

	# Specify how individual operations add up to "permissions"
	__ac_permissions__=(
		('View management screens', 
			('manage_main', 'manage_tabs',
			'help_Navigator', 'manage',)),
		('Change permissions', ('manage_access',)),
		('Manage ZNavigators', 
			('manage_misc', 'manage_addNavigator',
			'manage_addNavItem', 'manage_propertiesForm', 
			'manage_addNavigatorForm', 'manage_addNavItemForm',
			'manage_miscProperties')),
		('View', 
			('__call__', 'navItems', 'itemURL', 'itemIcon',
			'has_attr')),
		)

	_properties=(
		{'id' : 'title', 'type': 'string', 'mode': 'w'},
		{'id' : 'navlink', 'type': 'string', 'mode': 'w'},
		{'id' : 'navicon', 'type': 'string', 'mode': 'w'},
		)

	def __init__(self, id, title='', navlink='', navicon='', 
	             top=0, REQUEST=None):
		"Initialise a ZNavigator"
		self.id=id
		self.title=title
		self.navlink=navlink
		self.navicon=navicon
		if REQUEST is None:
			raise ValueError, "REQUEST"
		
		if top:
			self.postfix=''
			self.icon_path=''
			bp=self.pathInfo(REQUEST)
			self.base_path=re.sub('manage_addProduct.*', '', bp)
			#idx=string.rindex(bp, '/')
			#self.base_path=bp[:idx+1]

	manage=manage_main=manage_navigator
	manage_miscProperties=miscPropertyForm
	help_Navigator=helpNavigatorForm

	def pathInfo(self, REQUEST):
		url0=REQUEST['URL0']
		server_url=REQUEST['SERVER_URL']
		server_url=re.escape(server_url)
		return re.sub(server_url, '', url0, count=1)
	
	def setprop(self, name, val):
		if hasattr(self.aq_self, name):
			if hasattr(self.aq_parent, name):
				if not val: 
					delattr(self, name)
				elif val == getattr(self.aq_parent, name):
					delattr(self, name)
				else:
					setattr(self, name, val)
			else:
				setattr(self, name, val)
		elif val:
			if val != getattr(self.aq_parent, name):
				setattr(self, name, val)
		
	def manage_misc(self, bp='', pf='', ip='', REQUEST=None):
		"""Misc properties that should be acquired, unless explicitly
		set"""
		self.setprop('base_path', bp)
		self.setprop('icon_path', ip)
		self.setprop('postfix', pf)

		if REQUEST:
			return MessageDialog(title='Modification Accepted',
				message='Your item has been added.',
				action = './manage_main' )
	
	def manage_addNavigator(self, id, title='', navlink='', navicon='', 
	                        REQUEST=None):
		"""Add a navigator object"""
		top=0
		nav=ZNavigator(id, title, navlink, navicon, top, REQUEST)
		self._setObject(id, nav)
		if REQUEST: 
			return self.manage_main(self, REQUEST)

	def manage_addNavItem(self, id, title='', navlink='', navicon='', 
				date='', expire='', newage='0', typeset='1', 
				pf='0', REQUEST=None):
		"Add a NavItem"
		obj=NavItem(id, title, navlink, navicon, newage, typeset, date, 
		            expire, pf)
		self._setObject(id, obj)
		if REQUEST:
			return MessageDialog(title='NavItem Accepted',
				message='Your item has been added.',
				action = './manage_main' )
	
	manage_addNavigatorForm=addNavigatorForm
	manage_addNavItemForm=addNavItemForm
	help_Navigator=helpNavigatorForm
	
	def __call__(self, section=None):
		"""Return self, for use with 'in, var and with' tags.
		   If called with a 'section' argument, return a ZNavigator
		   object, e.g. 'language' or whatever."""
		if section:
			if not hasattr(self, section):
				return None
			ob=self._getOb(section)
			if ob.meta_type == 'ZNavigator':
				return ob
			raise AttributeError, 'Not a ZNavigator: '+ob.meta_type
		return self

	def has_attr(self, attr):
		"Return true if ZNavigator object has attribute 'attr'"
		if hasattr(self, attr):
			return 1
		return 0
	
	def navItems(self): 
		"Like objectValues, but performs validification as well"
		l=[]
		for i in self.objectValues('NavItem'):
			if i.validate():
				l.append(i)
		return l

	def itemURL(self, strip=0):
		"Return the full path to be used in the URL."
		return self.navlink

	def itemIcon(self):
		"Return the full path to be used in the URL."
		return self.navicon
	
	def DateToday(self):
		"Return current system _date_ (no _time_)."
		return DateTime().Date()
		

class NavItem(SimpleItem.Item, PropertyManager, RoleManager, Persistent,
	Acquisition.Implicit): 
	"""ZNavigator Item Class"""

	meta_type='NavItem'
	id='NavItem'
	icon='misc_/ZNavigator/navitem'
	
	idx_html=re.compile('index_html$')
	url_ident=re.compile('.*[^?].*://')
	
	manage_options=(
		{'label' : 'Main', 'action' : 'manage_main'},
		{'label' : 'Properties', 'action' : 'manage_propertiesForm'},
		{'label' : 'Security', 'action' : 'manage_access'},
		{'label' : 'Help', 'action' : 'help_NavItem'},
		)

	_properties=(
		{'id' : 'title', 'type': 'string', 'mode': 'w'},
		{'id' : 'navlink', 'type': 'string', 'mode': 'w'},
		{'id' : 'navicon', 'type': 'string', 'mode': 'w'},
		{'id' : 'active', 'type': 'boolean', 'mode': 'w'},
		)
	
	__ac_permissions__=(
		('View management screens', 
			('manage_main', 'manage_tabs',
			'help_NavItem', 'manage',)),
		('Change permissions', ('manage_access',)),
		('Manage ZNavigators', 
			('manage_propertiesForm', 'manage_editNavItem',)),
		('View', 
			('itemURL', 'itemIcon', 'isActive', 'inPath', 
			'isNew',)),
		)


	def __init__(self, id, title, navlink, navicon, newage, typeset, date, 
	             expire, pf):
		self.id=id

		if not title:
			msg="Missing title!"
			raise ValueError, msg
		self.title=title
		self.navlink=navlink
		
		if type(typeset) == types.StringType:
			typeset=string.atoi(typeset)
		if typeset < 1 or typeset > 5:
			raise ValueError, "Typeset out of range!"
		self.typeset=typeset
		
		if newage:
			self.newage=string.atoi(newage)
		else:
			self.newage=0

		if pf == '1': self.pf=1
		else: self.pf=0

		if expire: 
			self.expire=expire
		else: 
			self.expire=''
		
		if date: 
			self.date=date
		else: 
			self.date=DateTime().date()
		
		if navicon: self.navicon=navicon
		else: self.navicon=''

		self.active=1

	def itemURL(self, strip=0):
		"Return the full path to be used in the URL."
		if not self.navlink: 
			if strip:
				return self.base_path
			elif self.pf:
				if self.postfix:
					return self.base_path+'?'+self.postfix
				else: 
					return self.base_path
			else:
				return self.base_path
		
		if self.url_ident.match(self.navlink):
			return self.navlink
		
		if strip:
			idx=string.find(self.navlink, '?')
			if idx == -1: path=self.navlink
			else: path=self.navlink[:idx]
		elif self.pf:
			idx=string.find(self.navlink, '?')
			if self.postfix:
				if idx == -1: 
					path=self.navlink+'?'+self.postfix
				else: 
					path=self.navlink+'&'+self.postfix
			else:
				path=self.navlink
		else:
			path=self.navlink

		if path[0] != '/':
			path=self.base_path+path
		return path
	
	def itemIcon(self):
		"Return the full path to be used in the URL."
		if self.navicon == '': return ''
		if self.navicon[0] == '/':
			return self.navicon
		if self.icon_path:
			if self.icon_path[-1] == '/':
				return self.icon_path+self.navicon
			else:
				return self.icon_path+'/'+self.navicon
		return self.navicon
	
	manage=manage_main=manage_navItemForm
	help_NavItem=helpNavItemForm

	def manage_editNavItem(self, title='', navlink='', navicon='', date='', 
		expire='', typeset='1', newage='0', pf='0', REQUEST=None):
		"Edit item"
		
		if not title:
			msg="Missing title!"
			raise ValueError, msg
		self.title=title
		
		self.navlink=navlink

		if type(typeset) == types.StringType:
			typeset=string.atoi(typeset)
		if typeset < 1 or typeset > 5:
			raise ValueError, "Typeset out of range!"
		self.typeset=typeset

		if newage:
			self.newage=string.atoi(newage)
		else:
			self.newage=0
		
		if pf == '1': self.pf=1
		else: self.pf=0

		if expire: 
			self.expire=expire
		else: 
			self.expire=''
		
		if date: 
			self.date=date
		else: 
			self.date=DateTime().date()
		
		if navicon: 
			self.navicon=navicon
		else: 
			self.navicon=''

		if REQUEST:
			return MessageDialog(title='Edited',
				message='Entry has been edited.',
				action ='../manage_main',)
		
	def pathInfo(self, REQUEST):
		url0=REQUEST['URL0']
		server_url=REQUEST['SERVER_URL']
		server_url=re.escape(server_url)
		return re.sub(server_url, '', url0, count=1)
	
	def isActive(self, REQUEST):
		"""Returns true if itemURL() equals path_info""" 
		if not self.active: 
			return 0
		
		path_info=self.pathInfo(REQUEST)
		path_info=self.idx_html.sub('', path_info)
		
		path=self.itemURL(strip=1)
		## Check for external links  (http://, ftp://, ...)
		if self.url_ident.match(path):
			return 0
		path=self.idx_html.sub('', path)
		
		if path_info[-1] == '/':        # p_info is a folder
			path_info=path_info[:-1]   # strip last '/'
		if path and path[-1] == '/':
			path=path[:-1]
		if path == path_info:
			return 1
		return 0
	
	def inPath(self, REQUEST):
		"""Returns true if itemURL() is in path_info""" 
		if REQUEST.has_key('navigator_top'):
			top=REQUEST['navigator_top']
		else:
			top='/'
		
		path=self.itemURL(strip=1)
		ridx=string.rindex(path, '/')
		path=path[:ridx+1]
		
		path_info=self.pathInfo(REQUEST)
		ridx=string.rindex(path_info, '/')
		path_info=path_info[:ridx+1]

		if path == top:
			if path_info == top:
				return 1
			return 0

		if re.match(path, path_info):
			return 1
		return 0

	
	def validate(self): 
		"Check if the link is publishable"
		if self.expire:
			if DateTime(self.expire).isPast():
				return 0
		if DateTime(self.date).isFuture():
			return 0
		return 1
	
	def isNew(self): 
		"Check if the link is still new"
		if self.newage == -1:
			return 1
		if self.newage > 0:
			dt=DateTime(self.date)+self.newage
			if dt.isPast():
				return 0
			else: 
				return 1
		return 0

