Forum OpenACS Development: User Smart Search Widget with Ajax

Collapse
Posted by Ryan Gallimore on
Here is a tool I've been wanting to build for a long time to make selecting from huge lists easier: Smart Search Widget. This early preview handles users, but listbuilder could be modified to include this widget for almost any object in OpenACS.

It requires ajaxhelper 0.5d but was incredibly easy to put together, with only one line of Tcl required for the Ajax call.

Here is the source if you're curious.

Please offer ideas, comments and suggestions.

Enjoy!

Collapse
Posted by Malte Sussdorff on
Two questions:

a) I added this to contacts. When I wrote my lookup.tcl (which does the search) I returned the html just like you did, but in my resultbox it is quoted. Any idea on how to fix this?

b) What CSS magic do I have to do so the result box does not push the page downwards but just floats over it.

Other than that: Great!

Collapse
Posted by Ryan Gallimore on
Great to hear this is being added to contacts!

a) Does noquote-ing not work? I don't remember how I did it, but the quoting method suggested in the ajaxhelper docs should do it.

b) Just add this to the CSS of the results div:

div.results_box {
overflow: auto;
width: 200px;
height: 300px;
}

and you'll get a box of 200x300 with a scrollbar if any text goes beyond those dimensions.

Cheers.

Collapse
Posted by Malte Sussdorff on
Excellent, your trick worked. And with regards to a). Well, it would help if my lookup.adp would actually do the noquoting :).

So, now I have a page which supports what you did. Sadly I wanted to integrate it into the main search page for contacts and get these JS errors:

form has no properties
prototype.js (line 119)
anonymousprototype.js (line 119)
anonymousprototype.js (line 117)

In this ad_form generated form:

<form name="search" method="GET"
                    action="/intranet/contacts/"><input type="hidden" name="form:mode" value="edit" />
<input type="hidden" name="form:id" value="search" />
<!-- Form elements -->
  <input type="hidden" name="__confirmed_p" value="0" /><input type="hidden" name="__refreshing_p" value="0" /><input type="hidden" name="__submit_button_name" value="" /><input type="hidden" name="__submit_button_value" value="" /><input type="hidden" name="orderby" value="first_names,asc" /><input type="hidden" name="page_size" value="1" /><input type="hidden" name="format" value="normal" /><input type="hidden" name="extended_columns" value="" />
                  <span class="form-widget">
                <select name="search_id"  onChange="javascript:acs_FormRefresh('search')">
<option value="" selected="selected">All Contacts</option>
</select>
</span>
                  <span class="form-widget">
                <input type="text" name="query" value="" maxlength="255" autocomplete="off" onKeyUp="new Ajax.Updater ('results_box','lookup',{asynchronous:true,method:'post',parameters:Form.serialize('search')});  document.getElementById('results_box').style.visibility='visible';" size="20" />
              </span>
            <span class="form-element">
              <input type="submit" name="save" value="Search" />
            </span>
                  <span class="form-label">
                    &nbsp;&nbsp;<span style="font-size: smaller;">Results 136 </span>
              </span>
                  <span class="form-widget">
                <input type="hidden" name="results_count" />
              </span>
        </form>

Whereas the following form works like a charm:

<form id="searchform" method="post" action="/intranet/contacts/">
<table border="0" cellpadding="0" cellspacing="0">
  <tr>
    <td>

      <input type="text" name="query" id="query" onKeyUp="new Ajax.Updater ('results_box','test/lookup',{asynchronous:true,method:'post',parameters:Form.serialize('searchform')});  document.getElementById('results_box').style.visibility='visible';" autocomplete="off" value="" />
    </td>
  </tr>
  <tr>
    <td>
      <div id="results_box"></div>
    </td>
  </tr>
</table>
</form>

Collapse
Posted by Malte Sussdorff on
Okay, actually I have to take two things back.

a) In my form the results_box was missing, but even when I added it the thing did not work at all.

b) The overflow statement did not work. It pre reserves the space it needs (width / height) and thats it.

Collapse
Posted by Ryan Gallimore on
Malte,

Do you have a URL I can take a look at?

Collapse
Posted by Eduardo Santos on
Hi Ryan,

Your widget is just fantastic. Thank you very much for the code. I have just one question: I'm having encoding problems in the search results. Any idea on how to fix it?

Collapse
Posted by Ryan Gallimore on
Hi Eduardo,

I'm happy to hear my widget was helpful to you. What kind of encoding problems are you having? What do your search results look like?

Ryan

Collapse
Posted by Eduardo Santos on
In the results box, everytime I have a Latin character, such as á or ç I see this in the result: �

Do you have any ideas on how to fix it?

Collapse
Posted by Ryan Gallimore on
It seems to me that you need to quote the string returned by search-users.tcl. But it might be more complicated than that.

My understanding of character encoding is a bit hazy, but this (old) thread might help:

https://openacs.org/forums/message-view?message_id=28543

Good luck

Ryan

Collapse
Posted by Eduardo Santos on
Thank you very much for you help again, Ryan. Afther a long research, I found the answer in the link that you gave me. I've changed the following line in the search-users.tcl file:

ns_write [encoding convertto utf-8 $response]

I'm forcing the encoding to be utf-8. It seems to me like ns_write is ignoring the server's encoding (possibly an AOLServer bug). In your standard exit:

ns_write $response

the HTML response to the browser gets the wrong encoding, but the ns_log command, for example, has the right server encoding. It's an workaround but it's working.

Collapse
Posted by Lourdes Martínez on
Hi Ryan, I'll tried to open the tool "Smart Search Widget" and "the Source", but is not available. Can you enable.
Thanks.
Collapse
Posted by Ryan Gallimore on
Hi Lourdes,

Here is the Smart Search Widget code.

Let me know if you have any questions.

Cheers.

Thanks.
I'm new in TCL.

Can you explain me how do I implement a "simple" smart search widget in my tcl code, using a select one data from any table.

I put the three file that you enable in my server, but I see that I need ajax helper, where can I download.

Can you please, help me with the implementation of this files and the ajaxhelper?.

Thanks again.

Hi Ryan, I'm still trying to run the smart-search-widget.tcl
and return this error:

invalid command name "ah::ajaxupdate"
while executing
"ah::ajaxupdate -container "results_box" -url "search-users" -pars "Form.serialize('searchform')""
invoked from within
"set js_update_user_select [ah::ajaxupdate -container "results_box" -url "search-users" -pars "Form.serialize('searchform')"]"
("uplevel" body line 7)
invoked from within
"uplevel {
# /packages/private-messenging/lib/smart-search-widget.tcl

I put the package of ajaxhelper (scriptaculous-js-1.8.2) but I don't found anything with the name of ajaxupdate.

Thanks for your help.

Did you checkout and install ajaxhelper 0.78? You then need to follow the instructions in /AJAX-DIR/doc
No, I have not been able to install it. I downloaded the OpenACS Core 5.2.3 (core packages), but I don't found the ajaxhelper package. Thanks for your help.
You'll want to checkout the latest ajaxhelper from your packages directory. Something like:

cvs -d:pserver:mailto:anonymous@cvs.openacs.org/cvsroot checkout ajaxhelper

This version should work, however, I have not tested it as I wrote the script several years ago using an earlier ajaxhelper version. Compare the dates of my post above with the ajaxhelper version at the time and use that version if you run into any problems.

Once checked out, visit the APM at YOURSERVER/acs-admin/apm, click on Install Software and install the package.

Read the doc/ files in ajaxhelper.

And it should work!

Best of luck,
Ryan

Hi Ryan: I'm trying to install the Ajax helper Package in "server/acs-admin/apm/packages-install"

--------------------------------------------------------
Install Package Directory Comment
X ajaxhelper 5.2.2 /packages/ajaxhelper New install.
-----------------------------------------------------------

I select the package and then press next.. The message is "No packages selected." What can I do? Thanks.

Hi Ryan: I install the new version, and finally I could run the smart-search-widget.tcl, but when I go to type any letter in the box, the internet explorer says that the page is ready but have error:

'Ajax' is undefined
smart-search-widget.tcl
line 111
char 1

This has to be with the ajaxhelper? or is another thing?
thanks.

Collapse
Posted by Ryan Gallimore on
Hi Lourdes,

Yes, this looks like a problem with the JavaScript created by ajaxhelper. You will have to make sure the objects are referenced correctly.

I'd suggest opening the page in FireFox + Firebug to see exactly what line is producing the error. It most likely has something to do with differences between the ajaxhelper version on which I developed the widget, and the current ajaxhelper.

Once you find the line, see if you can figure out what ajaxhelper proc should be called.

Best regards,
Ryan

Collapse
Posted by Lourdes Martínez on
Hi Ryan. thank you for all your help..

In the Firefox + firebug show me 1 error:
-----------------------
Ajax is not defined
onkeyup(keyup charCode=0, keyCode=83) --- eRBcWOuq...3Fg%3D%3D (line 2)
new (Ajax.Updater)("results_box"...ameters: Form.serialize("searchform")});
------------------------
when I click in "onkeyup" or in the line "new(Ajax.Update...) the script show me:

function onkeyup(event) {
new (Ajax.Updater)("results_box", "search-users", {asynchronous: true, method: "post", parameters: Form.serialize("searchform")});
document.getElementById("results_box").style.visibility = "visible";
}

Where can I found this function, or what is wrong with this function?

Collapse
Posted by Eduardo Santos on
Hi Lourdes,

The problem you reported probably means that the JS code is not being loaded for the page. take a lok at the Ajax Helper docs in the /doc/ajaxhelper in you install. In order for the package to work, you need the following changes:

The ajax helper package must be installed and mounted in /ajax . The installer should automatically mount the ajax helper in /ajax/ upon installation of the package.

Lee Denison's template::head code must be available. This code will soon be committed to CVS head after consulting the community and some more testing.

In the mean time, you can find the files you need to implement template head from packages/ajaxhlper/www/docs. You should copy the files into the following locations
acs-integration-procs.tcl
openacs_root/packages/acs-templatng/tcl/
head-procs.tcl
openacs_root/packages/acs-templatng/tcl/
blank-master.adp
openacs_root/www/

blank-master.tcl
openacs_root/www/
site-master.adp
openacs_root/www/
site-master.tcl
openacs_root/www/
default-master.tcl
openacs_root/www/
default-master.adp
openacs_root/www/

Hi Eduardo, I install again the package in /ajax. When I copy the files (acs-integration-procs.tcl,head-procs.tcl,blank-master.adp....etc) into the following location I have that error in server:

Undefined callback subsite::get_extra_headers
while executing
"error "Undefined callback $callback""
(procedure "callback" line 11)
invoked from within
"callback subsite::get_extra_headers"
invoked from within
"join [callback subsite::get_extra_headers] "\n""
invoked from within
"append head [join [callback subsite::get_extra_headers] "\n"]"
("uplevel" body line 57)
invoked from within
"uplevel {
ad_page_contract {
This is the highest level site specific master template.
site-master adds site wide OpenACS functionality to e..."
(procedure "code::tcl::/var/lib/aolserver/oacspa/www/site-master" line 2)
invoked from within
"code::tcl::$__adp_stub"
invoked from within
"if { [file exists $__adp_stub.tcl] } { ....

What can I do.
Thanks.

Hi Lourdes,

This problem seems to be related with the package versions you are using. Can you take a look at wich versions you are using? Take a look at the file /acs-admin/apm link for the acs-templating and acs-subsite packages, please.

Sorry Lourdes. I've seen in your previous post that you are using OpenACS 5.2.3, right? In order to the Ajax Jelper to work in this version, there can be some complications. The problem you've reported indicates that there was'nt yet the subsite callback that creates the template::head API's.

Maybe this link can help you:

http://fisheye.openacs.org/browse/OpenACS/openacs-4/packages/acs-subsite/tcl/subsite-callback-procs.tcl

I remember to make it work on 5.2.3, but I don't remember how I did it. I'm going to check my commit log and show you latter.

Cheers

Hi Eduardo: The OpenACS is 5.2.2 and the AjaxHelper version is 0.87d.
Hi Eduardo: Do you check your commit logs ? thanks.
Hi Eduardo: Do you check?
Hi Lourdes,

What do you mean if I check my commit logs?

Collapse
Posted by Lourdes Martínez on
Hi Eduardo, you mention previously that you don't remember how you did it, and you were going to review your commit logs and show me later.

I could not have resolved the problem yet. I only need to integrate the smart search widget (autosuggest) in a text box with the openacs doing only a select /n from a table.

Thanks for your help.

Collapse
Posted by Ryan Gallimore on
Hi Lourdes,

Is it possible for you to upgrade your instance to version 5.4.3?

This is going back a ways, but I think the widget was developed without template::head.

If not, please post your code to pastebin.org so we can take a look.

Ryan

Collapse
Posted by Lourdes Martínez on
Ok, I will update my instance to the version 5.4.3. I don't know if that version have the ajaxhelper package.

I only need that the box show me the results of my select depending on the letter I write.

I show you my tree files:

1. smart-search-widget.tcl
----------------------------
set js_update_user_select [ah::ajaxupdate \
-container "results_box" \
-url "search-users" \
-pars "Form.serialize('searchform')"]

2. smart_search_widget.adp
---------------------------
.
.

&lt;.. id="searchform"&gt;
&lt;table border="0" cellpadding="0" cellspacing="0"&gt;
&lt;tr&gt;
&lt;td>
&lt;.. type="text" name="search" id="search" onKeyUp="@js_update_user_select;noquote@ document.getElementById('results_box').style.visibility='visible';" autocomplete="off" value="" /&gt;
.

3. search_users.tcl
---------------------
ad_page_contract {

@author Ryan Gallimore (mailto:rgallimore@viscousmedia.com)
@creation-date 2006-09-23

} {
{search ""}
}

#ns_log notice "private-messaging search='$search'"

if { [string length $search] == 0 } {
ns_write "No results"
ad_script_abort
} elseif { [string length $search] < 3 } {
# Show all results if search string is 1 or two characters.
set search ""
}

set search [string tolower $search]
set package_id [ad_conn subsite_id]
set response ""

# Get users in the current subsite only

db_foreach get_search_results "

select n,n,n
from my_table
order by n

# select distinct u.username,
# p.first_names || ' ' || p.last_name as name,
# u.username as email
# from pers p, use u, acs_rels rel, app_groups ag
# where p.person_id = u.user_id
# and u.user_id = rel.object_id_two
# and rel.object_id_one = ag.group_id
# and ag.package_id = :package_id
# and (lower(p.first_names || ' ' || p.last_name) like '%$search%'
# or lower(u.username) like '%$search%')
# order by name

" {

set one_result "$name ($email)"

# Bold search text as you type
set start [string first $search [string tolower $one_result]]
set end [expr $start + [string length $search]]
set one_result_bolded "[string range $one_result 0 [expr $start-1]]&lt;b&gt;[string range $one_result [expr $start] $end][string range $one_result [expr $end+1] [expr [string length $one_result] - 1]]"

append response "&lt;a href=\"#\"&gt;$one_result_bolded&lt;/a&gt;&lt;br&gt;"

} if_no_rows {
set response "No results"
}

ns_write $response

ad_script_abort

Hi Eduardo / Hi Ryan:

Finally I could run the smart-search-widget!!! doing the upgrade and install the ajaxhelper package. Thankssss!

Now I need another help: when I write any letter in the box, the result bring me all the word that exist in the table.

I only need that the box go recognizing letter by letter and do a filter, and when I select my choice, this return into the box..

I'm not sure what .js file do this function?

Thanks again.

Hi Eduardo / Ryan:

I found the way to return exactly the letters I write...

How I do to select my choice and return this value into the box?

Hi Lourdes,

I'm glad you got the widget working with ajaxhelper.

Just click on the link and that will select the user and send you to another page.

If you want to change the link, look for it in user.tcl

For different behaviour, read through the ajaxhelper docs and see if you can find what you're looking for there.

Regards.