Can a view define a __bobo_traverse__ method? - plone

I am trying to make ARFilePreview work with newer versions of Plone. The product defines a view which has its own __bobo_traverse__ method. Unfortunately ZPublisher does not invoke this method.
The traverseName() method in BaseRequest.py has:
if IPublishTraverse.providedBy(ob):
ob2 = ob.publishTraverse(self, name)
else:
adapter = queryMultiAdapter((ob, self), IPublishTraverse)
if adapter is None:
## Zope2 doesn't set up its own adapters in a lot of cases
## so we will just use a default adapter.
adapter = DefaultPublishTraverse(ob, self)
ob2 = adapter.publishTraverse(self, name)
I would like it to take the else case but it results in a 404 because the if returns True.
Is there an easy fix?

Define a method called publishTraverse(same args as __bobo_traverse__) and invoke the existing __bobo_traverse__ from it.

Related

Extend Plone-Controlpanel Form

Is it possible to extend the Controlpanel-View with each addon?
For Example
ca.db.core -> Makes basic fieldset/tab for DB Connection Settings
ca.db.person -> If installed, Adds to the "core" settings a new fieldset/tab for Person specific fields/settings
ca.db.schema -> If installed, also adds an new fieldset/tab for schema.org Fields
Yes it's possible, I once discussed this problem with a guy, who wrote the bda.plone.shop addon.
They faced the same problem, and solved it by using a ContextProxy object, which puts the different schema definitions together in one proxy object.
Using a proxy is IMHO a hack, but I don't know a better solution.
The proxy, tries to get/set a attribute from a list of schemas.
Be aware, you need to handle conflicting names, means if you have the same field name in more than one schema.
class ContextProxy(object):
def __init__(self, interfaces):
self.__interfaces = interfaces
alsoProvides(self, *interfaces)
def __setattr__(self, name, value):
if name.startswith('__') or name.startswith('_ContextProxy__'):
return object.__setattr__(self, name, value)
registry = getUtility(IRegistry)
for interface in self.__interfaces:
proxy = registry.forInterface(interface)
try:
getattr(proxy, name)
except AttributeError:
pass
else:
return setattr(proxy, name, value)
raise AttributeError(name)
def __getattr__(self, name):
if name.startswith('__') or name.startswith('_ContextProxy__'):
return object.__getattr__(self, name)
registry = getUtility(IRegistry)
for interface in self.__interfaces:
proxy = registry.forInterface(interface)
try:
return getattr(proxy, name)
except AttributeError:
pass
raise AttributeError(name)
Now you need to use the proxy in your ControlPanel form.
I assume you're using the RegistryEditForm from plone.registry:
class SettingsEditForm(controlpanel.RegistryEditForm):
schema = ISettings
label = _(u"Settings")
description = _(u"")
# IMPORTANT Note 1 - This is where you hook in your proxy
def getContent(self):
interfaces = [self.schema] # Base schema from ca.db.core
interfaces.extend(self.additionalSchemata) # List of additional schemas
return ContextProxy(interfaces)
# IMPORTANT Note 2 - You may get the additional schemas dynamically to extend the Settings Form. For example by name (startswith...)
# In this case they have a separate interface, which marks the relevant interfaces.
#property
def additionalSchemata(self):
registry = getUtility(IRegistry)
interface_names = set(record.interfaceName for record
in registry.records.values())
for name in interface_names:
if not name:
continue
interface = None
try:
interface = resolve(name)
except ImportError:
# In case of leftover registry entries of uninstalled Products
continue
if ISettingsProvider.providedBy(interface):
yield interface
...
You can find the full code here

When I call an AIF service from C# to create a SO, my XML doesn't look like I would expect

I'm experimenting with AIF to create a basic SO from C# following this general guide from Microsoft. I have two questions.
The only information I'm passing is this:
// Create instances of the entities that are used in the service and
// set the needed fields on those entities.
AxdEntity_SalesTable salesTable = new AxdEntity_SalesTable();
salesTable.CurrencyCode = "USD";
salesTable.CustAccount = "100003";
salesTable.DeliveryDate = Convert.ToDateTime("1/14/2016");
salesTable.Payment = "Net30";
salesTable.PurchOrderFormNum = "PO";
AxdEntity_SalesLine salesLine = new AxdEntity_SalesLine();
salesLine.ItemId = "44417";
salesLine.SalesQty = 3;
salesLine.SalesUnit = "ea";
Why is it that when I examine the XML, it looks like it's passing tons of extra fields:
What does this error mean? EInvoiceAccountCode appears to be a base field on SalesTable, and I tried Tools>Application Integration Framework>Update document service to update the SalesSalesOrder service.
Invalid document schema. The following error was returned: The element 'SalesTable' in namespace 'http://schemas.microsoft.com/dynamics/2008/01/documents/SalesOrder' has invalid child element 'EInvoiceAccountCode' in namespace 'http://schemas.microsoft.com/dynamics/2008/01/documents/SalesOrder'. List of possible elements expected: 'DlvTerm' in namespace 'http://schemas.microsoft.com/dynamics/2008/01/documents/SalesOrder'.
1) Everything is flagged as Nillable by Ax (for several reasons). Nillable VS MinOccurs 0
2) You are passing an element "EInvoiceAccountCode" in your request. But it's expecting an "DlvTerm" element at that location.

Umbraco: x.GetPropertyValue("myProp") vs x.myProp

I use Umbraco v4, but think this should be a common problem.
I have a generic property "myNode" of "Content Picker", that should obtain a DynamicNode...
so doying myObj.myNode I obtain the node itself... so can use myObj.myNode.Url
But doying the myObj.GetPropertyValue("myNode") I obtain the ... string ID value of the node... so can't anymore do myObj.GetPropertyValue("myNode").Url (string does not have Url property)
I can't use directly myObj.myNode, because the name is "dynamic" (the same function should use "your"+"Node" or "their"+"Node" upon conditions - the example is very aproximative, but hope the idea is clear)...
I even did myObj.GetPropertyValue<DynamicNode>("myNode"), but the result was the same: "8124" (the node id)
So, how to obtain the real property value, not just string representation of it?
Your content picker does not contain a node, it contains an id of a node.
myObj.GetPropertyValue("myNode") does exactly what is says, gets the value of a property called myNode on the instantiated DynamicNode object. It is not designed to return the node itself.
If you want to return the node whose ID your 'myNode' property contains then you have to use that value in a call to instantiate another DynamicNode
DynamicNode myNewNode = new DynamicNode(myObj.GetPropertyValue("myNode"))
or
Model.NodeById(myObj.GetPropertyValue("myNode"))
Use somethings like: mynode = Umbraco.Content(CurrentPage.myNode).Url (for Umbraco 6 and 7) For Umbraco 4 i use this Model.NodeById(Model.myNode).Url; in a script file. (I think it need at least Umbraco 4.7.x)
See also https://our.umbraco.org/documentation/Using-Umbraco/Backoffice-Overview/Property-Editors/Built-in-Property-Editors/Content-Picker
A not so elegant solution, but at least a solution that work:
var linkNode = image.GetPropertyValue("imgLinkNode" + model._lang.ToUpper());
if (linkNode is string)
{
string id = linkNode;
linkNode = model.NodeById(id);
}
var linkNodeUrl = linkNode.Url;

How to override VBScript GetObject method in .NET

I am having below code in VBScript
' Retrieve the keyword category for page section names
Set SectionCat = TDSE.GetObject(WebdavToUri(getPublicationWebDav(WEBDAV_SECTION_CAT)), 1)
' Retrieve the localized section keyword
Set SectionKeyword = SectionCat.GetKeywordByTitle(meta)
' Open the English translated section keyword
Set SectionKeyword = TDSE.GetObject(SectionKeyword.Id, 1, WEBDAV_UKEN_PUB)
SectionName = SectionKeyword.Title
Where WEBDAV_UKEN_PUB is the WebDavPath, now in VBScript GetObject method we have got option to pass three parameters 1) Item.ID, 2) TDSDefines.OpenModeEditWithFallback and 3) WebDavPath from where to make the object.
Now I want to write same logic in 2009 .Net templating, below is the sample code, I am trying to write but not able to get rid of VBScript Object.
Category cat = engine.GetSession().GetObject(WebdavToUri(getPublicationWebDav(Constants.WEBDAV_SECTION_CAT,package,engine), engine)) as Category;
if (cat != null)
{
//_log.Info("Category" + cat.Title);
Keyword keyword = cat.GetKeywordByTitle(meta);
//_log.Info("keyword 1" + keyword.Title);
keyword = engine.GetObject(Constants.WEBDAV_UKEN_PUB) as Keyword;
//_log.Info("keyword 2 " + keyword.Title);
if (keyword != null)
{
sectionName = keyword.Title;
}
keyword = null;
I am able to create Category object, however when I am trying to make Keyword object its getting failed and giving object reference error.
Do we have any class or method which work same like VBScript GetObject which will make the Object from the passed webdavpath or can somebody can give sample code on this.
I think your problem is here:
keyword = engine.GetObject(Constants.WEBDAV_UKEN_PUB) as Keyword;
You are using the WEBDav URL of a publication, and then attempting a dynamic cast to Keyword. You can't cast a Publication to a Keyword, so the cast fails and your keyword variable is assigned null.
Using dynamic casts in this way is an easy way to fool yourself. The "As" keyword (C# keyword not Tridion keyword) should be used when you don't know at compile time what type you expect. If you know that you expect an item of type Keyword, then you should write:
keyword = (Keyword)engine.GetObject(Constants.WEBDAV_UKEN_PUB);
This way - when the cast fails, you'll get an exception that identifies the problem correctly.
In TOM.NET we cannot get an object and specify which pub to read it from, we need to modify the TcmUri to be in context.
So:
Repository context = (Repository)session.GetObject(WEBDAV_UKEN_PUB);
TcmUri keywordInContext = new TcmUri(keyword.Id.ItemId, keyword.Id.ItemType, context.Id.ItemId);
Keyword keyword = (Keyword)session.GetObject(keywordInContext);

Multiple instances of views in PureMVC: Am I doing this right?

What I'm doing NOW:
Often multiple instances of the view component would be used in multiple places in an application. Each time I do this, I register the same mediator with a different name.
When a notification is dispatched, I attach the name of the mediator to the body of the notification, like so:
var obj:Object = new Object();
obj.mediatorName = this.getMediatorName();
obj.someParameter = someParameter;
sendNotification ("someNotification", obj);
Then in the Command class, I parse the notification body and store the mediatorName in the proxy.
var mediatorName:String = notification.getBody().mediatorName;
var params:String = notification.getBody().someParameter;
getProxy().someMethod(params, mediatorName);
On the return notification, the mediatorName is returned with it.
var obj:Object = new Object();
obj.mediatorName = mediatorName;
obj.someReturnedValue= someReturnedValue;
sendNotification ("someReturnedNotification", obj);
In the multiple mediators that might be watching for "someReturnedNotification," in the handleNotification(), it does an if statement, to see
if obj.mediatorName == this.getMediatorName
returns true. If so, process the info, if not, don't.
My Question is:
Is this the right way of using Multiton PureMVC? My gut feeling is not. I am sure there's a better way of architecting the application so that I don't have to test for the mediator's name to see if the component should be updated with the returned info.
Would someone please help and give me some direction as to what is a better way?
Thanks.
I checked with Cliff (the puremvc.org guy) and he said it's fine.

Resources