Plate Definitions

A plate definition is a style, or 'layout', for a cell holding details of a record. The main use of plates is in pedigrees, where each ancestor cell is a rendition of an animal record using a plate. The user may choose from available plates using a pull-down menu above the pedigree screen. There are various inbuilt plates with names such as 'Compact Layout', 'Default Layout', etc. By selecting an entry from this list, the user can have some control over the pedigree appearance.

Plates are also used in search results screens in 'merged' mode, when related records are listed along with the primary search results. E.g., in a search for animal records, the litter records for each located animal can be listed along with the animal itself. Or, when viewing litter records, the offspring for each litter can be included. Each primary search result record is rendered using a plate, with the 'dependent' records tabulated to the right.

Plates are also used in search results 'grid mode', when the search results are shown in a grid. Each cell in this grid is a record rendered using a plate.

There are 2 ways you can customize plates. Firstly, you can add completely new, custom plates. And secondly, you can insert custom, additional HTML above and/or below, all plate layouts (including the inbuilt plates).

Custom Plate Definitions

You can add your own custom plate definitions - i.e. your own layouts for record cells. This gives you a great deal of control over the layout of pedigrees etc. if you want it. To add your own plate, you need to:

  • Add an entry to the 'plates' pull-down menu that appears above pedigrees and search results pages (when in merged or grid mode).
  • Reimplement some hooks in pedserve-hook-plate.pl.

These steps will now be described in more detail, demonstrating the steps necessary to produce a layout that includes the name of the animal at the top, and a bulleted list of fields beneath, including hyperlinked thumbnail image. This plate will work both in pedigrees (sample) and in search results pages (sample).

1. Adding The Plate Selector Entry

You need to edit pedserve-hook-setup.pl to add in the plate definition, e.g.:

.
.
&pdsSetPlateDef('A_myplate', 'label=My Plate;myplate=yes;');
.
.

The plate definition is given as a string containing name=value entries, separated by semicolons. There must be a label entry which gives the user-visible name to go in the plate selector. E.g. 'My Layout', etc. You should also include other entries that your hook code can check for in order to detect when your plate is in use (and not an internal Pedserve plate); in the above example this is what the myplate=yes entry is for.

The name of the plate definition - 'A_myplate' in the above example - must be a unique definition name, and must start with the table suffix of the table to which it applies, then followed by an underscore. Typically any plates you add will be for animal tables and so they will start with 'A_'.

2. Reimplementing the Hooks

You need to edit the pedserve-hook-plate.pl script and reimplement the hookPlate_Prepare, hookPlate_Render and possibly also the hookPlate_GetExtraFlds hooks.

You only need to reimplement hookPlate_GetExtraFlds if your plate layout needs any field data beyond that which is being fetched for whatever display fields definition has been selected (by the user). E.g. if your custom plate uses some specific user-defined fields you have added. The reason for this is that when Pedserve fetches data from the database, it does not automatically fetch all the fields - it only fetches those it needs. Which might not include the ones your custom plate wants. For this reason, when your custom plate is selected, you need to take steps to ensure that Pedserve will pull any fields you need. You do this by reimplementing the hookPlate_GetExtraFlds hook, e.g.:

Show/Hide Sample Code

# Hook function to return extra fields that are required
# by a given plate. The return value is a string, a comma
# separated list of full field names that the plate needs to be pulled
# from the database (in addition to normal 'display' fields).
#
# Arg 1 - table suffix
# Arg 2 - reference to a map holding the decoded plate definition
# Arg 3 - context
# Arg 4 - field prefix
# OPTIONAL
# Arg 5 - reference to map for future additional arguments; possibly undefined
sub hookPlate_GetExtraFlds {
   my ($strTableSuffix, $rmPlateDef, $strContext, $strFldPrefix, 
       $rmArgsEx) = @_;

   # You probably dont want to interfere with the correct working of 
   # pedserve's internally defined plates. So what you should do is 
   # make your plate definition identifiable - by including terms that you can test
   # for here. Then you can defer to the default implementation when necessary.
   
   my $fUseStdImpl = 1;
   my $strReturn = '';

   # Simple example that checks for the term 'myplate=yes' in the
   # plate definition, and if present returns a specific field name.
   # Note also the check against $strTableSuffix to ensure this is
   # only used with animal records, and the check that this is not
   # a pedserve-private definition:
   if (!defined($$rmPlateDef{'pdsprivate'}) &&
       $strTableSuffix eq 'A' && 
       defined($$rmPlateDef{'myplate'}) &&
       $$rmPlateDef{'myplate'} eq 'yes') {
      # Dont defer to standard implementation below:
      $fUseStdImpl = 0;

      # Set the fields to return. In this example we are telling
      # Pedserve that we want the thumbnail image field (A_IMG) (if this
      # field is enabled).
      if ($::custom_fAnimalTablesHaveImg) {
          $strReturn = $strFldPrefix.'A_IMG';
      }
   }

   # If the above code has not handled this plate definition
   # itself, defer to the standard implementation:
   if ($fUseStdImpl) {
      $strReturn = &pdsHookStdImpl_Plate_GetExtraFlds(
          $strTableSuffix, $rmPlateDef, $strContext, $strFldPrefix, $rmArgsEx);
   }
   
   return $strReturn;
}

The next hook you need to reimplement is hookPlate_Prepare. This 'prepares' a plate - creates and returns a map of 'plate information', that is subsequently passed to hookPlate_Render. What does 'preparing' a plate mean? Whatever you want really. Its just a hook that gives you a chance to do any lengthy preparation work prior to hookPlate_Render being called. A given page may 'prepare' a plate once, but 'render' it many times. E.g. in a pedigree page, a separate prepared plate is used for each generation of ancestors; but within each generation, the given prepared plate is rendered separately for each ancestor in that generation. So any lengthy operation in preparing a plate for output should be done at the preparation stage if possible.

Show/Hide Sample Code

# Hook function to 'prepare' a plate.
#
# Arg 1 - table suffix
# Arg 2 - reference to map containing decoded plate definition
# Arg 3 - context
# Arg 4 - reference to array holding the field names available
#         to be placed in the plate (subject to the plate definition)
# Arg 5 - reference to array holding the group names for each
#         field in the above array
# Arg 6 - field prefix
# Arg 7 - map containing additional options
# OPTIONAL
# Arg 8 - reference to map for future additional arguments; possibly undefined
sub hookPlate_Prepare {
   my ($strTableSuffix, $rmPlateDef, $strContext, $rastrAvailableFields,
       $rastrAvailableFieldGroups, $strFldPrefix, $rmOptions, $rmArgsEx) = @_;
   
   # You probably dont want to interfere with the correct working of 
   # pedserve's internally defined plates. So what you should do is 
   # make your plate definition identifiable - by including terms that you can test
   # for here. Then you can defer to the default implementation when necessary.
   
   my $fUseStdImpl = 1;
   my %mPreparedPlate = ();

   # Simple example that checks for the term 'myplate=yes' in
   # the plate definition, and if present does its own
   # plate 'preparation' - which simply stores a string in the 
   # 'prepared plate' map.
   # Note also the check against $strTableSuffix to ensure this is only
   # used with animal records, and the check that this is not a
   # pedserve-private definition:
   if (!defined($$rmPlateDef{'pdsprivate'}) &&
       $strTableSuffix eq 'A' && 
       defined($$rmPlateDef{'myplate'}) &&
       $$rmPlateDef{'myplate'} eq 'yes') {
      # Dont defer to standard implementation below:
      $fUseStdImpl = 0;

      # We store some trivial formatting
      # information in the 'prepared plate' map, that
      # is then used in our &hookPlate_Render() hook. 
      # With a real-world case you will probably either
      # (a) do nothing at all in your preparation step, or
      # (b) do as much work here as possible.
      # A good example of a real-world use of the plate preparation
      # phase can be seen in the internal code for the inbuilt plates.
      # [Only accessible with Pedserve Developer edition]
      $mPreparedPlate{'fontargs'} = 'face="Times, serif" size="4"';

      # We also store the reference to the array of names of fields
      # that we are expected to include in the rendered plate:
      $mPreparedPlate{'fieldnames'} = $rastrAvailableFields;
   }

   # If the above code has not handled this plate definition itself,
   # defer to the standard implementation:
   if ($fUseStdImpl) {
      %mPreparedPlate = &pdsHookStdImpl_Plate_Prepare(
          $strTableSuffix, $rmPlateDef, $strContext, $rastrAvailableFields, 
          $rastrAvailableFieldGroups, $strFldPrefix, $rmOptions, $rmArgsEx);
   }
   
   return %mPreparedPlate;
}

The final, and most important, hook you need to reimplement is hookPlate_Render. This is what actually generates the HTML for a given record. It is passed, amongst various other arguments, a reference to the map of 'prepared plate information' that hookPlate_Prepare created. E.g.:

Show/Hide Sample Code

# Hook function to render a record into HTML using a prepared plate.
#
# Arg 1 - table suffix
# Arg 2 - reference to map containing decoded plate definition
# Arg 3 - context
# Arg 4 - reference to prepared plate [as returned by &hookPlate_Prepare()]
# Arg 5 - map of field name -> field label
# Arg 6 - field prefix
# Arg 7 - primary key
# Arg 8 - reference to map holding extracted field results
# Arg 9 - text emphasis flags to be applied to all fields
# Arg 10 - class
# Arg 11 - reference to map holding other options
# OPTIONAL
# Arg 12 - reference to map for future additional arguments; possibly undefined
sub hookPlate_Render {
   my ($strTableSuffix, $rmPlateDef, $strContext, $rmPlate, $rmFieldLabels,
       $strFldPrefix, $nPrimaryKey, $rmResults, $nEmph, $strClass, 
       $rmOptions, $rmArgsEx) = @_;
   
   # You probably dont want to interfere with the correct working of
   # pedserve's internally defined plates. So what you should do is 
   # make your plate definition identifiable - by including terms that
   # you can test for here. Then you can defer to the default implementation
   # when necessary.
   
   my $fUseStdImpl = 1;
   my $htmlRet = '';

   # Example that checks for the term 'myplate=yes' in the
   # plate definition, and if present does its own plate rendering - which
   # involves displaying the name and the thumbnail image.
   # Note also the check against $strTableSuffix to ensure this is only
   # used with animal records, and the check that this is not a 
   # pedserve-private definition:
   if (!defined($$rmPlateDef{'pdsprivate'}) &&
       $strTableSuffix eq 'A' && 
       defined($$rmPlateDef{'myplate'}) &&
       $$rmPlateDef{'myplate'} eq 'yes') {
      # Dont defer to standard implementation below:
      $fUseStdImpl = 0;

      # Get the basic HTML for the animals ID (which is the name, linked):
      $htmlRet = &pdsMakeFullFieldValueHTML($strFldPrefix.'A_ID', 
                               $strContext, $nPrimaryKey, $rmResults);
      # Turn the name into a para:
      $htmlRet = $::g_q->p($htmlRet);

      # Now we add any other fields as a bulleted list:
      my @ahtmlListItems = ();
      my $rastrAvailableFields = $$rmPlate{'fieldnames'};
      for (my $i = 0; $i < scalar(@$rastrAvailableFields); $i++) {
         my $strFldName = $$rastrAvailableFields[$i];
         # Ignore A_ID field here - we've already included it:
         next if $strFldName eq $strFldPrefix.'A_ID';
         # Get the field label text:
         my $strFldLabel = $$rmFieldLabels{$strFldName};
         # Turn the field value text into HTML:
         my $htmlFldLabel = $::g_q->escapeHTML($strFldLabel);
         $htmlFldLabel .= ': ' if $htmlFldLabel ne '';
         # Get the field value. We handle A_IMG with special
         # code to generate a linked thumbnail:
         my $htmlFldValue = '';
         if ($strFldName eq $strFldPrefix.'A_IMG') {
            my $strURL = &pdsGetImageURLFromResults($rmResults, $nPrimaryKey,
                                                $strFldPrefix, 'A_IMG', 1);
            if ($strURL ne '') {
               my $htmlImg = $::g_q->img({'-src' => $strURL, 
                                          '-border' => '0', 
                                          '-width' => '50'});
               my $strScriptSuffix = '';
               my $strScriptArgs = '';
               &pdsRecordGetTargetScriptSuffixAndArgs('A', $nPrimaryKey, 
                       'results', \$strScriptSuffix, \$strScriptArgs, 
                       {'linksource' => 'image'});
               if ($strScriptSuffix ne '') {
                  $htmlImg = &pdsMakeScriptLinkHTML($htmlImg, 
                                        $strScriptSuffix, $strScriptArgs);
               }
               $htmlFldValue = $htmlImg;
               # Dont display field label for thumbnail:
               $htmlFldLabel = '';
            }
         } else {
           $htmlFldValue = &pdsMakeFullFieldValueHTML(
               $strFldName, $strContext, $nPrimaryKey, $rmResults);
         }
         # Ignore it if there is no value:
         next if $htmlFldValue eq '';
         # Post a list item to our array of field value list items:
         push(@ahtmlListItems, $::g_q->li($htmlFldLabel.$htmlFldValue));
      }
      if (scalar(@ahtmlListItems) > 0) {
         $htmlRet .= $::g_q->ul(\@ahtmlListItems);
      }

      # If any font args were stored in the plate prep phase,
      # use them to apply a font tag to the html:
      my $strFontArgs = $$rmPlate{'fontargs'};
      if (defined($strFontArgs) && $strFontArgs ne '') {
         $htmlRet = '<font '.$strFontArgs.'>'.$htmlRet.'</font>';
      }

      # Apply any emphasis the caller wants us to apply:
      $htmlRet = &pdsMakeEmphHTML($htmlRet, $nEmph);

      # Now enclose this in a <p> tag where the 'class'
      # attribute is that supplied by the caller.
      my %mAttrs = ();
      $mAttrs{'-class'} = $strClass if $strClass ne '';
      $mAttrs{'-align'} = 'center';
      $htmlRet = $::g_q->p(\%mAttrs, $htmlRet);
   }

   # If the above code has not handled this plate definition itself,
   # defer to the standard implementation:
   if ($fUseStdImpl) {
      $htmlRet = &pdsHookStdImpl_Plate_Render(
          $strTableSuffix, $rmPlateDef, $strContext, $rmPlate, $rmFieldLabels,
          $strFldPrefix, $nPrimaryKey, $rmResults, $nEmph, $strClass,
          $rmOptions, $rmArgsEx);
   }
   
   return $htmlRet;
}

Click the thumbnail images at the right of this page to see examples of how this custom plate appears in practice.

Adding Custom HTML To ALL Plates

There is a quite different way you can alter the appearance of rendered plates, and that is to reimplement the hookPlate_GetCustomHTML hook. This gives you an opportunity to alter the appearance of all plates in the system by returning extra HTML that is to be inserted at the front, or appended to the bottom, of the rendered plate HTML.

E.g. here is a trivial example that returns HTML giving the primary key value, to be displayed above the normal plate contents:

Show/Hide Sample Code

# Hook function to return custom HTML for a plate. This is your way to return
# additional HTML that is to be displayed alongside the default plate output.
#
# Arg 1 - table suffix
# Arg 2 - reference to map holding the decoded plate definition
# Arg 3 - context
# Arg 4 - reference to map holding prepared plate information
# Arg 5 - position - string giving the position for which custom html is
#         being sought. At present this may be 'above' or 'below'
# Arg 6 - reference to map of field values
# Arg 7 - primary key
# Arg 8 - field name prefix
# Arg 9 - reference to map containing options
# OPTIONAL
# Arg 10 - reference to map for future additional arguments; possibly undefined
sub hookPlate_GetCustomHTML {
   my ($strTableSuffix, $rmPlateDef, $strContext, $rmPlate, $strPosition,
       $rmFieldValues, $nPrimaryKey, $strFldPrefix, $rmOptions, $rmArgsEx) = @_;

   my $htmlRet = '';   
   
   # Trivial example returning HTML for primary key to
   # displayed above the normal plate contents:
   if ($strPosition eq 'above') {
      $htmlRet = "Record: $nPrimaryKey";
   }
   
   return $htmlRet;
}

Custom Plate - Pedigree
Custom Plate - Pedigree


Custom Plate - Search Results
Custom Plate - Search Results

USEFUL LINKS:
 EULA
 Pedserve Editions
 Sample City
 Consultancy
 Data Preparation
 Installation
 Regular Expressions
 Similarity Searching
 Configuration File
 Setup Script
 Stylesheet
 Database Design
 Hooks
 Date/Time
 User Defined Fields
 User Defined Records
 Display Fields Definitions
 Ordering Fields Definitions
 Highlighting
 Page Layout
 Field Definitions
 Field Formatting
 Shortcut Query Buttons
 Plates
 Command Buttons
 Connecting to the Database
 Warning Footer Message