• How to add custom editable fields to the user profile

last modified March 23, 2009 by ejucovy

> I tried searching for a function getFullname but can't find it anywhere.

> Wondering where to go and add my own setCityName and getCityName function

For any field "Foo", the "getFoo" and "setFoo" functions are
automatically generated by default.  (You can try googling for
something like "plone archetypes field accessor mutator" to find out
more -- though I couldn't find any good reference just now...)

So you probably don't actually need to add your own setCityName and
getCityName methods -- they should Just Work.

> From what I have found out this is the file which displays user information
> opencore/src/opencore/opencore/member/browser/profile.pt
> In this what do I need to provide in place of this
> <tal:city tal:define="fn memberdata/fullname" tal:condition="python:fn or view.viewingself()">

You'll actually need to add code in three places, I'm afraid.

1) First, you need to add a "cityname" key to the "memberdata"
dictionary that's being accessed in that profile template.  In
opencore/src/opencore/
opencore/browser/base.py, look for `def
member_info_for_member`.  There's a big code block in it that looks
like:
{{{
           result.update(
                   id          = id,
               Title       = member.Title(),
               fullname    = member.getFullname(),
[...]
               favorites   = member.getFavorites(),
               anon_email  = member.getUseAnonByDefault(),
               )
}}}

Add a line like  "cityname = member.getCityName()" to that code block.

2) Now you can render the field on the profile, back in
opencore/src/opencore/opencore/member/browser/profile.pt.  There's a
convenience function called "field_snippet" for generating a whole
widget for a field, including the "edit" link; search for
"field_snippt" in the profile.pt template to see it used.  Try adding
template code somewhere like:
{{{
       <span tal:replace="structure python:
           view.field_snippet(title='City',
                              value=memberdata.get('cityname'),
                              anchor='cityname')" />
}}}

3) Finally, you'll also need to add template code to the
profile-edit.pt file in the same directory.  Note the capitalizations
of "cityname" and "CityName" in the following code -- there's some
fragile code in the profile's save handler
(opencore/src/opencore/member/browser/view.py, the "handle_form"
function) that makes some assumptions about how fields are
capitalized.
{{{
                 <label i18n:translate="profile-edit_cityname_label"
for="CityName">City</label><br />
                 <input type="text" id="CityName" name="CityName"
                   class="oc-autoFocus"
                   tal:attributes="value request/CityName |
memberdata/cityname;
                                   class python:
request.form.get('field') == 'cityname' and default or nothing" />
}}}

Once you've done those steps, you ought to be able to edit and view
the field through the profile.

> I added the code in opencore/src/opencore-bundle/remember/content/member_schema.py

Small note -- it's not important, but you might want to move your code
to opencore/src/opencore/content/member.py instead.  (Look for
"nuischema")  It's a bit closer to the top of the stack, so it'll
probably be a little easier to keep track of there.

> I am trying to use SelectionWidget to show a drop down

OpenCore
overrides the entire Plone UI layer, so even if you get the Archetypes
widgets like SelectionWidget properly set up, they won't actually show
up.

We don't currently have any particularly good way to autogenerate
input widgets like this.  The most straightforward thing I can
recommend for now is some more modifications:
 1. hand-coding an HTML <SELECT> field in the template at
opencore/src/opencore/

opencore/member/browser/profile-edit.pt
 2. adding custom validation code to enforce your vocabulary set (if
you want to) when the form is submitted to the server.  The code that
you'll want to modify is in
opencore/src/opencore/opencore/member/browser/view.py -- look for
`handle_form` around L193, in the ProfileEditView class.  You can add
your validation code anywhere above the line "if validation_failed:
return" of course :) -- it should be pretty straightforward, here's a
commented example to give you the sense of it:
{{{
role = form.get('role')  # get the "role" value from the submitted form
if role not in ["a", "b", "c", "d", "e"]:
 error_msg = "%s is not a valid role" % role
 self.addPortalStatusMessage(error_msg)  # when the form is
re-rendered for the user, the error message will now appear in the
"status message" bar on the top of the screen
  validation_failed = True

}}}