CGI Perl - changing action and mode variables - perl

I am somewhat of a novice with CGI Perl and am working on a web app that uses 'mode' and 'action' variables to determine which pages load.
$mode = param('mode');
$action = param('action');
if ($mode eq 'page1') {
if ($action 'eq') {
&performAction;
}
displayPage1;
}
elsif ($mode eq 'page2') {
&displayPage2
}
During development I have been having trouble figuring out the best way to set these variables when trying to navigate to different modes/actions after a form submit.
In some cases, putting a hidden value in the form will work
hidden(-name=>'action',-value=>'save')
but sometimes it will not. In case of the latter, putting param('action',"save") before the form will make the action change when the form is submitted.
I am unable to figure out why this happens though, are there factors that affect these two variables that I am unaware of?
What I now need to do is have two buttons on the same form, one which will just set the action to save the form data, and another which will save the form data but navigate to another mode/page with that form data.
If anyone could at least point me in the right direction for what I should be researching I would be greatly appreciative.

By default the CGI module implements a state-preserving behavior called "sticky" fields. The way this works is that if you are regenerating a form, the methods that generate the form field values will interrogate param() to see if similarly-named parameters are present in the query string. If they find a like-named parameter, they will use it to set their default values.
You want
hidden(-name=>'action', -value=>$new_value, -override=>1)
or
hidden(-name=>'action', -value=>'default_value')
param('hidden_name', $new_value);

This is a try , not sure if it would work.
Try setting hidden variable before button and changing it before every button, so the new value should be taken.
For ex:
<input type='hidden' name='op' value='save'/>
<input type='submit' name='Save Form' value='SaveForm'/>
<input type='hidden' name='op' value='submit'/>
<input type='submit' name='Submit Form' value='SubmitForm'/>
<input type='hidden' name='op' value='cancel'/>
<input type='submit' name='Cancel Form' value='CancelForm'/>
You can check for hidden variable 'op' in perl script.

Related

How do I check if a CGI button is pressed and then redirect them

It's hard to explain but for example. If I have something like this:
print submit(value => Exit), "\n";
how do I make it so that when the person presses that exit button in the browser, it will redirect them to a page that says for example "cya later".
You're using the HTML generation functions from CGI.pm. These have been deprecated for some time (and they were never a good idea). I recommend that you look at using a templating engine instead.
Having said that, the changes the you require are pretty simple. You'll need to change your code in two places. Firstly, you need to change the code that generates the submit button so that it adds a name to the generated HTML.
# Don't do this. Use a template.
# NB: Note the '-' I've added to the option names.
print submit(-value => 'Exit', -name => 'Exit'), "\n";
Then you'll need to change the code which deals with your incoming parameters to deal with this new parameter.
if (param('Exit')) {
print redirect('http://somewhere_else.com/');
exit;
}
Note that a) the redirection header should be printed instead of any other headers that you program might be planning to return (so, don't include a content-type header) and b) your program should exit soon after printing the redirection header - you shouldn't print any other output.
Update: It's also worth pointing out that one of the reasons for avoiding the HTML generation functions is that they are (necessarily) over-complicated to use. A good example is your usage of them:
print submit(value => Exit)
This is actually wrong as it produces this output:
<input type="submit" name="value" value="Exit" />
What I suspect you meant was:
print CGI->new->submit(-value => 'Exit')
Which gives this:
<input type="submit" name=".submit" value="Exit" />
As you haven't given an explicit name, the function has synthesised one (.submit) for you. In my example above I have given the input an explicit name:
print submit(-value => 'Exit', -name => 'Exit')
Which produces this:
<input type="submit" name="Exit" value="Exit" />
Give the submit button a name. It will then appear in params if it is used to submit the form.

ValidForm Builder file type validation

Does anybody know how to validate the file type input.
I have modified (hard coded) the class.vf_file.php input.
$strOutput .= "<input accept=\".pdf,.doc,.docx\" etc----/>\n";
This helps with Google Chrome, but Safari, Firefox ignore the modifications
Preventing users to submit the form if any other type of file is detected would be the ideal solution.
Thank you
I would recommend using a third party file uploading library like Plupload. We always use ValidForm Builder together with Plupload; works like a charm.
However you can use the meta array to implement custom attributes in the <input> tag without having to hardcode anything:
$objForm->addField(
"upload-document",
"Upload Document",
ValidForm::VFORM_FILE,
array(), // Validation array
array(), // Error handling array
array( // Meta array
"fielddata-extensions" => "pdf,doc,docx"
)
);
By prefixing meta keys with the 'field' prefix, you add that specific meta to the <input> field itself instead of it's wrapping <div class='vf__optional'></div>
The above example will output:
<div class="vf__optional">
<label for="upload-image">Upload Image</label>
<input type="hidden" name="MAX_FILE_SIZE" value="2097152">
<input type="file" value="" name="upload-image[]" id="upload-image" class="vf__file" data-extensions="pdf,doc,docx">
</div>
So using a combination of meta and a third party file upload handler, you can actually to pretty cool stuff.
That being said -- I must admit that the file upload field didn't get as much attention as the other field types lately.

AngularJS retrieve from object based on entry in ng-repeat input

This application is for running a writing contest.
Coodinators are assigning entries to judges for them to judge. I have three sets of data I retrieve from the server, a judge list, an entries list and an assignment list that ties the two together. There can be a variable number of input fields...if a judge has agreed to judge 4 entries, there will be 4 inputs...if 7, then 7.
I have all of that working OK, but only insofar as the entry number can be input and the data updated.
Now I would like confirm that the entryID IS a valid ID by checking the list and also to show a field or two on the screen so the coordinator knows that they typed in the right entry.
The relevant section of the HTML
<div ng-app>
<div id="assignment" ng-controller="AssignData" ng-init="JudgeID=107;CategorySelect='MS';PublishSelect='P'">
<div ng-show="loaded">
<form class="entryform ng-cloak" name="assignform" ng-submit="sendForm()">
<p>Entry numbers assigned to this judge</p>
<p ng-repeat="assign in (formassigns =(assigns | filter:AssignedJudge))">
<input type="text" ng-model="assign.entryid" required/>
{{entries.authorname}} {{entries.entrytitle}}
</p>
<button type="submit">Save Assignments</button>
<p>This will keep the assignments attached to this judge.
You will be able to send all of your assignments to all
of your judges when you are finished.</p>
</form>
</div>
</div>
</div>
The part that I haven't been able to figure out is how to make entries.authorname and entries.entrytitle show up when the user types in an entryid that is in entries.entryid.
assigns and entries are both arrays of records using JSON
assigns is JSON made up of assigns.id, assigns.judgeid, assigns.entryid.
entries is JSON made up of entries.entryid, entries.entrytitle, entries.authorname
When assigns arrives, entryid is empty. The form is used to fill in the entryid and when it is filled in, I'd like to be able to show next to it the title and authorname for that entry.
NOTE: I've added some important information at the end of this answer. So please read to the end before you decide what you're going to do.
You're going to have to do something that does the look up.
Also a few other changes I'd add, mostly so you can actually validate the items in your repeat.
(There's a summary of what I did after the psuedo code below).
<div ng-app>
<div id="assignment" ng-controller="AssignData"
ng-init="JudgeID=107;CategorySelect='MS';PublishSelect='P'">
<div ng-show="loaded">
<form class="entryform ng-cloak" name="assignform" ng-submit="sendForm()">
<p>Entry numbers assigned to this judge</p>
<p ng-repeat="assign in (formassigns =(assigns | filter:AssignedJudge))"
ng-form="assignForm">
<input type="text" ng-model="assign.entryid"
ng-change="checkEntryId(assign, assignForm)"
name="entryid" required/>
<span ng-show="assignForm.entryid.$error.required">required</span>
<span ng-show="assignForm.$error.validEntry">
{{assignForm.$error.validEntry[0]}}</span>
{{assign.entry.authorname}} {{assign.entry.entrytitle}}
</p>
<button type="submit">Save Assignments</button>
<p>This will keep the assignments attached to this judge.
You will be able to send all of your assignments to all
of your judges when you are finished.</p>
</form>
</div>
</div>
</div>
Then in your controller, you'd add a function like so (be sure to inject $http or a service you wrote to pull the values from the server):
$scope.checkEntryId = function(assign, form) {
$http.get('/CheckEntry?id=' + assign.entryid,
function(entry) {
if(entry) {
assign.entry = entry;
form.$setValidity('validEntry', true);
} else {
form.$setValidity('validEntry', false, 'No entry found with that id');
}
}, function() {
form.$setValidity('validEntry', true, 'An error occurred during the request');
console.log('an error occurred');
});
};
The basic idea above:
Use ng-form on your repeating elements to allow for validation of those dynamic parts.
Create a function that you can pass your item and your nested form to.
In that function, make your AJAX call to see if the entry is valid.
Check the validity based on the response, and call $setValidity on your nested form you passed to the function.
Use ng-show on a span (or something) in your nested form to show your validation messages.
Also, assign your checked entry to your repeated object for display purposes. (you could use a seperate array if you want, I suppose, but that would probably get unnecessarily complicated).
I hope that helps.
EDIT: Other thoughts
You might want to wrap your call in a $timeout or some sort of throttling function to prevent the entry id check from spamming yoru server. This is an implementation detail that's totally up to you.
If this is a check you do all over the place, you'll probably want to create a directive to do it. The idea would be very similar, but you'll do the check inside of a $parser on the ngModelController.
The method I showed above will still actually update the model's entryid, even if it's invalid. This is usually not a big deal. If it is, you'll want to go with what I suggested in "other thought #2", which is a custom validation directive.
If you need more information about validation via custom directives I did a blog entry on that a while back

Play 2.0 Nested forms: generate <input id="email"> instead of <input id="user_email">

Posted this to Play user group; I account for the sole view, so hoping to get a view, or perhaps even an answer ;-)
Nested forms are great, but there's one glitch that adds boilerplate to either javascript or scala templates.
For example, given:
#inputText(field = _form("user.email"),
'_label-> "Email Address*",
'class-> "required email",
'placeholder-> "jdoe#gmail.com"
)
the generated input field is something like:
<input id="user_email" name="user.email" ...>
Now, when you want to validate the email address client-side you then have to reference DOM id: $('#user_email')
Where $('#email') would be more natural.
I know I can set the id attrib manually in the template but would prefer to, by default, have the nested name (user in this case) stripped out from the id attrib.
Looking in github views helper directory, I am not finding where I can get access to the generated id (i.e. which file I need to overload and how).
Anyone know how to pull this off and/or have a better approach?
Here is where the field's ID is auto-generated:
https://github.com/playframework/Play20/blob/master/framework/src/play/src/main/scala/play/api/data/Form.scala#L274
There's not really any way you can override that behaviour, but you could write your own #inputText helper that strips the "user_" part from the ID when generating the HTML.
Basically copy-paste the default helper and replace
<input type="text" id="#id" ...
with your own code, e.g.
<input type="text" id="#processFieldId(id)" ...
or (untested!):
<input type="text" id="#(id.split('_').last)" ...
Then just import your custom helper in your template, and use it just like you would use #inputText.

get checkbox group values

This has driven me really bananas. It's so simple and easy and yet I can't figure out what's wrong with it.
I want to get my checkbox value populated in my controller (for testing purposes).
Here is my form.
<a href='#' name='submitForm'>submit the form</a>
//I have jquery attached to this tag and will submit the form when user clicks it
echo form_open('test/show');
echo form_checkbox('checkbox[]','value1');
echo form_checkbox('checkbox[]','value2');
echo form_checkbox('checkbox[]','value3');
echo form_checkbox('checkbox[]','value4');
echo "<input type='text' name='text1' value='ddd'>";
echo form_close();
//My controller test
public function show(){
$data1=$this->input->post('text1');
//I can get text1 value from input box
$data2=$this->input->post('checkbox');
//it keeps giving me undefined index 'checkbox'
$data3=$_POST['checkbox'];
//same error message
//WTH is going on here!!!!!
}
Please help. This thing drives me nuts! Thanks.
UPDATE:
Thanks for the help. To be more precisely, my submit button is a <a> tag and outside of form tag. It appear that I have to include <a> tag inside my form tag to make them works. Is that true?
A checkbox will not submit any data if it is unchecked as they're not considered successful (as per the w3c specification here)
If you actually tick the box and submit, it'll work - in fact it does, I've just tested it.
You need to wrap calls to $_POST in the isset() function.
if( isset( $_POST['checkbox'] ) ) {}
Calling $this->input->post('checkbox') shouldn't give you an undefined index error as the method deals with this eventuality. the Input::post() method returns false or the value of the checkbox.
Edit --
In response to your amendment to your question, you must use an element of type input with the type attribute set to submit in order to submit your form data without the use of Javascript etc. This button must be INSIDE the <form></form> which you are intending to submit.
<input type="submit" value="Submit">
The type="submit" causes the browser to send the data as submit event occurs. If you wish to use another element insider or outside of the form to do this you need to use Javascript. This however can be disabled on a per browser/user basis and isn't reliable as a result.
// Standard Javascript
<form name="myform"...
<a onclick="javascript:document.myform.submit();" href="javascript:void(0)">Submit</a>
// jQuery
$('#my-a-tag-submit-button').live( 'click', function() {
$('#my-form').submit();
}