Auto compete plus new page with Entry Point

Hello fellas,
I’m just gonna share a bit of code that i’ve implemented and i think it made my suitecrm better.
First i’m gonna tell how to create a new php file with entry point:
Create a php file in the module you want to affect (mine is modules/AOS_Products/buscarAccounts.php) like so:


then go to [color=#0000ff]custom/Extension/application/Ext/EntreyPoint/[/color]
and create a new file called with the same name as your new php file:

after this you just need to do a QuickRepair&Rebuild so the program can change the file with entryPoints access.
Now to call that file first i’ll create the javacript and then call it in an ajax request:
create a new file just to leave suitecrm code cleaner call it ajaxAutocomplete.php :

<?php
/**
*@author Pedro Almeida
*@copyright 13-08-2015
*@access data/SugarBean.php
*@param echo de codigo javascript para fazer o autocomplete no AOS_Products
*/

echo <<<EOQ
        <script>
        var search = document.getElementById('url_basic');
        search.value = '$this->mine';
        //specify here the textbox element ID you want to see mine was an url I
        //created it in studio for other purposes but in this post i'll assume you just 
        //want to autocomplete a normal perfectly fine searchbox 
        urlSearch = $("#url_basic");
        //the new element where the search results will show
        AjaxResultHTML = "<div class='searching' id='searchResults' style='padding-right:5px; padding-left:5px; width:auto; min-width:150px; max-height:100px; height:auto; background-color:#F7F7F7; position:absolute; overflow-y:scroll;'></div>";
        //just another customization to see the number of results
        AjaxResultHTML+= "<div class ='searching' id='contagem' style='padding-top:5px; position:absolute; display:inline-block; background-color:#F7F7F7; width:30px; height:30px;'></div>";
        //remove their autocomplete of already searched data it's only helpfull if you already searched tons of data
        urlSearch.attr({autocomplete:"off"});
        urlSearch.addClass('searching');
        urlSearch.parent().append(AjaxResultHTML);
        AjaxResult = $("#searchResults");
        AjaxCounter = $("#contagem");
        AjaxResult.hide();
        //every element in the page that doesn't have the class "searching" will hide the
        //results
        $(document).click(function(e)
        {
                if(!$(e.target).is(".searching"))
                {
                    AjaxResult.hide();
                }
        });    
        //send the result from autocomplete to the searchbox
        $(document).on("click", ".resultList", function()
        {
            urlSearch.val($(this).attr("valor"));
        }); 
        //A bit of color to the results
        $(document).on("mouseover", ".resultList", function()
        {
            $(this).css({'background-color': '#3C8DBC', 'cursor':'pointer', 'color': 'white'});
        }).on("mouseleave", ".resultList", function(){
            $(this).css({'background-color': '#F7F7F7', 'cursor':'pointer', 'color': '#3C8DBC'});
        }); 
        urlSearch.on("keyup", function(e)
        {
            var searchAccount = $(this).val();
            var searchSize = searchAccount.length;
            // i wanted it to search only when there were more then 2 words
        	if(e.which !== 13 && searchSize > 2)
        	{
                // AjaxResult.empty();  
                searchAccount = extraSpace(searchAccount);
                $.ajax({
                    //this is the real deal why we had to create an entryPoint
                    //better explain in words than in the middle of the code @see *1234
                    url: "index.php?entryPoint=buscarAccounts",
                    type: "GET",
                    dataType: "json",
                    data: ({searchAccount : searchAccount}),
                    beforeSend: function(){
                                AjaxResult.show();
                    },
                    success: function(accounts){
                        cliente = "";
                        size = accounts.length;
                        AjaxCounter.html("("+size+")");

                        for(var account in accounts)
                        {
                            common = similarity(accounts[account].name, searchAccount);
                            cliente += "<div class='resultList' valor='"+accounts[account].name+"' style='color:#3C8DBC'>"+common+"</div>";
                        }
                        AjaxResult.html(cliente);
                    }
        		});
        	}
        });
function similarity(searching, where)
{
//this function will just bold the first words in the search that match with the results
    wherenow = where.replace(/\s+/g, ' ').trim();
    searching = accent(searching);
    var regex = new RegExp('('+wherenow+')', 'i');
    html = searching.replace(regex, "<span style='font-weight: bold;'>"+wherenow.toUpperCase()+"</span>");
    return html;
}
function extraSpace(string)
{
    return string.replace(/\s+/g, ' ');
}
function accent(string)
{
    string = string.replace(/(ã|á|à|â)/gi, "a");
    string = string.replace(/(í|ì)/gi, "i");
    string = string.replace(/(é|è)/gi, "e");
    string = string.replace(/(õ|ó|ò|ô)/gi, "o");
    return string;
}
    </script>
EOQ;
?>

*1234: without an EntryPoint you would just simply assume giving the path to the file would do it but it’s not that simple first the htaccess will block the word “modules” you can just remove it and it will work but it’s not safe and there will be more problems since the file that will handle the autocomplete search will not be able to receive a db connection and if you try to create one or call the module and create it will just give you errors of module not found and path not found better not go there.
Where to call this ajaxAutocomplete: [color=#0000ff]data/SugarBean[/color] inside this function: [color=#0000ff]get_list_view_array[/color] before the [color=#ff0000]return $return_array;[/color]
if ($this->module_name == 'AOS_Products') { require_once('modules/AOS_Products/ajaxAutoComplete.php'); }

And finally the file that handles the search and needs the entry point

<?php
/**
*@author Pedro Almeida
*@copyright 14-08-2015
*@see custom/Extension/application/Ext/buscarAccounts.php
*@param Recebe o pedido ajax do nome cliente 
*/
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');

if(isset($_GET["searchAccount"]) AND trim($_GET["searchAccount"]) != "")
{
	$name = $_GET["searchAccount"];
	$name = checkingQuery($name);
	$queryAccount = "SELECT id, name FROM accounts "
					."WHERE name LIKE '".$name."' "
					."OR name LIKE '%".$name."' "
					."OR name LIKE '".$name."%' ".addingFur($name);
	$queryAccOrder=	"ORDER BY "
				    ."name LIKE '".$name."' DESC, "
				    ."name LIKE '".$name."%' DESC";
	$queryAccount .= $queryAccOrder;
	$result = $GLOBALS['db']->query($queryAccount);
	$array = array();
	while($row = $GLOBALS['db']->fetchByAssoc($result) )
	{
	    $array[] = array("id"=>$row['id'], "name"=>$row['name']);
	}
	echo json_encode($array);
}
/**
*@param limpa palavras e tags "perigosas"
*@return query sem essas palavras/tags
*/
function checkingQuery($userQuery)
{
	//just some security 
	$regex = '/(DELETE|UPDATE|SELECT|function|script|&gt;|&lt;|#)/i';
	return preg_replace($regex, "", $userQuery);
}
/**
*@param Se o utilizador tiver escrito 1 espaço a seguir ao nome
* remove esse espaço para poder mostrar clientes que em vez de 1 espaço tinha 
* um - ou ,
*@return order by nome do account sem espaço no final
*/
function addingFur($name)
{
	//this removes the last character that's a space in case there's a "," or "-"
	//spliting the name and not a space
	if(substr($name, -1) == ' ')
	{
		$arr = substr_replace($name, "", -1);
		$furToQuery = "OR name LIKE '".$arr."%' ";
		return $furToQuery;
	}
}

Hope it was usefull i’ll be implementing it in more modules and will be searching for a better place for my ajaxAutocomplete since it’s inside the aos_products module.
Cheers

Forgot to show how it looks like after done :stuck_out_tongue:

1 Like