user/edit form - and some formslib questions

user/edit form - and some formslib questions

by Martín Langhoff -
Number of replies: 19

So far the score is Formslib:1, MartinL: 0

black eye

Out in the wilderness of 1.5 I have a minor customisation to user/edit where if the user is of a particular kind (auth==='special plugin') the form does some special stuff with the email field. Students get free webmail if they want or if they choose an ext email address their emails are just sent there.

The special stuff looks like a series of radio-buttons, imagine the items below are radiobuttons:

  • External email: [Textfield with your current email]
  • Webmail address suggestion 1
  • Webmail address suggestion 2
  • Webmail address suggestion 3
  • Or [type your desired name]@students.teo.ac.nz

So far so good. This was accomplished with a high-tech if() in user/edit.

Enter formslib... (cue eerie scary music)

After tracing a few levels of indirection, thanks to grep I finally found where the email field is defined (editlib.php:useredit_shared_definition()). But I don't see any practical way of customising this, and doing this inside this function would be wrong as it doesn't have the user data.

I am at a loss on 2 counts:

  • I'd like to edit (and replace!) the email item of the form once the form object is instantiated and defined. Does formslib give me a means to address the field and change it?
  • How do I create combined radio + textedit areas with formslib? This can be boiled down to the classic case of radio buttons with the last one saying other and letting you type your answer in.

Any hints on how user/edit is meant to be generally customised outside of the user_info fields?

Average of ratings: -
In reply to Martín Langhoff

Re: user/edit form - and some formslib questions

by Martín Langhoff -

Update: formslib:3, MartinL: 0

  • Penny tells me I have to write a plugin to have a combined type of radio buttons + text fields. I think she's right.
  • QuickForms has hooks I can use to fiddle with the form: the removeElement() and insertElementBefore() methods. Alas, formslib doesn't want me to use them, and even if I use them, this is going to be very brittle.

I've read the docs in the Wiki, and now I am reading through the 1600 loc in formslib and 2k loc in QuickForm just to change user/edit a bit. mixed

Edit: is Penny right for all the cases? I can't argue with the benefits of libification of a popular data-entry type like date/time... but life is full of ad-hoc situations like these, that call for a bit of elbow grease and flexibility to add a field or two to the form.

In reply to Martín Langhoff

Re: user/edit form - and some formslib questions

by Penny Leach -
Penny is always right.


ok, maybe not
In reply to Martín Langhoff

Re: user/edit form - and some formslib questions

by Jamie Pratt -
"Penny tells me I have to write a plugin to have a combined type of radio buttons + text fields. I think she's right."

You can use a group for this. See the addGroup method.
In reply to Jamie Pratt

Re: user/edit form - and some formslib questions

by Martín Langhoff -

With some more help from Jamie I'm mostly on my way. Looks like

  • The place to work on is user/edit_form.php:definition_after_data()
  • I might have to use QF's removeElement() and insertElementBefore()
  • Jamie pointed out addGroup()
  • I need more coffee
In reply to Martín Langhoff

Re: user/edit form - and some formslib questions

by James Dugal -
I am hoping you figure it out, and let us after-hours hackers in on the tricks! Perhaps the Moodle lib interface to Forms needs a few extensions, as I too wanted to modify a form, but only from the validation() function: under certain conditions, I want to add a radio box and reprint the form with an error message, where clicking the
new radio box is a new way to resolve the noted problem. I got very lost in all the levels of indirection! I need more coffee too, or maybe scotch?
--James
In reply to Martín Langhoff

Re: user/edit form - and some formslib questions

by Luke Hudson -
I'm still working my way through the formslib and QF. documentation & code, so forgive me if this question is a little RTFM-worthy....
I'm working on converting some custom code which was based on the old moodle course/edit.html page into formslib code, but the code has a javascript onChange event handler for a SELECT element, and I'm not sure how best to do this with formslib.
Any suggestions?

PS: I'd definitely second Martín on the coffee issue.

Cheers,
- L
In reply to Luke Hudson

Re: user/edit form - and some formslib questions

by Jamie Pratt -
Hi Luke,

You can add an onchange attribute to the select element using a fifth param for addElement :

$mform->addElement('select', 'type', get_string('forumtype', 'forum'), $FORUM_TYPES, $attributes);

Your fifth parameter here can be a string or array of attributes for the element. The following are equivalent :

 $attributes='onchange="blahblah"';
 $attributes=array('onchange'=>'blahblah');

Make sure your js is not doing something formlib could do for you. We have js for client side validation and for disabling controls based on the state of one or more 'master' controls. See disabledIf docs.

Jamie

In reply to Jamie Pratt

Re: user/edit form - and some formslib questions

by Luke Hudson -
Thanks Jamie, I'd missed that. I'm trying to port some code from Moodle 1.5.x-era which uses .js to populate other form fields based on the choice from a SELECT element.

It sounds like this is still a valid use of .js even with formlib, but I will keep reading the docs ;)

Cheers,
- Luke

In reply to Luke Hudson

Re: user/edit form - and some formslib questions

by Jamie Pratt -
You and probably Martin also should be aware that formslib uses the form definition to clean submitted form data. For the select, radio and checkbox elements this means that if the submitted data doesn't match an options value defined in definition then it won't be accepted, the default value for the element will be used instead.
In reply to Martín Langhoff

Re: user/edit form - and some formslib questions - patch

by Martín Langhoff -
Jamie - I'm finding that in most cases definition_after_data() is being called twice, and the first one is with null data.

Does the patch below make sense to you?
http://git.catalyst.net.nz/gitweb?p=moodle-r2.git;a=commitdiff_plain;h=637d8859e83786cc52926dcbc407b5ad8548c7a2;hp=9fced069eccc2cc7195a75560aed631b0b3436b0

(Happy to commit it to CVS, but just getting into the logic of formslib.)
In reply to Martín Langhoff

Re: user/edit form - and some formslib questions - patch

by Jamie Pratt -
About the patch Martin, it is not a good idea I think. The primary purpose of definition_after_data is to change the form definition dependent on data from set_data that has been loaded into the form not $customdata.

$customdata is available in definition method. If you're changing the structure of a form based on $customdata that should be happening in the defintion method.

definition_after_data should only be used as a last resort.
In reply to Jamie Pratt

Re: user/edit form - and some formslib questions - patch

by Martín Langhoff -

The primary purpose of definition_after_data is to change the form definition dependent on data from set_data that has been loaded into the form not $customdata.

Yes - that's what I understood as well. However... have a look at the context of the patch. It is being called in the object constructor. The result is that definition_after_data() gets called more than half of the times before set_data() is called.

That is because there are 2 scenarios:

  • If the form is "empty" of data (new user, for example), set_data() is never called. So definition_after_data() is called during the constructor "on empty". IT shouldn't.
  • If the form has data to load, it will be called in the constructor and again in set_data()

Right now we have a (minor) bug in the user/edit form because definition_after_data() assumes id/userid is set. I think that overall teh right fix is to not call it in the constructor unless we have data.

OTOH, If we never have form data in the constructor, then we should never call it there? smile

In reply to Martín Langhoff

Re: user/edit form - and some formslib questions - patch

by Jamie Pratt -
Hi Martin,

The call in the constructor to data_after_definition is to be able to change the form dependent on data that has been loaded into the form. In the constructor this data is submitted data. data_after_definition is called whenever data loaded into the form is changed. We could put a condition in there to test if the form has submitted data. But I think it is better that code in data_after_definition should be able to be cope with being called several times as data is loaded into the form from different sources.

Jamie
In reply to Jamie Pratt

Re: user/edit form - and some formslib questions - patch

by Martín Langhoff -
> In the constructor this data is submitted data.

Ah! Ok - that's what I was missing entirely. So the code is walked again on submit -- I thought we were saving all this (rather expensive) state somewhere (like in the session, but that'd carry a lot of problems), and never thought of the constructor being invoked on submit with the data in existence.

Fair enough then. So the bug is squarely in the user/edit form. I'll fix it there.
In reply to Martín Langhoff

Re: user/edit form - more and more questions

by Martín Langhoff -

Advancing very slowly on getting what I want out of formslib.

Perhaps it helps explaining what I'm trying to achieve. The attached screenshot shows a user/edit page in 1.5, that when editing a "student" user acct, the options are to

  • use your external, existing email address
  • Use webmail with one of X suggested email addresses
  • Use webmail with an address you supply

The internals of how we make that happen are a bit complex, but the form changes in 1.5 are trivial, and it looks reasonably good.

With formslib I've managed to replace the "email" form element "in-place" using the removeElement/insertElementBefore trick. But a few things have me stumped...

  • How do I do the top radio button?
  • How do I do the bottom one, specially with text right after the input element?
  • Is there a way to target the combination of input field and associated label? As I am also having problems getting the fields displayed well -- the 'separator' parameter isn't all that useful for what I need to do here. Perhaps I can work on the CSS here.

{I still feel I am losing the battle with MF/QF. Jamie -- your help's been great so far... maybe a few more hints? wink }

Note: several post-facto edits wink

Attachment Screenshot-Online_Campus_Edit_profile_-_Mozilla_Firefox.png
In reply to Martín Langhoff

Re: user/edit form - more and more questions

by Martín Langhoff -
I started exploring the path of creating a new class for this.

The rationale is that just handling the display seems to be tricky, and that the rules for populating those values and validating them later are also a bit tricky (nothing out-of-the ordinary, but we store just the email value and then at form build/display we preselect the right radio button according to whether it's @student... , etc).

But the fact is that even if I create a new element type (derived from the group class), I don't have _any_ control of the inner HTML (to do that nice input box right next to an '@student.openpoly' bit) unless I subclass or use my own classes all they way down to the renderer. So I ended up reading the renderer code... which is a bunch of static vars and str_replace calls. Hmmmm!?
In reply to Martín Langhoff

Re: user/edit form - more and more questions

by Jamie Pratt -
I think the way I might do this is to use a select box and two text boxes. The text boxes would be disabled and the respective boxes would be only enabled when 'Use an Existing Email Address' or 'New Email Address at @student....' This would be easy to do with disabledIf.

The text box with '@student....' following it could be done with a 'group' with a static element to contain the text.

Can you modify user/edit.php to pass the userid or what other data you need into the form and then set everything up in definition() where you can access customdata. I think this would be OK.


In reply to Jamie Pratt

Re: user/edit form - more and more questions

by Martín Langhoff -
Thanks! -- it kind of makes sense, I'll get to try it tomorrow. Almost midnight here and I'm no longer making sense...