TypeError("unsupported operand type(s) for +: 'NoneType' and 'int'",) (Also,
the following error occurred while attempting to render the standard error
message, please see the event log for full details: unsupported operand
type(s) for +: 'NoneType' and 'int')
This might be a result of me having installed collective.sharerizer 1.1 (via
buildout). I have taken this add-on out of the active add-ons, but that does
not help. So if anyone could help me out on this, I would be grateful :-)
Børge
The full traceback is attached here:
Traceback (innermost last):
Module ZPublisher.Publish, line 127, in publish
Module ZPublisher.mapply, line 77, in mapply
Module ZPublisher.Publish, line 47, in call_object
Module Shared.DC.Scripts.Bindings, line 324, in __call__
Module Products.PloneHotfix20110531, line 106, in _patched_bindAndExec
Module Shared.DC.Scripts.Bindings, line 361, in _bindAndExec
Module Products.CMFCore.FSPageTemplate, line 240, in _exec
Module Products.CMFCore.FSPageTemplate, line 180, in pt_render
Module Products.PageTemplates.PageTemplate, line 80, in pt_render
Module zope.pagetemplate.pagetemplate, line 115, in pt_render
Module zope.tal.talinterpreter, line 271, in __call__
Module zope.tal.talinterpreter, line 343, in interpret
Module zope.tal.talinterpreter, line 888, in do_useMacro
Module zope.tal.talinterpreter, line 343, in interpret
Module zope.tal.talinterpreter, line 533, in do_optTag_tal
Module zope.tal.talinterpreter, line 518, in do_optTag
Module zope.tal.talinterpreter, line 513, in no_tag
Module zope.tal.talinterpreter, line 343, in interpret
Module zope.tal.talinterpreter, line 852, in do_condition
Module zope.tal.talinterpreter, line 343, in interpret
Module zope.tal.talinterpreter, line 533, in do_optTag_tal
Module zope.tal.talinterpreter, line 518, in do_optTag
Module zope.tal.talinterpreter, line 513, in no_tag
Module zope.tal.talinterpreter, line 343, in interpret
Module zope.tal.talinterpreter, line 531, in do_optTag_tal
Module zope.tal.talinterpreter, line 513, in no_tag
Module zope.tal.talinterpreter, line 343, in interpret
Module zope.tal.talinterpreter, line 742, in do_insertStructure_tal
Module Products.PageTemplates.Expressions, line 220, in evaluateStructure
Module zope.tales.tales, line 696, in evaluate
- URL:
file:/usr/local/plone4/buildout-cache/eggs/plonetheme.sunburst-1.0.2-py2.6.egg/plonetheme/sunburst/skins/sunburst_templates/main_template.pt
- Line 35, Column 8
- Expression: <StringExpr u'plone.htmlhead.links'>
- Names:
{'container': <PloneSite at /kjeldstad>,
'context': <ATNewsItem at /kjeldstad/news/support-for-ai-weiwei>,
'default': ,
'here': <ATNewsItem at /kjeldstad/news/support-for-ai-weiwei>,
'loop': {},
'nothing': None,
'options': {'args': ()},
'repeat': <Products.PageTemplates.Expressions.SafeMapping object at
0x7f68150f02b8>,
'request': <HTTPRequest,
URL=https://test.kjeldstad.com/kjeldstad/news/support-for-ai-weiwei/newsitem_view>,
'root': <Application at >,
'template': <FSPageTemplate at /kjeldstad/newsitem_view used for
/kjeldstad/news/support-for-ai-weiwei>,
'traverse_subpath': [],
'user': <PropertiedUser 'admin'>}
Module zope.contentprovider.tales, line 80, in __call__
Module plone.app.viewletmanager.manager, line 85, in render
Module plone.app.layout.viewlets.common, line 48, in render
Module Products.Five.browser.pagetemplatefile, line 126, in __call__
Module Products.Five.browser.pagetemplatefile, line 60, in __call__
Module zope.pagetemplate.pagetemplate, line 115, in pt_render
Module zope.tal.talinterpreter, line 271, in __call__
Module zope.tal.talinterpreter, line 343, in interpret
Module zope.tal.talinterpreter, line 852, in do_condition
Module zope.tal.talinterpreter, line 343, in interpret
Module zope.tal.talinterpreter, line 531, in do_optTag_tal
Module zope.tal.talinterpreter, line 513, in no_tag
Module zope.tal.talinterpreter, line 343, in interpret
Module zope.tal.talinterpreter, line 583, in do_setLocal_tal
Module zope.tales.tales, line 696, in evaluate
- URL:
/usr/local/plone4/buildout-cache/eggs/plone.app.layout-2.0.2-py2.6.egg/plone/app/layout/nextprevious/links.pt
- Line 17, Column 4
- Expression: <PathExpr standard:u'view/next'>
- Names:
{'args': (),
'container': <ATNewsItem at /kjeldstad/news/support-for-ai-weiwei>,
'context': <ATNewsItem at /kjeldstad/news/support-for-ai-weiwei>,
'default': ,
'here': <ATNewsItem at /kjeldstad/news/support-for-ai-weiwei>,
'loop': {},
'nothing': None,
'options': {},
'repeat': <Products.PageTemplates.Expressions.SafeMapping object at
0x7f6818ae0730>,
'request': <HTTPRequest,
URL=https://test.kjeldstad.com/kjeldstad/news/support-for-ai-weiwei/newsitem_view>,
'root': <Application at >,
'template':
<Products.Five.browser.pagetemplatefile.ViewPageTemplateFile object at
0x5a56b50>,
'traverse_subpath': [],
'user': <PropertiedUser 'admin'>,
'view': <Products.Five.viewlet.metaconfigure.NextPreviousLinksViewlet
object at 0x7f68150da150>,
'views': <Products.Five.browser.pagetemplatefile.ViewMapper object at
0x7f68196f5f50>}
Module zope.tales.expressions, line 217, in __call__
Module Products.PageTemplates.Expressions, line 157, in _eval
Module Products.PageTemplates.Expressions, line 119, in render
Module plone.app.layout.nextprevious.view, line 19, in next
Module plone.app.folder.nextprevious, line 30, in getNextItem
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'
--
View this message in context: http://plone.293351.n2.nabble.com/Error-when-viewing-news-items-in-Plone-4-0-tp6518151p6518151.html
Sent from the General Questions mailing list archive at Nabble.com.
------------------------------------------------------------------------------
All of the data generated in your IT infrastructure is seriously valuable.
Why? It contains a definitive record of application performance, security
threats, fraudulent activity, and more. Splunk takes this data and makes
sense of it. IT sense. And common sense.
http://p.sf.net/sfu/splunk-d2d-c2
_______________________________________________
Plone-Users mailing list
Plone...@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/plone-users
-aj
--
View this message in context: http://plone.293351.n2.nabble.com/Error-when-viewing-news-items-in-Plone-4-0-tp6518151p6518972.html
Due to its traceback, this message is much more helpful
for an analysis than your previous post.
You will find some hints near line 30 of
"plone.app.folder.nextprevious". You should see there a "+" operation
which tries to combine "None" and an integer.
It is highly likely, that the "None" causes the problem.
Try to find out where this "None" comes from. This will lead you towards
the primary problem cause.
Some form of debugging might sometime during the process to be helpfull.
There is a HowTo how to debug Plone (there are also many threads about
debugging in this list's archive, easily searchable via "gmane").
--
Dieter
First I tried to copy one of my error-making news items to another Plone
site on the same instance. There it worked and showed up fine.
Then I went into the file:
/usr/local/plone4/buildout-cache/eggs/plone.app.folder-1.0.1-py2.6.egg/plone/app/folder/nextprevious.py
It's content up to line 35 is:
from zope.interface import implements
from zope.component import adapts
from Products.CMFCore.utils import getToolByName
from plone.app.layout.nextprevious.interfaces import INextPreviousProvider
from plone.app.folder.folder import IATUnifiedFolder
class NextPrevious(object):
""" adapter for acting as a next/previous provider """
implements(INextPreviousProvider)
adapts(IATUnifiedFolder)
def __init__(self, context):
self.context = context
props = getToolByName(context, 'portal_properties').site_properties
self.vat = props.getProperty('typesUseViewActionInListings', ())
@property
def enabled(self):
return self.context.getNextPreviousEnabled()
def getNextItem(self, obj):
""" return info about the next item in the container """
pos = self.context.getObjectPosition(obj.getId())
ordering = self.context.getOrdering()
try:
try: # first try `__getitem__`
next = ordering[pos + 1]
except TypeError:
next = ordering.idsInOrder()[pos + 1]
return self.getData(self.context[next])
except IndexError: # in case next > len(folder)
return None
def getPreviousItem(self, obj):
............ etc...
Now I remember that I did something with my news item/section, after I
had installed collective.sharerizer, to see if I could ad some
sharebutton on it. I think it was in the "Manage portlets". I had no
success and probably aborted the operation. In my "undo" list I see the
following transactions done at the most plausible error time:
/kjeldstad/news/plone_lock_operations/safe_unlock by admin
I tried to undo this transaction, but it had no effect.
Then I unticked the "Enable next previous navigation" under settings for
the News-folder; and Bob's your uncle!!!!
The problem disappeared... When I re-tick the "Enable next previous
navigation", the problem comes back again. The problem also shows up if
I enable next previous navigation on the news folder on my other plone site.
So this might not have anything to do with collective.sharerizer. But,
should not I be able to enable next previous navigation on my news items?
If I am adviced to do so, I'll find out how I can file a bug on this
issue, and file it.
Thank you
Børge
I expect that the previous line is the one with the "TypeError".
In this case, "pos" would be "None" and the problem would come from
"getObjectPosition" -- an indication, that the problem is not
with the news object itself but with its container or the catalog.
In your place, I would check how "getObjectPosition" is implemented
and why it returns "None" rather than an integer.
Once, you have found out, we may see how to fix it.
Alternatively, you could reindex your catalog and check whether this
gets rid of your problem. Chances are not that high, but reindexing
is much faster than analysing "getObjectPosition".
I believe I reindexed one of my Plone-sites' portal_catalog by pressing
the "Update Catalog" button. That did not help.
To investigate the "getObjectPosition" issue, I went one step back in my
traceback, to line 19 in the file
/usr/local/plone4/buildout-cache/eggs/plone.app.layout-2.0.2-py2.6.egg/plone/app/layout/nextprevious/view.py.
The file looks like this:
from zope.component import getMultiAdapter
from plone.app.layout.viewlets import ViewletBase
from plone.app.layout.nextprevious.interfaces import INextPreviousProvider
from Products.Five.browser import BrowserView
from Products.Five.browser.pagetemplatefile import ZopeTwoPageTemplateFile
from Acquisition import aq_inner, aq_parent
class NextPreviousView(BrowserView):
"""Information about next/previous navigation
"""
def next(self):
provider = self._provider()
if provider is None:
return None
return provider.getNextItem(aq_inner(self.context))
def previous(self):
provider = self._provider()
if provider is None:
return None
return provider.getPreviousItem(aq_inner(self.context))
def enabled(self):
provider = self._provider()
if provider is None:
return False
return provider.enabled
def _provider(self):
# Note - the next/previous provider is the container of this
object!
# This may not support next/previous navigation, so code
defensively
return INextPreviousProvider(aq_parent(aq_inner(self.context)),
None)
def isViewTemplate(self):
plone = getMultiAdapter((self.context, self.request),
name=u'plone_context_state')
return plone.is_view_template()
class NextPreviousViewlet(ViewletBase, NextPreviousView):
index = ZopeTwoPageTemplateFile('nextprevious.pt')
class NextPreviousLinksViewlet(ViewletBase, NextPreviousView):
index = ZopeTwoPageTemplateFile('links.pt')
....... End of file.....
This is far beyond my python knowledge, but it looks like None will be
returned here if "provider is None"
And that thing "INextPreviousProvider" is found in the file:
/usr/local/plone4/buildout-cache/eggs/plone.app.layout-2.0.2-py2.6.egg/plone/app/layout/nextprevious/interfaces.py.
This file's content looks like this:
from zope.interface import Interface
from zope.schema import Bool
class INextPreviousProvider(Interface):
"""A folderish component capable of describing the next and previous
item relative to a particular id.
"""
enabled = Bool(title=u"True if next/previous behaviour is enabled")
def getNextItem(obj):
"""Returns information about next item in the container relative to
the given object.
This is a dict with the following keys:
- id, the id of the object
- url, the url of the object
- title, the title of the object
- description, a description of the object
- portal_type, the object's portal type
"""
def getPreviousItem(obj):
"""Returns the previous item in the container relative to the given
object
"""
.....End of file.....
I am not quite sure where I should continue from now......
Thank you
Børge
Hope was small only.
>To investigate the "getObjectPosition" issue, I went one step back in my
>traceback, to line 19 in the file
>/usr/local/plone4/buildout-cache/eggs/plone.app.layout-2.0.2-py2.6.egg/plone/app/layout/nextprevious/view.py.
>The file looks like this:
>
>from zope.component import getMultiAdapter
>
>from plone.app.layout.viewlets import ViewletBase
>from plone.app.layout.nextprevious.interfaces import INextPreviousProvider
>
>from Products.Five.browser import BrowserView
>from Products.Five.browser.pagetemplatefile import ZopeTwoPageTemplateFile
>from Acquisition import aq_inner, aq_parent
>
>
>class NextPreviousView(BrowserView):
> """Information about next/previous navigation
> """
>
> def next(self):
> provider = self._provider()
> if provider is None:
> return None
> return provider.getNextItem(aq_inner(self.context))
You are not yet on the correct trace.
Your original traceback tells you that the provider has been found
(looking for it does not yield "None"), its "getNextItem" is called
and it is this "getNextIdem" call which results in the
"TypeError: None + Int".
This means (quite surely) that "self.context.getObjectPosition(obj.getId())"
near "Module plone.app.folder.nextprevious, line 20+, in getNextItem"
returns "None". You have to find out why.
I can see two potential causes:
* "self.context.getObjectPosition" is to blame.
Almost surely, "self.context" is the folder containing your object.
* "obj" (it is provided by "NextPriviousView.next") is wrong.
The easiest way to find out it by debugging -- e.g. by
means of "Products.PDBDebugMode" (or something like this),
by calling the involved methods in an interactive Python session
or by putting a breakpoint ("import pdb; pdb.set_trace()")
into the "except TypeError" block of
"plone.app.folder.nextprevious(getNextItem)".
An alternative (so less sure) would be code inspection of
"getObjectPosition" (likely a "Folder" method).
Should you start to embrace error analysis in an interactive
Python session (as I do), I can provide a small function ("definedBy")
which determines where a method definition comes from.
This can be helpful (e.g.) to determine where "getObjectPosition"
is defined. For isolated cases, a textual search over the source tree
might be sufficient, however.
Anyway: I did your:
> putting a breakpoint ("import pdb; pdb.set_trace()")
> into the "except TypeError" block of
> "plone.app.folder.nextprevious(getNextItem)"
It gave the following results:
2011-07-04 23:01:01 INFO Zope Ready to handle requests
>
/usr/local/plone4/buildout-cache/eggs/plone.app.folder-1.0.1-py2.6.egg/plone/app/folder/nextprevious.py(31)getNextItem()
-> next = ordering.idsInOrder()[pos + 1]
(Pdb) l
26 try:
27 try: # first try `__getitem__`
28 next = ordering[pos + 1]
29 except TypeError:
30 import pdb; pdb.set_trace()
31 -> next = ordering.idsInOrder()[pos + 1]
32 return self.getData(self.context[next])
33 except IndexError: # in case next > len(folder)
34 return None
35
36 def getPreviousItem(self, obj):
(Pdb) dir()
['obj', 'ordering', 'pdb', 'pos', 'self']
(Pdb) method
*** NameError: name 'method' is not defined
(Pdb) obj
<ATNewsItem at /kjeldstad/news/support-for-ai-weiwei>
(Pdb) ordering
<plone.folder.unordered.UnorderedOrdering object at 0x7a3ad50>
(Pdb) pos
(Pdb) self
<plone.app.folder.nextprevious.NextPrevious object at 0x7a3ac90>
....end of snip...
I then opened the file
/usr/local/plone4/buildout-cache/eggs/plone.folder-1.0-py2.6.egg/plone/folder/unordered.py
It's content is :
from Acquisition import aq_base
from zope.interface import implements
from zope.component import adapts
from plone.folder.interfaces import IOrdering, IOrderableFolder
class UnorderedOrdering(object):
""" This implementation provides no ordering. """
implements(IOrdering)
adapts(IOrderableFolder)
def __init__(self, context):
self.context = context
def notifyAdded(self, id):
pass
def notifyRemoved(self, id):
pass
def idsInOrder(self):
return aq_base(self.context).objectIds(ordered=False)
def getObjectPosition(self, id):
return None
...end of file...
There I see:
getObjectPosition(self, id):
return None
Can this be the error? Maybe I should comment out the "return None" and
see what happens?
By the way: I also googled your function "definedBy". Is it this one
(and how is it used)?:
def definedBy(name, class_):
'''return *class_* base class defining *name*.
*class_* may (now) also be an object. In this case, its class is used.
'''
class_ = aq_base(class_)
if not hasattr(class_, '__bases__'): class_ = class_.__class__
for cl in getmro(class_):
if hasattr(cl,'__dict__'):
if cl.__dict__.has_key(name): return cl
elif hasattr(cl, name): return cl
return None
...end of snip...
Thank you again
Børge
This proves the hypothesis that "pos" (wrongly) is "None".
To find out why, you can use one of the PDB "debug" or "j[ump]" commands.
"debug" allows you to interactively examine the evaluation of a command.
In your case, this would be the command that has produced the wrong
value for "pos".
"jump" allows you to transfer (execution) control to a different line (with
some restrictions). You may be able to use it to reexecute the
wrong "pos = ..." assignment under your control and examine it.
After both "debug" and "jump", you would use "s[tep]" (step into function
call) "n[ext]" (step over function call), "r[eturn]" (leave function call)
to control the execution and "p[rint]", "pp" (pretty print),
"args" to examine the state.
>(Pdb) self
><plone.app.folder.nextprevious.NextPrevious object at 0x7a3ac90>
>
>....end of snip...
>
>I then opened the file
>/usr/local/plone4/buildout-cache/eggs/plone.folder-1.0-py2.6.egg/plone/folder/unordered.py
>It's content is :
>
>from Acquisition import aq_base
>from zope.interface import implements
>from zope.component import adapts
>from plone.folder.interfaces import IOrdering, IOrderableFolder
>
>
>class UnorderedOrdering(object):
> """ This implementation provides no ordering. """
> implements(IOrdering)
> adapts(IOrderableFolder)
>
> ...
>There I see:
>
>getObjectPosition(self, id):
> return None
This may explain the wrong "None" which is causing the difficulty
you observe.
For me, this looks like a bug in "plone.app.folder".
When Plone expects that some folders do not support the next/previous
functionality (because they are unordered), this functionality
should be automatically disabled for those folders.
You could try to add as a workaround a line:
if pos is None: return None
after the "pos" assignment in "nextprevious".
If this solves your problem, please file a bug report against
"plone.app.folder".
> ...
>Can this be the error? Maybe I should comment out the "return None" and
>see what happens?
>
>
>By the way: I also googled your function "definedBy". Is it this one
>(and how is it used)?:
>
>def definedBy(name, class_):
> '''return *class_* base class defining *name*.
>
> *class_* may (now) also be an object. In this case, its class is used.
> '''
It is. However, it might be an old version (one which does not
yet use the method resolution order in newer Python versions).
Should you see problems, you may tell me and I will look whether
I can provide a newer version.
How is it used?
Well, I am a strong protagonist of well chosen names
and terse but concise documentation. Usually, you can have
confidence in the names I have chosen.
You see here a function with name "definedBy" and arguments
"name" and "class_". Its name suggests that "definedBy(name, cls)"
tells you where "name" is defined in the context of "cls".
The function's docstring tells you that the result is the defining
class (from which you can ask the module, the class is defined in).
The docstring also tells you that you can pass an object rather
than a class and that the object's class is used in this case.
As an example: in order to find out which class
defines "idsInOrder" after you entered your breakpoint above,
you could use "!import DUtils; DUtils.definedBy('idsInOrder', ordering)"
(this assumes, that "DUtils.py" has been placed somewhere where
Python looks for modules, e.g. the current working directory).
If you have the class, its "__module__" attribute tells you the module.
Den 05. juli 2011 07:58, skrev Dieter Maurer:
> Børge Kjeldstad wrote at 2011-7-4 23:21 +0200:
>> (Pdb) dir()
>> ['obj', 'ordering', 'pdb', 'pos', 'self']
>> ...
>> (Pdb) pos
>
> This proves the hypothesis that "pos" (wrongly) is "None".
>
>
> To find out why, you can use one of the PDB "debug" or "j[ump]" commands.
>
Out of some reason I think the "debug" comand made Plone hang. Or at
least I could hardly do anything more in the terminal window and shut it
down. But "j[ump]" worked, and I jumped back to the pos assignment in
nextprevious and found some reference to ATFolder:
>
/usr/local/plone4/buildout-cache/eggs/plone.app.folder-1.0.1-py2.6.egg/plone/app/folder/nextprevious.py(31)getNextItem()->None
-> next = ordering.idsInOrder()[pos + 1]
(Pdb) l
19 def enabled(self):
20 return self.context.getNextPreviousEnabled()
21
22 def getNextItem(self, obj):
23 """ return info about the next item in the container """
24 -> pos = self.context.getObjectPosition(obj.getId())
25 ordering = self.context.getOrdering()
26 try:
27 try: # first try `__getitem__`
28 next = ordering[pos + 1]
29 except TypeError:
(Pdb) self.context.getObjectPosition
<bound method ATFolder.getObjectPosition of <ATFolder at /kjeldstad/news>>
(Pdb)
After a bit of googling I opened the file:
/usr/local/plone4/buildout-cache/eggs/Products.Archetypes-1.6.1-py2.6.egg/Products/Archetypes/OrderedBaseFolder.py
It contains a getObjectPosition definition at the bottom of the
following extract:
"""
OrderedBaseFolder derived from OrderedFolder by Stephan Richter, iuveno AG.
OrderedFolder adapted to Zope 2.7 style interface by
Jens....@jensquadrat.de
"""
from zope.interface import implements
from AccessControl import ClassSecurityInfo
from App.class_init import InitializeClass
from DocumentTemplate import sequence
from OFS.interfaces import IOrderedContainer
from Products.CMFCore.utils import getToolByName
from Products.CMFCore.interfaces import IDynamicType
from Products.CMFCore import permissions
from zExceptions import NotFound
from Products.Archetypes.BaseFolder import BaseFolder
from Products.Archetypes.ExtensibleMetadata import ExtensibleMetadata
class OrderedContainer:
implements(IOrderedContainer)
security = ClassSecurityInfo()
security.declareProtected(permissions.ModifyPortalContent,
'moveObject')
def moveObject(self, id, position):
obj_idx = self.getObjectPosition(id)
if obj_idx == position:
return None
elif position < 0:
position = 0
metadata = list(self._objects)
obj_meta = metadata.pop(obj_idx)
metadata.insert(position, obj_meta)
self._objects = tuple(metadata)
# TODO here the implementing of IOrderedContainer starts
# this should be replaced by mixing in the 2.7 specific class
# OSF.OrderedContainer.OrderedContainer
security.declareProtected(permissions.ModifyPortalContent,
'moveObjectsByDelta')
def moveObjectsByDelta(self, ids, delta, subset_ids=None):
""" Move specified sub-objects by delta.
"""
if isinstance(ids, basestring):
ids = (ids,)
min_position = 0
objects = list(self._objects)
if subset_ids == None:
# OLD: subset_ids = [ obj['id'] for obj in objects ]
subset_ids = self.getCMFObjectsSubsetIds(objects)
else:
subset_ids = list(subset_ids)
# unify moving direction
if delta > 0:
ids = list(ids)
ids.reverse()
subset_ids.reverse()
counter = 0
for id in ids:
try:
old_position = subset_ids.index(id)
except ValueError:
continue
new_position = max( old_position - abs(delta), min_position )
if new_position == min_position:
min_position += 1
if not old_position == new_position:
subset_ids.remove(id)
subset_ids.insert(new_position, id)
counter += 1
if counter > 0:
if delta > 0:
subset_ids.reverse()
obj_dict = {}
for obj in objects:
obj_dict[ obj['id'] ] = obj
pos = 0
for i in range( len(objects) ):
if objects[i]['id'] in subset_ids:
try:
objects[i] = obj_dict[ subset_ids[pos] ]
pos += 1
except KeyError:
raise ValueError('The object with the id "%s"
does '
'not exist.' % subset_ids[pos])
self._objects = tuple(objects)
return counter
security.declarePrivate('getCMFObjectsSubsetIds')
def getCMFObjectsSubsetIds(self, objs):
"""Get the ids of only cmf objects (used for moveObjectsByDelta)
"""
ttool = getToolByName(self, 'portal_types')
cmf_meta_types = [ti.Metatype() for ti in ttool.listTypeInfo()]
return [obj['id'] for obj in objs if obj['meta_type'] in
cmf_meta_types ]
security.declareProtected(permissions.ModifyPortalContent,
'getObjectPosition')
def getObjectPosition(self, id):
objs = list(self._objects)
om = [objs.index(om) for om in objs if om['id']==id ]
if om: # only 1 in list if any
return om[0]
raise NotFound, 'Object %s was not found' % str(id)
security.declareProtected(permissions.ModifyPortalContent,
'moveObjectsUp')
def moveObjectsUp(self, ids, delta=1, RESPONSE=None):
""" Move an object up """
...end of zip...
Do you think this could be the getObjectPosition I am looking for?
>
> For me, this looks like a bug in "plone.app.folder".
> When Plone expects that some folders do not support the next/previous
> functionality (because they are unordered), this functionality
> should be automatically disabled for those folders.
>
> You could try to add as a workaround a line:
>
> if pos is None: return None
>
> after the "pos" assignment in "nextprevious".
>
> If this solves your problem, please file a bug report against
> "plone.app.folder".
>
Yes, this solved the problem, and I will file a bug against
plone.app.folder.
>> By the way: I also googled your function "definedBy". Is it this one
>> (and how is it used)?:
...
> You see here a function with name "definedBy" and arguments
> "name" and "class_". Its name suggests that "definedBy(name, cls)"
> tells you where "name" is defined in the context of "cls".
> The function's docstring tells you that the result is the defining
> class (from which you can ask the module, the class is defined in).
> The docstring also tells you that you can pass an object rather
> than a class and that the object's class is used in this case.
Conceptually a bit advanced for me... But I believe i understand that
the "getObjectPosition" is a function. Is it then also an object that I
could pass to "definedBy"?
> As an example: in order to find out which class
> defines "idsInOrder" after you entered your breakpoint above,
> you could use "!import DUtils; DUtils.definedBy('idsInOrder', ordering)"
Does this mean that "ordering" is the context for "idsInOrder" (from the
line "next = ordering.idsInOrder()[pos + 1]" in nextprevious)? And would
I know that "ordering", as an argument to "definedBy", is a class?
Well, a few maybe silly and off topic questions at the end here :-)
Thank you again Dieter
After you jumped before the "pos" assignment", you must
"step" into the function computing the value. This will
tell you where and how the value is computed.
> ...
> def getObjectPosition(self, id):
>
> objs = list(self._objects)
> om = [objs.index(om) for om in objs if om['id']==id ]
>
> if om: # only 1 in list if any
> return om[0]
>
> raise NotFound, 'Object %s was not found' % str(id)
>...
>
>Do you think this could be the getObjectPosition I am looking for?
No. This method cannot return "None".
--
Den 10. juli 2011 07:38, skrev Dieter Maurer:
> After you jumped before the "pos" assignment", you must
> "step" into the function computing the value. This will
> tell you where and how the value is computed.
What about this one :-)
>
/usr/local/plone4/buildout-cache/eggs/plone.app.folder-1.0.1-py2.6.egg/plone/app/folder/nextprevious.py(25)getNextItem()
-> ordering = self.context.getOrdering()
(Pdb) l
20 return self.context.getNextPreviousEnabled()
21
22 def getNextItem(self, obj):
23 """ return info about the next item in the container """
24 pos = self.context.getObjectPosition(obj.getId())
25 -> ordering = self.context.getOrdering()
26 try:
27 try: # first try `__getitem__`
28 next = ordering[pos + 1]
29 except TypeError:
30 import pdb; pdb.set_trace()
(Pdb) j 23
>
/usr/local/plone4/buildout-cache/eggs/plone.app.folder-1.0.1-py2.6.egg/plone/app/folder/nextprevious.py(23)getNextItem()
-> """ return info about the next item in the container """
(Pdb) s
--Call--
>
/usr/local/plone4/buildout-cache/eggs/Products.Archetypes-1.6.1-py2.6.egg/Products/Archetypes/BaseObject.py(191)getId()
-> def getId(self):
(Pdb) s
>
/usr/local/plone4/buildout-cache/eggs/Products.Archetypes-1.6.1-py2.6.egg/Products/Archetypes/BaseObject.py(194)getId()
-> return self.id
(Pdb) s
--Return--
>
/usr/local/plone4/buildout-cache/eggs/Products.Archetypes-1.6.1-py2.6.egg/Products/Archetypes/BaseObject.py(194)getId()->'art-against-rape'
-> return self.id
(Pdb) s
--Call--
>
/usr/local/plone4/buildout-cache/eggs/plone.folder-1.0-py2.6.egg/plone/folder/ordered.py(86)getObjectPosition()
-> def getObjectPosition(self, id):
.....
I then opened this file (extract following with getObjectPosition
definition at the end):
/usr/local/plone4/buildout-cache/eggs/plone.folder-1.0-py2.6.egg/plone/folder/ordered.py
with content:
from zope.component import getAdapter, queryAdapter
from zope.interface import implements
from zope.annotation.interfaces import IAttributeAnnotatable
from AccessControl import ClassSecurityInfo
from AccessControl.Permissions import access_contents_information
from AccessControl.Permissions import manage_properties
from OFS.interfaces import IOrderedContainer
from Products.BTreeFolder2.BTreeFolder2 import BTreeFolder2Base, _marker
from plone.folder.cmf import PortalFolderBase
from plone.folder.cmf import ModifyPortalContent
from plone.folder.interfaces import IOrderableFolder
from plone.folder.interfaces import IOrdering
from plone.folder.interfaces import IExplicitOrdering
from webdav.NullResource import NullResource
class OrderedBTreeFolderBase(BTreeFolder2Base):
""" BTree folder base class with ordering support. The ordering
is done by a named adapter (to IOrdering), which makes the policy
changeable. """
implements(IOrderedContainer, IOrderableFolder, IAttributeAnnotatable)
_ordering = u'' # name of adapter defining ordering policy
security = ClassSecurityInfo()
def __nonzero__(self):
""" a folder is something, even if it's empty """
return True
security.declareProtected(access_contents_information, 'getOrdering')
def getOrdering(self):
""" return the currently active ordering adapter for this
folder """
adapter = queryAdapter(self, IOrdering, name=self._ordering)
if adapter is None:
adapter = getAdapter(self, IOrdering)
return adapter
security.declareProtected(manage_properties, 'setOrdering')
def setOrdering(self, ordering=u''):
""" (re)set ordering adapter to be used for this folder """
if ordering:
# make sure the adapter exists...
getAdapter(self, IOrdering, name=ordering)
self._ordering = ordering
# IObjectManager
def _getOb(self, id, default=_marker):
""" Return the named object from the folder. """
try:
return super(OrderedBTreeFolderBase, self)._getOb(id, default)
except KeyError, e:
raise AttributeError(e)
def _setOb(self, id, object):
""" Store the named object in the folder. """
super(OrderedBTreeFolderBase, self)._setOb(id, object)
self.getOrdering().notifyAdded(id) # notify the ordering
adapter
def _delOb(self, id):
""" Remove the named object from the folder. """
super(OrderedBTreeFolderBase, self)._delOb(id)
self.getOrdering().notifyRemoved(id) # notify the ordering
adapter
def objectIds(self, spec=None, ordered=True):
if not ordered:
return super(OrderedBTreeFolderBase, self).objectIds(spec)
ordering = self.getOrdering()
if spec is None:
return ordering.idsInOrder()
else:
ids = super(OrderedBTreeFolderBase, self).objectIds(spec)
idxs = []
for id in ids:
idxs.append((ordering.getObjectPosition(id), id))
return [x[1] for x in sorted(idxs, key=lambda a: a[0])]
# IOrderSupport - mostly deprecated, use the adapter directly instead
security.declareProtected(access_contents_information,
'getObjectPosition')
def getObjectPosition(self, id):
""" Get the position of an object by its id. """
return self.getOrdering().getObjectPosition(id)
security.declareProtected(manage_properties, 'moveObjectsUp')
def moveObjectsUp(self, ids, delta=1, subset_ids=None):
...end of snip...
And then pdb-stepped further:
(Pdb) s
>
/usr/local/plone4/buildout-cache/eggs/plone.folder-1.0-py2.6.egg/plone/folder/ordered.py(88)getObjectPosition()
-> return self.getOrdering().getObjectPosition(id)
(Pdb) s
--Call--
>
/usr/local/plone4/buildout-cache/eggs/plone.folder-1.0-py2.6.egg/plone/folder/ordered.py(36)getOrdering()
-> def getOrdering(self):
(Pdb) s
>
/usr/local/plone4/buildout-cache/eggs/plone.folder-1.0-py2.6.egg/plone/folder/ordered.py(38)getOrdering()
-> adapter = queryAdapter(self, IOrdering, name=self._ordering)
(Pdb) s
--Call--
>
/usr/local/plone4/buildout-cache/eggs/zope.component-3.7.1-py2.6.egg/zope/component/_api.py(101)queryAdapter()
-> def queryAdapter(object, interface=Interface, name=u'', default=None,
(Pdb) s
>
/usr/local/plone4/buildout-cache/eggs/zope.component-3.7.1-py2.6.egg/zope/component/_api.py(103)queryAdapter()
-> if context is None:
(Pdb) s
>
/usr/local/plone4/buildout-cache/eggs/zope.component-3.7.1-py2.6.egg/zope/component/_api.py(104)queryAdapter()
-> return adapter_hook(interface, object, name, default)
(Pdb) s
--Call--
>
/usr/local/plone4/buildout-cache/eggs/zope.site-3.6.1-py2.6.egg/zope/site/hooks.py(93)adapter_hook()
-> def adapter_hook(interface, object, name='', default=None):
(Pdb) s
>
/usr/local/plone4/buildout-cache/eggs/zope.site-3.6.1-py2.6.egg/zope/site/hooks.py(94)adapter_hook()
-> try:
(Pdb)
>
/usr/local/plone4/buildout-cache/eggs/zope.site-3.6.1-py2.6.egg/zope/site/hooks.py(95)adapter_hook()
-> return siteinfo.adapter_hook(interface, object, name, default)
(Pdb)
--Call--
>
/usr/local/plone4/buildout-cache/eggs/plone.folder-1.0-py2.6.egg/plone/folder/unordered.py(12)__init__()
-> def __init__(self, context):
(Pdb) s
>
/usr/local/plone4/buildout-cache/eggs/plone.folder-1.0-py2.6.egg/plone/folder/unordered.py(13)__init__()
-> self.context = context
(Pdb) s
--Return--
>
/usr/local/plone4/buildout-cache/eggs/plone.folder-1.0-py2.6.egg/plone/folder/unordered.py(13)__init__()->None
-> self.context = context
(Pdb) s
--Return--
>
/usr/local/plone4/buildout-cache/eggs/zope.site-3.6.1-py2.6.egg/zope/site/hooks.py(95)adapter_hook()-><plone.f...40d97510>
-> return siteinfo.adapter_hook(interface, object, name, default)
(Pdb)
...
Then I opened the file
/usr/local/plone4/buildout-cache/eggs/plone.folder-1.0-py2.6.egg/plone/folder/unordered.py
And that is my old friend that returned "None". It's content is:
from Acquisition import aq_base
from zope.interface import implements
from zope.component import adapts
from plone.folder.interfaces import IOrdering, IOrderableFolder
class UnorderedOrdering(object):
""" This implementation provides no ordering. """
implements(IOrdering)
adapts(IOrderableFolder)
def __init__(self, context):
self.context = context
def notifyAdded(self, id):
pass
def notifyRemoved(self, id):
pass
def idsInOrder(self):
return aq_base(self.context).objectIds(ordered=False)
def getObjectPosition(self, id):
return None
...end of file...
Thank you again Dieter!
Børge
Indeed, there you are.
It shows that Plone's "nextprevious" has a weakness: it should
automatically handle "unordered" folders -- and for the moment fails
to do so. But you have already filed a bug report for this.
Note: an important issue with debugging is when to use "next"
(step over call) and "step" (step into call). You use next to step
over calls that are unlikely to contribute to the analysis of
the current problem. Infrastructure calls (such as adapter lookups)
fall under this category. You use "step" to find out about details
in the called function.
Often, you must step into a function ("getId" in your example) even
when you do not really want to (e.g. when the uninteresting call
is on the same line as an interesting one). In this case,
you can use "return" to quickly leave the called function again.
Things like this will get optimized with experience.
--
Dieter