From 6e8e05e09b72f3f8a24f308cbd80499f222788a8 Mon Sep 17 00:00:00 2001 From: drfho Date: Sun, 8 Feb 2026 01:34:58 +0800 Subject: [PATCH 01/18] minimize calls of getRefObj/getLinkObj --- Products/zms/_objattrs.py | 12 ++++---- Products/zms/_zreferableitem.py | 49 ++++++++++++++++++++------------- Products/zms/version.txt | 2 +- Products/zms/zmslinkelement.py | 8 +++++- 4 files changed, 44 insertions(+), 27 deletions(-) diff --git a/Products/zms/_objattrs.py b/Products/zms/_objattrs.py index c59e70ebf..613d1ff1f 100644 --- a/Products/zms/_objattrs.py +++ b/Products/zms/_objattrs.py @@ -570,12 +570,12 @@ def getObjProperty(self, key, REQUEST={}, par=None): datatype = obj_attr['datatype_key'] value = self.getObjAttrValue( obj_attr, REQUEST) if value: - # Text-Fields - if datatype in _globals.DT_TEXTS: - value = self.validateInlineLinkObj(value) - # Url-Fields - if datatype == _globals.DT_URL: - value = self.validateLinkObj(value) + # Text-Fields: validate inline-links (skip in manage to avoid redundant getLinkObj calls) + if datatype in _globals.DT_TEXTS and not standard.isManagementInterface(self): + value = self.validateInlineLinkObj(value) + # Url-Fields: validate link-attributes (skip in manage to avoid redundant getLinkObj calls) + if datatype == _globals.DT_URL and not standard.isManagementInterface(self): + value = self.validateLinkObj(value) # Executable fields. value = standard.dt_exec(self, value) diff --git a/Products/zms/_zreferableitem.py b/Products/zms/_zreferableitem.py index b5e9b9f51..fd021fa16 100644 --- a/Products/zms/_zreferableitem.py +++ b/Products/zms/_zreferableitem.py @@ -502,30 +502,41 @@ def getLinkObj(self, url, REQUEST=None): i = max(url.find('#'),url.find(',')) if i > 0: url = url[:i] - if url.find('id:') >= 0: - catalog = self.getZMSIndex().get_catalog() - q = catalog({'get_uid':url}) - for r in q: - path = '%s/'%r['getPath'] + #-- [ReqBuff]: Fetch buffered value from Http-Request. + reqBuffId = 'getLinkObj.%s'%url + try: + ob = self.getDocumentElement().fetchReqBuff(reqBuffId) + except: + if url.find('id:') >= 0: + catalog = self.getZMSIndex().get_catalog() + q = catalog({'get_uid':url}) + for r in q: + path = '%s/'%r['getPath'] + l = [x for x in path.split('/') if x] + ob = self.getRootElement() + if l: + [l.pop(0) for x in ob.getPhysicalPath() if l[0] == x] + for id in l: + ob = getattr(ob,id,None) + break + elif not url.startswith('__'): + path = url.replace('@','/content/') l = [x for x in path.split('/') if x] - ob = self.getRootElement() + ob = self.getDocumentElement() if l: [l.pop(0) for x in ob.getPhysicalPath() if l[0] == x] for id in l: ob = getattr(ob,id,None) - break - elif not url.startswith('__'): - path = url.replace('@','/content/') - l = [x for x in path.split('/') if x] - ob = self.getDocumentElement() - if l: - [l.pop(0) for x in ob.getPhysicalPath() if l[0] == x] - for id in l: - ob = getattr(ob,id,None) - # Prepare request - ids = self.getPhysicalPath() - if ob is not None and ob.id not in ids: - ob.set_request_context(request, ref_params) + #-- [ReqBuff]: Store value in buffer of Http-Request. + self.getDocumentElement().storeReqBuff(reqBuffId, ob) + # Prepare request (only if ref_params are provided) + if ob is not None and ref_params: + ids = self.getPhysicalPath() + if ob.id not in ids: + ob.set_request_context(request, ref_params) + # DEBUG: logging/counting getLinkObj calls + request.set('getLinkObj_calls', request.get('getLinkObj_calls', 0) + 1) + standard.writeStdout(self, '[getLinkObj] url=%s, ob=%s, calls=%d'%(url, getattr(ob, 'id', None), request.get('getLinkObj_calls', 0))) return ob diff --git a/Products/zms/version.txt b/Products/zms/version.txt index 25b5c408e..cda981080 100644 --- a/Products/zms/version.txt +++ b/Products/zms/version.txt @@ -1 +1 @@ -2025.9.1+b976ca4 +2025.9.1+8a0d574 diff --git a/Products/zms/zmslinkelement.py b/Products/zms/zmslinkelement.py index 77b9315db..397e9a98e 100644 --- a/Products/zms/zmslinkelement.py +++ b/Products/zms/zmslinkelement.py @@ -193,12 +193,18 @@ def getRef(self): # ZMSLinkElement.getRefObj: # -------------------------------------------------------------------------- def getRefObj(self): + #-- [ReqBuff]: Fetch buffered value from Http-Request. + docelmnt = self.getDocumentElement() + reqBuffId = 'getRefObj.%s'%self.get_uid() + try: return docelmnt.fetchReqBuff(reqBuffId) + except: pass ref_obj = self.getLinkObj(self.getRef()) if ref_obj == self: ref_obj = None if ref_obj is not None and ref_obj.meta_type == 'ZMSLinkElement': ref_obj = ref_obj.getRefObj() - return ref_obj + #-- [ReqBuff]: Returns value and stores it in buffer of Http-Request. + return docelmnt.storeReqBuff(reqBuffId, ref_obj) # -------------------------------------------------------------------------- From e8eb17b6bb32dde892e1be4ddc098ba7416c8f15 Mon Sep 17 00:00:00 2001 From: drfho Date: Sun, 8 Feb 2026 02:09:12 +0800 Subject: [PATCH 02/18] debug code for call-counting --- Products/zms/_zreferableitem.py | 10 ++++++++-- Products/zms/version.txt | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Products/zms/_zreferableitem.py b/Products/zms/_zreferableitem.py index fd021fa16..a9eed43d3 100644 --- a/Products/zms/_zreferableitem.py +++ b/Products/zms/_zreferableitem.py @@ -535,8 +535,14 @@ def getLinkObj(self, url, REQUEST=None): if ob.id not in ids: ob.set_request_context(request, ref_params) # DEBUG: logging/counting getLinkObj calls - request.set('getLinkObj_calls', request.get('getLinkObj_calls', 0) + 1) - standard.writeStdout(self, '[getLinkObj] url=%s, ob=%s, calls=%d'%(url, getattr(ob, 'id', None), request.get('getLinkObj_calls', 0))) + request.set('getLinkObj_counter', request.get('getLinkObj_counter', 0) + 1) + standard.writeStdout(self, '[getLinkObj] url=%s, ob=%s, ref_params=%s, counter=%d'%( + url, + ob.absolute_url() if ob is not None else None, + ref_params, + request.get('getLinkObj_counter', 0) + ) + ) return ob diff --git a/Products/zms/version.txt b/Products/zms/version.txt index cda981080..56fd130fb 100644 --- a/Products/zms/version.txt +++ b/Products/zms/version.txt @@ -1 +1 @@ -2025.9.1+8a0d574 +2025.9.1+6e8e05e From 78285006d8251f644b4c4cd174fb6da22e4d00f0 Mon Sep 17 00:00:00 2001 From: drfho Date: Sun, 8 Feb 2026 17:07:01 +0800 Subject: [PATCH 03/18] added traceback for debugging --- Products/zms/_zreferableitem.py | 18 ++++++++++++++---- Products/zms/version.txt | 2 +- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Products/zms/_zreferableitem.py b/Products/zms/_zreferableitem.py index a9eed43d3..c032af11e 100644 --- a/Products/zms/_zreferableitem.py +++ b/Products/zms/_zreferableitem.py @@ -23,6 +23,8 @@ # Product Imports. from Products.zms import standard from zope.globalrequest import getRequest +# FOR DEBUGGING PURPOSES ONLY: import traceback to log the call stack of getLinkObj calls +import traceback # ------------------------------------------------------------------------------ # isMailLink: @@ -534,15 +536,23 @@ def getLinkObj(self, url, REQUEST=None): ids = self.getPhysicalPath() if ob.id not in ids: ob.set_request_context(request, ref_params) + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # DEBUG: logging/counting getLinkObj calls + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ request.set('getLinkObj_counter', request.get('getLinkObj_counter', 0) + 1) - standard.writeStdout(self, '[getLinkObj] url=%s, ob=%s, ref_params=%s, counter=%d'%( + # show traceback in debugging output to identify which function is calling getLinkObj and causing multiple calls + traceback_stack = [] + for frame in reversed(traceback.extract_stack()[-5:-2]): + traceback_stack.append('%s:%s:%s' % (str(frame.filename).split('/')[-1], frame.lineno, frame.name)) + standard.writeStdout(self, '%d. [getLinkObj] %s, ID=%s, ref_params=%s\n...was called from:\n\t%s\n'%( + request.get('getLinkObj_counter', 0), url, - ob.absolute_url() if ob is not None else None, - ref_params, - request.get('getLinkObj_counter', 0) + ob.id if ob is not None else None, + ref_params, + '\n\t'.join(traceback_stack) ) ) + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ return ob diff --git a/Products/zms/version.txt b/Products/zms/version.txt index 56fd130fb..47ae8d390 100644 --- a/Products/zms/version.txt +++ b/Products/zms/version.txt @@ -1 +1 @@ -2025.9.1+6e8e05e +2025.9.1+e8eb17b From 61f506451e3bef9d77bad24934490930ad4a3c74 Mon Sep 17 00:00:00 2001 From: drfho Date: Mon, 9 Feb 2026 20:50:47 +0800 Subject: [PATCH 04/18] more traceback for debugging --- Products/zms/_zreferableitem.py | 6 ++++-- Products/zms/version.txt | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Products/zms/_zreferableitem.py b/Products/zms/_zreferableitem.py index c032af11e..23464688d 100644 --- a/Products/zms/_zreferableitem.py +++ b/Products/zms/_zreferableitem.py @@ -544,10 +544,12 @@ def getLinkObj(self, url, REQUEST=None): traceback_stack = [] for frame in reversed(traceback.extract_stack()[-5:-2]): traceback_stack.append('%s:%s:%s' % (str(frame.filename).split('/')[-1], frame.lineno, frame.name)) - standard.writeStdout(self, '%d. [getLinkObj] %s, ID=%s, ref_params=%s\n...was called from:\n\t%s\n'%( + standard.writeStdout(self, '%d. [%s:getLinkObj] %s, Target-ID=%s (%s), ref_params=%s\n...was called from:\n\t%s\n'%( request.get('getLinkObj_counter', 0), + self.meta_id, url, - ob.id if ob is not None else None, + ob.id if ob is not None else None, + ob.meta_id if ob is not None else None, ref_params, '\n\t'.join(traceback_stack) ) diff --git a/Products/zms/version.txt b/Products/zms/version.txt index 47ae8d390..e55b57b02 100644 --- a/Products/zms/version.txt +++ b/Products/zms/version.txt @@ -1 +1 @@ -2025.9.1+e8eb17b +2025.9.1+7828500 From e1201ac85538ede8eacf883c8eb10864bc39c918 Mon Sep 17 00:00:00 2001 From: drfho Date: Mon, 9 Feb 2026 23:03:10 +0800 Subject: [PATCH 05/18] revoke inline-validation in zmi --- Products/zms/_objattrs.py | 12 ++++++------ Products/zms/version.txt | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Products/zms/_objattrs.py b/Products/zms/_objattrs.py index 613d1ff1f..c59e70ebf 100644 --- a/Products/zms/_objattrs.py +++ b/Products/zms/_objattrs.py @@ -570,12 +570,12 @@ def getObjProperty(self, key, REQUEST={}, par=None): datatype = obj_attr['datatype_key'] value = self.getObjAttrValue( obj_attr, REQUEST) if value: - # Text-Fields: validate inline-links (skip in manage to avoid redundant getLinkObj calls) - if datatype in _globals.DT_TEXTS and not standard.isManagementInterface(self): - value = self.validateInlineLinkObj(value) - # Url-Fields: validate link-attributes (skip in manage to avoid redundant getLinkObj calls) - if datatype == _globals.DT_URL and not standard.isManagementInterface(self): - value = self.validateLinkObj(value) + # Text-Fields + if datatype in _globals.DT_TEXTS: + value = self.validateInlineLinkObj(value) + # Url-Fields + if datatype == _globals.DT_URL: + value = self.validateLinkObj(value) # Executable fields. value = standard.dt_exec(self, value) diff --git a/Products/zms/version.txt b/Products/zms/version.txt index e55b57b02..348ccb5df 100644 --- a/Products/zms/version.txt +++ b/Products/zms/version.txt @@ -1 +1 @@ -2025.9.1+7828500 +2025.9.1+61f5064 From 934f1734b241783c90a479c900fec5f54197cb1a Mon Sep 17 00:00:00 2001 From: drfho Date: Tue, 10 Feb 2026 00:44:11 +0800 Subject: [PATCH 06/18] removed debug output for merge --- Products/zms/_zreferableitem.py | 21 --------------------- Products/zms/version.txt | 2 +- 2 files changed, 1 insertion(+), 22 deletions(-) diff --git a/Products/zms/_zreferableitem.py b/Products/zms/_zreferableitem.py index 23464688d..d78a96169 100644 --- a/Products/zms/_zreferableitem.py +++ b/Products/zms/_zreferableitem.py @@ -23,8 +23,6 @@ # Product Imports. from Products.zms import standard from zope.globalrequest import getRequest -# FOR DEBUGGING PURPOSES ONLY: import traceback to log the call stack of getLinkObj calls -import traceback # ------------------------------------------------------------------------------ # isMailLink: @@ -536,25 +534,6 @@ def getLinkObj(self, url, REQUEST=None): ids = self.getPhysicalPath() if ob.id not in ids: ob.set_request_context(request, ref_params) - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # DEBUG: logging/counting getLinkObj calls - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - request.set('getLinkObj_counter', request.get('getLinkObj_counter', 0) + 1) - # show traceback in debugging output to identify which function is calling getLinkObj and causing multiple calls - traceback_stack = [] - for frame in reversed(traceback.extract_stack()[-5:-2]): - traceback_stack.append('%s:%s:%s' % (str(frame.filename).split('/')[-1], frame.lineno, frame.name)) - standard.writeStdout(self, '%d. [%s:getLinkObj] %s, Target-ID=%s (%s), ref_params=%s\n...was called from:\n\t%s\n'%( - request.get('getLinkObj_counter', 0), - self.meta_id, - url, - ob.id if ob is not None else None, - ob.meta_id if ob is not None else None, - ref_params, - '\n\t'.join(traceback_stack) - ) - ) - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ return ob diff --git a/Products/zms/version.txt b/Products/zms/version.txt index 348ccb5df..655cdc9b7 100644 --- a/Products/zms/version.txt +++ b/Products/zms/version.txt @@ -1 +1 @@ -2025.9.1+61f5064 +2025.9.1+e1201ac From 44aff6ff0f997e01eb55862af5c19b6cfe41a10e Mon Sep 17 00:00:00 2001 From: drfho Date: Tue, 10 Feb 2026 01:02:53 +0800 Subject: [PATCH 07/18] restored lines for debug output Ref: https://github.com/zms-publishing/ZMS/pull/459/changes/934f1734b241783c90a479c900fec5f54197cb1a --- Products/zms/_zreferableitem.py | 21 +++++++++++++++++++++ Products/zms/version.txt | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/Products/zms/_zreferableitem.py b/Products/zms/_zreferableitem.py index d78a96169..23464688d 100644 --- a/Products/zms/_zreferableitem.py +++ b/Products/zms/_zreferableitem.py @@ -23,6 +23,8 @@ # Product Imports. from Products.zms import standard from zope.globalrequest import getRequest +# FOR DEBUGGING PURPOSES ONLY: import traceback to log the call stack of getLinkObj calls +import traceback # ------------------------------------------------------------------------------ # isMailLink: @@ -534,6 +536,25 @@ def getLinkObj(self, url, REQUEST=None): ids = self.getPhysicalPath() if ob.id not in ids: ob.set_request_context(request, ref_params) + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # DEBUG: logging/counting getLinkObj calls + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + request.set('getLinkObj_counter', request.get('getLinkObj_counter', 0) + 1) + # show traceback in debugging output to identify which function is calling getLinkObj and causing multiple calls + traceback_stack = [] + for frame in reversed(traceback.extract_stack()[-5:-2]): + traceback_stack.append('%s:%s:%s' % (str(frame.filename).split('/')[-1], frame.lineno, frame.name)) + standard.writeStdout(self, '%d. [%s:getLinkObj] %s, Target-ID=%s (%s), ref_params=%s\n...was called from:\n\t%s\n'%( + request.get('getLinkObj_counter', 0), + self.meta_id, + url, + ob.id if ob is not None else None, + ob.meta_id if ob is not None else None, + ref_params, + '\n\t'.join(traceback_stack) + ) + ) + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ return ob diff --git a/Products/zms/version.txt b/Products/zms/version.txt index 655cdc9b7..4cd9d52fa 100644 --- a/Products/zms/version.txt +++ b/Products/zms/version.txt @@ -1 +1 @@ -2025.9.1+e1201ac +2025.9.1+f94c041 From 94810567660b739fda2b8c65ea6f4e625de0e275 Mon Sep 17 00:00:00 2001 From: zmsdev Date: Tue, 10 Feb 2026 18:34:57 +0100 Subject: [PATCH 08/18] call getRefObj for embedded links --- Products/zms/zmslinkelement.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Products/zms/zmslinkelement.py b/Products/zms/zmslinkelement.py index 397e9a98e..c190da575 100644 --- a/Products/zms/zmslinkelement.py +++ b/Products/zms/zmslinkelement.py @@ -233,9 +233,9 @@ def isMetaTypePROXY(self, proxy, meta_type, REQUEST={'preview':'preview'}): b = False if not (self.NOREF == meta_type or (isinstance(meta_type, list) and self.NOREF in meta_type)): b = b or zmsobject.ZMSObject.isMetaType(self, meta_type, REQUEST) - ref_obj = self.getRefObj() - if ref_obj is not None and self.isEmbedded(): - if not (self.NORESOLVEREF == meta_type or (isinstance(meta_type, list) and self.NORESOLVEREF in meta_type)): + if self.isEmbedded(): + ref_obj = self.getRefObj() + if ref_obj is not None and not (self.NORESOLVEREF == meta_type or (isinstance(meta_type, list) and self.NORESOLVEREF in meta_type)): b = b or ref_obj.isMetaType(meta_type, REQUEST) return b From f1b0167e96667832a2affc6e1e1add6c34e7021f Mon Sep 17 00:00:00 2001 From: zmsdev Date: Tue, 10 Feb 2026 18:48:34 +0100 Subject: [PATCH 09/18] call getRefObj for embedded links --- Products/zms/zmslinkelement.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/Products/zms/zmslinkelement.py b/Products/zms/zmslinkelement.py index c190da575..6f2607708 100644 --- a/Products/zms/zmslinkelement.py +++ b/Products/zms/zmslinkelement.py @@ -378,11 +378,10 @@ def isPage(self): rtnVal = False if self.getEmbedType() == 'remote': return self.getRemoteObj().get('is_page',False) - else: - if self.isEmbedded(): - ref_obj = self.getRefObj() - if ref_obj is not None: - rtnVal = rtnVal or ref_obj.isPage() + elif self.isEmbedded(): + ref_obj = self.getRefObj() + if ref_obj is not None: + rtnVal = rtnVal or ref_obj.isPage() return rtnVal @@ -415,7 +414,7 @@ def getTypePROXY(self, proxy): rtn = 'ZMSObject' if proxy != self and proxy is not None and self.isEmbeddedRecursive(): rtn = proxy.getType() - else: + elif self.isEmbedded(): ref_obj = self.getRefObj() if ref_obj is not None: rtn = ref_obj.getType() @@ -494,7 +493,7 @@ def getNavElementsPROXY(self, proxy, REQUEST, expand_tree=1, current_child=None, recursive = self.isEmbeddedRecursive() if proxy != self and proxy is not None and recursive: rtn = proxy.getNavElements( REQUEST, expand_tree, current_child, subElements) - else: + elif self.isEmbedded(): ref_obj = self.getRefObj() if isinstance(ref_obj, zmscontainerobject.ZMSContainerObject): rtn = super(zmslinkelement.ZMSLinkElement, zself).getNavElements( REQUEST, expand_tree, current_child, subElements) @@ -535,7 +534,6 @@ def getHref2IndexHtml(self, REQUEST, deep=1): # -------------------------------------------------------------------------- def _getBodyContent(self, REQUEST): rtn = '' - ref_obj = self.getRefObj() ref = self.getObjProperty('attr_ref', REQUEST) if self.getEmbedType() == 'remote': @@ -552,6 +550,7 @@ def _getBodyContent(self, REQUEST): if proxy != self and proxy is not None and self.isEmbeddedRecursive(): rtn = proxy._getBodyContent(REQUEST) elif proxy == self and proxy is not None and self.isEmbedded(): + ref_obj = self.getRefObj() if ref_obj is None: ref_obj = self.getLinkObj(ref) if ref_obj is not None and ref_obj != self: @@ -570,7 +569,6 @@ def _getBodyContent(self, REQUEST): # -------------------------------------------------------------------------- def renderShort(self, REQUEST): rtn = '' - ref_obj = self.getRefObj() ref = self.getObjProperty('attr_ref', REQUEST) if self.getEmbedType() == 'remote': @@ -582,6 +580,7 @@ def renderShort(self, REQUEST): elif self.isEmbedded(): REQUEST.set('ZMS_RELATIVATE_URL', False) + ref_obj = self.getRefObj() if ref_obj is None: ref_obj = self.getLinkObj(ref) if ref_obj is None or ref_obj.isPage(): @@ -705,7 +704,7 @@ def printHtmlPROXY(self, proxy, level, sectionizer, REQUEST, deep=True): recursive = self.isEmbeddedRecursive() if proxy != self and proxy is not None and recursive: rtn = proxy.printHtml( level, sectionizer, REQUEST, deep) - else: + elif self.isEmbedded(): ref_obj = self.getRefObj() if ref_obj is not None: rtn = ref_obj.printHtml( level, sectionizer, REQUEST, deep) From 307580e150ff89ba835fad63c6b4e02b9648d738 Mon Sep 17 00:00:00 2001 From: drfho Date: Wed, 11 Feb 2026 05:00:33 +0100 Subject: [PATCH 10/18] debug-helper: added buffer-counter for getLinkObj --- Products/zms/_cachemanager.py | 4 +++- Products/zms/_zreferableitem.py | 8 +++++++- Products/zms/version.txt | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Products/zms/_cachemanager.py b/Products/zms/_cachemanager.py index dad2f5b78..e580015a4 100644 --- a/Products/zms/_cachemanager.py +++ b/Products/zms/_cachemanager.py @@ -60,8 +60,10 @@ def clearReqBuff(self, prefix='', REQUEST=None): # # @throws Exception # -------------------------------------------------------------------------- - def fetchReqBuff(self, key, REQUEST=None): + def fetchReqBuff(self, key=None, REQUEST=None): request = getattr(self, 'REQUEST', getRequest()) + if key is None: # For debugging purposes, return whole buffer. + return request.get('__buff__',{}) buff = request['__buff__'] reqBuffId = self.getReqBuffId(key) return getattr(buff, reqBuffId) diff --git a/Products/zms/_zreferableitem.py b/Products/zms/_zreferableitem.py index 23464688d..f36f4dc48 100644 --- a/Products/zms/_zreferableitem.py +++ b/Products/zms/_zreferableitem.py @@ -490,6 +490,7 @@ def findObject(self, url): # ---------------------------------------------------------------------------- def getLinkObj(self, url, REQUEST=None): request = getattr(self, 'REQUEST', getRequest()) + was_buffered = True ob = None if isInternalLink(url): # Params. @@ -509,6 +510,7 @@ def getLinkObj(self, url, REQUEST=None): try: ob = self.getDocumentElement().fetchReqBuff(reqBuffId) except: + was_buffered = False if url.find('id:') >= 0: catalog = self.getZMSIndex().get_catalog() q = catalog({'get_uid':url}) @@ -544,12 +546,16 @@ def getLinkObj(self, url, REQUEST=None): traceback_stack = [] for frame in reversed(traceback.extract_stack()[-5:-2]): traceback_stack.append('%s:%s:%s' % (str(frame.filename).split('/')[-1], frame.lineno, frame.name)) - standard.writeStdout(self, '%d. [%s:getLinkObj] %s, Target-ID=%s (%s), ref_params=%s\n...was called from:\n\t%s\n'%( + request.set('count_buffered_getLinkObj_calls', request.get('count_buffered_getLinkObj_calls', 0) + int(was_buffered)) + standard.writeStdout(self, '%d. [%s:getLinkObj] %s, Target-ID=%s (%s), URL=%s, was_buffered=%s (%s), ref_params=%s\n...was called from:\n\t%s\n'%( request.get('getLinkObj_counter', 0), self.meta_id, url, ob.id if ob is not None else None, ob.meta_id if ob is not None else None, + ob.absolute_url(relative=1) if ob is not None else None, + was_buffered, + request.get('count_buffered_getLinkObj_calls', 0), ref_params, '\n\t'.join(traceback_stack) ) diff --git a/Products/zms/version.txt b/Products/zms/version.txt index 4cd9d52fa..88b8ba55d 100644 --- a/Products/zms/version.txt +++ b/Products/zms/version.txt @@ -1 +1 @@ -2025.9.1+f94c041 +2025.9.1+f1b0167 From f6f97fa87081f93579a5d65766a73611e4163851 Mon Sep 17 00:00:00 2001 From: zmsdev Date: Wed, 11 Feb 2026 13:26:53 +0100 Subject: [PATCH 11/18] Persist embed_type (attr_type) --- Products/zms/zmslinkelement.py | 54 ++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/Products/zms/zmslinkelement.py b/Products/zms/zmslinkelement.py index 6f2607708..7f91a0fe3 100644 --- a/Products/zms/zmslinkelement.py +++ b/Products/zms/zmslinkelement.py @@ -103,16 +103,32 @@ def getSelf(self, meta_type=None): # -------------------------------------------------------------------------- # ZMSLinkElement.getEmbedType: + # + # Special handling for _embed_type, because it is a raw attribute and not a property. # -------------------------------------------------------------------------- def getEmbedType(self): - request = getattr(self, 'REQUEST', getRequest()) - embed_type = self.getObjAttrValue( self.getObjAttr( 'attr_type'), request) - if embed_type in [ 'embed', 'recursive', 'remote']: - ref_obj = self.getRefObj() - if ref_obj is not None and ref_obj.isAncestor( self): - embed_type = 'cyclic' # Error! + # _embed_type is a raw attribute and should be accessed directly + embed_type = getattr(self, '_attr_type', None) + # if _embed_type is not set, try to get it from the property (for backward compatibility) + if embed_type is None: + request = getattr(self, 'REQUEST', getRequest()) + embed_type = self.getObjAttrValue( self.getObjAttr( 'attr_type'), request) return embed_type + # -------------------------------------------------------------------------- + # ZMSLinkElement.setEmbedType: + # + # Special handling for _embed_type, because it is a raw attribute and not a property. + # -------------------------------------------------------------------------- + def setEmbedType(self, REQUEST): + embed_type = REQUEST.get('attr_type', '') + if embed_type in [ 'embed', 'recursive', 'remote']: + # check for cyclic embedding + ref_obj = self.getRefObj() + if ref_obj is not None and ref_obj.isAncestor( self): + embed_type = 'cyclic' # Error! + self._embed_type = embed_type + # -------------------------------------------------------------------------- # ZMSLinkElement.isEmbedded: @@ -161,10 +177,13 @@ def manage_changeProperties(self, lang, REQUEST, RESPONSE): obj_attr = self.getObjAttr(key) if obj_attr['xml']: self.setReqProperty(key, REQUEST) - + ##### VersionManager #### self.onChangeObj(REQUEST) + ##### Special handling for _embed_type, because it is a raw attribute and not a property #### + self.setEmbedType(REQUEST) + ##### Success Message #### message = self.getZMILangStr('MSG_CHANGED') @@ -638,15 +657,14 @@ def initProxy(self, base, url_base, proxy, recursive=False): # Returns self or referenced object (if embedded) as ZMSProxyObject # -------------------------------------------------------------------------- def __proxy__(self): - req = getattr(self, 'REQUEST', getRequest()) rtn = self - if req.get( 'ZMS_PROXY', True): - if req.get( 'URL', '').find( '/manage') < 0 or req.get( 'ZMS_PATH_HANDLER', False): + req = getattr(self, 'REQUEST', getRequest()) + if req.get( 'URL', '').find( '/manage') < 0 or req.get( 'ZMS_PATH_HANDLER', False): if self.isEmbeddedRecursive(): - ref_obj = self.getRefObj() - if ref_obj is not None: - recursive = True - rtn = zmsproxyobject.ZMSProxyObject( self, self.aq_parent, self.absolute_url(), self.id, ref_obj, recursive) + ref_obj = self.getRefObj() + if ref_obj is not None: + recursive = True + rtn = zmsproxyobject.ZMSProxyObject( self, self.aq_parent, self.absolute_url(), self.id, ref_obj, recursive) return rtn @@ -658,10 +676,10 @@ def __proxy__(self): # -------------------------------------------------------------------------- def getProxy(self): req = getattr(self, 'REQUEST', getRequest()) - rtn = self - if req.get( 'ZMS_PROXY', True): - rtn = req.get( 'ZMS_PROXY_%s'%self.id, self.__proxy__()) - return rtn + key = 'ZMS_PROXY_%s'%self.id + if not req.get(key, False): + req.set(key, self.__proxy__()) + return req.get(key) # -------------------------------------------------------------------------- From b87103b72377cf313dec79f3eceefc3c3a19aece Mon Sep 17 00:00:00 2001 From: drfho Date: Wed, 11 Feb 2026 21:27:34 +0800 Subject: [PATCH 12/18] monotonized indents of former commit --- Products/zms/version.txt | 2 +- Products/zms/zmslinkelement.py | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Products/zms/version.txt b/Products/zms/version.txt index 88b8ba55d..92083d486 100644 --- a/Products/zms/version.txt +++ b/Products/zms/version.txt @@ -1 +1 @@ -2025.9.1+f1b0167 +2025.9.1+f6f97fa diff --git a/Products/zms/zmslinkelement.py b/Products/zms/zmslinkelement.py index 7f91a0fe3..1e99cd903 100644 --- a/Products/zms/zmslinkelement.py +++ b/Products/zms/zmslinkelement.py @@ -111,8 +111,8 @@ def getEmbedType(self): embed_type = getattr(self, '_attr_type', None) # if _embed_type is not set, try to get it from the property (for backward compatibility) if embed_type is None: - request = getattr(self, 'REQUEST', getRequest()) - embed_type = self.getObjAttrValue( self.getObjAttr( 'attr_type'), request) + request = getattr(self, 'REQUEST', getRequest()) + embed_type = self.getObjAttrValue( self.getObjAttr( 'attr_type'), request) return embed_type # -------------------------------------------------------------------------- @@ -121,13 +121,13 @@ def getEmbedType(self): # Special handling for _embed_type, because it is a raw attribute and not a property. # -------------------------------------------------------------------------- def setEmbedType(self, REQUEST): - embed_type = REQUEST.get('attr_type', '') - if embed_type in [ 'embed', 'recursive', 'remote']: - # check for cyclic embedding - ref_obj = self.getRefObj() - if ref_obj is not None and ref_obj.isAncestor( self): - embed_type = 'cyclic' # Error! - self._embed_type = embed_type + embed_type = REQUEST.get('attr_type', '') + if embed_type in [ 'embed', 'recursive', 'remote']: + # check for cyclic embedding + ref_obj = self.getRefObj() + if ref_obj is not None and ref_obj.isAncestor( self): + embed_type = 'cyclic' # Error! + self._embed_type = embed_type # -------------------------------------------------------------------------- @@ -608,7 +608,7 @@ def renderShort(self, REQUEST): rtn = ref_obj.renderShort(REQUEST) REQUEST.set('ZMS_RELATIVATE_URL', True) else: - rtn = self._getBodyContent( REQUEST) + rtn = self._getBodyContent( REQUEST) return rtn @@ -660,11 +660,11 @@ def __proxy__(self): rtn = self req = getattr(self, 'REQUEST', getRequest()) if req.get( 'URL', '').find( '/manage') < 0 or req.get( 'ZMS_PATH_HANDLER', False): - if self.isEmbeddedRecursive(): - ref_obj = self.getRefObj() - if ref_obj is not None: - recursive = True - rtn = zmsproxyobject.ZMSProxyObject( self, self.aq_parent, self.absolute_url(), self.id, ref_obj, recursive) + if self.isEmbeddedRecursive(): + ref_obj = self.getRefObj() + if ref_obj is not None: + recursive = True + rtn = zmsproxyobject.ZMSProxyObject( self, self.aq_parent, self.absolute_url(), self.id, ref_obj, recursive) return rtn From 9d69e49c5956f998f004811c2807d2c7dd244e03 Mon Sep 17 00:00:00 2001 From: zmsdev Date: Thu, 12 Feb 2026 14:06:33 +0100 Subject: [PATCH 13/18] fixed redundant call of getLinkObj --- Products/zms/zmslinkelement.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Products/zms/zmslinkelement.py b/Products/zms/zmslinkelement.py index 7f91a0fe3..395029c43 100644 --- a/Products/zms/zmslinkelement.py +++ b/Products/zms/zmslinkelement.py @@ -570,8 +570,6 @@ def _getBodyContent(self, REQUEST): rtn = proxy._getBodyContent(REQUEST) elif proxy == self and proxy is not None and self.isEmbedded(): ref_obj = self.getRefObj() - if ref_obj is None: - ref_obj = self.getLinkObj(ref) if ref_obj is not None and ref_obj != self: rtn = ref_obj._getBodyContent( REQUEST) else: @@ -600,8 +598,6 @@ def renderShort(self, REQUEST): elif self.isEmbedded(): REQUEST.set('ZMS_RELATIVATE_URL', False) ref_obj = self.getRefObj() - if ref_obj is None: - ref_obj = self.getLinkObj(ref) if ref_obj is None or ref_obj.isPage(): rtn = super(ZMSLinkElement, self).renderShort(REQUEST) elif ref_obj != self: From b880fd559ec0caee953de7ce177670f97bd8bfca Mon Sep 17 00:00:00 2001 From: drfho Date: Fri, 13 Feb 2026 14:46:05 +0800 Subject: [PATCH 14/18] modified validateLinkObj description --- Products/zms/_confmanager.py | 4 ++-- Products/zms/version.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Products/zms/_confmanager.py b/Products/zms/_confmanager.py index 9305b1c6a..c9cb451f6 100644 --- a/Products/zms/_confmanager.py +++ b/Products/zms/_confmanager.py @@ -549,8 +549,8 @@ def getConfPropertiesDefaults(self): {'key':'ZCatalog.TextIndexType','title':'Search with TextIndex-type','desc':'Use specified TextIndex-type (default: ZCTextIndex)','datatype':'string','default':'ZCTextIndex'}, {'key':'ZMSIndexZCatalog.ObjectImported.reindex','title':'Reindex ZMSIndex on content import','desc':'Please be aware that activating implicit ZMSIndex-resync on content import can block bigger sites for a while','datatype':'boolean','default':0}, {'key':'ZMSIndexZCatalog.ObjectImported.resync','title':'Resync ZMSIndex on content import','desc':'Please be aware that activating implicit ZMSIndex-resync on content import can block bigger sites for a while','datatype':'boolean','default':0}, - {'key':'ZReferableItem.validateLinkObj','title':'Auto-correct link-attributes on save','desc':'Ensure valid link-attributes by parsing and using ZMSIndex for refreshing target urls on save event','datatype':'boolean','default':1}, - {'key':'ZReferableItem.validateInlineLinkObj','title':'Auto-correct inline-links on save','desc':'Ensure valid inline-links by text-parsing and using ZMSIndex for refreshing target urls on save event','datatype':'boolean','default':1}, + {'key':'ZReferableItem.validateLinkObj','title':'Auto-correct link-attributes','desc':'Ensure valid link-attributes by parsing and using ZMSIndex for refreshing target urls on rendering','datatype':'boolean','default':1}, + {'key':'ZReferableItem.validateInlineLinkObj','title':'Auto-correct inline-links','desc':'Ensure valid inline-links by text-parsing and using ZMSIndex for refreshing target urls on rendering','datatype':'boolean','default':1}, ] """ diff --git a/Products/zms/version.txt b/Products/zms/version.txt index 33f5ced17..2f4fab285 100644 --- a/Products/zms/version.txt +++ b/Products/zms/version.txt @@ -1 +1 @@ -2025.9.1+1dd1435 +2025.9.1+d48572e From 7d6faadbe68b0b66288c3ccfe653948f9a6ea5e7 Mon Sep 17 00:00:00 2001 From: zmsdev Date: Fri, 13 Feb 2026 11:39:10 +0100 Subject: [PATCH 15/18] embed remote objects and prepare impoved validation of ref-objects --- .vscode/ZMS_host.code-workspace | 2 +- Products/zms/_zreferableitem.py | 12 ++++++- Products/zms/zmslinkelement.py | 57 ++++++++++++++++----------------- 3 files changed, 40 insertions(+), 31 deletions(-) diff --git a/.vscode/ZMS_host.code-workspace b/.vscode/ZMS_host.code-workspace index 7d1c43b77..af7362a23 100644 --- a/.vscode/ZMS_host.code-workspace +++ b/.vscode/ZMS_host.code-workspace @@ -26,7 +26,7 @@ "editor.renderWhitespace": "all", "editor.renderControlCharacters": false, "workbench.iconTheme": "vs-minimal", - "workbench.colorTheme": "Visual Studio Light", + "workbench.colorTheme": "Visual Studio Dark", "files.eol": "\n", "files.autoSave": "afterDelay", "files.associations": { diff --git a/Products/zms/_zreferableitem.py b/Products/zms/_zreferableitem.py index ddbde42bd..ad30b8f7f 100644 --- a/Products/zms/_zreferableitem.py +++ b/Products/zms/_zreferableitem.py @@ -447,7 +447,6 @@ def validateInlineLinkObj(self, text): text = text.replace(old, new) return text - # ---------------------------------------------------------------------------- # ZReferableItem.validateLinkObj: # @@ -461,6 +460,17 @@ def validateLinkObj(self, url): url = ild['data-id'] return url + # ---------------------------------------------------------------------------- + # Validates internal object-references. + # + # @param s: String to validate + # ---------------------------------------------------------------------------- + def validateRefObj(self, s): + if isInternalLink(s): + return self.validateLinkObj(s) + return self.validateInlineLinkObj(s) + + # ---------------------------------------------------------------------------- # ZReferableItem.findObject: # diff --git a/Products/zms/zmslinkelement.py b/Products/zms/zmslinkelement.py index ba1da1280..d6d562153 100644 --- a/Products/zms/zmslinkelement.py +++ b/Products/zms/zmslinkelement.py @@ -547,22 +547,29 @@ def getHref2IndexHtml(self, REQUEST, deep=1): # -------------------------------------------------------------------------- - # ZMSLinkElement._getBodyContent: + # ZMSLinkElement.embedRemoteContent: # - # HTML presentation of link-element. + # Embeds content from remote reference if embed_type is 'remote'. # -------------------------------------------------------------------------- - def _getBodyContent(self, REQUEST): - rtn = '' - ref = self.getObjProperty('attr_ref', REQUEST) - + def embedRemoteContent(self, REQUEST): + rtn = None if self.getEmbedType() == 'remote': + ref = self.getObjProperty('attr_ref', REQUEST) remote_ref = rest_api.get_rest_api_url( ref) try: rtn = self.http_import( remote_ref + '/get_body_content') except: - rtn = standard.writeError(self, '[_getBodyContent]: can\'t embed from remote_ref=%s'%remote_ref) - - else: + rtn = standard.writeError(self, '[_getBodyContent]: can\'t embed from remote_ref=%s'%remote_ref) + return rtn + + # -------------------------------------------------------------------------- + # ZMSLinkElement._getBodyContent: + # + # HTML presentation of link-element. + # -------------------------------------------------------------------------- + def _getBodyContent(self, REQUEST): + rtn = self.embedRemoteContent( REQUEST) + if rtn is None: if self.isEmbedded(): REQUEST.set('ZMS_RELATIVATE_URL', False) proxy = self.getProxy() @@ -585,26 +592,18 @@ def _getBodyContent(self, REQUEST): # Renders short presentation of link-element. # -------------------------------------------------------------------------- def renderShort(self, REQUEST): - rtn = '' - ref = self.getObjProperty('attr_ref', REQUEST) - - if self.getEmbedType() == 'remote': - remote_ref = rest_api.get_rest_api_url( ref) - try: - rtn = self.http_import( remote_ref + '/get_body_content') - except: - rtn = standard.writeError(self, '[renderShort]: can\'t embed from remote_ref=%s'%remote_ref) - - elif self.isEmbedded(): - REQUEST.set('ZMS_RELATIVATE_URL', False) - ref_obj = self.getRefObj() - if ref_obj is None or ref_obj.isPage(): - rtn = super(ZMSLinkElement, self).renderShort(REQUEST) - elif ref_obj != self: - rtn = ref_obj.renderShort(REQUEST) - REQUEST.set('ZMS_RELATIVATE_URL', True) - else: - rtn = self._getBodyContent( REQUEST) + rtn = self.embedRemoteContent( REQUEST) + if rtn is None: + if self.isEmbedded(): + REQUEST.set('ZMS_RELATIVATE_URL', False) + ref_obj = self.getRefObj() + if ref_obj is None or ref_obj.isPage(): + rtn = super(ZMSLinkElement, self).renderShort(REQUEST) + elif ref_obj != self: + rtn = ref_obj.renderShort(REQUEST) + REQUEST.set('ZMS_RELATIVATE_URL', True) + else: + rtn = self._getBodyContent( REQUEST) return rtn From f8f06d15bc15abbb82570841356a44b704c9441e Mon Sep 17 00:00:00 2001 From: drfho Date: Wed, 18 Feb 2026 09:43:21 +0100 Subject: [PATCH 16/18] prepare for merge --- .vscode/ZMS_host.code-workspace | 11 +++++++++- .vscode/tasks.json | 39 --------------------------------- Products/zms/_cachemanager.py | 2 +- Products/zms/_zreferableitem.py | 27 ----------------------- Products/zms/version.txt | 2 +- 5 files changed, 12 insertions(+), 69 deletions(-) delete mode 100644 .vscode/tasks.json diff --git a/.vscode/ZMS_host.code-workspace b/.vscode/ZMS_host.code-workspace index af7362a23..8609524a4 100644 --- a/.vscode/ZMS_host.code-workspace +++ b/.vscode/ZMS_host.code-workspace @@ -26,7 +26,7 @@ "editor.renderWhitespace": "all", "editor.renderControlCharacters": false, "workbench.iconTheme": "vs-minimal", - "workbench.colorTheme": "Visual Studio Dark", + "workbench.colorTheme": "Visual Studio Light", "files.eol": "\n", "files.autoSave": "afterDelay", "files.associations": { @@ -49,6 +49,15 @@ "**/Data.*": true, "**/docker/**/var/*": true }, + "python.testing.unittestArgs": [ + "-v", + "-s", + "./tests", + "-p", + "test*.py" + ], + "python.testing.pytestEnabled": false, + "python.testing.unittestEnabled": true }, "launch": { diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index d571db58b..000000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - // https://marketplace.visualstudio.com/_apis/public/gallery/publishers/ms-python/vsextensions/python/2022.2.1924087327/vspackage - // First make sure that docker is running: - // > sudo systemctl restart docker - "version": "2.0.0", - "tasks": [ - // // { - // // "label": "Downgrade Py-Extension for Py2", - // // "type": "shell", - // // "command": [ - // // "code --uninstall-extension ms-toolsai.jupyter", - // // "code --uninstall-extension ms-toolsai.jupyter-keymap", - // // "code --uninstall-extension ms-toolsai.vscode-jupyter-cell-tag", - // // "code --install-extension ms-python.python@2022.2.1924087327", - // // "code --install-extension atariq11700.debugpy-old@2023.1.12492010", - // // "code --install-extension github.copilot@1.270.0", - // // "code --install-extension github.copilot-chat@0.23.2", - // // ], - // // "runOptions": { - // // "runOn": "folderOpen" - // // } - // // }, - // { - // "label": "Upgrade Py-Extension for Py3", - // "type": "shell", - // "command": [ - // "code --install-extension ms-python.python --force", - // "code --install-extension ms-toolsai.jupyter --force", - // "code --install-extension ms-toolsai.jupyter-keymap --force", - // "code --install-extension ms-toolsai.vscode-jupyter-cell-tag --force", - // "code --install-extension github.copilot --force", - // "code --install-extension github.copilot-chat --force", - // ], - // "runOptions": { - // "runOn": "folderOpen" - // } - // }, - ] -} \ No newline at end of file diff --git a/Products/zms/_cachemanager.py b/Products/zms/_cachemanager.py index e580015a4..b4ff0f6e0 100644 --- a/Products/zms/_cachemanager.py +++ b/Products/zms/_cachemanager.py @@ -63,7 +63,7 @@ def clearReqBuff(self, prefix='', REQUEST=None): def fetchReqBuff(self, key=None, REQUEST=None): request = getattr(self, 'REQUEST', getRequest()) if key is None: # For debugging purposes, return whole buffer. - return request.get('__buff__',{}) + return None # request.get('__buff__',{}) buff = request['__buff__'] reqBuffId = self.getReqBuffId(key) return getattr(buff, reqBuffId) diff --git a/Products/zms/_zreferableitem.py b/Products/zms/_zreferableitem.py index ad30b8f7f..9c4878735 100644 --- a/Products/zms/_zreferableitem.py +++ b/Products/zms/_zreferableitem.py @@ -23,8 +23,6 @@ # Product Imports. from Products.zms import standard from zope.globalrequest import getRequest -# FOR DEBUGGING PURPOSES ONLY: import traceback to log the call stack of getLinkObj calls -import traceback # ------------------------------------------------------------------------------ @@ -501,7 +499,6 @@ def findObject(self, url): # ---------------------------------------------------------------------------- def getLinkObj(self, url, REQUEST=None): request = getattr(self, 'REQUEST', getRequest()) - was_buffered = True ob = None if isInternalLink(url): # Params. @@ -521,7 +518,6 @@ def getLinkObj(self, url, REQUEST=None): try: ob = self.getDocumentElement().fetchReqBuff(reqBuffId) except: - was_buffered = False if url.find('id:') >= 0: catalog = self.getZMSIndex().get_catalog() q = catalog({'get_uid':url}) @@ -549,29 +545,6 @@ def getLinkObj(self, url, REQUEST=None): ids = self.getPhysicalPath() if ob.id not in ids: ob.set_request_context(request, ref_params) - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # DEBUG: logging/counting getLinkObj calls - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - request.set('getLinkObj_counter', request.get('getLinkObj_counter', 0) + 1) - # show traceback in debugging output to identify which function is calling getLinkObj and causing multiple calls - traceback_stack = [] - for frame in reversed(traceback.extract_stack()[-5:-2]): - traceback_stack.append('%s:%s:%s' % (str(frame.filename).split('/')[-1], frame.lineno, frame.name)) - request.set('count_buffered_getLinkObj_calls', request.get('count_buffered_getLinkObj_calls', 0) + int(was_buffered)) - standard.writeStdout(self, '%d. [%s:getLinkObj] %s, Target-ID=%s (%s), URL=%s, was_buffered=%s (%s), ref_params=%s\n...was called from:\n\t%s\n'%( - request.get('getLinkObj_counter', 0), - self.meta_id, - url, - ob.id if ob is not None else None, - ob.meta_id if ob is not None else None, - ob.absolute_url(relative=1) if ob is not None else None, - was_buffered, - request.get('count_buffered_getLinkObj_calls', 0), - ref_params, - '\n\t'.join(traceback_stack) - ) - ) - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ return ob diff --git a/Products/zms/version.txt b/Products/zms/version.txt index 2f4fab285..e5cd1206f 100644 --- a/Products/zms/version.txt +++ b/Products/zms/version.txt @@ -1 +1 @@ -2025.9.1+d48572e +2025.9.1+7d6faad From bef6f77216b4e7cd7f91b2c2344a927c95513819 Mon Sep 17 00:00:00 2001 From: drfho Date: Wed, 18 Feb 2026 09:56:03 +0100 Subject: [PATCH 17/18] restore debug code --- Products/zms/_cachemanager.py | 2 +- Products/zms/_zreferableitem.py | 28 +++++++++++++++++++++++++++- Products/zms/version.txt | 2 +- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/Products/zms/_cachemanager.py b/Products/zms/_cachemanager.py index b4ff0f6e0..e580015a4 100644 --- a/Products/zms/_cachemanager.py +++ b/Products/zms/_cachemanager.py @@ -63,7 +63,7 @@ def clearReqBuff(self, prefix='', REQUEST=None): def fetchReqBuff(self, key=None, REQUEST=None): request = getattr(self, 'REQUEST', getRequest()) if key is None: # For debugging purposes, return whole buffer. - return None # request.get('__buff__',{}) + return request.get('__buff__',{}) buff = request['__buff__'] reqBuffId = self.getReqBuffId(key) return getattr(buff, reqBuffId) diff --git a/Products/zms/_zreferableitem.py b/Products/zms/_zreferableitem.py index 9c4878735..f2a0091e1 100644 --- a/Products/zms/_zreferableitem.py +++ b/Products/zms/_zreferableitem.py @@ -23,7 +23,8 @@ # Product Imports. from Products.zms import standard from zope.globalrequest import getRequest - +# FOR DEBUGGING PURPOSES ONLY: import traceback to log the call stack of getLinkObj calls +import traceback # ------------------------------------------------------------------------------ # isMailLink: @@ -499,6 +500,7 @@ def findObject(self, url): # ---------------------------------------------------------------------------- def getLinkObj(self, url, REQUEST=None): request = getattr(self, 'REQUEST', getRequest()) + was_buffered = True ob = None if isInternalLink(url): # Params. @@ -518,6 +520,7 @@ def getLinkObj(self, url, REQUEST=None): try: ob = self.getDocumentElement().fetchReqBuff(reqBuffId) except: + was_buffered = False if url.find('id:') >= 0: catalog = self.getZMSIndex().get_catalog() q = catalog({'get_uid':url}) @@ -545,6 +548,29 @@ def getLinkObj(self, url, REQUEST=None): ids = self.getPhysicalPath() if ob.id not in ids: ob.set_request_context(request, ref_params) + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # DEBUG: logging/counting getLinkObj calls + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + request.set('getLinkObj_counter', request.get('getLinkObj_counter', 0) + 1) + # show traceback in debugging output to identify which function is calling getLinkObj and causing multiple calls + traceback_stack = [] + for frame in reversed(traceback.extract_stack()[-5:-2]): + traceback_stack.append('%s:%s:%s' % (str(frame.filename).split('/')[-1], frame.lineno, frame.name)) + request.set('count_buffered_getLinkObj_calls', request.get('count_buffered_getLinkObj_calls', 0) + int(was_buffered)) + standard.writeStdout(self, '%d. [%s:getLinkObj] %s, Target-ID=%s (%s), URL=%s, was_buffered=%s (%s), ref_params=%s\n...was called from:\n\t%s\n'%( + request.get('getLinkObj_counter', 0), + self.meta_id, + url, + ob.id if ob is not None else None, + ob.meta_id if ob is not None else None, + ob.absolute_url(relative=1) if ob is not None else None, + was_buffered, + request.get('count_buffered_getLinkObj_calls', 0), + ref_params, + '\n\t'.join(traceback_stack) + ) + ) + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ return ob diff --git a/Products/zms/version.txt b/Products/zms/version.txt index e5cd1206f..0c7642644 100644 --- a/Products/zms/version.txt +++ b/Products/zms/version.txt @@ -1 +1 @@ -2025.9.1+7d6faad +2025.9.1+f8f06d1 From ca3b92267f2fe56d0492aa51b03b7e7cd24ce3ef Mon Sep 17 00:00:00 2001 From: drfho Date: Wed, 18 Feb 2026 10:29:05 +0100 Subject: [PATCH 18/18] refact conditionioning link-validation --- Products/zms/_objattrs.py | 4 ++-- Products/zms/_zreferableitem.py | 2 -- Products/zms/version.txt | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Products/zms/_objattrs.py b/Products/zms/_objattrs.py index c59e70ebf..527213203 100644 --- a/Products/zms/_objattrs.py +++ b/Products/zms/_objattrs.py @@ -571,10 +571,10 @@ def getObjProperty(self, key, REQUEST={}, par=None): value = self.getObjAttrValue( obj_attr, REQUEST) if value: # Text-Fields - if datatype in _globals.DT_TEXTS: + if datatype in _globals.DT_TEXTS and bool(self.getConfProperty('ZReferableItem.validateInlineLinkObj', 0)): value = self.validateInlineLinkObj(value) # Url-Fields - if datatype == _globals.DT_URL: + if datatype == _globals.DT_URL and bool(self.getConfProperty('ZReferableItem.validateLinkObj', 0)): value = self.validateLinkObj(value) # Executable fields. value = standard.dt_exec(self, value) diff --git a/Products/zms/_zreferableitem.py b/Products/zms/_zreferableitem.py index f2a0091e1..c4fadf056 100644 --- a/Products/zms/_zreferableitem.py +++ b/Products/zms/_zreferableitem.py @@ -427,7 +427,6 @@ def refreshRefToObjs(self): # Validates internal links. # ---------------------------------------------------------------------------- def validateInlineLinkObj(self, text): - if not bool(self.getConfProperty('ZReferableItem.validateInlineLinkObj', 1)): return text for pq in [('', 'href'), ('', 'src')]: p = pq[0] q = pq[1] @@ -452,7 +451,6 @@ def validateInlineLinkObj(self, text): # Validates internal links. # ---------------------------------------------------------------------------- def validateLinkObj(self, url): - if not bool(self.getConfProperty('ZReferableItem.validateLinkObj', 1)): return url if isInternalLink(url): if not url.startswith('{$__'): ild = getInternalLinkDict(self, url) diff --git a/Products/zms/version.txt b/Products/zms/version.txt index 0c7642644..b9f673540 100644 --- a/Products/zms/version.txt +++ b/Products/zms/version.txt @@ -1 +1 @@ -2025.9.1+f8f06d1 +2025.9.1+bef6f77