How to populate zend form field using session? - zend-framework

I am using sessions to populate a multi select box with options in my Zend application.
The user selects one or more options and fills in other fields on the form and then submits. If the user didn't select all of the options in the multi select then the form is displayed again but the multi select only has the options that the user did not select the last time. This process goes on until there are no more options from the multi select left to process.
Here is the code I use to get rid of the options that have already been processed so that they are not used to populate the multi select box:
if($form_successful){
// TODO remove $post['keyword_names'] (i.e. already processed) from $keyword_names (that come from $_SESSION)
$keyword_names = array_diff($keyword_names, $post['keyword_names']);
print_r($keyword_names);
if(is_array($keyword_names) && !empty($keyword_names)){
// save updated $keyword_names into $_SESSION['workflow1']
$session = new Zend_Session_Namespace('workflow1');
$session->keyword_names = $keyword_names;
// set flag to false so that we display form again
$form_successful = false;
}else{ // all keywords have been assigned
// go to next step
$this->_redirect('/workflow-1/step-'.($step+1).'/');
}
}
print_r($keyword_names); displays the correct options, however when the form is loaded when the user submits, the multi select displays the options that were there from the begining ie the options the user has just selected and submitted are not being taken out of the multi select, it is only when the user submits the form again then the multi select box updates.
Appreciate the help.

Solved the issue by making use of URL parameters. Here is the code (might differ a lot from what I posted first because some big changes were made):
// after successful form submission
if($form_successful){
// remove $post['keyword_names'] (i.e. already processed) from $keyword_names (that come from $_SESSION)
$keyword_names = array_diff($keyword_names, $post['keyword_names']);
// save remaining $keyword_names into $_SESSION['workflow1']
$session = new Zend_Session_Namespace('workflow1');
$session->keyword_names = $keyword_names;
if(is_array($keyword_names) && !empty($keyword_names)){
// redirect to the same step again - to ensure that the form will reflect (in select lists) newly created AdGroup and/or Campaign
// GET parameteres ($params_array) provide a way to remember user's choice
$params_array = array();
if(!empty($post['match_type_id'])){
$params_array['match_type_id'] = $post['match_type_id'];
}
if(!empty($post['with_permutations'])){
$params_array['with_permutations'] = $post['with_permutations'];
}
if(!empty($ad_group_id)){
$params_array['ad_group_id'] = $ad_group_id;
}
$this_step_url = UrlUtils::assemble('', $this->getRequest()->getActionName(), $this->getRequest()->getControllerName(), $this->getRequest()->getModuleName(), $params_array);
$this->_redirect($this_step_url);
}else{ // all keywords have been assigned
// go to next step
$this->_redirect('/workflow-1/step-'.($step+1).'/');
}
}

So you don't have any code about Zend_Form object here. How do you populate the form element? If you post your class code which extends Zend_Form (or any other code dials with your form) then I may help. But in any case you can populate your multiselectbox with setMultiOptions() method or addMultiOption() for each item in multiselectbox.

Related

Reload a field from the controller.php

I have a logic hook that changes a field's value, but in order to see the change I need to refresh the whole page... I want it to refresh the field in real time.
So I'm trying to accomplish something like this(https://suitecrm.com/suitecrm/forum/suitecrm-7-0-discussion/21178-refresh-sub-panel-values-when-another-sub-panel-is-updated#75194)
But instead of reloading a whole subpanel through the controller.php I'm trying to find a way to refresh a single field.
Can anyone advise what method needs to be used to reload a field?
For example to reload a subpanel it's
**
showSubPanel('SUBPANEL_NAME',null,true);
**
But what is the JS method that reload a single field?
To refresh a detailView through a logic hook, you can use an easy "hack".
In your logic_hook, place the following js code as your last "echo" before your return or exit:
echo "<script>location.reload();</script>";
Simple but effective.
I couldn't find any built-in functions in suiteCRM that do it, spent a lot of time going through the chrome debugger, but nothing worked.
Here's a video that explains what's happening and the actual code example
https://youtu.be/ebuwWZoSYCk
You need to get the new status from the back-end then update the field inside the controller.php with:
document.querySelector('div[type="enum"][field="$field_to_update"]').innerHTML = "$inventory_status_c";
The whole controller.php file example is here, it all makes sense if you watch the 5 minute video:
class un_inventoryController extends SugarController {
/**
*
*/
function action_SubPanelViewer() {
require_once 'include/SubPanel/SubPanelViewer.php';
// only if this is creation of new sale under accounts, refresh the screen so the salerow subpanel will be refreshed too
if ( array_key_exists('module', $_REQUEST) && array_key_exists('subpanel', $_REQUEST) && array_key_exists('action', $_REQUEST) &&
$_REQUEST['module'] == 'un_inventory' && $_REQUEST['subpanel'] == "un_inventory_leads_1" && $_REQUEST['action'] == "SubPanelViewer") {
write_to_log(array("request" => $_REQUEST), "all conditions filled, custom controller called", true);
// Get the ID of the inventory unit so we can fetch the new status_c field and update the field right away (otherwise we'll have to refresh the page
$inventory = BeanFactory::getBean($_REQUEST["module"], $_REQUEST["record"]);
$inventory_status_c = ucwords(str_replace("_", " ", $inventory->status_c));
$field_to_update = "status_c";
$js=<<<EOQ
<script>
// Update the status
$( document ).ready(function() {
document.querySelector('div[type="enum"][field="$field_to_update"]').innerHTML = "$inventory_status_c";
});
</script>
EOQ;
echo $js;
}
}
}
?>

Angular 2 - Removing a Validator error

I wrote a function to update Validator rules on an input if a certain option was selected, using this method (the forms are built using FormGroup):
onValueChanged(data : any) {
let appVIP1 = this.vip1TabForm.get('option1');
let appVIP2 = this.vip2TabForm.get('option2');
let appVIP3 = this.vip3TabForm.get('option3');
//Set required validation if data is 'option3'
if(data != 'option3') {
//Due to initialization errors in UI, need to start with the case
//That there are validations, check to remove them
appVIP1.setValidators([]);
appVIP2.setValidators([]);
appVIP3.setValidators([]);
}
else {
appVIP1.setValidators([Validators.required]);
appVIP2.setValidators([Validators.required]);
appVIP3.setValidators([Validators.required]);
}
}
And I bind that function call to a click event on radio buttons (I initially used the guide from this answer, but the onChange function didn't bind correctly).
This works great, and if the user selects option 1 or 2, the validations are empty, and won't be triggered. If they select option 3, the validations are shown and submission is stopped. However, I run into the problem where the user submits, sees the error, and goes back to change to option 1 or 2. While the validator is cleared, my form still reads as invalid. I have multiple input fields I am validating, so I can't just set the form to valid if the validator is removed this way. How would I go about doing this? Can I remove the has-error for one particular field in the formgroup?
If the correct validators are in place, you can manually call AbstractControl#updateValueAndValidity after they select an option:
this.formBuilder.updateValueAndValidity();
(Where, of course, this.formBuilder is your FormBuilder instance.)
You can also call it on FormElements directly.
This is commonly used to trigger validation after a form element's value has been programmatically changed.
Instead of removing and adding validations. It is more simple to enable and disable fields. You need to add the Validators.required for all required fields. And disable the fields which are not required.
onValueChanged(data : any) {
let appVIP1 = this.vip1TabForm.get('option1');
let appVIP2 = this.vip2TabForm.get('option2');
let appVIP3 = this.vip3TabForm.get('option3');
if(data != 'option3') {
appVIP1.disable();
appVIP2.disable();
appVIP3.disable();
}
else {
appVIP1.enable();
appVIP2.enable();
appVIP3.enable();
}
}

Crm 2011 - How to set a default form depending on attribute value (without using Javascript)?

I have a little requirement that is making me crazy:
We have 8 different forms for the Contact Entity.
We also have a pick list with 8 options.
The idea is that based on the option selected we could open that Contact record showing by default a particular form WITHOUT USING JAVASCRIPT in order to avoid performance problems (each record has to be loaded twice). Example:
Forms:
Form 1
Form 2
Form 3
Pick List Values - Default Form:
Form 1
Form 2
Form 3
If Form 3(pick list value) is selected then, the next time I open that record, Form 3 should be displayed by default.
If Form 1(pick list value) is selected then, the next time I open that record, Form 1 should be displayed by default.
I've trayed registering a plugin at the systemform entity, in RetrieveFilteredForms message, updating the userentityuisettings table and I've been able to set a "DEFAULT" that is displayed every time the records is opened regardless the last opened form.
I've trayed registering a plugin at the contact entity, in Retrieve message, updating the userentityuisettings table but I found that Crm only consults the table Once if there is no attribute updated, the following times Crm take the the default form to open value from the cache.
This is an old question but since it's coming up in my searches for this problem I wanted to add my solution.
We use Dynamics CRM 2013. To my knowledge, later versions of 2011 also support this technique.
The form that is displayed when an entity is opened is determined by a few things - the default form, the security roles and fallback settings for a form and the last form used by the current user for that entity. We had a similar problem to the asker where we wanted a different account form displayed based on the value of a form. We were also tired of the constant reload/refresh that javascript techniques are subject to.
I found some blog posts (in particular this one: http://gonzaloruizcrm.blogspot.com/2014/11/avoiding-form-reload-when-switching-crm.html) which mentioned that it is possible to write a plugin to the Retrieve of the entity that allows you to read out the value (LastViewedFormXml) from UserEntityUISettings that stores which form was last used. If it's not the form you want, you can write in the desired value. This avoids the javascript form refreshing.
I had to modify some code from the samples I found to get it to work, but I'm happy with the results. You need to generate an entity class using CrmSvcUtil and include it in the project. You can get your form guids from the url of the form editor.
using System;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Description;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Client;
namespace CRM.Plugin.AccountFormSwitcher
{
public class Plugin : IPlugin
{
public enum accountType
{
Customer = 100000000,
Vendor = 100000001,
Partner = 100000002,
Other = 100000003
}
public const string CustomerAccountFormId = "00000000-E53C-4DF4-BC99-93856EDD168C";
public const string VendorAccountFormId = "00000000-E49E-4197-AB5E-F353EF0E806E";
public const string PartnerAccountFormId = "00000000-B8C6-4E2B-B84E-729AA11ABE61";
public const string GenericAccountFormId = "00000000-8F42-454E-8E2A-F8196B0419AF";
public const string AccountTypeAttributeName = "cf_accounttype";
public void Execute(IServiceProvider serviceProvider)
{
if (serviceProvider == null)
{
throw new ArgumentNullException("serviceProvider");
}
// Obtain the execution context from the service provider.
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
var pluginContext = (IPluginExecutionContext)context;
if (pluginContext.Stage == 20) //pre-operation stage
{
var columns = (ColumnSet)pluginContext.InputParameters["ColumnSet"];
if (!columns.Columns.Contains(AccountTypeAttributeName))
columns.AddColumn(AccountTypeAttributeName);
}
else if (pluginContext.Stage == 40) //post-operation stage
{
EntityReference currentEntity = (EntityReference)context.InputParameters["Target"];
if (currentEntity == null)
return;
var query = new QueryExpression(Account.EntityLogicalName);
query.Criteria.AddCondition("accountid", ConditionOperator.Equal, currentEntity.Id);
query.ColumnSet = new ColumnSet(AccountTypeAttributeName);
var accounts = service.RetrieveMultiple(query).Entities;
Account currentAccount = (Account)accounts[0];
SetForm(currentAccount, service, context.UserId);
}
}
private void SetForm(Account account, IOrganizationService service, Guid userId)
{
var query = new QueryExpression(UserEntityUISettings.EntityLogicalName);
query.Criteria.AddCondition("ownerid", ConditionOperator.Equal, userId);
query.Criteria.AddCondition("objecttypecode", ConditionOperator.Equal, Account.EntityTypeCode);
query.ColumnSet = new ColumnSet("lastviewedformxml");
var settings = service.RetrieveMultiple(query).Entities;
// Some users such as SYSTEM have no UserEntityUISettings, so skip.
if (settings == null || settings.Count != 1 || account.cf_AccountType == null) return;
var setting = settings[0].ToEntity<UserEntityUISettings>();
string formToUse;
switch ((accountType)account.cf_AccountType.Value)
{
case accountType.Customer:
formToUse = String.Format("<MRUForm><Form Type=\"Main\" Id=\"{0}\" /></MRUForm>", CustomerAccountFormId);
break;
case accountType.Vendor:
formToUse = String.Format("<MRUForm><Form Type=\"Main\" Id=\"{0}\" /></MRUForm>", VendorAccountFormId);
break;
case accountType.Partner:
formToUse = String.Format("<MRUForm><Form Type=\"Main\" Id=\"{0}\" /></MRUForm>", PartnerAccountFormId);
break;
case accountType.Other:
formToUse = String.Format("<MRUForm><Form Type=\"Main\" Id=\"{0}\" /></MRUForm>", GenericAccountFormId);
break;
default:
formToUse = String.Format("<MRUForm><Form Type=\"Main\" Id=\"{0}\" /></MRUForm>", GenericAccountFormId);
return;
}
// Only update if the last viewed form is not the one required for the given opportunity type
if (!formToUse.Equals(setting.LastViewedFormXml, StringComparison.InvariantCultureIgnoreCase))
{
var s = new UserEntityUISettings { Id = setting.Id, LastViewedFormXml = formToUse };
service.Update(s);
}
}
}
}
And to address the asker's issue with it only consulting the UserEntityUISettings once, I'm not sure why that's happening. But why not use javascript to change the form when the triggering attribute is changed? That what I do and I haven't ran into any problems with the plugin not displaying the desired for.
EDIT: the OP specified after that he needs a solution without javascript, I leave this reply for future references.
You can use javascript, inside the OnLoad event you check the picklist value and navigate to the desired form. Check this code as example
var value = Xrm.Page.getAttribute("new_optionset").getValue();
switch(value) {
case 100000000:
Xrm.Page.ui.formSelector.items.get(0).navigate();
break;
case 100000001:
Xrm.Page.ui.formSelector.items.get(1).navigate();
break;
case 100000002:
Xrm.Page.ui.formSelector.items.get(2).navigate();
break;
/// ... other cases here
default:
// default form to open when there is no value
Xrm.Page.ui.formSelector.items.get(0).navigate();
}
This is an oldy but a goody... I use CRM Rules to generate Hide Tab and Show Tab actions that essentially show each user role a different form.
Steps:
Create one, massive form with all of the fields you want to display (if you already have many forms, this would include all fields across all forms).
Organize the form into TABS, with each tab showing 'one form' worth of data. (You can also have many TABS for each user group). Typically, I create one 'General' tab that has the key option set that will set the rest of the form up, and any fields that are common across roles / user groups / forms, like status, name, etc...
3) Hide all of the tabs except the General tab by unchecking the visible box on those tab form property forms in the Admin UI.
4) Using CRM Rules (crm-rules.com), you can then bring in the metadata, with the form, and all of the tabs and sections in there. Then you just have to write one rule for each 'form' you are trying to show... Each rule is of this format:
IF User_Role contains 'Sales' THEN Show Tab: Sales
IF User_Role contains 'Marketing' THEN Show Tab: Marketing
You can also do this, of course, with an option set, or any field on the form as the condition... One of the benefits of this approach is that if users cross role boundaries (or some users were part of security roles that could access multiple forms), this technique shows them both forms at once...
HTH somebody, CRM Rules (www.crm-rules.com) generates the JavaScript to make this happen...

On a HTML Edit form, what is a good approach to have both reset and remember the posted values features?

I have a form which has both server side and client side validation.
It is an edit form, so the original user values are originally pre-populated.
e.g. The original pre-populated values are:
username = yeo
surname = yang
phonenumber = 11-12345
Now, the user edits to the below and submits.
e.g. The edited submitted values are:
username = yeoNew
surname = yangNew
phonenumber = 12-1111
This gets submitted to the serverside and fails the serverside validation because the phonenumber starting with 12 is not allowed.
Anyway, so the form is displayed back to the user as
e.g. The redisplayed form values are:
username = yeoNew
surname = yangNew
phonenumber = 12-1111
This is because my form allows the user to remember their submitted values.
At this stage, I'd like to allow the user to have the ability to reset the form values to the original values using clientside javascript. This is like a reset feature.
e.g. The reset button will restore the form values to:
username = yeo
surname = yang
phonenumber = 11-12345
The reason for this reset feature is that I want the user to have the option to edit the phonenumber again from the original values.
My question is:
What is a good way to keep track of the original values within the HTML so that I can restore it with javascript?
I'm thinking a new attribute called orig='' within the form elements which will store this value.
Is that a good idea?
Any other approaches?
thanks
I would use the HTML5 local storage.
See http://www.w3schools.com/html/html5_webstorage.asp
Using jquery I would do it this way:
<script type="text/javascript">
function load() {
if (localStorage["username"]) {
$('#username').val(localStorage["username"]);
}
if (localStorage["surname"]) {
$('#surname').val(localStorage["surname"]);
}
if (localStorage["phone"]) {
$('#phone').val(localStorage["phone"]);
}
}
function save() {
localStorage["username"] = $('#username ').val();
localStorage["surname"] = $('#surname').val();
localStorage["phone"] = $('#phone').val();
}
</script>

Trying to copy values into multiple embedded forms

I have a Symfony 1.4 application to allow users to enter data to a electrical appliance testing database. The page in questions consists of multiple embedded "new" forms so the user can submit many tests in one go. The form validates and saves correctly, but feedback is that it will be tedious to use.
As much of the data may be the same in each test (e.g. same date, same result, same person doing the testing), I would like the user to be able to fill in values in the top row, then click a button to fill the same information in the rows below. I'm pretty sure this would require javascript, but I don't have much experience.
I would appreciate any suggestions.
Many Thanks.
Well, I managed to figure it out without using javascript.
I put a button on the page
<input type="submit" name="copy_values" value="duplicate">
In the action for the page I included the code...
elseif (isset($_POST['copy_values'])) {
// get values from first embedded test
$newTests = $testList['new_tests'];
$testDate = $newTests[0]['et_date_tested'];
$testedBy = $newTests[0]['et_tester_id'];
$formOptions = array('test_date'=>$testDate, 'tester'=>$testedBy);
$this->form = new MultiTestForm(null, $formOptions);
}
$this->setTemplate('multiAdd');
... which takes the values from the widgets in the top row of the form and creates an array. This is passed as the options array to create a new form.
In the top level form class..
public function configure()
{
...
$subform = new sfForm();
for($i = 0;$i < sfConfig::get('app_new_test_rows'); $i ++)
{
$formToAdd = new TestsForMultiAddForm(null,$this->getOptions());
$subform->embedForm($i, $formToAdd);
}
$this->embedForm('new_tests', $subform);
}
...and in the embedded form class...
public function configure()
{
...
if ($this->getOption('test_date')) {
$this->setDefault('et_date_tested', $this->getOption('test_date'));
}
if ($this->getOption('tester')) {
$this->setDefault('et_tester_id', $this->getOption('tester'));
}
...
}
Not sure if this is the conventional way to approach the problem, but it works!