Drupal autocomplete, callback with multiple parameters - drupal

I am adding some autocomplete on a form alter. The problem is that in the callback, only the string in the textfield The autocomplete is on, is available. I also want to access a value from another textfield in the callback. How is this possible ?
/**
* Implements hook_form_alter().
*/
function webform_conversion_jquery_form_webform_client_form_1_alter(&$form, &$form_state, $form_id) {
//Load some extra function to process data
module_load_include('inc', 'webform_conversion_jquery', '/includes/dataqueries');
//Add extra js files
drupal_add_js(drupal_get_path('module', 'webform_conversion_jquery') . '/js/conversionform.js');
$form['submitted']['correspondentadress']['cor_street']['#autocomplete_path'] = 'conversionform/conversion_street';
}
}
/**
* Implements hook_menu().
*/
function webform_conversion_jquery_menu() {
$items = array();
$items['conversionform/conversion_street'] = array(
'title' => 'Conversion street autocomplete',
'page callback' => 'conversion_street_autocomplete',
'access callback' => 'user_access',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Retrieve a JSON object containing autocomplete suggestions for streets depending on the zipcode.
*/
function conversion_street_autocomplete($street = '') {
$street = "%" . $street . "%";
$matches = array();
$result = db_select('conversion_adresslist')
->fields('conversion_adresslist', array('street'))
->condition('street', $street, 'like')
->execute();
foreach ($result as $street) {
$matches[$street->street] = $street->street;
}
drupal_json_output($matches);
}
I just want to be able to post extra information in the function:
conversion_street_autocomplete($street = '', $extraparameter)

I had the same problem and have figured out a way, which is not too strenuous. It involves overriding the textfield theme and then passing your parameter to the theme function.
First create declare your theme function:
function mymodule_theme() {
$theme_hooks = array(
'my_module_autocomplete' => array(
'render element' => 'element',
),
);
return $theme_hooks;
}
Next we need to add the theme and the variable to our form element. In my case, the form element is part of a field widget:
function my_module_field_widget_form($form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
if($instance['widget']['type'] == 'my_module_field_type') {
$element['my_module_field'] = array(
'#type' => 'textfield',
'#autocomplete_path' => 'my-module/autocomplete',
// THIS IS THE IMPORTANT PART - ADD THE THEME AND THE VARIABLE.
'#theme' => 'my_module_autocomplete',
'#my_module_variable' => $field['field_name'],
);
}
return $element;
}
Then implement the theme function. This is a copy of theme_textfield from includes/form.inc with one important difference - we append the variable to the autocomplete path:
function theme_my_module_autocomplet($variables) {
$element = $variables['element'];
$element['#attributes']['type'] = 'text';
element_set_attributes($element, array('id', 'name', 'value', 'size', 'maxlength'));
_form_set_class($element, array('form-text'));
$extra = '';
if ($element['#autocomplete_path'] && drupal_valid_path($element['#autocomplete_path'])) {
drupal_add_library('system', 'drupal.autocomplete');
$element['#attributes']['class'][] = 'form-autocomplete';
$attributes = array();
$attributes['type'] = 'hidden';
$attributes['id'] = $element['#attributes']['id'] . '-autocomplete';
// THIS IS THE IMPORTANT PART. APPEND YOUR VARIABLE TO THE AUTOCOMPLETE PATH.
$attributes['value'] = url($element['#autocomplete_path'] . '/' . $element['#my_module_variable'], array('absolute' => TRUE));
$attributes['disabled'] = 'disabled';
$attributes['class'][] = 'autocomplete';
$extra = '<input' . drupal_attributes($attributes) . ' />';
}
$output = '<input' . drupal_attributes($element['#attributes']) . ' />';
return $output . $extra;
}
Now the variable will be available as the first parameter on the autocomplete callback function:
function _my_module_autocomplete($my_module_variable, $search_string) {
// Happy days, we now have access to our parameter.
}

Just in case anyone is still having trouble with this I found a great solution while trying to figure out how to do this. I had a year select list and that dictated what data was displayed in the autocomplete field. The solution basically has an ajax callback function for the select list that can then update the autocomplete field with an extra parameter in the url. Anyways, it is really well explained in the following article.
http://complexdan.com/passing-custom-arguments-drupal-7-autocomplete/
*A note of caution, I was going crazy trying to figure out why it did not work and it turns out you can't have the same form on the page twice (I needed to because I was displaying it differently for mobile devices) because you are using an id for the ajax callback. I added an extra argument to accomplish that. It is called uniqueid in the below example.
function report_cards_comparison_form($form, &$form_state, $uniqueid) {
$curryear = t('2012');
$form['year_select'] = array(
'#title' => t('School Year'),
'#type' => 'select',
'#options' => array(
'2012' => t('2012'),
'2013' => t('2013'),
'2014' => t('2014'),
'2015' => t('2015'),
),
'#default_value' => $curryear,
'#ajax' => array(
'callback' => 'report_cards_comparison_form_callback',
'wrapper' => $uniqueid,
'progress' => array(
'message' => 'Updating Schools...',
'type' => 'throbber'
),
),
);
$form['choice'] = array(
//'#title' => t('Search By: School Name'),
'#type' => 'textfield',
'#attributes' => array(
'class' => array('school-choice'),
'placeholder' => t('Start Typing School Name...'),
),
'#required' => TRUE,
'#autocomplete_path' => 'reportcards/autocomplete/' . $curryear,
'#prefix' => '<div id="' . $uniqueid . '">',
'#suffix' => '</div>',
);
$form['submit'] = array(
'#type' => 'submit',
'#prefix' => '<div class="submit-btn-wrap">',
'#suffix' => '</div>',
'#value' => t('Search'),
'#attributes' => array('id' => 'add-school-submit'),
);
return $form;
}
/**
* Ajax Callback that updates the autocomplete ajax when there is a change in the Year Select List
*/
function report_cards_comparison_form_callback($form, &$form_state) {
unset($form_state['input']['choice'], $form_state['values']['choice']);
$curryear = $form_state['values']['year_select'];
$form_state['input']['choice'] = '';
$form['choice']['#value'] = '';
$form['choice']['#autocomplete_path'] = 'reportcards/autocomplete/' . $curryear;
return form_builder($form['#id'], $form['choice'], $form_state);
}
and I can call the form by doing this...
print render(drupal_get_form('report_cards_comparison_form', 'desktop-schoolmatches'));

You can do it by overriding methods from autocomplete.js in your own js. Here is example:
(function($) {
Drupal.behaviors.someModuleOverrideAC = {
attach: function(context, settings) {
// Next is copied and adjusted method from autocomplete.js
Drupal.jsAC.prototype.populatePopup = function() {
var $input = $(this.input);
var position = $input.position();
// Show popup.
if (this.popup) {
$(this.popup).remove();
}
this.selected = false;
this.popup = $('<div id="autocomplete"></div>')[0];
this.popup.owner = this;
$(this.popup).css({
top: parseInt(position.top + this.input.offsetHeight, 10) + 'px',
left: parseInt(position.left, 10) + 'px',
width: $input.innerWidth() + 'px',
display: 'none'
});
$input.before(this.popup);
// Do search.
this.db.owner = this;
if ($input.attr('name') === 'field_appartment_complex') {
// Overriden search
// Build custom search string for apartments autocomplete
var $wrapper = $('div.apartments-autocomplete');
var $elements = $('input, select', $wrapper);
var searchElements = {string: this.input.value};
$elements.each(function() {
searchElements[$(this).data('address-part')] = $(this).val();
});
var string = encodeURIComponent(JSON.stringify(searchElements));
this.db.search(string);
}
else {
// Default search
this.db.search(this.input.value);
}
};
}
};
}(jQuery));
In your server callback:
function some_module_autocomplete_ajax($string) {
// Decode custom string obtained using overriden autocomplete js.
$components = drupal_json_decode(rawurldecode($string));
// Do you search here using multiple params from $components
}

Ok, for as far as I can see it is not possible. maybe you can roll your own with the ajax functionality in fapi http://api.drupal.org/api/drupal/developer--topics--forms_api_reference.html/7#ajax
For now I solved it by implementing jquery.ui.autocomplete which is included in drupal 7

Related

Drupal 7 | Import CSV, row by row, into custom table

Assuming I have a CSV like this:
value1,value2
value1,value2
and a table with two columns
column1|column2
how can I programatically import the CSV into the table?
#erier's answer is really good and will get the job done.
As the question is tagged with Drupal 7 I thought I would submit a Drupal custom module method, adapting #erier's answer and prettifying it.
For the basics of a custom module, you can see this simple example.
function custom_module_form($form, &$form_state) {
$form['csv_upload'] = array(
'#type' => 'file',
'#title' => t('Choose a file'),
'#title_display' => 'invisible',
'#size' => 22,
'#upload_validators' => array('file_clean_name' => array()),
);
$form['upload'] = array(
'#type' => 'submit',
'#value' => 'Upload',
);
}
function custom_module_form_validate($form, &$form_state) {
$validators = array('file_validate_extensions' => array('csv'));
// Check for a new uploaded file.
$file = file_save_upload('csv_upload', $validators);
//$file = $form_state['values']['csv_upload'];
if (isset($file)) {
// File upload was attempted.
if ($file) {
// Put the temporary file in form_values so we can save it on submit.
$form_state['values']['csv_upload_file'] = $file;
}
else {
// File upload failed.
form_set_error('csv_upload', t('The file could not be uploaded.'));
}
}
}
function custom_module_submit($form, &$form_state) {
$file = $form_state['values']['csv_upload_file'];
$file->status = FILE_STATUS_PERMANENT;
$file->filename = str_replace(' ', '_', $file->filename);
file_save($file);
$csv_file = file_load($file->fid);
$file = fopen($csv_file->uri, "r");
while(! feof($file))
{
$customer = fgetcsv($file));
db_insert('your_db_table')
->fields(array(
'column1' => $customer[0],
'column2' => $customer[1]
))
->execute();
}
fclose($file);
drupal_set_message('CSV data added to the database');
}
function file_clean_name($file) {
$file->filename = str_replace(' ', '_', $file->filename);
}
That is assuming your question was about a database table.
If you meant into an html table, you can adapt the submit function after $csv_file = file_load($file->fid); like this:
$headers = array('column 1', 'column 2');
$rows = array();
$file = fopen("contacts.csv","r");
while(! feof($file))
{
$customer = fgetcsv($file));
$rows[] = array($customer[0], $customer[1]);
}
fclose($file);
return theme('table', array('header' => $headers, 'rows' => $rows);
Or adapt the two by adding the data to the database and displaying to the screen without a second database hit.
This is what I've come up with - I'm no PHP expert - and it doesn't seem pretty - but it works!
$handle = fopen('/path/to/file/filename.csv', 'r');
$row = fgetcsv($handle);
for ($e = 0; $row = fgetcsv($handle); $e++) {
$record = array();
foreach ($row as $field) {
$record[] = $field;
}
db_insert('your_db_table')
->fields(array(
'column1' => $record[0],
'column2' => $record[1]
))
->execute();
}
fclose($handle);
The table will then look like this:
column1|column2
---------------
value1|value2
value1|value2
This module may help you
https://www.drupal.org/sandbox/draenen/2442165
It requires Feeds but it extends it to work with custom tables (non drupal entities)

Why content appears misplaced?

I am building a custom module which should display a calendar created by a third-part library. So far, so good. The only problem is, the calendar appears on top of page, even above header. It does not seems to be css-related.
This is the way it looks:
This is my module file:
require_once 'includes/apphp-calendar/calendar.class.php';
function calendario_menu(){
$items = array();
$items['eventos/calendario'] = array(
'title' => 'Calendario',
'description' => 'Calendario de Eventos.',
'page callback' => '_page_calendario',
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
return $items;
}
function _page_calendario() {
$objCalendar = new Calendar();
$calendar = $objCalendar->Show();
return array(
'#markup' => $calendar
);
}
Seems to me like the Show function of the calendar class outputs rather than returning a string.
try this:
function _page_calendario() {
$objCalendar = new Calendar();
ob_start();
$objCalendar->Show();
$calendar = ob_get_contents();
ob_end_clean();
return array(
'#markup' => $calendar
);
}

How do I trigger a Drupal-Function with Javascript/jQuery?

I have a Checkbox with different values. When a user change the Checkbox I will trigger the Drupal-Function field_attach_update http://api.drupal.org/api/drupal/modules!field!field.attach.inc/function/field_attach_update/7
I know how I check the checkbox-change with jQuery but how can I trigger the Drupal-Function then?
You'll want to check out the Form API ajax options. Specifically I think you'll want to define an ajax['callback'] function that calls field_attach_update.
<?php
function my_form_func($form, $form_state) {
$my_checkbox_val = isset($form_state['values']['my_checkbox']) ? $form_state['values']['my_checkbox'] : NULL;
$form['my_checkbox'] = array(
'#type' => 'checkbox',
'#title' => t('Check me'),
'#default_value' => $my_checkbox_val,
'#return_value' => $nid, // Assuming you are working with a node, but could be any entity
'#ajax' => array(
'callback' => 'my_form_field_update_func',
'event' => 'click',
),
);
return $form;
}
function my_form_field_update_func($form, $form_state) {
if (isset($form_state['values']['my_checkbox'])) {
$node = node_load($form_state['values']['my_checkbox']);
field_attach_update('node', $node);
}
return $form['my_checkbox'];
}
?>

Creating Custom Field Formatter is not working

I'm trying to create a new custom field formatter for text fields but nothing is showing at all.
I tried un-installing and re-installing the module and flushed the cache many times, and nothing worked.
Here's the code I used
/**
* Implementation of hook_field_formatter_info()
*/
function facebooklink_field_formatter_info()
{
return array(
'cfieldformatter_text_1' => array(
'label' => t('Text 1'),
'field types' => array('text', 'text_long', 'text_with_summary'),
'settings' => array(
'pic_size' => 'small',
'tooltip' => 'Link to user Facebook page',
),
),
);
}
/**
* Implementation of hook_field_formatter_settings_form()
*/
function facebooklink_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state)
{
//This gets the view_mode where our settings are stored
$display = $instance['display'][$view_mode];
//This gets the actual settings
$settings = $display['settings'];
//Initialize the element variable
$element = array();
$element['pic_size'] = array(
'#type' => 'select', // Use a select box widget
'#title' => t('Button Size'), // Widget label
'#description' => t('Select what size of FB button'), // Helper text
'#default_value' => $settings['pic_size'], // Get the value if it's already been set
'#options' => array(
'small' => 'Small',
'medium' => 'Medium',
'large' => 'Large',
),
);
$element['tooltip'] = array(
'#type' => 'textfield', // Use a textbox
'#title' => t('Tool Tip'), // Widget label
'#description' => t('This text will appear when a user mouses over.'), // helper text
'#default_value' => $settings['tooltip'], // Get the value if it's already been set
);
return $element;
}
/**
* Implementation of hook_field_formatter_settings_summary()
*/
function facebooklink_field_formatter_settings_summary($field, $instance, $view_mode)
{
$display = $instance['display'][$view_mode];
$settings = $display['settings'];
$summary = t('Use a #size Facebook button with the tooltip of "#tooltip"', array(
'#size' => $settings['pic_size'],
'#tooltip' => $settings['tooltip'],
));
return $summary;
}
/**
* Implementation of hook_field_formatter_view()
*/
function facebooklink_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display)
{
$element = array(); // Initialize the var
$settings = $display['settings']; // get the settings
$size = $settings['pic_size']; // The Size setting selected in the settings form
$tooltip = $settings['tooltip']; // The tool tip assigned in settings
// Create the image - Note that I'm storing the images in our module but they could be anywhere
$image = '<img src="/' . drupal_get_path('module', 'facebooklink') . 'fb-' . $size . '.png">';
foreach ($items as $delta => $item) {
$fb = $item['safe_value']; // Getting the actual value
}
$options = array(
'html' => TRUE, // This tells Drupal that we're sending HTML, not plain text, otherwise it would encode it
'attributes' => array(
'title' => $tooltip, // This sets our tooltip
),
);
if(isset($fb)) {
$link = l($image, $fb, $options); // Create the Link
$element[0]['#markup'] = $link; // Assign it to the #markup of the element
}
return $element;
}
Any help with this crazy issue?!!!.
I would suggest you either name your formatter 'facebooklink' instead 'cfieldformatter_text_1' in hook_field_formatter_info (this works when you have one single formatter in a module) or explicity trigger the formatter in hook_field_formatter_view as below:
/**
* Implementation of hook_field_formatter_view()
*/
function facebooklink_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display)
{
$element = array(); // Initialize the var
$settings = $display['settings']; // get the settings
switch ($display['type']) {
case 'cfieldformatter_text_1':
$size = $settings['pic_size']; // The Size setting selected in the settings form
$tooltip = $settings['tooltip']; // The tool tip assigned in settings
// Create the image - Note that I'm storing the images in our module but they could be anywhere
$image = '<img src="/' . drupal_get_path('module', 'facebooklink') . 'fb-' . $size . '.png">';
foreach ($items as $delta => $item) {
$fb = $item['safe_value']; // Getting the actual value
}
$options = array(
'html' => TRUE, // This tells Drupal that we're sending HTML, not plain text, otherwise it would encode it
'attributes' => array(
'title' => $tooltip, // This sets our tooltip
),
);
if(isset($fb)) {
$link = l($image, $fb, $options); // Create the Link
$element[0]['#markup'] = $link; // Assign it to the #markup of the element
}
break;
}
return $element;
}

How can i add my custom form to custom block in my custom module

I've made a custom Drupal module. Inside which I've created a block and a form. How can I make the form appear in the block content? Cheers.
Block Code:
function module_block($op = 'list', $delta = 0, $edit = array()) {
$block = array();
if ($op == "list") {
// Test
$block[0]["info"] = t('Block');
}
else if ($op == 'view') {
$block['content'] = module_function();
}
return $block;
}
// End module_block
Form Code:
function module_my_form($form_state) {
$form['email'] = array(
'#type' => 'textfield',
'#title' => t('Email'),
'#required' => TRUE,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => 'Submit',
);
return $form;
}
Cheers again for any help.
For anyone looking, change:
$block['content'] = module_function();
to
$block['content'] = drupal_get_form('module_my_form');
Cheers

Resources