Getting initial_filter to work with subpanel's Select button - sugarcrm

In Suitecrm have a subpanel and when clicking 'Select' I want it to show only a specific set of records.
I have a subpanel definition file that looks like this and tried all possible variations in the section "initial_filter" but when the pop-up comes up it shows all of the records
$layout_defs["un_inventory"]["subpanel_setup"]['un_inventory_leads_1'] = array (
'order' => 0,
'module' => 'Leads',
'subpanel_name' => 'default',
'sort_order' => 'asc',
'sort_by' => 'id',
'title_key' => 'LBL_UN_INVENTORY_LEADS_1_FROM_LEADS_TITLE',
//'get_subpanel_data' => 'un_inventory_leads_1',
'get_subpanel_data' => 'function:get_parent_leads',
'function_parameters' =>
array('import_function_file' => 'custom/modules/Leads/func/get_parent_leads_file.php',),
'top_buttons' =>
array (
0 =>
array (
'widget_class' => 'SubPanelTopButtonQuickCreate',
),
1 =>
array (
//'widget_class' => 'SubPanelTopSelectButton',
'widget_class' => 'SubPanelTopSelectButtonParentProjectLeads',
'mode' => 'MultiSelect',
// 'initial_filter_fields' => "&first_name_advanced=hello",
// 'initial_filter' => array('parent_project_id_c_advanced' => array('83b30b20-83a6-8099-b3b9-5d4a491888e6')),
// 'initial_filter' => array('parent_project_id_c_advanced' => '83b30b20-83a6-8099-b3b9-5d4a491888e6'),
// 'initial_filter' => array('account_type_advanced' => array('Student')),
// 'initial_filter' => '&parent_project_id_c=83b30b20-83a6-8099-b3b9-5d4a491888e6',
),
),
);
There are many example of how this is done for 'relate' fields in editview, but not so much for subpanel's such as above, I'm pretty sure many would find this valuable.
Solution would probably be applicable to sugarcrm CE also

Solution is to override the default TopButtonSelect class and hardcode the $initial_filter variable.
So if we hardcode the value like so
$initial_filter.='&parent_project_id_c_advanced='.urlencode("83b30b20-83a6-8099-b3b9-5d4a491888e6");
It will only show records which have the parent_project_id_c field with the value 83b30b20-83a6-8099-b3b9-5d4a491888e6
Hope this helps
Source: http://qc-digital.com/filter-values-shown-when-we-click-on-select-button-inside-a-subpanel/

The above soluton proposed by Robert Sinclair does work, but it creates a dedicated-use situation. If instead you wanted to create a solution that will let you use the changes to have a functional initial_filter for any subpanel, make the following edits.
Thank you to Robert Sinclair and qc-digital.com for not only showing how to make his solution work but also for explaining why the expected method does not work, enabling the edits below to fix the SugarWidget and have it behave as expected.
Two fixes in this post:
a) Have the initial_filter_fields array be checked against the target
module not the host module
b) have the key=>value arrangement in the
layoutdefs work like it does in other situations
Two steps to make this fix work:
Create a custom SugarWidget
Tell the subpanel to use the custom SugarWidget and add the initial_filter_fields array to the TopSelectButton definition
You can then use the custom SugarWidget in any other subpanel and the initial_filter_field will work
Create the custom SugarWidget
mkdir -p custom/include/generic/SugarWidgets/
cp -a include/generic/SugarWidgets/SugarWidgetSubPanelTopSelectButton.php custom/include/generic/SugarWidget/SugarWidgetSubPanelTopSelectButtonWithFilter.php
nano custom/include/generic/SugarWidgets/SugarWidgetSubPanelTopSelectButtonWithFilter.php
Change
class SugarWidgetSubPanelTopSelectButton extends SugarWidgetSubPanelTopButton
To
class SugarWidgetSubPanelTopSelectButtonWithFilter extends SugarWidgetSubPanelTopButton
Change
if (isset($widget_data['initial_filter_fields'])) {
if (is_array($widget_data['initial_filter_fields'])) {
foreach ($widget_data['initial_filter_fields'] as $value=>$alias) {
if (isset($focus->$value) and !empty($focus->$value)) {
$initial_filter.="&".$alias . '='.urlencode($value);
}
}
}
}
To
/*
* Edited Below to use the target module not the host module
* and to use the normal key=>value arrangement
if (isset($widget_data['initial_filter_fields'])) {
if (is_array($widget_data['initial_filter_fields'])) {
foreach ($widget_data['initial_filter_fields'] as $value=>$alias) {
if (isset($focus->$value) and !empty($focus->$value)) {
$initial_filter.="&".$alias . '='.urlencode($value);
}
}
}
}
*/
if (isset($widget_data['initial_filter_fields'])) {
if (is_array($widget_data['initial_filter_fields'])) {
foreach ($widget_data['initial_filter_fields'] as $alias=>$value) {
$initial_filter.="&".$alias . '='.urlencode($value);
}
}
}
Tell the subpanel to use the custom SugarWidget (in my case, editing the Events module (FP_events) subpanel I created pointing to Accounts ... edit to suit your situation)
nano custom/Extension/modules/FP_events/Ext/Layoutdefs/fp_events_accounts_1_FP_events.php
Change
array (
'widget_class' => 'SubPanelTopSelectButton',
'mode' => 'MultiSelect',
),
To
array (
'widget_class' => 'SubPanelTopSelectButtonWithFilter',
'mode' => 'MultiSelect',
'initial_filter_fields' => array(
'account_type_advanced' => 'Venue',
),
),
You will now be able to use the initial_filter_fields functionality in any subpanel by specifying the custom SugarWidget and including the initial_filter_fields array definition in the TopSelectButton definition.

Related

TYPO3 TCA make the 'default' value dynamic

The title is rather self explanatory, but what i would like to have is a dynamic default value.
The idea behind it is to get the biggest number from a column in the database and then add one to the result. This result should be saved as the default value.
Lets take for example this code:
$GLOBALS['TCA'][$modelName]['columns']['autojobnumber'] = array(
'exclude' => true,
'label' => 'LLL:EXT:path/To/The/LLL:tx_extension_domain_model_job_autojobnumber',
'config' => [
'type' => 'input',
'size' => 10,
'eval' => 'trim,int',
'readOnly' =>1,
'default' => $result,
]
);
The SQL looks like this:
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_extension_domain_model_job');
$getBiggestNumber = $queryBuilder
->select('autojobnumber')
->from('tx_extension_domain_model_job')
->groupBy('autojobnumber')
->orderBy('autojobnumber', 'DESC')
->setMaxResults(1)
->execute()
->fetchColumn(0);
$result = $getBiggestNumber + 1;
So how can i do that "clean"?
I thought about processCmdmap_preProcess but i dont know how to pass the value to the coorisponding TCA field. Plus i do not get any results on my backend when i use the DebuggerUtility like i get them when i use processDatamap_afterAllOperations after saving the Object.
Can someone point me to the right direction?
I don't think it is supported to create a dynamic default value, see default property of input field.
What you can do however, is to create your own type (use this instead of type="input"). You can use the "user" type. (It might also be possible to create your own renderType for type="input", I never did this, but created custom renderTypes for type= "select").
You can look at the code of InputTextElement, extend that or create your own from scratch.
core code: InputTextElement
documentation for user type
more examples in FormEngine documentation
Example
(slightly modified, from documentation)
ext_localconf.php:
$GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['nodeRegistry'][<current timestamp>] = [
'nodeName' => 'customInputField',
'priority' => 40,
'class' => \T3docs\Examples\Form\Element\CustomInputElement::class,
];
CustomInputElement
<?php
declare(strict_types = 1);
namespace Myvendor\MyExtension\Backend\FormEngine\Element\CustomInputElement;
use TYPO3\CMS\Backend\Form\Element\AbstractFormElement;
// extend from AbstractFormElement
// alternatively, extend from existing Type and extend it.
class CustomInputElement extends AbstractFormElement
{
public function render():array
{
$resultArray = $this->initializeResultArray();
// add some HTML
$resultArray['html'] = 'something ...';
// ... see docs + core for more info what you can set here!
return $resultArray;
}
}

Symfony2 - Functional Testing File uploads with dynamically created fields

I'm fighting with functional testing of files uploading.
I will try to simplify my situation. Let's say I have
a company entity, which has 3 fields.
Company {
protected name;
protected tags;
protected images;
}
Images is array of CompanyImage entities, which serve for
storing image files and tags contains array of Tag entities,
which can be m:n connected with companies.
In the form I use jquery, for adding tags and images dynamically.
(you can create images and add them to the company similar to the
collection type symfony tutorial)
Because images and tag arrays are created with jquery, I cannot
simply use something like the turorial line below in the functional test of the company form.
$form['images'][0]->upload('/path/to/image.jpg');
For the setting
of the form values I use simple a little trick described by sstok here
(https://github.com/symfony/symfony/issues/4124)
public function testCompanyCreation() {
...
//option1
$image = new UploadedFile(
'/path/to/image.jpg',
'image.jpg',
'image/jpeg',
123
);
//or option2
//$image = array('tmp_name' => '/path/to/image.jpg', 'name' => 'image.jpg', 'type' => 'image/jpeg', 'size' => 300, 'error' => UPLOAD_ERR_OK);
$companyFormNode = $companyCrawler->selectButton('Create');
$companyForm = $companyFormNode->form();
$values = array(
'company' => array(
'_token' => $companyForm['company[_token]']->getValue(),
'name' => 'test company',
'tags' => array('1'),
'images' => array('0' => (array('file' =>$image))),
),
);
$companySubmitCrawler = $client->request($companyForm->getMethod(), $companyForm->getUri(), $values, $companyForm->getPhpFiles());
}
this works perfectly until I try to upload the image file.
With the option1 I get following exception
Exception: Serialization of 'Symfony\Component\HttpFoundation\File\UploadedFile' is not allowed
when I use option2 I get this
Argument 1 passed to Acme\myBundle\Entity\CompanyImage::setFile() must be an instance of Symfony\Component\HttpFoundation\File\UploadedFile, array given, called in ...\PropertyAccess\PropertyAccessor.php on line 347 and defined (500 Internal Server Error)
I would also like to point out, that the whole form and uploading of the files works without any problems in the browser. I also tried to make the entities serializable, and it didn't help. Do I have a bug somewhere?
I have figured it out (took couple of hours). Files have to be uploaded in a separate array.
$companyForm = $companyFormNode->form();
$values = array(
'company' => array(
'_token' => $companyForm['company[_token]']->getValue(),
'name' => 'test company',
'tags' => array('1')
),
);
$files = array(
'company' => array('images' => array('0' => (array('file' => $image))))
);
$companySubmitCrawler = $client->request(
$companyForm->getMethod(),
$companyForm->getUri(),
$values,
$files
);

Cakephp automatically filled form select for two word named belongTo model

What is right naming or what am I missing to get automagic run for two word named Model. Actual model belong to the two words named model.
Exact example:
Tour belongs to Accommodation type.
in database there is table tours and table accommodation_types
foreign key from tours is tours.accommodation_type_id
Snapshots of code below.
ToursController.php
public function add() {
//...
$accommodation_types = $this->Tour->AccommodationType->find('list');
//...
$this->set(compact('accommodation_types', ...));
}
Tour.php
//...
public $belongsTo = array(
//...
'AccommodationType' => array(
'className' => 'AccommodationType',
'foreignKey' => 'accommodation_type_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
//...
);
Tours/add.ctp (inside a form)
echo $this->Form->input('accommodation_type_id', array('label' => 'Accommodation type'));
As per convention the view vars names should be camelBacked. So rename the view var from $accommodation_types to $accommodationTypes. If you don't follow convention you have to explicitly specify the options var to use like this:
echo $this->Form->input('accommodation_type_id', array('options' => $accommodation_types, 'label' => 'Accommodation type'));

Example of Zend Form with Collection Element using Forms not Fieldsets in Zend Framework2

I need a straight forward working example how I can include a collection element in Zend Form, I have seen some examples from Zend Framework 2 site and from previous posts in StackOverflow where most of them pointed to this link. But right now I am not using Fieldsets and staying with Forms, so in case if someone can direct me in the right way, how I can include a simple collection element when the user gets a page where the user can choose multiple choices from the shown collection form. Much better would be populating the collection form from database.
I have searched in the internet for quite a sometime now and thought I would post here, so that Zend profis can give their suggestions.
Just For Information:
Normally one can include a static dropdownbox in Zend Form in this fashion
$this->add(
array(
'name' => "countr",
'type' => 'Zend\Form\Element\Select',
'options' => array(
'label' => "Countries",
'options' => array(
'country1' => 'Brazil',
'country2' => 'USA',
'country3' => 'Mexico',
'country4' => 'France',
)
)
)
);
So I am expecting a simple example which could give me a basic idea how this can be done.
To be honest, I don't see your problem here. Since form collections extend Fieldset which extends Element, you can just add it to the form as a regular element. The view helpers will take care of the rendering recursively.
Step 1: Create a form collection (create an instance of Zend\Form\Element\Collection). If the elements have to be added dynamically in some way, I'd create a factory class for this purpose.
Step 2: Add it to the form. (For example using $form->add($myCollectionInstance).)
Step 3: Render it. Zend\Form\View\Helper\Collection is a pretty good view helper to render the whole form without any pain.
You can also create a new class extending Zend\Form\Element\Collection and use the constructor to add the fields you need. Thus, you can add it to the form using the array you've pasted in your question. Also, you could directly use it in annotations.
Hope this helps.
If you just want to fill in a select list with option values you can add the array to the select list in a controller:
$form = new MyForm();
$form->get('countr')->setOptions(array('value_options'=>array(
'country1' => 'Brazil',
'country2' => 'USA',
'country3' => 'Mexico',
'country4' => 'France',
));
the array can be fetched from db.
this is a different example for using form collections in the simplest way.
In this example it creates input text elements in a collection and fills them in. The number of elements depends on the array:
class MyForm extends \Zend\Form\Form
{
$this->add(array(
'type' => '\Zend\Form\Element\Collection',
'name' => 'myCollection',
'options' => array(
'label' => 'My collection',
'allow_add' => true,
)
));
}
class IndexController extends AbstractActionController
{
public function indexAction
{
$form = new MyForm();
$this->addElementsFromArray($form, array(
'country1' => 'Brazil',
'country2' => 'USA',
'country3' => 'Mexico',
'country4' => 'France',
));
//the above line can be replaced if fetching the array from a db table:
//$arrayFromDb = getArrayFromDb();
//$this->addElementsFromArray($form, $arrayFromDb);
return array(
'form' => $form
);
}
private function addElementsFromArray($form, $array)
{
foreach ($array as $key=>$value)
{
$form->get('myCollection')->add(array(
//'type' => '\Zend\Form\Element\SomeElement',
'name' => $key,
'options' => array(
'label' => $key,
),
'attributes' => array(
'value' => $value,
)
));
}
}
}
index.phtml:
$form->setAttribute('action', $this->url('home'))
->prepare();
echo $this->form()->openTag($form);
echo $this->formCollection($form->get('myCollection'));
echo $this->form()->closeTag();

Zend_Validate_Db_RecordExists against 2 fields

I usualy use Zend_Validate_Db_RecordExists to update or insert a record. This works fine with one field to check against. How to do it if you have two fields to check?
$validator = new Zend_Validate_Db_RecordExists(
array(
'table' => $this->_name,
'field' => 'id_sector,day_of_week'
)
);
if ($validator->isValid($fields_values['id_sector'],$fields_values['day_of_week'])){
//true
}
I tried it with an array and comma separated list, nothing works... Any help is welcome.
Regards
Andrea
To do this you would have to extend the Zend_Validate_Db_RecordExists class.
It doesn't currently know how to check for the existence of more than one field.
You could just use two different validator instances to check the two fields separately. This is the only work around that I can see right now besides extending it.
If you choose to extend it then you'll have to find some way of passing in all the fields to the constructor ( array seems like a good choice ), and then you'll have to dig into the method that creates the sql query. In this method you'll have to loop over the array of fields that were passed in to the constructor.
You should look into using the exclude parameter. Something like this should do what you want:
$validator = new Zend_Validate_Db_RecordExists(
array(
'table' => $this->_name,
'field' => 'id_sector',
'exclude' => array(
'field' => 'day_of_week',
'value' => $fields_values['day_of_week']
)
);
The exclude field will effectively add to the automatically generated WHERE part to create something equivalent to this:
WHERE `id_sector` = $fields_values['id_sector'] AND `day_of_week` = $fields_values['day_of_week']
Its kind of a hack in that we're using it for the opposite of what it was intended, but its working for me similar to this (I'm using it with Db_NoRecordExists).
Source: Zend_Validate_Db_NoRecordExists example
Sorry for the late reply.
The best option that worked for me is this:
// create an instance of the Zend_Validate_Db_RecordExists class
// pass in the database table name and the first field (as usual)...
$validator = new Zend_Validate_Db_RecordExists(array(
'table' => 'tablename',
'field' => 'first_field'
));
// reset the where clause used by Zend_Validate_Db_RecordExists
$validator->getSelect()->reset('where');
// set again the first field and the second field.
// :value is a named parameter that will be substituted
// by the value passed to the isValid method
$validator->getSelect()->where('first_field = ?', $first_field);
$validator->getSelect()->where('second_field = :value', $second_field);
// add your new record exist based on 2 fields validator to your element.
$element = new Zend_Form_Element_Text('element');
$element->addValidator($validator);
// add the validated element to the form.
$form->addElement($element);
I hope that will help someone :)
Although, I would strongly recommend a neater solution which would be to extend the Zend_Validate_Db_RecordExists class with the above code.
Enjoy!!
Rosario
$dbAdapter = Zend_Db_Table::getDefaultAdapter();
'validators' => array('EmailAddress', $obj= new Zend_Validate_Db_NoRecordExists(array('adapter'=>$dbAdapter,
'field'=>'email',
'table'=>'user',
'exclude'=>array('field'=>'email','value'=>$this->_options['email'], 'field'=>'is_deleted', 'value'=>'1')
))),
For those using Zend 2, If you want to check if user with given id and email exists in table users, It is possible this way.
First, you create the select object that will be use as parameter for the Zend\Validator\Db\RecordExists object
$select = new Zend\Db\Sql\Select();
$select->from('users')
->where->equalTo('id', $user_id)
->where->equalTo('email', $email);
Now, create RecordExists object and check the existence this way
$validator = new Zend\Validator\Db\RecordExists($select);
$validator->setAdapter($dbAdapter);
if ($validator->isValid($username)) {
echo 'This user is valid';
} else {
//get and display errors
$messages = $validator->getMessages();
foreach ($messages as $message) {
echo "$message\n";
}
}
This sample is from ZF2 official doc
You can use the 'exclude' in this parameter pass the second clause that you want to filter through.
$clause = 'table.field2 = value';
$validator = new Zend_Validate_Db_RecordExists(
array(
'table' => 'table',
'field' => 'field1',
'exclude' => $clause
)
);
if ($validator->isValid('value') {
true;
}
I am using zend framework v.3 and validation via InputFilter(), it uses same validation rules as zend framework 2.
In my case I need to check, if location exists in db (by 'id' field) and has needed company's id ('company_id' field).
I implemented it in next way:
$clause = new Operator('company_id', Operator::OP_EQ, $companyId);
$inputFilter->add([
'name' => 'location_id',
'required' => false,
'filters' => [
['name' => 'StringTrim'],
['name' => 'ToInt'],
],
'validators' => [
[
'name' => 'Int',
],
[
'name' => 'dbRecordExists',
'options' => [
'adapter' => $dbAdapterCore,
'table' => 'locations',
'field' => 'id',
'exclude' => $clause,
'messages' => [
'noRecordFound' => "Location does not exist.",
],
]
],
],
]);
In this case validation will pass, only if 'locations' table has item with columns id == $value and company_id == $companyId, like next:
select * from location where id = ? AND company_id = ?