Creating Events with Plone Form Gen - plone

I am trying to create an Event content type using Plone Form Gen. I have been using this tutorial in order to do this.
When creating an Event content type using the Add New... menu, you are given two fields to fill in that are start and end dates for the event, I would like my form to pull the information from these fields and apply it to the event content type I am using to create it.
My problem as I understand it is described with examples below:
The custom script adapter script contains the following:
obj.setDescription(form['replyto'])
I can see that it gets the contents for the Description of the Event content type from the following:
<input id="replyto" class="" type="text" size="30" name="replyto" />
The date/Time field when added to a PFG form, is made up of multiple <select> inputs and not just one like the above, I guess this means there isn't a simple obj.setEndDate() command for this... Although with no way to reference the select boxes I'm kind of stuck.
Does anyone know if it is possible to create an Event content type and specify start and end dates on it, using Plone Form Gen?
Edit
Using this link I have gotten around the original issue but I have run into more problems
I have adapted my script (using the above link) to look like the following:
target = context.viewjobs
form = request.form
from DateTime import DateTime
uid = str(DateTime().millis())
loc = form['location-of-event']
target.invokeFactory("Event", id=uid, title=form['topic'], event_url=loc)
obj = target[uid]
obj.setFormat('text/plain')
obj.setText(form['comments'])
obj.setDescription(form['replyto'])
obj.reindexObject()
(I used event_url just to test as I wasn't having any luck with the event_start option).
It creates the event alright, but when I go to view the event I get:
Module zope.tales.expressions, line 217, in __call__
Module Products.PageTemplates.Expressions, line 147, in _eval
Module zope.tales.expressions, line 124, in _eval
Module Products.PageTemplates.Expressions, line 74, in boboAwareZopeTraverse
Module OFS.Traversable, line 317, in restrictedTraverse
Module OFS.Traversable, line 285, in unrestrictedTraverse
__traceback_info__: ([], 'location')
AttributeError: location
I have not referenced location anywhere in my script, and when I do, I get the same error.
Any thoughts would be appreciated

You can simplify your code and avoid the reindex call by doing something like this:
target = context.viewjobs
form = request.form
from DateTime import DateTime
uid = str(DateTime().millis())
target.invokeFactory(
"Event",
id=uid,
title=form['job-title'],
description=form['description-1'],
text=form['comments'],
location=form['location-of-event'],
startDate=form['start-date'],
endDate=form['end-date-due-by']
)
With regard to collecting the starting and ending date. If you use the Date/Time widget and look at the HTML that is generated you will notice there is a hidden input field who's name matches the short-name of the widget. That hidden input contains a full textual representation of what was selected by the various select boxes, thus giving you want you achieved by using a text field but without having to rely on the user to use a specific format.
If you're wondering how to find the names of the various fields to specify in the invokeFactory call, find the python file that defines the content type your trying to create. In the case of the event object it's /Plone/buildout-cache/eggs/Products.ATContentTypes-2.1.8-py2.7.egg/Products/ATContentTypes/content/event.py
Line 32 starts "ATEventSchema = ..." and from there you will see the field names for all the parts of an event.

I managed to resolve this by using a text field and asking the users to enter the date in this format: 2013-12-12, I then used obj.setStartDate(form['name-of-field']) and obj.setEndDate(form['name-of-field']) to set that on the Event.
To get around the Location traceback, I used obj.setLocation() and removed the location line from the invoke method shown in the script above.
Script for anyone who's interested:
target = context.viewjobs
form = request.form
from DateTime import DateTime
uid = str(DateTime().millis())
target.invokeFactory("Event", id=uid, title=form['job-title'])
obj = target[uid]
obj.setFormat('text/plain')
obj.setText(form['comments'])
obj.setDescription(form['description-1'])
obj.setLocation(form['location-of-event'])
obj.setStartDate(form['start-date'])
obj.setEndDate(form['end-date-due-by'])
obj.reindexObject()

Related

How to filter options in app maker's SuggestBox

I need to implement filtering for options in SuggestBox.
I've tried to do it differently, but none of my attempts worked out:
Using Model instead of Options/Value; Create calculated SQL model and pass the parameter for filtration:
Didn't work: suggestBox doesn't have event or anything to call before it loads data, so if you pass param for query before typing anything in -- it is not seen an you get an error:
Please provide value for the parameter ":Param" used in the query Error: Please provide value for the parameter ":Param" used in the query
Query for record suggest oracle: (Error) : Please provide value for the parameter ":Param" used in the query
Query for record suggest oracle failed.
Using Options/Value
It seems that it is impossible to have different properties of an object when passing it to Suggest Box. For example, I want user to see an user-friendly field Name and work with a record or at least record's Id, but if you specify Name as an option and Id as a value, Name is being written to a SuggestBox and now we get a validation error because Name is a string and Id is a Number.
Please help me realize where I'm wrong or show me a workaround for this.
Thanks!
I had the same issue as well. I gave up on the suggest box and created a pop up form. With a query builder model for the data source. The only thing i don't like is that it seems to be case sensitive.
something like:
YourFilterColumn startsWith? :Param
Add a table view and then you can add a text box and set the oninputchange event to:
widget.datasource.query.parameters.Param = widget.value;
widget.datasource.load();
Then you can start typing select the record you want and pass it back to your form with a button.
like this if it is a relational field:
app.pages.YourPage.children.YourForm.datasource.item.YourItem =
widget.datasource.item;
or something like this if you just need a value (haven't tested this):
app.pages.YourPage.children.YourForm.datasource.item.YourItem =
widget.datasource.item.YourValue;
Probably add a couple lines to clear the form out:
widget.root.descendants.TextBox1.value = "";
widget.datasource.query.parameters.Param = "";
widget.datasource.load();

I need to edit a existing form in plone

I've never used plone but I need to edit a form to add a field. I've already looked everywhere but I can't find anything to do this, is there portal_type?
It's a big problem we have on my company, last programmer ran away and left us with Plone :\
Thanks.
Look at your URL, find the Name View, the last Part of url, e.g /fileupload-form
Search with grep on the Commandline in your Productfolder to the Name, you can find a Browserview with the Name 'fileupload-form'
Look at the Definition in the zcml-File you can find the Classdefinition of View/Form
Search the Interface of Form, it could be defined like:
class IMyForm(form.Schema):
""" Define form fields """
name = schema.TextLine(
title=u"Your name",
)
add a new Field of Type NamedBlobFile or NamedFile, look in the Documentation Example

Adding fields dynamically to Share form

I want to add a text field for each file that is added to the attached package items in alfresco to write notes regarding each file, is it possible to do?
I have implemented something that could be reused for your use case.
You can define a property with multiple values that will contain the list of notes associated with each attachment.
There is a simple trick to post a property with multiple values: add "[]" to the name of the property. For example:
<input id="template_x002e_edit-metadata_x002e_edit-metadata_x0023_default_prop_someco_notes_0"
name="prop_someco_notes[]"
tabindex="0"
type="text"
value="Meeting minutes"
title="Notes"
noderef="workflow://...."
>
<input id="template_x002e_edit-metadata_x002e_edit-metadata_x0023_default_prop_someco_notes_1"
name="prop_someco_notes[]"
tabindex="1"
type="text"
value="Meeting minutes"
title="Notes"
noderef="workflow://...."
>
As you can see, the name of the input ends with []. Both input textfields have the same name.
The Alfresco Form Engine will consider these two inputs as the value for the property with multiple values: "someco:notes".
The bigger problem is that you need to generate this html with some smart javascript and free marker template.
You can write a custom free marker template to render the initial html: if a user opens a task on which documents have been already attached, you will need to generate the list of inputs using a custom control (you can of course start from textfield.ftl).
It won't be easy to generate the initial list because unfortunately Alfresco returns the list of values as a single comma separated value.
You can customise the webscript that injects the model in the free marker template "org.alfresco.web.scripts.forms.FormUIGet" to pass an array instead of a csv.
A quicker and dirtier solution is to split the csv value. In share-config-custom.xml, you can specify what textfield.ftl show use as a separator instead of the comma.
When a user adds/remove elements from the package, you can intercept the update and add/remove the correspondent note. Notice that I have added the filed "noderef" to each input so it is possible to know the relation between the notes and the nodes in the package.
UPDATE:
For the associations (used for example to define the package in a workflow task), Share uses a javascript library called "object finder" (or "object picker"). This library fires an event called "formValueChanged" that you can intercept:
YAHOO.Bubbling.fire("formValueChanged",
{
eventGroup: this,
addedItems: addedItems,
removedItems: removedItems,
selectedItems: selectedItems,
selectedItemsMetaData: Alfresco.util.deepCopy(this.selectedItems)
});

How can I ask the state for a content object?

At the end of this tutorial several object attributes are listed. But I need access to the state (published, private,...). I also search that attribute using dir() but I don't see an attribute named as state or something similar. i.e, I need something like this:
>>> app.Plone.foo.bar.state
"published"
Or to keep your code more readable and not having to remember strange method names, you can use plone.api to do this:
from plone import api
api.content.get_state(obj=your_object)
Of course, you need to add plone.api to your eggs first, and re-run buildout.
You can always use the plone_workflow to determine current status:
workflowTool = getToolByName(self.portal, "portal_workflow")
status = workflowTool.getStatusOf("plone_workflow", object)
# where "object" is your content object
print (status)
Unfortunately there is no "state" attribute. Instead, check review_state using the workflow tool e.g.:
>>> app.Plone.portal_workflow.getInfoFor(app.Plone.foo.bar, "review_state")

How to get a line number when parsing with QXmlDefaultHandler?

I am parsing a xml file with QXmlDefaultHandler like this:
void Parser::ParseFilename(const char* pFilename)
{
LOG_DEBUG("Parser::ParseFilename(%s)", pFilename);
ClearState();
m_inputFile.setFileName(pFilename);
QXmlInputSource source( &m_inputFile );
QXmlSimpleReader reader;
reader.setContentHandler( this );
reader.parse( source );
}
I need to know line numbers for error messages. How can I get them?
Use the exception that is passed to QXmlErrorHandler::error() function. You can set a custom error handler using QXmlReader::setErrorHandler().
Answering myself.
I was not completely clear in my question because I didn't write that I need to generate my own custom error messages. For example I need to make complicate validations of certain attributes. Then, if the attribute is invalid, I need to write message like: "Error on line 15454 column 48, attritubute 'number' should be a prime number but is 65536'.
The approach suggested by Ariya Hidayat works only for messages generated by the SAX parser itself (like malformed XML).
For custom messages I need to overload setDocumentLocator ( QXmlLocator * locator ) which tells me about the current locator, and save somewhere the value of the locator like savedLocator = locator; Actual line number is obtained by locator->lineNumber().

Resources