Author: ctheune
Date: Tue Aug 1 10:54:04 2006
New Revision: 4258
Added:
CMFLinkChecker/trunk/link.py
- copied, changed from r4255, CMFLinkChecker/trunk/LinkInfo.py
CMFLinkChecker/trunk/url.py (contents, props changed)
CMFLinkChecker/trunk/www/link.zpt
- copied, changed from r4255, CMFLinkChecker/trunk/www/linkinfo.zpt
CMFLinkChecker/trunk/www/url.zpt
- copied, changed from r4255, CMFLinkChecker/trunk/www/linkinfo.zpt
Removed:
CMFLinkChecker/trunk/LinkInfo.py
CMFLinkChecker/trunk/www/linkinfo.zpt
Modified:
CMFLinkChecker/trunk/LinkCheckerTool.py
CMFLinkChecker/trunk/database.py
CMFLinkChecker/trunk/doc/CHANGES.txt
CMFLinkChecker/trunk/doc/INSTALL.txt
CMFLinkChecker/trunk/doc/TODO.txt
CMFLinkChecker/trunk/examples/WikiRetriever/__init__.py
CMFLinkChecker/trunk/interfaces.py
CMFLinkChecker/trunk/reports.py
CMFLinkChecker/trunk/retrievemanager.py
CMFLinkChecker/trunk/tests/test_linkchecker.py
CMFLinkChecker/trunk/tests/test_normalizer.py
CMFLinkChecker/trunk/utils.py
Log:
- cleaned up links and urls a bit more
- made stuff workg
Modified: CMFLinkChecker/trunk/LinkCheckerTool.py
==============================================================================
--- CMFLinkChecker/trunk/LinkCheckerTool.py (original)
+++ CMFLinkChecker/trunk/LinkCheckerTool.py Tue Aug 1 10:54:04 2006
(at)(at) -29,7 +29,6 (at)(at)
# Sibling imports
from Products.CMFLinkChecker.interfaces import \
ILinkManager, IUIDManager, IUIDProvider
-from Products.CMFLinkChecker.LinkInfo import LinkInfo
from Products.CMFLinkChecker.utils import resolveRelativeLink
from Products.CMFLinkChecker import permissions, shorturls, config
Modified: CMFLinkChecker/trunk/database.py
==============================================================================
--- CMFLinkChecker/trunk/database.py (original)
+++ CMFLinkChecker/trunk/database.py Tue Aug 1 10:54:04 2006
(at)(at) -6,7 +6,6 (at)(at)
# Python imports
import socket
-from md5 import md5
from xmlrpclib import ServerProxy, Fault
# Zope imports
(at)(at) -20,8 +19,10 (at)(at)
from Products.CMFCore import CMFCorePermissions
# CMFLinkChecker imports
+import Products.CMFLinkChecker.link
+import Products.CMFLinkChecker.url
+import Products.CMFLinkChecker.utils
from Products.CMFLinkChecker.interfaces import ILinkDatabase
-from Products.CMFLinkChecker.LinkInfo import LinkInfo
from Products.CMFLinkChecker import permissions, config
(at)(at) -40,12 +41,7 (at)(at)
class LinkDatabase(BTreeFolder2):
"""A database of links.
- Manages the physical storage of link information.
-
- This link database depends on the fact that there is exactly
- one non-ILinkInfo-Object here: the catalog.
-
- Otherwise the amount of links will be calculated wrong.
+ Manages the physical storage of link and url information.
"""
__implements__ = (ILinkDatabase, ) + BTreeFolder2.__implements__
(at)(at) -61,36 +57,51 (at)(at)
def manage_afterAdd(self, item, container):
LinkDatabase.inheritedAttribute('manage_afterAdd')(self, item,
container)
+
+ def insertIndexes(catalog, indexes):
+ existing = catalog.Indexes.objectIds()
+ for index_name, index_type in indexes:
+ if index_name not in existing:
+ catalog.addIndex(index_name, index_type)
+
+ def insertCacheColumns(catalog, columns):
+ existing_columns = catalog.schema()
+ for c, value in columns:
+ if c not in existing_columns:
+ catalog.addColumn(c, value)
+
+ # Setup the link catalog
if 'link_catalog' not in self.objectIds():
- # The link catalog that manages links
self.manage_addProduct['ZCatalog'].\
- manage_addZCatalog('link_catalog', 'Link Catalog')
- lc = self.link_catalog
+ manage_addZCatalog('link_catalog', 'Link catalog')
+ indexes = [('state', 'KeywordIndex'),
+ ('url', 'FieldIndex'),
+ ('object', 'KeywordIndex')]
+ insertIndexes(self.link_catalog, indexes)
- indexes = [('url', 'FieldIndex'),
- ('state', 'KeywordIndex'),
- ('object', 'KeywordIndex'),
- ('unused', 'FieldIndex'),
- ('lastupdate', 'DateIndex'),
- ('registered', 'FieldIndex')]
- existing = lc.Indexes.objectIds()
- for index_name, index_type in indexes:
- if index_name not in existing:
- lc.addIndex(index_name, index_type)
-
- columns = [("lastcheck", None),
- ("lastupdate", None),
- ("laststate", None),
- ("object", None),
+ columns = [("object", None),
+ ("url", ""),
("reason", ""),
+ ("lastcheck", ""),
("state", ""),
- ("url", ""),
("link", ""),
("getId", "")]
- existing_columns = lc.schema()
- for c, value in columns:
- if c not in existing_columns:
- lc.addColumn(c, value)
+ insertCacheColumns(self.link_catalog, columns)
+
+ # Setup the url catalog
+ if 'url_catalog' not in self.objectIds():
+ self.manage_addProduct['ZCatalog'].\
+ manage_addZCatalog('url_catalog', 'URL catalog')
+ indexes = [('url', 'FieldIndex'),
+ ('unregistered', 'FieldIndex')]
+ insertIndexes(self.url_catalog, indexes)
+
+ columns = [("url", ""),
+ ("id", ""),
+ ("reason", ""),
+ ("lastcheck", ""),
+ ("state", "grey")]
+ insertCacheColumns(self.url_catalog, columns)
###############
# ILinkDatabase
(at)(at) -134,19 +145,20 (at)(at)
self._updateWSRegistrations()
def _register_link(self, link, object):
- id = self._hashLink(link, object)
- info = getattr(self, id, None)
- if info is None:
- uid = self.portal_linkchecker.getUIDForObject(object)
- info = LinkInfo(link, id, object, uid)
- # Due to containment/context/acquisition stuff, we set the uid
- # directly over here
- self._setObject(id, info)
- info = getattr(self, id)
- # info.unused is set to true when Site crawling begins. Once we re-add
the link,
- # we need to mark it as not-unused so it doesn't get deleted later.
- info.unused = False
- return info
+ link_id = Products.CMFLinkChecker.utils.hash_link(link, object)
+ if link_id in self.objectIds():
+ return
+ uid = self.portal_linkchecker.getUIDForObject(object)
+ link = Products.CMFLinkChecker.link.Link(link, link_id, uid)
+ # Make sure the URL object exists before we trigger indexing
+ # and such
+ url = Products.CMFLinkChecker.utils.resolveRelativeLink(link.link,
object)
+ url_id = Products.CMFLinkChecker.utils.hash_url(url)
+ if url_id not in self.objectIds():
+ url = Products.CMFLinkChecker.url.URL(url)
+ self._setObject(url_id, url)
+ # Now we can add the link to the database
+ self._setObject(link_id, link)
security.declarePrivate("_updateWSRegistrations")
def _updateWSRegistrations(self):
(at)(at) -154,35 +166,23 (at)(at)
May also register yet non-registered other links.
"""
- unregistered_candidates = self.query(registered=False)
- unregistered = []
-
- # Ok. Maybe someone else already registered the same url, then we can
update this one and ignore it.
- for link in unregistered_candidates:
- registered = len(self.query(url=link.url, registered=True))
- if registered:
- link = link.getObject()
- link.registered = True
- link.index()
- continue
- unregistered.append(link.url)
-
+ unregistered = self.queryURLs(registered=False)
+ # Nothing to do?
if not unregistered:
- # nothing to do
return
+ # Do we have an LMS connection?
lms = self._getWebServiceConnection()
if lms is None:
- # no connection to the server
- return
-
+ return
+ unregistered = [url.url for url in unregistered]
states = lms.registerManyLinks(unregistered)
for url, state, reason in states:
state = WEBSERVICE_STATEMAP[state]
- for link in self.byURL(url):
- link.registered = True
- link.updateStatus(state, reason)
+ url = self[Products.CMFLinkChecker.utils.hash_url(url)]
+ url.registered = True
+ url.updateStatus(state, reason)
- security.declareProtected(permissions.USE_LINK_MANAGEMENT,
'unregisterLink')
+ security.declarePrivate('unregisterLink')
def unregisterLink(self, link, object):
"""Unregisters a given link/object pair at the database.
(at)(at) -191,38 +191,18 (at)(at)
Returns None.
"""
- id = self._hashLink(link)
- info = getattr(self, id)
- self._unregisterLinks([info], object)
+ id = Products.CMFLinkChecker.utils.hash_link(link)
+ self.manage_delObject(id)
- security.declareProtected(
- permissions.USE_LINK_MANAGEMENT, 'unregisterObject')
+ security.declarePrivate('unregisterObject')
def unregisterObject(self, object):
"""Unregisters all links for this object.
Returns None.
"""
- links = self.byObject(object)
- self._unregisterLinks(links, object)
-
- security.declarePrivate('_unregisterLinks')
- def _unregisterLinks(self, infos, object):
- """Unregisters a number of links from one object with the database.
-
- If any of the links isn't referenced by any object anymore, it will
- be removed from the database completely.
-
- infos: sequence of LinkInfo instances
- object: content object
-
- Returns None.
- """
- sm = getSecurityManager()
- if not sm.checkPermission(
- CMFCorePermissions.ModifyPortalContent, object):
- raise Unauthorized, \
- "Can't modify link registrations for this object."
- self._deleteLinks(infos)
+ links = self.getLinksForObject(object)
+ link_ids = [x.getId() for x in links]
+ self.manage_delObjects(link_ids)
def _getWebServiceConnection(self):
"""return connection to lms
(at)(at) -237,7 +217,6 (at)(at)
zLOG.LOG('CMFLinkChecker', zLOG.ERROR,
'Connection to LMS not possible: %s' % (conn.error, ))
client = None
-
return client
def _get_connection_object(self):
(at)(at) -247,70 +226,60 (at)(at)
self._v_lms_client = client
return client
- def _deleteLinks(self, infos):
+ def _deleteURLs(self, urls):
# Unregister with LMS
lms = self._getWebServiceConnection()
if lms is None:
return
- # Remember urls, then delete objects, then query for remaining urls
- # Only unregister urls that aren't used anymore
- urls = [info.url for info in infos]
- self.manage_delObjects([info.id for info in infos])
- to_unregister = []
- for url in urls:
- remaining = len(self.query(url=url))
- if not remaining:
- to_unregister.append(url)
- lms.unregisterManyLinks(to_unregister)
+ self.manage_delObjects([url.id for url in urls])
+ lms.unregisterManyLinks([url.url for url in urls])
- security.declareProtected(permissions.USE_LINK_MANAGEMENT, 'byURL')
- def byURL(self, url):
+ security.declareProtected(permissions.USE_LINK_MANAGEMENT,
'getLinksForURL')
+ def getLinksForURL(self, url):
"""Retrieve links for an url."""
- links = [x.getObject() for x in self.query(url=url)]
+ links = [x.getObject() for x in self.queryLinks(url=url)]
links = filter(None, links)
return links
- security.declareProtected(permissions.USE_LINK_MANAGEMENT, 'byObject')
- def byObject(self, object, state=None):
+ security.declareProtected(permissions.USE_LINK_MANAGEMENT,
'getLinksForObject')
+ def getLinksForObject(self, object, state=None):
"""Retrieve information about an object.
- Returns a list of ILinkInfo objects.
+ Returns a list of ILink objects.
Returns an empty list if the object hasn't been registered yet.
"""
sm = getSecurityManager()
- if not
sm.checkPermission(CMFCorePermissions.AccessContentsInformation,
- object):
+ if not sm.checkPermission(
+ CMFCorePermissions.AccessContentsInformation, object):
raise Unauthorized, "Can't view links on this object."
- lc = self.getLinkManager()
- uid = lc.getUIDForObject(object)
-
+ uid = self.getLinkManager().getUIDForObject(object)
if uid is None:
links = []
else:
query_args = {}
if state is not None:
query_args['state' ] = state
- links = self.link_catalog(object=[uid], **query_args)
+ links = self.queryLinks(object=[uid], **query_args)
links = [x.getObject() for x in links]
links = filter(None, links)
return links
security.declareProtected(permissions.USE_LINK_MANAGEMENT, 'getAllLinks')
def getAllLinks(self):
- """Returns a list of all ILinkInfo objects.
+ """Returns a list of all ILink objects.
Warning: This is likely to be a slow method in large data sets.
"""
- return self.objectValues(['Link Information'])
+ return self.objectValues(['Link'])
security.declareProtected(permissions.USE_LINK_MANAGEMENT,
'getAllLinkIds')
def getAllLinkIds(self):
- """Returns a list of the ids of all ILinkInfo objects.
+ """Returns a list of the ids of all ILink objects.
Warning: This is likely to be a slow method in large data sets.
"""
- return self.objectIds(['Link Information'])
+ return self.objectIds(['Link'])
security.declareProtected(
permissions.USE_LINK_MANAGEMENT, 'getLinkIterator')
(at)(at) -322,47 +291,34 (at)(at)
security.declareProtected(permissions.USE_LINK_MANAGEMENT, 'getLinkCount')
def getLinkCount(self):
"""Returns the amount of links currently in the database."""
- # Never,
- # Never,
- # Never put anything more than the catalog in here!
- return len(self) - 1
-
- security.declareProtected(permissions.USE_LINK_MANAGEMENT, 'query')
- def query(self, **args):
- """Returns the result of querying the ZCatalog that indexes
- the link database.
- """
- return self.getCatalog()(**args)
-
- security.declareProtected(permissions.USE_LINK_MANAGEMENT, 'getCatalog')
- def getCatalog(self):
- """Returns the used catalog.
- """
- return self.link_catalog
+ return len(self.queryLinks())
- security.declareProtected(
- CMFCorePermissions.ManagePortal, 'removeAllRegistrations')
- def removeAllRegistrations(self):
- """Removes all object registrations from the known links."""
- for link in self.objectValues(["Link Information"]):
- link.unused = True
- link.index()
+ security.declareProtected(permissions.USE_LINK_MANAGEMENT, 'queryLinks')
+ def queryLinks(self, **args):
+ """Returns the result of querying the ZCatalog that indexes links."""
+ return self.link_catalog(**args)
+
+ security.declareProtected(permissions.USE_LINK_MANAGEMENT, 'queryURLs')
+ def queryURLs(self, **args):
+ """Returns the result of querying the ZCatalog that indexes urls."""
+ return self.url_catalog(**args)
security.declareProtected(
CMFCorePermissions.ManagePortal, 'cleanup')
def cleanup(self):
"""Removes all object registrations from the known links."""
- links = [link.getObject() for link in self.query(unused=True)]
- links = filter(None, links)
- self._deleteLinks(links)
+ urls = self.queryURLs(links=[])
+ urls = filter(None, urls)
+ self._deleteURLs(urls)
security.declareProtected(
CMFCorePermissions.ManagePortal, 'updateLinkStatus')
def updateLinkStatus(self, url, state, reason):
"""XML-RPC connector for LMS"""
state = WEBSERVICE_STATEMAP[state]
- for link in self.byURL(url):
- link.updateStatus(state, reason)
+ for url in self.queryURLs(url=url):
+ url = url.getObject()
+ url.updateStatus(state, reason)
security.declarePublic('updateManyStates')
def updateManyStates(self, client_id, password, update_list):
(at)(at) -397,18 +353,18 (at)(at)
server = self._getWebServiceConnection()
if server is None:
return
- for link in self.query():
+ for url in self.queryURLs():
try:
- status, reason = server.getStatus(link.url)
+ status, reason = server.getStatus(url.url)
except KeyError:
# not registered with server
- server.registerLink(link.url)
+ server.registerLink(url.url)
status = 'temporary unavailable'
reason = 'No status response from LMS yet.'
status = WEBSERVICE_STATEMAP[status]
- if item.state != status:
- item = item.getObject()
- item.updateStatus(status, reason)
+ if url.state != status:
+ url = url.getObject()
+ url.updateStatus(status, reason)
security.declareProtected(
permissions.USE_LINK_MANAGEMENT, 'checkConnection')
(at)(at) -454,20 +410,6 (at)(at)
return ""
return server.getInfoFrameURL()
- #################
- # private methods
-
- security.declarePrivate('_hashLink')
- def _hashLink(self, link, object):
- """Provides a hash which can be used as an id for a LinkInfo object.
- """
- uid = self.portal_linkchecker.getUIDForObject(object)
- if link is None:
- link = ""
- if isinstance(link, unicode):
- link = link.encode('utf-8')
- return md5("%s:%s" % (link, uid)).hexdigest()
-
class LMSClient:
"""client to lms"""
Modified: CMFLinkChecker/trunk/doc/CHANGES.txt
==============================================================================
--- CMFLinkChecker/trunk/doc/CHANGES.txt (original)
+++ CMFLinkChecker/trunk/doc/CHANGES.txt Tue Aug 1 10:54:04 2006
(at)(at) -1,4 +1,4 (at)(at)
-CMFLinkChecker 2.1
+CMFLinkChecker 2.1 (unreleased)
New features
(at)(at) -8,6 +8,7 (at)(at)
- Some code refactorings and source cleanup.
+
CMFLinkChecker 2.0 (2006-06-27)
New features
(at)(at) -30,17 +31,16 (at)(at)
- XMLRPC protocol version: With a new LMS protocol version, the
linkchecker now validates the protocol version.
-
+
- CMFLinkChecker now supports PloneHelpCenter (patch by Maurits van
Rees).
-
+
- Added function retrieveAllRichTextFields that can be used by all
retrievers, just like the retrieveSTX and retrieveHTML functions.
It finds all fields that use the RichTextWidget and extracts links
from there trying both retrieveSTX and retrieveHTML (patch by
Maurits van Rees).
-
Bugs fixed
- Reindex the portal catalog's portal_linkchecker_uid index upon crawling
(at)(at) -54,7 +54,8 (at)(at)
- It is now possible to re-register retrievers; used to raise ValueError
(fixes bug #3293, reported by Martijn Pieters)
- - Using toLocalizedTime instead of toPortalTime (patch by Maurits van
Rees).
+ - Using toLocalizedTime instead of toPortalTime (patch by Maurits van
+ Rees).
- portlet_links.pt is now a Plone 2.1 portlet (patch by Maurits van Rees).
(at)(at) -86,7 +87,7 (at)(at)
portal_catalog queries.
Bugs fixed
-
+
- Extended anti-normalization of urls for content highlighting a bit.
- Fixed removal of links: Links were never unregistered when they got
(at)(at) -94,7 +95,7 (at)(at)
- Improved interoperability with AlphaFlow: reindex doesn't fail if the
user doesn't have the Modify portal content permission.
-
+
- Updated french translations (provided by Nicolas Laurance)
- Crawling is not possible when no connection to the lms can be
(at)(at) -102,15 +103,16 (at)(at)
- Unassigning retrievers in configlet is possible now (bug #2576)
+
CMFLinkChecker 1.9.2 (2005-04-12)
-
+
New features
-
+
- Integrated Archetype support (ATDocument retriever and automatic
retrieving of all BaseObjects)
-
+
- Admin can switch on/off notifications in configlet
-
+
- Added support for kupu's 'resolveuid' method to find backreferences.
Bugs fixed
(at)(at) -124,7 +126,7 (at)(at)
- Matured Mail notification (fixes bug #2439, reported by Simon Pamies)
- Fixed balanced score card linking to correct detail pages.
-
+
- Fixed None filter when retrieving objects from the catalog
(at)(at) -141,10 +143,11 (at)(at)
- Broken links portlet gets a scrollbar if it becomes too wide
+
CMFLinkChecker 1.9 (2005-02-02)
- 1.9 will beome 2.0 once it's stable
-
+
- Complete refactoring to integrate an external link monitoring server
(lms) instead of doing checks in a Zope thread.
(at)(at) -153,9 +156,9 (at)(at)
- In-content highlighting of link-state (green, orange, red, grey)
- Bi-directional capabilities added (referenced-by portlet)
-
- re-install via quickinstaller possible
+
CMFLinkChecker 1.1
- Added french translation file. (Thanks to Nicolas)
(at)(at) -163,6 +166,7 (at)(at)
- Updated Bot-String for http protocol. (Thanks to Nicolas
again)
+
CMFLinkChecker 1.1beta1
- Fixed the LinksForAuthenticatedUser report.
(at)(at) -188,7 +192,8 (at)(at)
- Fixed small problem with FindSupport in LinkInfo.py (thanks to Ira
Fuchs).
-
+
+
CMFLinkChecker 1.0rc8
- Refactoring of retrievers.py that holds if CMFDefault
(at)(at) -206,14 +211,15 (at)(at)
- Introduced unit tests (finally) for most components.
+
CMFLinkChecker 1.0rc7
- Fixed the thread "staying alive" feature.
- Fixed the permission for checking links for ManagePortal to "View"
-
+
- Not comitting checker transactions when the checker failed.
-
+
- Fixed a bug making checkers die when meeting ReadConflictErrors when
loading a LinkInfo object from the database. ReadConflicts are now
logged and the link is rescheduled. An additional one second pause is
(at)(at) -226,8 +232,9 (at)(at)
famous python based open source web application server) give wrong
responses.
- - Added a couple of Dia UML diagrams that document the dependency and
behaviour
- of the linkchecker components.
+ - Added a couple of Dia UML diagrams that document the dependency and
+ behaviour of the linkchecker components.
+
CMFLinkChecker 1.0rc6
(at)(at) -240,6 +247,7 (at)(at)
- Made threads run within an infinite loop for
more convenient exception behaviour.
+
CMFLinkChecker 1.0rc5
- Scheduling period given in hours instead of seconds.
(at)(at) -291,6 +299,7 (at)(at)
- Corrected packaging mistake that forgot shorturls.py.
+
CMFLinkChecker 1.0rc3
- Added more details to the balanced score card: Site wide administrator
(at)(at) -299,7 +308,8 (at)(at)
- More I18N
- - Document links now have a title attribute that display the
DC::Description field.
+ - Document links now have a title attribute that display the
+ DC::Description field.
- The balanced score card only shows colors when at least one link
matches the state. Otherwise the entries remain black.
(at)(at) -308,9 +318,10 (at)(at)
- All Author/Owner information fields are mailto: links now
- - URL display in lc_object_status is 50 characters long now (increased
from 30)
- but the algorithm to shorten them has been implemented in a more
intelligent
- fashion. (See shorturls.py)
+ - URL display in lc_object_status is 50 characters long now (increased
+ from 30) but the algorithm to shorten them has been implemented in a
+ more intelligent fashion. (See shorturls.py)
+
CMFLinkChecker 1.0rc2
(at)(at) -318,105 +329,111 (at)(at)
- Fixed handling of Timeout exception in HTTP code
+
CMFLinkChecker 1.0rc
- - Added example to support your own content types. (See
examples/WikiRetriever,
- just copy or link this into your Products directory.)
+ - Added example to support your own content types. (See
+ examples/WikiRetriever, just copy or link this into your Products
+ directory.)
- - Changed viewing behaviour of portlet.
-
- - Fixed relative link handling.
+ - Changed viewing behaviour of portlet.
+
+ - Fixed relative link handling.
+
+ - Fixed security handling for getLinksForObject and findLinksForObject.
- - Fixed security handling for getLinksForObject and findLinksForObject.
CMFLinkChecker 1.0beta
- - Completed configlet for starting threads and getting status
information.
+ - Completed configlet for starting threads and getting status information.
- - Added support for email notifications to the authors.
+ - Added support for email notifications to the authors.
- - Added nice icon.
+ - Added nice icon.
+
+ - Made the default method for HTTP handling "HEAD". Fallback is "GET" if
+ the server responds with a "405 Method not allowed"
- - Made the default method for HTTP handling "HEAD". Fallback is "GET" if
the
- server responds with a "405 Method not allowed"
+ - Added automatic update of link lists for each object when edited. This
+ works only automatically for the builtin types.
- - Added automatic update of link lists for each object when edited.
This
- works only automatically for the builtin types.
+ See Extensions/Install how to override the default actions of the
+ CMFFormController to activate this feature for your own content types.
- See Extensions/Install how to override the default actions of the
- CMFFormController to activate this feature for your own content types.
+ - Removed tabs/border from Balanced Score Card.
- - Removed tabs/border from Balanced Score Card.
+ - Made HTTP Checker available for HTTPS. Requires SSL sockets to be
+ available see http://www.python.org/doc/2.2/lib/module-httplib.html
on
+ how to activate them if your python doesn't support them.
- - Made HTTP Checker available for HTTPS. Requires SSL sockets to be
available
- see http://www.python.org/doc/2.2/lib/module-httplib.html
on how to activate
- them if your python doesn't support them.
+ - Made some changes to the httplib for better blocking behaviour, still
+ need to have better timings in the checker thread.
- - Made some changes to the httplib for better blocking behaviour, still
- need to have better timings in the checker thread.
+ A new socket class that handles timeouts earlier doesn't DOS the worker
+ threads by waiting one minute for a connect, but times out at 15
+ seconds.
- A new socket class that handles timeouts earlier doesn't DOS the
worker
- threads by waiting one minute for a connect, but times out at 15
- seconds.
+ - Fixed Bug in UID generation (forgot to unwrap objects and not acquire
+ UIDs)
- - Fixed Bug in UID generation (forgot to unwrap objects
- and not acquire UIDs)
+ - Some cleanup on ILinkInformation
- - Some cleanup on ILinkInformation
+ - Added FTP support (good bandwith usage by calling REST if the server
+ supports it.)
- - Added FTP support (good bandwith usage by calling REST if the server
- supports it.)
+ - Made protocol support pluggable
- - Made protocol support pluggable
+ - Removed redundant status column
- - Removed redundant status column
+ - Added colors to overview portlet
- - Added colors to overview portlet
+ - Fixed (un)installation
- - Fixed (un)installation
+ - Fixed permissions for "My Links" and "Link" tab
- - Fixed permissions for "My Links" and "Link" tab
CMFLinkChecker 0.3beta
- - Some code cleanup (legal stuff, coding style)
+ - Some code cleanup (legal stuff, coding style)
- - Debugging output available via zLOG.BLATHER
+ - Debugging output available via zLOG.BLATHER
- - Introduced the automatic scheduler. You can start it by activating a
- scheduler thread in the ZMI (Link Checking). It assures to check
- every known link at least once within the given period of time as
- configured in the configlet.
+ - Introduced the automatic scheduler. You can start it by activating a
+ scheduler thread in the ZMI (Link Checking). It assures to check every
+ known link at least once within the given period of time as configured
+ in the configlet.
- - Introduced "configlet" to configure the automatic scheduler and
default
- URI prefix.
+ - Introduced "configlet" to configure the automatic scheduler and default
+ URI prefix.
- - Introduced "balanced score card" that gives strategic information
- about the current state of links in the site.
+ - Introduced "balanced score card" that gives strategic information about
+ the current state of links in the site.
- - Introduced "My (broken) Links" which gives an author an overview
- on all his broken links.
+ - Introduced "My (broken) Links" which gives an author an overview on all
+ his broken links.
- - Using a UID to maintain stable references to the objects.
+ - Using a UID to maintain stable references to the objects.
- - Using a ZCatalog to query LinkInfo objects.
+ - Using a ZCatalog to query LinkInfo objects.
- - Using an ObjectManager for the LinkInfo objects now.
+ - Using an ObjectManager for the LinkInfo objects now.
- - Some usability tweaks on the link list as proposed by Alexander Limi.
+ - Some usability tweaks on the link list as proposed by Alexander Limi.
- - Fixed UnboundLocalError in Document retriever (#821984)
+ - Fixed UnboundLocalError in Document retriever (#821984)
- - Fixed possible crash of checker thread when urlsplit didn't
- find a hostname
+ - Fixed possible crash of checker thread when urlsplit didn't find a
+ hostname
- - Bug 1503: Included a modified version for prepending a default element
- on URLs without hostname ("relative" links). Thanks to Sebastian
Stark.
+ - Bug 1503: Included a modified version for prepending a default element
+ on URLs without hostname ("relative" links). Thanks to Sebastian Stark.
+
+ - Updated documentation
+
+ - Catching errors when parsing HTML now to avoid breaking the link
+ discovery process.
- - Updated documentation
- - Catching errors when parsing HTML now to avoid breaking the link
- discovery process.
-
CMFLinkChecker 0.1
+ - Started CMFLinkChecker project.
Modified: CMFLinkChecker/trunk/doc/INSTALL.txt
==============================================================================
--- CMFLinkChecker/trunk/doc/INSTALL.txt (original)
+++ CMFLinkChecker/trunk/doc/INSTALL.txt Tue Aug 1 10:54:04 2006
(at)(at) -26,14 +26,17 (at)(at)
.. [*] It used to check links inside Zope until version 1.9.
+
Prerequisites
=============
-To allow updating links centrally, we require lxml to be installed. You can
get lxml for your Python installation easily by using easy_install.
+To allow updating links centrally, we require lxml to be installed. You can
+get lxml for your Python installation easily by using easy_install.
Visit http://peak.telecommunity.com/DevCenter/EasyInstall
for more information
about easy_install.
+
Installation
============
(at)(at) -85,7 +88,6 (at)(at)
Download lms from http://www.gocept.com,
more information is inside the lms
package.
-
Crawling
++++++++
(at)(at) -97,7 +99,6 (at)(at)
Please be aware that crawling consumes a considerable amount of time and might
be very resource intensive.
-
Retriever assignments
+++++++++++++++++++++
Modified: CMFLinkChecker/trunk/doc/TODO.txt
==============================================================================
--- CMFLinkChecker/trunk/doc/TODO.txt (original)
+++ CMFLinkChecker/trunk/doc/TODO.txt Tue Aug 1 10:54:04 2006
(at)(at) -42,6 +42,16 (at)(at)
2.1
===
+- Important: Move from hashing of urls and links to encoding. Encoding needs
+ to be url compatible (which makes the code long :/)
+
+- Fix or totally destroy email reports. They can not possibly work correctly
+ right now.
+
+- Bug: getting notified about link status might not update links with the same
+ url that are retrieved in between: normalize the dependency between link and
+ url and status
+
- Allow users to update links centrally
- Search and replace on links
Modified: CMFLinkChecker/trunk/examples/WikiRetriever/__init__.py
==============================================================================
--- CMFLinkChecker/trunk/examples/WikiRetriever/__init__.py (original)
+++ CMFLinkChecker/trunk/examples/WikiRetriever/__init__.py Tue Aug 1 10:54:04
2006
(at)(at) -7,11 +7,13 (at)(at)
##############################################################################
"""CMFLinkChecker - retriever for ZWiki pages
-$Id: __init__.py,v 1.14 2004/07/24 12:28:30 ctheune Exp $"""
+$Id$"""
from Products.ZWiki.ZWikiPage import ZWikiPage
from Products.CMFLinkChecker.retrievers import retrieveHTML, retrievers,
resolveRelativeLink
-from Products.CMFLinkChecker.LinkInfo import LinkInfo
+
+# This is way out of date. Therefor I am disallowing any usage.
+raise Exception, "Can't use WikiRetriever -- it's very out of date and does
not work"
def retrieverWiki(linkchecker, object):
"""Usable for ZWiki.ZWikiPage objects."""
(at)(at) -26,12 +28,12 (at)(at)
# No Wiki editlinks
if link.find("editform?page") != -1:
continue
- if link.find("createform?page") != -1:
- continue
- if link.find("in_reply_to") != -1:
- continue
- if link.find("#") != -1 and link.find("(at)") != -1: # Probably a comment
- continue
+ if link.find("createform?page") != -1:
+ continue
+ if link.find("in_reply_to") != -1:
+ continue
+ if link.find("#") != -1 and link.find("(at)") != -1: #
Probably a comment
+ continue
# No internal wiki Links
is_wiki_link = 0
(at)(at) -41,7 +43,7 (at)(at)
break
if is_wiki_link:
continue
-
+
link = resolveRelativeLink(link, object)
info = linkchecker.getInfoForURL(link)
info.addObject(object)
(at)(at) -65,5 +67,3 (at)(at)
old_edit = EditingSupport.edit
EditingSupport.edit = new_edit
-
-
Modified: CMFLinkChecker/trunk/interfaces.py
==============================================================================
--- CMFLinkChecker/trunk/interfaces.py (original)
+++ CMFLinkChecker/trunk/interfaces.py Tue Aug 1 10:54:04 2006
(at)(at) -25,20 +25,21 (at)(at)
"""Tells if the object is managed by the link manager."""
def resolveRelativeLink(url, context):
- """resolve realtive url from context to an absolute url
+ """Resolve relative URL from context to an absolute URL.
- This is simpilar to absolute_url, but does not require an object and
- honours the prefix settings from the Link Management settings
+ This is similar to the absolute_url(), but does not require an object
+ and honours the prefix settings from the link management settings.
"""
def isUserAllowed():
- """returns whether the user is allowed to use Link Management services
+ """Returns whether the user is allowed to use link management
+ services.
- returns True if the currently logged in user is allowed to use Link
- Management services at all, False otherwise.
+ Returns True, iff the currently logged in user is allowed to use link
+ management services at all.
- It's checked if the users has the 'Use link management functions'
- permission in the Plone root
+ This function checks whether the user has the 'Use link management
functions'
+ permission in the Plone root.
"""
def getLinkManager():
(at)(at) -144,27 +145,30 (at)(at)
Returns None.
"""
- def byObject(object):
+ def getLinksForObject(object):
"""Retrieve information about an object.
- Returns a list of ILinkInfo objects.
+ Returns a list of ILink objects.
Returns an empty list if the object hasn't been registered yet.
"""
- def byURL(url):
+ def getLinksForURL(url):
"""Retrieve links for an url.
Returns all links that point to the given url.
"""
+ def getURL(url):
+ """Return the URL object for the given URL."""
+
def getAllLinks():
- """Returns a list of all ILinkInfo objects.
+ """Returns a list of all ILink objects.
Warning: This is likely to be a slow method in large data sets.
"""
def getAllLinkIds():
- """Returns a list of the ids of all ILinkInfo objects.
+ """Returns a list of the ids of all ILink objects.
Warning: This is likely to be a slow method in large data sets.
"""
(at)(at) -172,17 +176,11 (at)(at)
def getLinkCount():
"""Returns the amount of links currently in the database."""
- def query(**args):
- """Returns the result of querying the ZCatalog that indexes
- the link database.
- """
+ def queryLinks(self, **args):
+ """Returns the result of querying the ZCatalog that indexes links."""
- def getCatalog():
- """Returns the used catalog.
- """
-
- def removeAllRegistrations():
- """Removes all object registrations from the known links."""
+ def queryURLs(self, **args):
+ """Returns the result of querying the ZCatalog that indexes urls."""
class IRetrieveManager(Interface):
(at)(at) -265,25 +263,50 (at)(at)
"""Returns a list of all retriever names."""
-class ILinkInformation(Interface):
- """Information about a link."""
+class IURL(Interface):
+ """URL status information
+
+ The URL as transmitted to the LMS. It must be absolute and include
protocol
+ and netlocation.
+
+ A URL can be referred to by many links as, depending on the context, a URL
+ can be expressed by several relative spellings.
+
+ """
- object = Attribute("The UID of the object containing the link.")
url = Attribute("Contains the URL the link refers to.")
- link = Attribute("Contains the link as stored on the object")
+ registered = Attribute("Has this link been registered with the web
service?")
lastcheck = Attribute("DateTime when the last check was performed.")
lastupdate = Attribute("DateTime when the last status change ocurred.")
- laststate = Attribute("The last state the link was in before switching to
the current.")
+ laststate = Attribute("The state before the current state was assumed.")
state = Attribute("The current state of the link. Can be one of ['red', "
\
"'green', 'orange', 'grey'].")
reason = Attribute("A verbose reason describing the error")
- registered = Attribute("Has this link been registered with the web
service?")
+ links = Attribute(
+ "A property that returns all currently associated links"
+ "(as catalog brains).")
+
+ def updateStatus(state, reason):
+ """Updates the status information for this URL."""
+
+
+class ILink(Interface):
+ """A link as used from an object."""
+
+ link = Attribute("Link as stored on the object")
+ object = Attribute("The UID of the object containing the link.")
+ url = Attribute(
+ "URL as computed from the link and the object as context.")
+
+ # Fields to support caching of data
+ state = Attribute("The current state of the URL.")
+ reason = Attribute("The current state of the URL.")
def getObject():
"""Returns a list of objects referencing this link."""
- def updateStatus(state, reason):
- """Updates the status information for this link."""
+ def getURL():
+ """Return the URL object this link refers to."""
class IRetriever(Interface):
Copied: CMFLinkChecker/trunk/link.py (from r4255,
CMFLinkChecker/trunk/LinkInfo.py)
==============================================================================
--- CMFLinkChecker/trunk/LinkInfo.py (original)
+++ CMFLinkChecker/trunk/link.py Tue Aug 1 10:54:04 2006
(at)(at) -13,80 +13,48 (at)(at)
from Products.CMFCore.utils import getToolByName
# Sibling imports
-from Products.CMFLinkChecker.interfaces import ILinkInformation
-from Products.CMFLinkChecker.utils import resolveRelativeLink
+import Products.CMFLinkChecker.interfaces
+import Products.CMFLinkChecker.utils
-class LinkInfo(SimpleItem):
+class Link(SimpleItem):
+ """A link as used from an object."""
- meta_type = "Link Information"
+ meta_type = "Link"
- __implements__ = (ILinkInformation,)
+ __implements__ = (Products.CMFLinkChecker.interfaces.ILink,)
# XXX make accessor functions
__allow_access_to_unprotected_subobjects__ = 1
object = None
- url = None
link = None
- lastcheck = None
- lastupdate = None
- laststate = "grey"
- state = "grey"
- reason = ""
- registered = False
-
- # Private flags
- unused = False
+ url = None
manage_options = ({'label': 'Info', 'action':'manage_info'},) + \
SimpleItem.manage_options
+ manage_info = PageTemplateFile('www/link', globals(),
__name__='manage_info')
- manage_info = PageTemplateFile('www/linkinfo', globals(),
__name__='manage_info')
-
- def __init__(self, link, id, object, uid):
- self.object = uid
+ def __init__(self, link, id, object):
+ self.object = object
self.link = link
self.id = id
- self.url = resolveRelativeLink(link, object)
-
- def __repr__(self):
- return "<LinkInfo %s (%s)>" % (self.url, self.state)
-
- def __getitem__(self, key):
- try:
- value = getattr(self, key)
- except AttributeError:
- raise KeyError, "LinkInfo has no key %r" % key
- return value
- def _p_resolveConflict(self, oldState, savedState, newState):
- return newState
-
- def getId(self):
- return self.id
-
- def title(self):
- return self.url
+ # Products.CMFLinkChecker.interfaces.ILink
+ def getURL(self):
+ """Return the URL object this link refers to."""
+ lc = getToolByName(self, "portal_linkchecker")
+ return lc.database.queryURLs(url=self.url)[0]
def getObject(self):
"""Return a reference to the object."""
- return self.portal_linkchecker.getObjectForUID(self.object)
-
- def updateStatus(self, state, reason):
- assert state in ['red', 'green', 'orange', 'grey'], \
- "Invalid state %s" % state
- self.reason = reason
- if state != self.state:
- self.laststate = self.state
- self.state = state
- self.lastupdate = DateTime()
- self.index()
+ lc = getToolByName(self, "portal_linkchecker")
+ return lc.getObjectForUID(self.object)
- def getGroupId(self):
- return self.state
+ # Python/Zope helpers
+ def __repr__(self):
+ return "<Link %s at %s>" % (self.link, self.object)
# Catalog support
-
def manage_afterAdd(self, container, object):
self.index()
(at)(at) -94,6 +62,11 (at)(at)
self.unindex()
def index(self):
+ self.url =
Products.CMFLinkChecker.utils.resolveRelativeLink(self.link, self.getObject())
+ url = self.getURL()
+ self.state = url.state
+ self.reason = url.reason
+ self.lastcheck = url.lastcheck
path = '/'.join(self.getPhysicalPath())
self.link_catalog.catalog_object(self, path)
Modified: CMFLinkChecker/trunk/reports.py
==============================================================================
--- CMFLinkChecker/trunk/reports.py (original)
+++ CMFLinkChecker/trunk/reports.py Tue Aug 1 10:54:04 2006
(at)(at) -145,7 +145,7 (at)(at)
docs = catalog(Creator=user_id, portal_type=types, Language='all')
uids = [ lc.getUIDForBrain(x) for x in docs ]
uids = filter(None, uids)
- links = lc.database.query(object=uids)
+ links = lc.database.queryLinks(object=uids)
groups = {'red': [], 'orange': [], 'grey': [], 'green': []}
for link in links:
(at)(at) -193,7 +193,7 (at)(at)
_marker = object()
- links = lc.database.query(state=[state])
+ links = lc.database.queryLinks(state=[state])
for link in links:
doc_uid = link.object
if not doc_uid:
(at)(at) -230,7 +230,7 (at)(at)
lc = self._context.getLinkManager()
# get and massage data from the catalog
- links = lc.database.query()
+ links = lc.database.queryLinks()
link_count_per_document = {}
link_count_per_state = {'red': 0, 'grey': 0, 'green': 0, 'orange': 0}
Modified: CMFLinkChecker/trunk/retrievemanager.py
==============================================================================
--- CMFLinkChecker/trunk/retrievemanager.py (original)
+++ CMFLinkChecker/trunk/retrievemanager.py Tue Aug 1 10:54:04 2006
(at)(at) -6,24 +6,20 (at)(at)
"""
# Zope imports
-from OFS.SimpleItem import SimpleItem
+import transaction
+import zLOG
from AccessControl import ClassSecurityInfo, getSecurityManager, Unauthorized
from Globals import InitializeClass, PersistentMapping
-import zLOG
-try:
- import transaction
- use_old_transaction = False
-except ImportError:
- use_old_transaction = True
-
+from OFS.SimpleItem import SimpleItem
+
# CMF/Plone imports
-from Products.CMFCore.CMFCorePermissions import \
- ManagePortal, ModifyPortalContent
+from Products.CMFCore.CMFCorePermissions import ManagePortal
+from Products.CMFCore.CMFCorePermissions import ModifyPortalContent
from Products.CMFCore.utils import getToolByName
# CMFLinkChecker imports
-from Products.CMFLinkChecker.interfaces import \
- IRetrieveManager, IGlobalRetrieverRegistry
+from Products.CMFLinkChecker.interfaces import IGlobalRetrieverRegistry
+from Products.CMFLinkChecker.interfaces import IRetrieveManager
def manage_addRetrieveManager(container, id):
(at)(at) -138,10 +134,8 (at)(at)
# Not actually necessary on every crawl, but it doesn't seem to work
# in the installer, so this seems the next logical place to do it.
self.portal_catalog.reindexIndex('portal_linkchecker_uid',
self.REQUEST)
-
try:
database = self.getParentNode().database
- database.removeAllRegistrations()
# gather all objects that are of a type we can check for links
for type in self.listSupportedTypes():
objects = self.portal_catalog(portal_type=type,
Language='all')
(at)(at) -153,14 +147,12 (at)(at)
"Site Crawl Status",
"%s of %s (%s)" % (i, os_, ob.getPath()))
ob = ob.getObject()
- if ob is None: # Maybe the catalog isn't up to date
+ if ob is None:
+ # Maybe the catalog isn't up to date
continue
self.retrieveObject(ob)
- if use_old_transaction:
- get_transaction().commit(1)
- else:
- transaction.commit(1)
- # Remove stale links
+ transaction.commit(1)
+ # Remove unused urls
database.cleanup()
finally:
server.setClientNotifications(True)
Modified: CMFLinkChecker/trunk/tests/test_linkchecker.py
==============================================================================
--- CMFLinkChecker/trunk/tests/test_linkchecker.py (original)
+++ CMFLinkChecker/trunk/tests/test_linkchecker.py Tue Aug 1 10:54:04 2006
(at)(at) -14,7 +14,7 (at)(at)
from Products.Archetypes.tests.test_sitepolicy import makeContent
-from Products.CMFLinkChecker.interfaces import ILinkInformation
+from Products.CMFLinkChecker.interfaces import ILink
class Dummy(Acquisition.Implicit):
(at)(at) -76,7 +76,7 (at)(at)
for doc in docs:
l = lc.database.byObject(doc)
self.assertEqual(len(l), 1)
- self.failUnless(ILinkInformation.isImplementedBy(l[0]))
+ self.failUnless(ILink.isImplementedBy(l[0]))
self.failUnless(l[0].url in links)
def test_crawl_fail(self):
Modified: CMFLinkChecker/trunk/tests/test_normalizer.py
==============================================================================
--- CMFLinkChecker/trunk/tests/test_normalizer.py (original)
+++ CMFLinkChecker/trunk/tests/test_normalizer.py Tue Aug 1 10:54:04 2006
(at)(at) -27,7 +27,7 (at)(at)
doc1 = portal.doc1
doc1.setText('bla<a
href="index_html">index.html</a>\nbumm')
pl.retrieving.retrieveObject(doc1)
- db_cont = pl.database.query()
+ db_cont = pl.database.queryLinks()
self.assertEqual(1, len(db_cont))
self.assertEqual(portal.absolute_url() + "/index_html",
db_cont[0].url)
(at)(at) -46,7 +46,7 (at)(at)
uid = pl.getUIDForObject(f1d)
pl.retrieving.retrieveObject(f1d)
- db_cont = pl.database.query(object=[uid])
+ db_cont = pl.database.queryLinks(object=[uid])
self.assertEquals(1, len(db_cont))
self.assertEqual(portal.absolute_url() + "/f2/f2d",
db_cont[0].url)
Added: CMFLinkChecker/trunk/url.py
==============================================================================
--- (empty file)
+++ CMFLinkChecker/trunk/url.py Tue Aug 1 10:54:04 2006
(at)(at) -0,0 +1,88 (at)(at)
+# Copyright (c) 2003-2005 gocept gmbh & co. kg
+# See also LICENSE.txt
+# $Id$
+"""CMF link checker tool - url object
+"""
+
+# Zope imports
+from DateTime import DateTime
+from OFS.SimpleItem import SimpleItem
+from Products.PageTemplates.PageTemplateFile import PageTemplateFile
+
+# CMF/Plone imports
+from Products.CMFCore.utils import getToolByName
+
+# Sibling imports
+import Products.CMFLinkChecker.interfaces
+import Products.CMFLinkChecker.utils
+
+
+class URL(SimpleItem):
+ """URL status information"""
+
+ meta_type = "URL"
+
+ __implements__ = (Products.CMFLinkChecker.interfaces.IURL,)
+ # XXX make accessor functions
+ __allow_access_to_unprotected_subobjects__ = 1
+
+ url = None
+ lastcheck = None
+ lastupdate = None
+ laststate = "grey"
+ state = "grey"
+ reason = ""
+ registered = False
+
+ manage_options = ({'label': 'Info', 'action':'manage_info'},) + \
+ SimpleItem.manage_options
+
+ manage_info = PageTemplateFile('www/url', globals(),
__name__='manage_info')
+
+ def __init__(self, url):
+ self.id = Products.CMFLinkChecker.utils.hash_url(url)
+ self.url = url
+
+ # Products.CMFLinkChecker.interfaces.IURL
+ def getLinks(self):
+ lc = getToolByName(self, "portal_linkchecker")
+ return lc.database.getLinksForURL(self.url)
+
+ def updateStatus(self, state, reason):
+ assert state in ['red', 'green', 'orange', 'grey'], \
+ "Invalid state %s" % state
+ self.reason = reason
+ if state != self.state:
+ self.laststate = self.state
+ self.state = state
+ self.lastupdate = DateTime()
+ self.index()
+ # Reindex link objects to update their status caches
+ for link in self.getLinks():
+ link.index()
+
+ # Python/Zope helper functions
+ def __repr__(self):
+ return "<URL %s (%s)>" % (self.url, self.state)
+
+ def _p_resolveConflict(self, oldState, savedState, newState):
+ # This happens if the LMS starts talking to us and we do crawling
+ # and the site is busy in general.
+ # However, we are only interested in the news information we get,
+ # so we can happily do something simple here.
+ return newState
+
+ # Catalog support
+ def manage_afterAdd(self, container, object):
+ self.index()
+
+ def manage_beforeDelete(self, item, container):
+ self.unindex()
+
+ def index(self):
+ path = '/'.join(self.getPhysicalPath())
+ self.url_catalog.catalog_object(self, path)
+
+ def unindex(self):
+ path = '/'.join(self.getPhysicalPath())
+ self.url_catalog.uncatalog_object(path)
Modified: CMFLinkChecker/trunk/utils.py
==============================================================================
--- CMFLinkChecker/trunk/utils.py (original)
+++ CMFLinkChecker/trunk/utils.py Tue Aug 1 10:54:04 2006
(at)(at) -13,6 +13,7 (at)(at)
from urlparse import urlparse, urlunparse, urljoin
from sgmllib import SGMLParseError
import lxml.etree
+import md5
import formatter
# CMF/Plone imports
(at)(at) -205,3 +206,17 (at)(at)
def _getRichTextFields(object):
return [f for f in object.Schema().fields()
if isinstance(f.widget, RichWidget)]
+
+
+def hash_link(link, object):
+ """Provides a hash which can be used as an id for a Link object."""
+ uid = getToolByName(object, "portal_linkchecker").getUIDForObject(object)
+ if link is None:
+ link = ""
+ if isinstance(link, unicode):
+ link = link.encode('utf-8')
+ return md5.md5("%s:%s" % (link, uid)).hexdigest()
+
+
+def hash_url(url):
+ return md5.md5(url).hexdigest()
Copied: CMFLinkChecker/trunk/www/link.zpt (from r4255,
CMFLinkChecker/trunk/www/linkinfo.zpt)
==============================================================================
--- CMFLinkChecker/trunk/www/linkinfo.zpt (original)
+++ CMFLinkChecker/trunk/www/link.zpt Tue Aug 1 10:54:04 2006
(at)(at) -16,9 +16,6 (at)(at)
<dt>Last Change</dt>
<dd tal:content="here/lastupdate"/>
- <dt>Last Status</dt>
- <dd tal:content="here/laststate"/>
-
<dt>Status</dt>
<dd tal:content="here/state"/>
Copied: CMFLinkChecker/trunk/www/url.zpt (from r4255,
CMFLinkChecker/trunk/www/linkinfo.zpt)
==============================================================================
--- CMFLinkChecker/trunk/www/linkinfo.zpt (original)
+++ CMFLinkChecker/trunk/www/url.zpt Tue Aug 1 10:54:04 2006
(at)(at) -16,9 +16,6 (at)(at)
<dt>Last Change</dt>
<dd tal:content="here/lastupdate"/>
- <dt>Last Status</dt>
- <dd tal:content="here/laststate"/>
-
<dt>Status</dt>
<dd tal:content="here/state"/>
|