I'm having problems in getting my Dexterity content type to show a custom Add Form. I have already done this in a previous product, but, amazingly, I cannot accomplish this using Plone 4.1 and plone.app.dexterity 1.0.3
My CrmContact content type, living in package.name.types.contact.py, has its schema defined in this way:
from five import grok
from zope import schema
from zope.interface import implements
from plone.directives import form, dexterity
class ICrmContact(form.Schema):
"""A contact item for the CRM"""
title = schema.TextLine(
title=_(u"Company name"),
)
...
class CrmContact(dexterity.Container):
implements(ICrmContact)
class Add(dexterity.AddForm):
grok.context(ICrmContact)
grok.name('package.name.contacts.types.contact')
grok.template('add')
My template lives in package/name/types/contact_templates. It's a typical template. I know it's not being rendered because it has a dummy node that will call a non existing method using tal:content, in order to raise an exception; so I'm sure the template itself is not the issue.
My content type FTI is registered correctly during installation, and the content type is available and addable.
Finally, in profiles/default/types.package.name.types.contact.xml:
<?xml version="1.0"?>
<object name="package.name.types.contact" meta_type="Dexterity FTI"
i18n:domain="package.name" xmlns:i18n="http://xml.zope.org/namespaces/i18n">
...
<!-- Method aliases -->
<alias from="(Default)" to="(dynamic view)" />
<alias from="edit" to="##edit" />
<alias from="sharing" to="##sharing" />
<alias from="view" to="(selected layout)" />
<!-- Actions -->
<action title="View" action_id="view" category="object"
condition_expr="" url_expr="string:${object_url}" visible="True">
<permission value="View" />
</action>
<action title="Edit" action_id="edit" category="object"
condition_expr="" url_expr="string:${object_url}/edit" visible="True">
<permission value="Modify portal content" />
</action>
</object>
Unrelated, but maybe I have to add something here...
I think I followed the correct procedure, as you may see, but I still cannot get it to work.
I know class Add is getting instanced because if I provide an updateWidgets() method and insert a breakpoint, it gets called; and when I introspect the object, self.template is None; even though:
(Pdb) getattr(self, 'grokcore.view.directive.template')
'add'
How can I provide a custom template to the Add Form of my custom type?
You should remove the line grok.context(ICrmContact).
From http://plone.org/products/dexterity/documentation/manual/developer-manual/advanced/forms:
Also note that we do not specify a context here. Add forms are always registered for any IFolderish context.
Related
My overall goal is: launch a process step that runs a java class that gathers a list, then in the next workflow step this list needs to be displayed inside a custom dialog.
This is what i have so far:
Java Class that uses the metadata put and saves the list into the metadata
The custom dialog made that uses a textarea which is where this list needs to be displayed
Another Java class that retrieves gets the list from the metadata
The issue i have now is: how can i get the list from the metadata to show in my custom dialog?
I been searching for ages and seems like no one has ever done it before
Please note: this answer was tested and developed on AEM 6.3
Here is a simple granite UI widget to display ALL of the key/value pair in the current workflow MetaDataMap (including ones set in previous workflow steps)
First, let's create the workflow dialog:
create a component under apps: /apps/so-wf-test/wf-components/wf-metadata
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
componentGroup=".hidden"
jcr:primaryType="cq:Component"
jcr:title="A dummy component needed for the dialog"
sling:resourceSuperType="foundation/components/parbase"/>
Create the dialog: /apps/so-wf-test/wf-components/wf-metadata/cq:dialog
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
jcr:primaryType="nt:unstructured"
jcr:title="Display all workflow metadata"
sling:resourceType="cq/gui/components/authoring/dialog">
<content
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container">
<layout
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/layouts/fixedcolumns"/>
<items jcr:primaryType="nt:unstructured">
<columns
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container">
<items jcr:primaryType="nt:unstructured">
<metadataList
jcr:primaryType="nt:unstructured"
sling:resourceType="/apps/so-wf-test/wf-granite-widgets/wf-metadata-list"/>
</items>
</columns>
</items>
</content>
</jcr:root>
note that above dialog includes a custom granite widget to display the metdata list: <metadataList jcr:primaryType="nt:unstructured" sling:resourceType="/apps/so-wf-test/wf-granite-widgets/wf-metadata-list"/>
Now The granite widget JSP
create sling folder: /apps/so-wf-test/wf-granite-widgets/wf-metadata-list
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
jcr:primaryType="sling:Folder"/>
Inside create wf-metadata-list.jsp:
This can be done via Use API or sling models with HTL Or via a servlet. I went with JSP for fast solution for the purposes of this answer.
<%#include file="/libs/granite/ui/global.jsp"%><%
%><%#page import="com.adobe.granite.workflow.WorkflowSession,
com.adobe.granite.workflow.exec.WorkItem,
com.adobe.granite.workflow.exec.WorkflowData,
com.adobe.granite.workflow.metadata.MetaDataMap,
org.slf4j.Logger,
org.slf4j.LoggerFactory"%>
<%
// get a logger
Logger logger = LoggerFactory.getLogger(this.getClass());
// when a workflow dialog is requested, the workitem id is passed as an attribute "item"
String workitemId = request.getParameter("item");
// get workflow session
WorkflowSession wfSession = resourceResolver.adaptTo(WorkflowSession.class);
// get the current workitem
WorkItem workitem = wfSession.getWorkItem(workitemId);
// get workflow data
WorkflowData workflowData = workitem.getWorkflowData();
// get metadata map
MetaDataMap metaDataMap = workflowData.getMetaDataMap();
%>
<h1>MetaDataMap Values:</h1>
<ul>
<%
// Iterate over metaDataMap and print all key/val pairs to a list item
for (Object entry : metaDataMap.keySet())
{
Object objVal = metaDataMap.get(entry);
String val = objVal == null ? "" : objVal.toString();
%>
<li><h3><%=entry%>: <%=val%></h3></li>
<%
}
%>
</ul>
** Now just wire the dialog into your dialog workflow step **
using the path /apps/so-wf-test/dialogs/wf-dialog/cq:dialog"
Once the workflow reaches the dialog step, you'll see the list of metadata items.
Example:
I have created a working example that has the simple steps
step 1: add "someKey"="SOME VALUE" to MetaDataMap
step 2: get "someKey" and print it
step 3: dialog step pointing to dialog above
Here is a sceenshot of the dialog:
Here is a package you can download and install
https://drive.google.com/file/d/0B-_At1NXpw0EOFNaUVdYcGVHNnM/view?usp=sharing&resourcekey=0-a_8b0SoC25dxwTTCHw2vRg
I want to use unity to manage my mongo repository but when I try registering it using the Web.config and LoadConfiguration() I get an error I am unable to decipher:
An exception of type 'System.ArgumentException' occurred in
Microsoft.Practices.Unity.Configuration.dll but was not handled in
user code
Additional information: The container named "" is not defined in this
configuration section.
This is my Global.asax
using System.Web;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;
namespace OOP_project
{
public class MvcApplication : System.Web.HttpApplication
{
internal static readonly IUnityContainer unity = new UnityContainer();
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
unity.LoadConfiguration();
}
}
}
and this is the relevant part of my Web.config
<configSections>
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
</configSections>
<unity>
<typeAliases>
<typeAlias alias="string" type="System.String, mscorlib" />
</typeAliases>
<containers>
<container name="container">
<types>
<type type="MongoRepository.MongoRepository`1, MongoRepository" mapTo="MongoRepository.MongoRepository`1, MongoRepository">
<lifetime type="Singleton" />
<constructor>
<param name="connectionString" parameterType="string" value="mongodb://127.0.0.1/Blog"></param>
</constructor>
</type>
</types>
</container>
</containers>
</unity>
I would like to understand what this error actually means and how to fix it.
I see two problems with your code:
First, you need to specify the container name when you call LoadConfiguration. In the configuration file, the container name is "container" (in the <container> xml element), so you need to specify that like this:
unity.LoadConfiguration("container");
Or alternatively, change the container name in the configuration file to an empty string like this:
<container name="">
The second problem which is not directly related to your question is that the singleton lifetime should be specified with a small letter like this:
<lifetime type="singleton" />
By the way, why do you want to use a configuration file to configure Unity? Configuring Unity with a configuration file is brittle. For example, if you change a class name in code, it will not change in the configuration file. You would have to change it manually or your application would break.
Unless you need to be able to change dependencies without recompilation, your should prefer to configure the container via code.
Please note that you could make some of the registrations through code and some in the configuration file (for the dependencies that you need to be able to change without recompilation). You could easily find a resource online of how to do that.
I am trying to use Thymeleaf to create custom tags, just like in JSP.
The tag I have now is:
<select th:include="fragments/combobox :: combobox_beans (beans=${#accountService.getAccounts()}, innerHTML='id,description,currency', separator=' - ', dumbHtmlName='List of accounts', name='sender' )" th:remove="tag"></select>
The purpose is just defining the beans list, the properties of the bean to show on screen, the separator between them, the default value when shown as a native template, and the property name of the original bean we are processing here.
combobox.html:
<div th:fragment="combobox_beans (beans, innerHTML, separator, dumbHtmlName, name)">
<select th:field="*{__${name}__}" class="combobox form-control" required="required">
<option th:each="obj : ${beans}" th:with="valueAsString=${#strings.replace( 'obj.' + innerHTML, ',', '+'' __${separator}__ ''+ obj.')}"
th:value="${obj}" th:text="${valueAsString}" >
<p th:text="${dumbHtmlName}" th:remove="tag"></p>
</option>
</select>
I need the text of the option tag to be based on the properties set in innerHTML property (innerHTML='id,description,devise') of the fragment.
I end up having an option with this text:
<option value="...">obj.id+' - '+ obj.description+' - '+ obj.currency</option>
instead of the desired result
<option value="...">2 - primary - USD</option>
I know this is due to the usage of Strings library which results in a string.
Is there a way Thymeleaf can re-evaluate this string to be understood as an object again?
Maybe using strings library is just so wrong in this situation... Maybe I need to use a th:each to process the each bean as an object and read its properties, but yet again, how to only get the properties specified in innerHtml ?
Anyone has a solution or work-around for this ?
thanks.
If there is a way to do what you want in Thymeleaf/Spring expression alone, it most certainly very complicated and long winded, plus it would probably be a pain to read.
The easier way to do it would be add a custom utility object to the expression context. Very little code is needed. This answer shows it.
Then you need to add you new dialect as additional dialect to the template engine in your Spring xml config. Assuming you have a fairly standard Spring config, it should be similar to this.
<bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine">
<property name="templateResolver" ref="templateResolver" />
<property name="additionalDialects">
<set>
<bean class="mypackage.MyUtilityDialect" />
</set>
</property>
</bean>
Now for the utility object
What you want is to get the properties from objects by name, and combine their values with a separator. It seems that the list of property names can be of any size. For accessing properties by name, the most convenient thing is to use a library like the Apache beanutils.
Your custom utility object could the look something like this using the Java 8 streams library, lambdas and Beanutils:
public class MyUtil {
public String joinProperties(Object obj, List<String> props, String separator){
return props.stream()
.map(p -> PropertyUtils.getProperty(obj,p).toString())
.collect(Collectors.joining(separator))
}
}
Then when you add you dialect to SpringTemplateEngine you can call your utility:
th:with="valueAsString=${#myutils.joinProperties(obj,properties,separator)}"
I have replaced you innerHTML parameter with properties which is a List<String>, because it makes more sense. It is essentially a list of property names, and Spring EL supports inline lists.
Your calling tag should then look like this.
<select th:include="fragments/combobox :: combobox_beans (beans=${#accountService.getAccounts()}, properties=${ {'id','description','currency'} }, separator=' - ', dumbHtmlName='List of accounts', name='sender' )" th:remove="tag"></select>
I want to inject a string value into a property using Unity. I can't find a syntax that works. In this case, PutBunniesHere
In this case the error is it doesn't recognise "type" as a valid attribute of value. I added that because it couldn't resolve the type before.
The class has this property:
[Dependency("PutBunniesHere")]
public string PutBunniesHere { get; set; }
And this is the config I'm using for unity.
<?xml version="1.0" encoding="utf-8" ?>
<unity2 xmlns="http://schemas.microsoft.com/practices/2010/unity">
<alias alias="IInjectMe1" type="CommonLib.IInjectMe1, CommonLib"/>
<alias alias="singleton" type="Microsoft.Practices.Unity.ContainerControlledLifetimeManager, Microsoft.Practices.Unity" />
<container name="unity2">
<register type="CommonLib.IInjectMe2, CommonLib" name="Injected2" mapTo="CommonLib.InjectMe2, CommonLib">
<lifetime type="singleton"/>
<constructor>
<param name="theDependency" dependencyType="IInjectMe1" />
</constructor>
<property name="PutBunniesHere">
<value value="my bunnies" type="System.String"/>
</property>
</register>
</container>
</unity2>
Ok, solved the problem. Once again it was a problem with having a name on the register element. This makes value on the value element take on a different meaning. value means name of a registered type, or a type if there is a name attribute on the parent register element. Take out the name and value means a value (and possibly also a type)
Yuk.
I am trying to build a messaging system and for this i have the table definition below
Message
Id
From
To
Body
ParentId // Subcollection, i want to get Asnwers (Message.ParentId== Message.Id)
IsRead
and i have this in the Message.cs
IList<Message> Answers;
I have tried this but it gives me all the messages and all the answers in the main collection.
But i dont want answers to be seen like a message (like the main item).
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="RealEstate.Core" namespace="RealEstate.Core.Domain">
<class name="Message" table="Message" lazy="true">
<id column="id" type="Int64" name="Id">
<generator class="native" />
</id>
<property name="From" column="[From]" type="Int64" />
<property name="To" column="[To]" type="Int64" />
<property name="Body" column="Body" />
<property name="ParentId" column="ParentId" type="Int64" />
<property name="SenderType" column="SenderType" type="Byte" />
<property name="IsRead" column="IsRead" type="Boolean" />
<bag name="Answers" lazy="true" cascade="delete">
<key column="ParentId" />
<one-to-many class="Message"/>
</bag>
</class>
</hibernate-mapping>
How can this mapping be done, they are in the same table ?
Thank you very much
Before attempting an answer, I'd strongly recommend that you search the NHibernate Users Group as there are tons of helpful NHibernate folks that lurk there answering all kinds of questions.
But let me see if I can help here.
Hmmm, I'm not totally sure I understand the question. You say:
I have tried this but it gives me all
the messages and all the answers in
the main collection.
But i dont want answers to be seen
like a message (like the main item).
Do you mean that the Answers collection contains all answers in the database?
Can you post up more code, showing the query your running, and the class code?
One potential problem you have with your scenario is that ParentId can be NULL in the database. This gives NHibernate problems when mapping a one-to-many.
Try making the association bidirectional (documentation reference). That sometimes helps avoid a few traps.
To do that, add this to your class
public class Message {
///<summary>Reference to parent message</summary>
public Message Parent {get;set;}
//... rest of class
Add this to your mapping:
<bag name="Answers" lazy="true" cascade="delete" inverse="true">
<key column="ParentId" />
<one-to-many class="Message"/>
</bag>
<many-to-one name="Parent"/>
The inverse=true will make NHibernate manage the relationship from the Parent property, not the collection. This is necessary because ParentId can be null.
In your code, rather than using myMessage.Answers.Add( blah ); you can use answer.Parent = myMessage. Of course, you can write nice helper methods to make this more meaningful.
someMessage.AddAnswer( someAnswer );
Which looks like this:
public void AddAnswer(Message answer)
{
answer.Parent = this;
if( ! this.Answers.Contains(answer) )
this.Answers.Add(answer);
}
Hope this helps.
You want to map a tree ?
Maybe this could help:
how to map a tree in nhibernate