Adding an Auto-Completer to a non-workflow page

From EPrints Documentation
Jump to: navigation, search

NOTE: THIS IS INCOMPLETE - CAN SOMEONE FILL IN THE DETAILS??

Known to work for EPrints 3.2

Using the EPrints application framework, we can create auto-completion for any input, but we need to understand how the system works, and build the pages by the rules.

How Auto-Completion works, in general

Understanding AJAX Auto-Completion is a bit of a circular process, so bear with me.

You need three things

  1. An Auto-Completer "engine" (a complex Javascript code-base)
  2. A script to do the lookup work & return stuff
  3. (x)html structure to accept the returned stuff

The process:

  • When the user types into an input box, a javascript event is triggered which sends that input value to a script.
  • The script returns an unordered list, which the Auto-Completer code displays for the user to select from
  • The Javascript engine then fills in the identified inputs with the selected values.

A very simple example

Input

Here we have an input:

<input type="textfield" size="25" id="oarj_oname"
       name="organisation_name"
       onkeypress="return EPJS_block_enter( event )" />

There are two important factors here:

  • id="oarj_oname" defines the identity of the element, which we need to read the "seed" value from, and fill in with the selected data. This element actually breaks down into two sub-parts:
    • "oarj" is the component
    • "oname" gives the part of the components
  • onkeypress="return EPJS_block_enter( event )" is the EPrints application magic incantation" to trigger some javascript when the user types something into the input box.
Input with return locations

What we also need the (x)html to receive the output of the mysterious javascript call:

 <input type="textfield" onkeypress="return EPJS_block_enter( event )"
        size="25" id="oarj_oname" name="organisation name">
 <div style="display: none;" id="oarj_oname_drop" class="ep_drop_target"></div>
 <div style="display: none;" id="oarj_oname_drop_loading" class="ep_drop_loading">
  <img src="/style/images/loading.gif" alt="" style="border: 0pt none ;">
 </div>

Some of this is more magic incantation There are two divs - one to accept the return from the AJAX magic, and one to display the "I'm doing stuff" graphic.

Their ids are declared in the Javascript call, however they should be derived from the id of the input field:

  • "oarj_oname_drop" is the named element to receive the output of the javascript
  • "xxxx_loading" is the element that displays the "I'm busy" graphic. It's name is specific: it is the same as the drop element, with _loading appended.
Input with return locations and javascript call

Finally, we need to append the bit that makes the Auto-Completer call:

 <input type="textfield" onkeypress="return EPJS_block_enter( event )"
        size="25" id="oarj_oname" name="organisation name">
 <div style="display: none;" id="oarj_oname_drop" class="ep_drop_target"></div>
 <div style="display: none;" id="oarj_oname_drop_loading" class="ep_drop_loading">
  <img src="/style/images/loading.gif" alt="" style="border: 0pt none ;">
 </div>
 <script type="text/javascript">
 // <![CDATA[
 ep_autocompleter( "oarj_oname", "oarj_oname_drop", "/cgi/get_orgs",
                   {relative: "oarj_oname", component: "oarj"  },
                 [ $("oarj_oname")], [], "&field=name")
 // ]]></script>

The javascript has multiple fields:

  1. "oarj_oname" is the id of the element that the Auto-Completer reads from
  2. "oarj_oname_drop" is the id of the element the return data is put into
  3. "/cgi/get_orgs" is the name of the script called to get the return data
  4. {relative: "oarj_oname", component: "oarj" } is magic incantation - referred to as basenames in the javascript code, I give it the id of the element and the component level name
  5. [ $("oarj_oname")] more magic incantation - width_of_these ??
  6. [] would be the fields_to_send
  7. "&field=name" is extra_params - a way of passing extra cgi parameters to the script

The Returned Data

The final part of this loop is the returned data. It doesn't particularly matter what is used to create this data, so long as the users client receives the data in the right format

With the EPrints Auto-Completer, the format for this return is fairly simple:

   <ul>
    <li>Some informative text<br />
      (with something else on a new line
     <ul>
      <li id="for:value:component:_oname">Fandangle</li>
      <li id="for:value:component:_diddle">Fruit Loops</li>
      <li id="for:value:component:_a1">Another One</li>
      <li id="for:value:component:_b2">Befuddled Too</li>
     </ul>
    </li>
    <li>
      .....
    </li>
   </ul>

To break this down, we're looking at a list of returns, each of which has a list of "fill-in" items

Lets break the list down into parts:

   <ul>
    <li>Some informative text<br />
      (with something else on a new line</li>
    <li>
      Another entry
    </li>
    <li>
      The Third Way
    </li>
     <li>
      Option Four
    </li>
  </ul>

This is the list of items to display to the user - in this case, 4 items. What we don't have here is the data to be filled in, once a selection has been made. That looks like this:

    <li>Some informative text<br />
      (with something else on a new line
     <ul>
      <li id="for:value:component:_oname">Fandangle</li>
      <li id="for:value:component:_diddle">Fruit Loops</li>
      <li id="for:value:component:_a1">Another One</li>
      <li id="for:value:component:_b2">Befuddled Too</li>
     </ul>
    </li>
 

Each item has a sub-list, where each item has a magic incantation for it's id: for:value:component: is the bit that the EPrints Auto-Completer code looks for, and _xxx names the sub_element that the data does into.

Given the ep_autocompeter call above, we have defined a component of oarj. Thus:

  • Fandangle
  • puts "Fandangle" in the element id="oarj_oname"
  • Fruit Loops
  • puts "Fruit Loops" in the element id="oarj_diddle"
  • Another One
  • puts "Another One" in the element id="oarj_a1"
  • Befuddled Too
  • puts "Befuddled Too" in the element id="oarj_b2"

Note: If the element does not exist, then the value is ignored... sensibly.

Creating the (x)html in an EPrints page

To make an Auto-Completer form within eprints, it's basically a case of assembling the DOM:

  $d = $session->make_element("div");
  $d->appendChild( $session->render_input_field( type=>"textfield",
                                                     name=>"oarj_oname",
                                                     id=>"oarj_oname",
                                                     class=>"ep_form_text",
                                                     size=>25
                                                   )
                 );
  $d->appendChild( $session->make_element("div",
                                      class=>"ep_drop_target",
                                      id=>"oarj_oname_drop",
                                      style=>"display:none"
                                             )
                  );

  $div = $session->make_element("div", class=>"ep_drop_loading",
                                   id=>"oarj_oname_drop_loading",
                                   style=>"display:none");
  $div->appendChild( $session->make_element(
                                    "img", style=>"border:0", alt=>"",
                                    src=>"/style/images/loading.gif"
                                              )
                   );
  $d->appendChild( $div );
  $d->appendChild( $session->make_javascript( 'ep_autocompleter( "oarj_oname", "oarj_oname_drop",
                                              "/cgi/get_orgs",
                                              {relative: "oarj_oname", component: "oarj" },
                                             [ $("oarj_oname")], [], "&field=name")' ) );

Making an EPrints page from a CGI script

Making a page from a CGI script is fairly easy:

#!/path/to/appropraite/perl
use EPrints;

my $session = EPrints::Session->new();

my( $page, $title ) = make_page( $session );

$session->build_page( $title, $page );
$session->send_page();

$session->terminate();

exit;
sub make_page {
	my( $repository) = @_;

	my $page = $repository->make_doc_fragment;
	my $title = $repository->make_text( "OARJ Organisation management tool" );

        ## Stuff to build 

	return( $page, $title );
}

Making an EPrints page within the interface

For this, you need a new module in perl_lib/EPrints/Plugins/Screen/ - see (somewhere else)

The code to create the return stuff

Auto-Completers are fine, but they need something to look-up information and return stuff for the user to select from.

The important factors you need to know to write a script are:

  • The value from the input field will be in a parameter called "q"
  • You need to return data with an xml header
  • You need to return an unordered list
  • You can send back more data than you are going to use

An example

Here is my lookup from Open Access Repository Junction:

use EPrints;

my $dbh = /// connect to database

sub get_orgs {
  my ($dbh, $index_key, $index_value) = @_;

  my $db_return = /// a reference to a list of returns from a database query
                  //// name, acronym, url
  my %data = ();

  # build up a lump of (x)html for each item
  foreach my $ref (@$db_return) {
     my $html = "<li>".$ref->[0];
     $html .= "(".$ref->[1].")" if $ref->[1];
     $html .= "[".$ref->[2]."]" if $ref->[2];
     $html .= "<ul>";
     $html .= "<li id='for:value:component:_oname'>".$ref->[0]."</li>";
     $html .= "<li id='for:value:component:_ourl'>".$ref->[1]."</li>";
     $html .= "<li id='for:value:component:_oacronym'>".$ref->[2]."</li>";
     $html .= "</ul></li>\n";
     $data{$ref->[2]} = $html;
  }
  
  my @html = ();
  if (!scalar keys %data) 
  {
    push @html, "<!-- no matches -->";
    return (join "\n", @html)
  }

  push @html, "<ul class='journals'>\n";
  foreach my $id (keys %data) 
  {
    push @html, "$data{$id}\n";
  }
  push @html, "</ul>\n";
  return (join "\n", @html)

}

my $session = EPrints::Session->new();

# we need the send an initial content-type
print <<END;
<?xml version="1.0" encoding="UTF-8" ?>

END

# then we send the fragment of html for the autocompleter
my $value = "";
my $field = "";
$value = $session->param( "q" );
$field = $session->param( "field" );

print get_orgs($dbh, $field, $value);

#print STDERR get_journals( $q );

$session->terminate;