Plone custom edit/add form using Dexterity/Grok - plone

I am trying to create a custom edit/add form for my form package that I built using Dexterity types. I am following the Schema-driven types tutorial on the Plone Developer Documentation in the Dexterity Developer Manual section. So far I have successfully created a Dexterity package with two content types: FAQ and Question. My FAQ Dexterity content type is a container, and my Question Dexterity content type can only be added within my FAQ container.
FAQ Content Type - FAQ.py
from product.faq import MessageFactory as _
from five import grok
from plone.dexterity.content import Container
from plone.directives import dexterity, form
from zope import schema
from zope import interface
from Acquisition import aq_inner
from Products.CMFCore.utils import getToolByName
from product.faq.question import IQuestion
class IFAQ(form.Schema):
""" Project FAQ Container """
class FAQ(Container):
grok.implements(IFAQ)
class View(grok.View):
""" FAQ View Class """
grok.context(IFAQ)
grok.require('zope2.View')
def questions(self):
""" Return a catalog search result of questions to show """
context = aq_inner(self.context)
catalog = getToolByName(context, 'portal_catalog')
return catalog(object_provides=IQuestion.__identifier__,
path='/'.join(context.getPhysicalPath()),
sort_on='sortable_title')
Question Type - Question.py
from product.faq import MessageFactory as _
from five import grok
from plone.dexterity.content import Container
from plone.directives import dexterity, form
from zope import schema
from zope import interface
class IQuestion(form.Schema):
""" Project FAQ Question Type """
title = schema.TextLine(
title=_(u"Question"),
)
answer = schema.TextLine(
title=_(u"Answer"),
)
# Used to group questions into sections
section = schema.TextLine(
title=_(u"Section"),
)
class Question(Container):
grok.implements(IQuestion)
class Edit(grok.Form):
""" FAQ Question Edit Class """
grok.context(IQuestion)
grok.require('zope2.View')
I need to be able to customize the form markup for both the add and edit views. So far I have been able to create an Edit view with no functionally. How do I add the functionally back to my form?
edit.py
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal"
xmlns:i18n="http://xml.zope.org/namespaces/i18n"
lang="en"
metal:use-macro="context/main_template/macros/master"
i18n:domain="product.faq">
<body>
<metal:main fill-slot="content-core">
<metal:content-core define-macro="content-core">
<h2>This is a Edit Form</h2>
<ul class="list-unstyled">
<li><strong>Plone's Title is: </strong><i tal:content="context/Title"></i></li>
<li><strong>Plone's Description is: </strong><i tal:content="context/Description"></i></li>
</ul>
<form class="form-horizontal clearfix" role="form">
<div class="form-group">
<label for="faqQuestion" class="col-lg-2 control-label">Question</label>
<div class="col-lg-10">
<input tal:attributes="value context/title" type="textfield" class="form-control" id="faqQuestion" placeholder="Enter a question">
</div>
</div>
<div class="form-group">
<label for="faqAnswer" class="col-lg-2 control-label">Answer</label>
<div class="col-lg-10">
<input tal:attributes="value context/answer" type="textfield" class="form-control" id="faqAnswer" placeholder="Enter a answer">
</div>
</div>
<div class="form-group">
<label for="faqSection" class="col-lg-2 control-label">Section</label>
<div class="col-lg-10">
<input tal:attributes="value context/section" type="textfield" class="form-control" id="faqSection" placeholder="Enter a grouping">
</div>
</div>
<div class="btn-group pull-right">
Save
Cancel
</div>
</form>
</metal:content-core>
</metal:main>
</body>
</html>
My other issue is that how do I create a custom add view? When I click add my url points to http://localhost:8080/demo/faq/++add++product.faq.question. Is the ++add++ denoting that it is a widget?
Thanks in advance.

For your custom dexterity add/edit form, you might want to read the dexterity documentation at http://docs.plone.org/external/plone.app.dexterity/docs/advanced/custom-add-and-edit-forms.html

Please go through following url, I followed it for my custom add and edit view, and it worked http://developer.plone.org/content/dexterity.html#custom-add-form-view
Note: It has DefaultAddForm, DefaultAddView base class for custom AddForm

Related

ASP.NET Core 2.1 strange submenu appears in Manage page

This is my Manage page that was generated by the Scaffolded Identity.
Previously this weird Manage your account header with the subheading: Change your account settings and menu did not appear.
The only thing that showed was the form in the image and the Manage Account header located directly above the form but I'm not sure why suddenly this extra header and menu appears. I've tried searching on the page itself but it doesn't show up in the code. Is this a configuration somewhere?
EDIT:
Added structure of Manage
You can try to open the file _Layout.cshtml in the folder Manage, maybe there is something like this:
<h2>Manage your account</h2>
<div>
<h4>Change your account settings</h4>
<hr />
<div class="row">
<div class="col-md-3">
<partial name="_ManageNav" />
</div>
<div class="col-md-9">
#RenderBody()
</div>
</div>
</div>
#section Scripts {
#RenderSection("Scripts", required: false)
}
Based on your update, I think the problem comes from the way you add scaffolded item.
I suggest you to follow these steps to add all scaffolded items, then deleting which one you don't use.
Right-click on the solution -> Add -> New Scaffolded Item... -> Identity -> Identity -> Add
Check with the option: Override all files.
In the layout input, clicking on the ... button to select the default layout:
In the Data context class input, clicking on + button to create new context class:
After scaffolding, you will receive all the files:
Lastly, you can open the file _Layout.cshtml to check the title, the file content could be:
#{
Layout = "/Areas/Identity/Pages/_Layout.cshtml";
}
<h2>Manage your account</h2>
<div>
<h4>Change your account settings</h4>
<hr />
<div class="row">
<div class="col-md-3">
<partial name="_ManageNav" />
</div>
<div class="col-md-9">
#RenderBody()
</div>
</div>
</div>
#section Scripts {
#RenderSection("Scripts", required: false)
}

What is the best way to validate input (Skeleton + Vue.js)?

I realy like this tools, because of their simplicity and compactness. But sometimes I face lack of plugins / snippets (my JS skills also pretty weak, usually I'm backend developer), so here's one of those cases:
For example, I have simple form like this one:
<div class="row">
<div class="six columns">
<label for="email">Contact email*</label>
<input v-model="email" class="u-full-width" type="email" id="email" placeholder="admin#exchange.com" name="email">
</div>
<div class="six columns">
<label for="date">Launch date*</label>
<input v-model="date" class="u-full-width" type="text" id="date" placeholder="June, 2014">
</div>
</div>
As you can see, I want to make this fields required, email input should be in email format, something like ***#***.***, date field can be anything.
What is the best way to realize it? Also I've found 3 Vue plugins, who's your favorite?
vue-validator
vee-validate
vue-form
Thanks for any examples/snippets/etc
Since you come from backend and are not expert with JS ( neither am I :D ) I suggest you do it yourself. You will learn more.
This is how I would do it:
<input name="email" v-model="email" #keyup="validateEmail()" />
... vue component or instance ...
data: function() { // if you are doing this on Vue instance and not component then data property will look differently but if in component it has to be a function that returns an object
return {
email: ""
}
},
methods: {
validateEmail: function() {
console.log(this.email)
// here you have access to email input as it changes so you can use either regex or substring or some other string manipulation to determine if the string satisfies your criteria
}

AngularJS ng-style with ng-model scope variable

I'm building a website to let users design their own mobile app. One side of the website has a form that the user fills out, and the other side has a live preview of the app based off of the form data. This is how it is set up:
Controller:
$scope.primaryColor = "#325490";
Form Input:
<!-- Primary Color -->
<div class="form-group">
<label for="primaryColor" class="col-sm-2 control-label">Primary:</label>
<div class="col-sm-10">
<input type="text" class="form-control" ng-model="$parent.primaryColor">
</div>
</div>
Live Preview:
<div class="mockView" ng-style="{'background-image':'url({{$parent.backgroundFile}})', 'background-color':'{{$parent.primaryColor}}'}">
I have to use $parent because I am using ng-include on my index page to include the form.htm and preview.htm files. I have tested the form and I know it is changing all of the scope variables that I have, but the previewer is not changing. Any help is appreciated!
Remove {{}} and '' and it should work, like this:
<div class="mockView" ng-style="{'background-image':'url({{$parent.backgroundFile}})', 'background-color': $parent.primaryColor }">

Meteor.js : add a date picker to a page

I'm trying hard to get a date picker on my meteor application. I tested lot of packages found by googling, but none seems to work, or, probably, I'm doing it wrong. Can someone tell me clearly how to add a date picker?
Thanks a lot!
I would use the bootstrap 3 package and the bootstrap 3 date time picker package:
$ meteor add twbs:bootstrap
$ meteor add tsega:bootstrap3-datetimepicker
Then, simply add the form in your HTML file:
<template name="myTemplate">
<form>
<div class="form-group">
<label for="myInput">Input name</label>
<div class="input-group datetimepicker">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
<input name="idInput" id="idInput" class="set-due-date form-control" type="text"/>
</div>
</div>
</form>
</template>
And in your corresponding JavaScript file, you have to initialize your date time picker:
Template.myTemplate.rendered = function(){
$('.datetimepicker').datetimepicker();
};
And if you have different date input in your page (for example):
Template.myTemplate.rendered = function(){
$('.datetimepicker').each(function(){
$(this).datetimepicker();
});
}
Finally, I invite you to read the Documentation if you need more options (like date format etc).
Hope it will help you!
I use the one from eonasdon (Bootstrap required) and you get get it prepackaged by doing this:
meteor add hujhax:bootstrap3-datetimepicker
I have these packages added
twbs:bootstrap
jquery
rajit:bootstrap3-datepicker
and used this in the template
<div class="box-body no-padding">
<!--The calendar -->
<div id="calendar" style="width: 100%"></div>
</div><!-- /.box-body -->
and added helper below
Template.myCalendar.rendered = function() {
$('#calendar').datepicker();
}

CSS styling in Django forms

I would like to style the following:
forms.py:
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
email = forms.EmailField(required=False)
message = forms.CharField(widget=forms.Textarea)
contact_form.html:
<form action="" method="post">
<table>
{{ form.as_table }}
</table>
<input type="submit" value="Submit">
</form>
For example, how do I set a class or ID for the subject, email, message to provide an external style sheet to?
Taken from my answer to:
How to markup form fields with <div class='field_type'> in Django
class MyForm(forms.Form):
myfield = forms.CharField(widget=forms.TextInput(attrs={'class': 'myfieldclass'}))
or
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.fields['myfield'].widget.attrs.update({'class': 'myfieldclass'})
or
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
widgets = {
'myfield': forms.TextInput(attrs={'class': 'myfieldclass'}),
}
--- EDIT ---
The above is the easiest change to make to original question's code that accomplishes what was asked. It also keeps you from repeating yourself if you reuse the form in other places; your classes or other attributes just work if you use the Django's as_table/as_ul/as_p form methods. If you need full control for a completely custom rendering, this is clearly documented
-- EDIT 2 ---
Added a newer way to specify widget and attrs for a ModelForm.
This can be done using a custom template filter. Consider rendering your form this way:
<form action="/contact/" method="post">
{{ form.non_field_errors }}
<div class="fieldWrapper">
{{ form.subject.errors }}
{{ form.subject.label_tag }}
{{ form.subject }}
<span class="helptext">{{ form.subject.help_text }}</span>
</div>
</form>
form.subject is an instance of BoundField which has the as_widget() method.
You can create a custom filter addclass in my_app/templatetags/myfilters.py:
from django import template
register = template.Library()
#register.filter(name='addclass')
def addclass(value, arg):
return value.as_widget(attrs={'class': arg})
And then apply your filter:
{% load myfilters %}
<form action="/contact/" method="post">
{{ form.non_field_errors }}
<div class="fieldWrapper">
{{ form.subject.errors }}
{{ form.subject.label_tag }}
{{ form.subject|addclass:'MyClass' }}
<span class="helptext">{{ form.subject.help_text }}</span>
</div>
</form>
form.subjects will then be rendered with the MyClass CSS class.
If you don't want to add any code to the form (as mentioned in the comments to #shadfc's Answer), it is certainly possible, here are two options.
First, you just reference the fields individually in the HTML, rather than the entire form at once:
<form action="" method="post">
<ul class="contactList">
<li id="subject" class="contact">{{ form.subject }}</li>
<li id="email" class="contact">{{ form.email }}</li>
<li id="message" class="contact">{{ form.message }}</li>
</ul>
<input type="submit" value="Submit">
</form>
(Note that I also changed it to a unsorted list.)
Second, note in the docs on outputting forms as HTML, Django:
The Field id, is generated by
prepending 'id_' to the Field name.
The id attributes and tags are
included in the output by default.
All of your form fields already have a unique id. So you would reference id_subject in your CSS file to style the subject field. I should note, this is how the form behaves when you take the default HTML, which requires just printing the form, not the individual fields:
<ul class="contactList">
{{ form }} # Will auto-generate HTML with id_subject, id_email, email_message
{{ form.as_ul }} # might also work, haven't tested
</ul>
See the previous link for other options when outputting forms (you can do tables, etc).
Note - I realize this isn't the same as adding a class to each element (if you added a field to the Form, you'd need to update the CSS also) - but it's easy enough to reference all of the fields by id in your CSS like this:
#id_subject, #id_email, #email_message
{color: red;}
Per this blog post, you can add css classes to your fields using a custom template filter.
from django import template
register = template.Library()
#register.filter(name='addcss')
def addcss(field, css):
return field.as_widget(attrs={"class":css})
Put this in your app's templatetags/ folder and you can now do
{{field|addcss:"form-control"}}
You can do like this:
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
subject.widget.attrs.update({'id' : 'your_id'})
Hope that works.
Ignas
You could use this library: https://pypi.python.org/pypi/django-widget-tweaks
It allows you to do the following:
{% load widget_tweaks %}
<!-- add 2 extra css classes to field element -->
{{ form.title|add_class:"css_class_1 css_class_2" }}
Write your form like:
class MyForm(forms.Form):
name = forms.CharField(widget=forms.TextInput(attr={'class':'name'}),label="Your Name")
message = forms.CharField(widget=forms.Textarea(attr={'class':'message'}), label="Your Message")
In your HTML field do something like:
{% for field in form %}
<div class="row">
<label for="{{ field.name}}">{{ field.label}}</label>{{ field }}
</div>
{% endfor %}
Then in your CSS write something like:
.name{
/* you already have this class so create it's style form here */
}
.message{
/* you already have this class so create it's style form here */
}
label[for='message']{
/* style for label */
}
Hope this answer is worth a try! Note you must have written your views to render the HTML file that contains the form.
You can do:
<form action="" method="post">
<table>
{% for field in form %}
<tr><td>{{field}}</td></tr>
{% endfor %}
</table>
<input type="submit" value="Submit">
</form>
Then you can add classes/id's to for example the <td> tag. You can of course use any others tags you want. Check Working with Django forms as an example what is available for each field in the form ({{field}} for example is just outputting the input tag, not the label and so on).
Didn't see this one really...
https://docs.djangoproject.com/en/1.8/ref/forms/api/#more-granular-output
More granular output
The as_p(), as_ul() and as_table() methods are simply shortcuts for lazy developers – they’re not the only way a form object can be displayed.
class BoundField
Used to display HTML or access attributes for a single field of a Form instance.
The str() (unicode on Python 2) method of this object displays the HTML for this field.
To retrieve a single BoundField, use dictionary lookup syntax on your form using the field’s name as the key:
>>> form = ContactForm()
>>> print(form['subject'])
<input id="id_subject" type="text" name="subject" maxlength="100" />
To retrieve all BoundField objects, iterate the form:
>>> form = ContactForm()
>>> for boundfield in form: print(boundfield)
<input id="id_subject" type="text" name="subject" maxlength="100" />
<input type="text" name="message" id="id_message" />
<input type="email" name="sender" id="id_sender" />
<input type="checkbox" name="cc_myself" id="id_cc_myself" />
The field-specific output honors the form object’s auto_id setting:
>>> f = ContactForm(auto_id=False)
>>> print(f['message'])
<input type="text" name="message" />
>>> f = ContactForm(auto_id='id_%s')
>>> print(f['message'])
<input type="text" name="message" id="id_message" />
One solution is to use JavaScript to add the required CSS classes after the page is ready. For example, styling django form output with bootstrap classes (jQuery used for brevity):
<script type="text/javascript">
$(document).ready(function() {
$('#some_django_form_id').find("input[type='text'], select, textarea").each(function(index, element) {
$(element).addClass("form-control");
});
});
</script>
This avoids the ugliness of mixing styling specifics with your business logic.
You may not need to override your form class' __init__, because Django sets name & id attributes in the HTML inputs. You can have CSS like this:
form input[name='subject'] {
font-size: xx-large;
}
There is a very easy to install and great tool made for Django that I use for styling and it can be used for every frontend framework like Bootstrap, Materialize, Foundation, etc. It is called widget-tweaks Documentation: Widget Tweaks
You can use it with Django's generic views
Or with your own forms:
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
email = forms.EmailField(required=False)
message = forms.CharField(widget=forms.Textarea)
Instead of using default:
{{ form.as_p }} or {{ form.as_ul }}
You can edit it your own way using the render_field attribute that gives you a more html-like way of styling it like this example:
template.html
{% load widget_tweaks %}
<div class="container">
<div class="col-md-4">
{% render_field form.subject class+="form-control myCSSclass" placeholder="Enter your subject here" %}
</div>
<div class="col-md-4">
{% render_field form.email type="email" class+="myCSSclassX myCSSclass2" %}
</div>
<div class="col-md-4">
{% render_field form.message class+="myCSSclass" rows="4" cols="6" placeholder=form.message.label %}
</div>
</div>
This library gives you the opportunity to have well separated yout front end from your backend
In Django 1.10 (possibly earlier as well) you can do it as follows.
Model:
class Todo(models.Model):
todo_name = models.CharField(max_length=200)
todo_description = models.CharField(max_length=200, default="")
todo_created = models.DateTimeField('date created')
todo_completed = models.BooleanField(default=False)
def __str__(self):
return self.todo_name
Form:
class TodoUpdateForm(forms.ModelForm):
class Meta:
model = Todo
exclude = ('todo_created','todo_completed')
Template:
<form action="" method="post">{% csrf_token %}
{{ form.non_field_errors }}
<div class="fieldWrapper">
{{ form.todo_name.errors }}
<label for="{{ form.name.id_for_label }}">Name:</label>
{{ form.todo_name }}
</div>
<div class="fieldWrapper">
{{ form.todo_description.errors }}
<label for="{{ form.todo_description.id_for_label }}">Description</label>
{{ form.todo_description }}
</div>
<input type="submit" value="Update" />
</form>
For larger form instead of writing css classed for every field you could to this
class UserRegistration(forms.ModelForm):
# list charfields
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'email', 'password', 'password2')
def __init__(self, *args, **kwargs):
super(UserRegistration, self).__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].widget.attrs['class'] = 'form-control'
Edit: Another (slightly better) way of doing what I'm suggesting is answered here: Django form input field styling
All the above options are awesome, just thought I'd throw in this one because it's different.
If you want custom styling, classes, etc. on your forms, you can make an html input in your template that matches your form field. For a CharField, for example, (default widget is TextInput), let's say you want a bootstrap-looking text input. You would do something like this:
<input type="text" class="form-control" name="form_field_name_here">
And as long as you put the form field name matches the html name attribue, (and the widget probably needs to match the input type as well) Django will run all the same validators on that field when you run validate or form.is_valid() and
Styling other things like labels, error messages, and help text don't require much workaround because you can do something like form.field.error.as_text and style them however you want. The actual fields are the ones that require some fiddling.
I don't know if this is the best way, or the way I would recommend, but it is a way, and it might be right for someone.
Here's a useful walkthrough of styling forms and it includes most of the answers listed on SO (like using the attr on the widgets and widget tweaks).
https://simpleisbetterthancomplex.com/article/2017/08/19/how-to-render-django-form-manually.html
Styling widget instances
If you want to make one widget instance look different from another, you will need to specify additional attributes at the time when the widget object is instantiated and assigned to a form field (and perhaps add some rules to your CSS files).
https://docs.djangoproject.com/en/2.2/ref/forms/widgets/
To do this, you use the Widget.attrs argument when creating the widget:
class CommentForm(forms.Form):
name = forms.CharField(widget=forms.TextInput(attrs={'class': 'special'}))
url = forms.URLField()
comment = forms.CharField(widget=forms.TextInput(attrs={'size': '40'}))
You can also modify a widget in the form definition:
class CommentForm(forms.Form):
name = forms.CharField()
url = forms.URLField()
comment = forms.CharField()
name.widget.attrs.update({'class': 'special'})
comment.widget.attrs.update(size='40')
Or if the field isn’t declared directly on the form (such as model form fields), you can use the Form.fields attribute:
class CommentForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['name'].widget.attrs.update({'class': 'special'})
self.fields['comment'].widget.attrs.update(size='40')
Django will then include the extra attributes in the rendered output:
>>> f = CommentForm(auto_id=False)
>>> f.as_table()
<tr><th>Name:</th><td><input type="text" name="name" class="special" required></td></tr>
<tr><th>Url:</th><td><input type="url" name="url" required></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" size="40" required></td></tr>
I was playing around with this solution to maintain consistency throughout the app:
def bootstrap_django_fields(field_klass, css_class):
class Wrapper(field_klass):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def widget_attrs(self, widget):
attrs = super().widget_attrs(widget)
if not widget.is_hidden:
attrs["class"] = css_class
return attrs
return Wrapper
MyAppCharField = bootstrap_django_fields(forms.CharField, "form-control")
Then you don't have to define your css classes on a form by form basis, just use your custom form field.
It's also technically possible to redefine Django's forms classes on startup like so:
forms.CharField = bootstrap_django_fields(forms.CharField, "form-control")
Then you could set the styling globally even for apps not in your direct control. This seems pretty sketchy, so I am not sure if I can recommend this.

Resources