More fixes to the mesh tool

"Garth T Kidd" <garth-OnzZ1s1DREKDegMON/[email protected]> Tue, 6 Jul 2004 18:08:42 +1000
Newsgroups gmane.comp.pythin.pyds.devel
Organization Deadly Bloody Serious
Message-ID <[email protected]>
Relying on MeshTool? Finding that when you make weblog entries with
unresolvable internal links, PyDS tries to create new items in the weblog
tool? Would you prefer new Wiki or Story items? 

* MeshTool now has preferences, stored in the preferences tool.

* The MeshTool `status page`_ now also displays the 404 renderer.

* For 404 renders and link target creators, you can click on the green 'yes'
to set that tool as the default. This over-rides the default tool order. 

* Having set the default (try 'wiki' for both), PyDS will behave better. 

* If the 404 renderer or link target creator throws ``KeyError`` or
``ValueError``, MeshTool will keep trying with other tools with the right
signature. If it can't find any tool that works, it'll cop out gracefully
and point to ``about:blank``. 

* Unresolved links now use the appropriate ``.undefined*`` classes when
rendered locally if if WikiTool, StoryTool, or BlogmarkTool are the link
target creators, and when rendered to the cloud if the WikiTool is the 404
handler. No other tools are currently 404 handlers. 

Next, I'll probably fix the crufty ambiguity resolver and the link creation
ambiguity the same way: linking to a page from which you can select what to
do. Anyone got some funky JavaScript that'll give me a pop-up menu to save a
page refresh? 

Whilst I'm asking questions: can anyone tell me why ``node['title']=`` isn't
setting link titles any more? Did docutils change, or something? 

.. _status page: http://localhost:4334/mesh/

.. If you put the link in the middle of the list, you end up with two lists.
Docutils still has a little way to go in some areas. :) 

WARNING: the diff affects an awful lot of files, including ``pyds.css``. If
you're automatically copying that over, you'll want to check the diff before
proceeding. If in doubt, wait for Georg to update CVS. 

Regards,
Garth.
20040706-1658-meshprefs.diff (application/octet-stream, 17.7 KB)
Index: pyds.css
===================================================================
RCS file: /pyds/PyDS/pyds.css,v
retrieving revision 1.23
diff -c -r1.23 pyds.css
*** pyds.css	26 Jan 2004 10:51:59 -0000	1.23
--- pyds.css	6 Jul 2004 07:35:57 -0000
***************
*** 61,68 ****
--- 68,88 ----
  a:hover {
  	text-decoration: underline;
  }
+ .undefinedstory {
+ 	/* used for undefined links liable to be created in StoryTool */
+ 	color: red;
+ }
+ .undefinedpost {
+ 	/* used for undefined links liable to be created in BlogmarkTool */
+ 	color: red;
+ }
  .undefinednode {
+ 	/* used for undefined links liable to be created in WikiTool */
  	color: red;
+ }
+ .meshsetdefaulttool {
+ 	text-decoration: none; 
+ 	color: green; 
  }
  p { 
  	line-height: 1.5em; 
Index: PyDS/BlogmarkTool.py
===================================================================
RCS file: /pyds/PyDS/PyDS/BlogmarkTool.py,v
retrieving revision 1.28
diff -c -r1.28 BlogmarkTool.py
*** PyDS/BlogmarkTool.py	26 Apr 2004 18:15:04 -0000	1.28
--- PyDS/BlogmarkTool.py	6 Jul 2004 07:36:16 -0000
***************
*** 327,333 ****
      # Return a link to a creation page for an item, based on link text. 
  	def getDesktopCreateLinkDetailsForProposedLinkText(self, linkText): 
  		"""Return the edit URL for a non-existent item, based on `linkText`."""
! 		return ('undefinedpost', self.getUrl('edit_html', id=_munge(linkText)))
  
  	# Render an item by its unique ID
  	def renderItemByUniqueId(self, uniqueId):
--- 327,335 ----
      # Return a link to a creation page for an item, based on link text. 
  	def getDesktopCreateLinkDetailsForProposedLinkText(self, linkText): 
  		"""Return the edit URL for a non-existent item, based on `linkText`."""
! 		return ('undefinedpost', 
! 		        self.getUrl('edit_html', id=_munge(linkText)),
! 			_("Create this item in BlogmarkTool"))
  
  	# Render an item by its unique ID
  	def renderItemByUniqueId(self, uniqueId):
Index: PyDS/MeshTool.py
===================================================================
RCS file: /pyds/PyDS/PyDS/MeshTool.py,v
retrieving revision 1.23
diff -c -r1.23 MeshTool.py
*** PyDS/MeshTool.py	31 May 2004 13:26:28 -0000	1.23
--- PyDS/MeshTool.py	6 Jul 2004 07:56:59 -0000
***************
*** 70,84 ****
  	_toolsWithStrayLinkResolvers = tools
  	return tools
  
! def orderToolsByContext(foundTools = None): 
  	"Return the prioritised list of tools, with the current tool first."
  	if foundTools is None: 
  		foundTools = PyDS.Tool.getTools() # ordered by priority
! 	if [t for t in foundTools if t.name == _flet.current.name]:
  		tools = [_flet.current] 
  	else: 
  		tools = []
! 	tools.extend([t for t in foundTools if t.name != _flet.current.name])
  	return tools
  
  def scanToolsForSignatures(toolList, *methodList): 
--- 70,95 ----
  	_toolsWithStrayLinkResolvers = tools
  	return tools
  
! def orderToolsByContext(foundTools = None, prefer = ''): 
  	"Return the prioritised list of tools, with the current tool first."
  	if foundTools is None: 
  		foundTools = PyDS.Tool.getTools() # ordered by priority
! 	currentToolName = _flet.current.name
! 	
! 	# If one of the found tools matches the current tool, make it first. 
! 	if [t for t in foundTools if t.name == currentToolName]:
  		tools = [_flet.current] 
  	else: 
  		tools = []
! 	
! 	# If one of the found tools matches `prefer`, make it second. 
! 	if prefer and prefer != currentToolName: 
! 		prefTools = [t for t in foundTools if t.name == prefer]
! 		if prefTools: 
! 			tools.append(prefTools[0])
! 
! 	# Append the remaining tools and return the list. 
! 	tools.extend([t for t in foundTools if not t in tools])
  	return tools
  
  def scanToolsForSignatures(toolList, *methodList): 
***************
*** 114,130 ****
  		self.cloud404PageRenderer = \
  			scan(tools, 'getCloudLinkDetailsFor404')
  			
! 	def contextSortedLinkTextResolvers(self): 
  		"Return a context sorted list of link text resolvers."
! 		return orderToolsByContext(self.linkTextResolvers)
  
! 	def contextSortedUnresolvedLinkCreators(self): 
  		"Return a context sorted list of unresolved link creators."
! 		return orderToolsByContext(self.unresolvedLinkCreators)
  		
! 	def contextSortedCloud404PageRenderer(self): 
  		"Return a context sorted list of renderers for 404 cloud pages."
! 		return orderToolsByContext(self.cloud404PageRenderer)
  		
  # Notes for the new stray link resolution method: 
  # OPTIONAL .getCloudLinkDetailsForUniqueId(uniqueId) => (cssclass, URI, title) or (cssclass, None, errmsg)
--- 125,141 ----
  		self.cloud404PageRenderer = \
  			scan(tools, 'getCloudLinkDetailsFor404')
  			
! 	def contextSortedLinkTextResolvers(self, prefer=''): 
  		"Return a context sorted list of link text resolvers."
! 		return orderToolsByContext(self.linkTextResolvers, prefer)
  
! 	def contextSortedUnresolvedLinkCreators(self, prefer=''): 
  		"Return a context sorted list of unresolved link creators."
! 		return orderToolsByContext(self.unresolvedLinkCreators, prefer)
  		
! 	def contextSortedCloud404PageRenderer(self, prefer=''): 
  		"Return a context sorted list of renderers for 404 cloud pages."
! 		return orderToolsByContext(self.cloud404PageRenderer, prefer)
  		
  # Notes for the new stray link resolution method: 
  # OPTIONAL .getCloudLinkDetailsForUniqueId(uniqueId) => (cssclass, URI, title) or (cssclass, None, errmsg)
***************
*** 381,391 ****
  		self.itemLinks = itemDetail.links
  		toolCache = ToolCache()
  		self.linkTextResolvers = toolCache.contextSortedLinkTextResolvers()
! 		self.unresolvedLinkCreators = toolCache.contextSortedUnresolvedLinkCreators()
! 		self.cloud404PageRenderer = toolCache.contextSortedCloud404PageRenderer()
! 		#print "looking up matches for links from %s:%s..." % (
! 		#	itemDetail.homeTool, 
! 		#	itemDetail.uniqueId )
  	
  	def resolveStrayLink(self, stray): 
  		"""Resolve a stray by consulting all tools capable of doing so."""
--- 392,402 ----
  		self.itemLinks = itemDetail.links
  		toolCache = ToolCache()
  		self.linkTextResolvers = toolCache.contextSortedLinkTextResolvers()
! 		prefs = meshTool.getPreferencesTool()
! 		targ404 = prefs.get('mesh', 'targ404')
! 		targcreate = prefs.get('mesh', 'targcreate')
! 		self.unresolvedLinkCreators = toolCache.contextSortedUnresolvedLinkCreators(targ404)
! 		self.cloud404PageRenderer = toolCache.contextSortedCloud404PageRenderer(targcreate)
  	
  	def resolveStrayLink(self, stray): 
  		"""Resolve a stray by consulting all tools capable of doing so."""
***************
*** 409,422 ****
  
  		# If we have no matches, return something appropriate. 
  		if len(matches) == 0: 
  			if _flet.desktop: 
! 				if len(self.unresolvedLinkCreators): 
! 					# TODO: just taking the first is a cheat
! 					return self.unresolvedLinkCreators[0].getDesktopCreateLinkDetailsForProposedLinkText(stray)
  			else: 
! 				if len(self.cloud404PageRenderer): 
! 					# TODO: just taking the first is a cheat
! 					return self.cloud404PageRenderer[0].getCloudLinkDetailsFor404()
  
  		# If we have matches, return something appropriate.
  		# TODO: this is not a good way of handling ambiguity. 
--- 420,447 ----
  
  		# If we have no matches, return something appropriate. 
  		if len(matches) == 0: 
+ 			# Select which tools to ask, and how to ask them.
  			if _flet.desktop: 
! 				def getDetails(tool, stray): 
! 					return tool.getDesktopCreateLinkDetailsForProposedLinkText(stray)
! 				tools = self.unresolvedLinkCreators
  			else: 
! 				def getDetails(tool, stray): 
! 					return tool.getCloudLinkDetailsFor404()
! 				tools = self.cloud404PageRenderer
! 
! 			# Iterate through the tools, trying to get link 
! 			# details from the named method. 
! 			for tool in tools: 
! 				try: 
! 					return getDetails(tool, stray) 
! 				except (KeyError, ValueError): 
! 					pass
! 
! 			# Cop out if we couldn't find anything. 
! 			return ('undefinednode', 
! 			        'about:blank', 
! 				_('PyDS failed to figure out somewhere better to send you. Sorry about that.'))
  
  		# If we have matches, return something appropriate.
  		# TODO: this is not a good way of handling ambiguity. 
***************
*** 495,503 ****
  				if method is None: 
  					match = self.resolveStrayLink(refuri)
  					if match: 
! 						cssclass, uri = match
! 						node['class'] = cssclass
! 						node['refuri'] = uri
  			return # whether resolved or not
  		
  		if node.hasattr('refid'): 
--- 520,529 ----
  				if method is None: 
  					match = self.resolveStrayLink(refuri)
  					if match: 
! 						applymatch(node, match)
! 						#cssclass, uri = match
! 						#node['class'] = cssclass
! 						#node['refuri'] = uri
  			return # whether resolved or not
  		
  		if node.hasattr('refid'): 
***************
*** 578,585 ****
  		db.getas("items[homeTool:S,uniqueId:S,handle:I,exists:I]")
  		db.getas("links[originHandle:I,linkText:S,destHandle:I]")
  		db.getas("state[maxhandle:I]")
- 		if 0:
- 			db.getas("prefs[slackness:I]")
  
  	_createdb = classmethod(_createdb)
  
--- 604,609 ----
***************
*** 598,608 ****
  			# Known texts are registered in items. 
  			# The links table gathers the links. If destHandle is 0, the link 
  			# is not resolved. 
- 			if 0:
- 				self.prefs = self.db.view('prefs')
- 				if len(self.prefs) == 0:
- 					self.prefs.append(slackness=1)
- 					self._commit()
  			self.importantTool = 1
  		finally: self._release()
  	
--- 622,627 ----
***************
*** 867,880 ****
  		p = lambda t: html.append('<p>%s</p>' % (t,))
  		a('<h2>%s</h2>' % _('Status'))
  		a(self._status())
! 		signatureSpecs = (
  			('getCloudUrlMatchingStrayLink', _('old stray link resolver')),
  			('getUniqueIdsMatchingLinkText', _('resolve stray link')),
  			('getCloudLinkDetailsForUniqueId', _('return cloud link')),
  			('getDesktopDisplayLinkDetailsForUniqueId', _('return show/edit link')),
  			('getDesktopCreateLinkDetailsForProposedLinkText', _('return create link')),
  			('renderItemByUniqueId', _('render item'))
! 			)
  		signatures = [signatureSpec[0] for signatureSpec in signatureSpecs]
  		signatureDescriptions = dict(signatureSpecs)
  		tools = PyDS.Tool.getTools()
--- 886,904 ----
  		p = lambda t: html.append('<p>%s</p>' % (t,))
  		a('<h2>%s</h2>' % _('Status'))
  		a(self._status())
! 		signatureSpecs = [
  			('getCloudUrlMatchingStrayLink', _('old stray link resolver')),
  			('getUniqueIdsMatchingLinkText', _('resolve stray link')),
  			('getCloudLinkDetailsForUniqueId', _('return cloud link')),
+ 			('getCloudLinkDetailsFor404', _('cloud 404 page renderer')), 
  			('getDesktopDisplayLinkDetailsForUniqueId', _('return show/edit link')),
  			('getDesktopCreateLinkDetailsForProposedLinkText', _('return create link')),
  			('renderItemByUniqueId', _('render item'))
! 			]
! 		signatureRadios = {
! 			'getDesktopCreateLinkDetailsForProposedLinkText': 'targcreate',
! 			'getCloudLinkDetailsFor404': 'targ404'
! 			}
  		signatures = [signatureSpec[0] for signatureSpec in signatureSpecs]
  		signatureDescriptions = dict(signatureSpecs)
  		tools = PyDS.Tool.getTools()
***************
*** 918,936 ****
  				align='left')
  			for tool in toolsWithSigs: 
  				if sigToolBucket.get(tool): 
! 					ca('yes')
  				else: 
! 					ca('no')
  			row(cells)
  		a('</table>')
  		p('<a href="%s">%s</a>' % (
  			req.getUrl('clear_redir'),
  			_('Click here to clear all cached links')))
- 		p('%d <a href="%s">%s</a>' % (
- 			len(self.items), 
- 			self.getUrl('items_html'),
- 			_('items')))
  		return req.renderPage('BaseTemplate', body='\n'.join(html))
  
  	def items_html(self, req): 
  		req.setLocalValue('title', _('Mesh Tool'))
--- 942,976 ----
  				align='left')
  			for tool in toolsWithSigs: 
  				if sigToolBucket.get(tool): 
! 					disp = 'yes'
  				else: 
! 					disp = 'no'
! 				if signature in signatureRadios.keys() and disp=='yes': 
! 					pref = signatureRadios[signature]
! 					prefs = self.getPreferencesTool()
! 					target = prefs.get('mesh', pref)
! 					if target == tool.name: 
! 						disp = '<strong>%s</strong>' % disp
! 					disp = '<a href="%s" class="%s">%s</a>' % (
! 						req.getUrl('setdefaulttool_redir', 
! 						           pref=pref, 
! 							   target=tool.name), 
! 						'meshsetdefaulttool', 
! 						disp)
! 				ca(disp)
  			row(cells)
  		a('</table>')
  		p('<a href="%s">%s</a>' % (
  			req.getUrl('clear_redir'),
  			_('Click here to clear all cached links')))
  		return req.renderPage('BaseTemplate', body='\n'.join(html))
+ 
+ 	def setdefaulttool_redir(self, req): 
+ 		pref = req.getFirstValue('pref')
+ 		target = req.getFirstValue('target')
+ 		prefs = self.getPreferencesTool()
+ 		prefs.set('mesh', pref, target)
+ 		return req.getUrl('index_html')
  
  	def items_html(self, req): 
  		req.setLocalValue('title', _('Mesh Tool'))
Index: PyDS/PreferencesTool.py
===================================================================
RCS file: /pyds/PyDS/PyDS/PreferencesTool.py,v
retrieving revision 1.44
diff -c -r1.44 PreferencesTool.py
*** PyDS/PreferencesTool.py	25 Mar 2004 09:40:05 -0000	1.44
--- PyDS/PreferencesTool.py	6 Jul 2004 07:15:00 -0000
***************
*** 59,64 ****
--- 59,69 ----
  		('username', 'S20', _('Username')),
  		('password', 'P20', _('Password')),
  	],
+ 	[('mesh', _('Mesh Tool Configuration'), _('These are preferences adjusting automatic link resolution')), 
+ 		('targ404', 'S20', _('Preferred tool to issue 404 page)')), 
+ 		('targcreate', 'S20', _('Preferred tool to create new items via unresolved links')),
+ 	],
+ 		
  ]
  
  globalname = ''
***************
*** 321,326 ****
--- 326,339 ----
  				self.hooks[pb][pf] =[]
  			self.hooks[pb][pf].append(func)
  		finally: self._release()
+ 
+ 	# set the value of a preference in a preference block
+ 	def set(self, pb, pref, val): 
+ 		self._set(pb, pref, val)
+ 		if self.get(pb, pref) == val: 
+ 			return 1
+ 		else: 
+ 			return 0
  
  	def _set(self, pb, pref, val):
  		try:
Index: PyDS/StoryTool.py
===================================================================
RCS file: /pyds/PyDS/PyDS/StoryTool.py,v
retrieving revision 1.61
diff -c -r1.61 StoryTool.py
*** PyDS/StoryTool.py	26 Apr 2004 18:15:04 -0000	1.61
--- PyDS/StoryTool.py	6 Jul 2004 07:32:47 -0000
***************
*** 710,716 ****
  	# Return a link to a creation page for an item, based on link text. 
  	def getDesktopCreateLinkDetailsForProposedLinkText(self, linkText): 
  		"""Return the edit URL for a non-existent item, based on `linkText`."""
! 		return ('undefinedstory', self.getUrl('edit_html', title=linkText))
  
  	# Render an item by its unique ID
  	def renderItemByUniqueId(self, uniqueId):
--- 710,718 ----
  	# Return a link to a creation page for an item, based on link text. 
  	def getDesktopCreateLinkDetailsForProposedLinkText(self, linkText): 
  		"""Return the edit URL for a non-existent item, based on `linkText`."""
! 		return ('undefinedstory', 
! 		        self.getUrl('edit_html', title=linkText), 
! 			_("Create this item in StoryTool"))
  
  	# Render an item by its unique ID
  	def renderItemByUniqueId(self, uniqueId):
Index: PyDS/WeblogTool.py
===================================================================
RCS file: /pyds/PyDS/PyDS/WeblogTool.py,v
retrieving revision 1.120
diff -c -r1.120 WeblogTool.py
*** PyDS/WeblogTool.py	31 May 2004 13:26:28 -0000	1.120
--- PyDS/WeblogTool.py	6 Jul 2004 05:18:16 -0000
***************
*** 312,321 ****
  		else: 
  			raise KeyError, uniqueId
  
!     # Return a link to a creation page for an item, based on link text. 
! 	def getDesktopCreateLinkDetailsForProposedLinkText(self, linkText): 
! 		"""Return the edit URL for a non-existent item, based on `linkText`."""
! 		return ('undefinedpost', self.getUrl('edit_html', id=_munge(linkText)))
  
  	# Render an item by its unique ID
  	def renderItemByUniqueId(self, uniqueId):
--- 312,321 ----
  		else: 
  			raise KeyError, uniqueId
  
! 	# Return a link to a creation page for an item, based on link text. 
! 	#def getDesktopCreateLinkDetailsForProposedLinkText(self, linkText): 
! 	#	"""Return the edit URL for a non-existent item, based on `linkText`."""
! 	#	return ('undefinedpost', self.getUrl('edit_html', id=_munge(linkText)))
  
  	# Render an item by its unique ID
  	def renderItemByUniqueId(self, uniqueId):
Index: PyDS/WikiTool.py
===================================================================
RCS file: /pyds/PyDS/PyDS/WikiTool.py,v
retrieving revision 1.24
diff -c -r1.24 WikiTool.py
*** PyDS/WikiTool.py	31 May 2004 13:26:28 -0000	1.24
--- PyDS/WikiTool.py	6 Jul 2004 07:33:43 -0000
***************
*** 263,269 ****
  
  	# Return the upload target for a nonexistent page for the current context. 
  	def getCloudLinkDetailsFor404(self):
! 		return self.getCloudLinkDetailsForUniqueId('404')
  
  	# Return a URL for an edit page for a given item.
  	def getDesktopDisplayLinkDetailsForUniqueId(self, uniqueId): 
--- 263,270 ----
  
  	# Return the upload target for a nonexistent page for the current context. 
  	def getCloudLinkDetailsFor404(self):
! 		cssclass, url, title = self.getCloudLinkDetailsForUniqueId('404')
! 		return 'undefinednode', url, title
  
  	# Return a URL for an edit page for a given item.
  	def getDesktopDisplayLinkDetailsForUniqueId(self, uniqueId): 
***************
*** 277,283 ****
  	# Return a link to a creation page for an item, based on link text. 
  	def getDesktopCreateLinkDetailsForProposedLinkText(self, linkText): 
  		"""Return the edit URL for a non-existent item, based on `linkText`."""
! 		return ('undefinednode', self.getUrl('edit_html', id=_munge(linkText)))
  
  	# Render an item by its unique ID
  	def renderItemByUniqueId(self, uniqueId):
--- 278,286 ----
  	# Return a link to a creation page for an item, based on link text. 
  	def getDesktopCreateLinkDetailsForProposedLinkText(self, linkText): 
  		"""Return the edit URL for a non-existent item, based on `linkText`."""
! 		return ('undefinednode', 
! 		        self.getUrl('edit_html', id=_munge(linkText)),
! 			_("Create this item in WikiTool"))
  
  	# Render an item by its unique ID
  	def renderItemByUniqueId(self, uniqueId):