banner — display banner ads or messages, based on category and optional weighting


Attribute Pos. Req. Default Description
category Yes   default For a weighted banner display, this field specifies category name; only database entries where the category field matches this value are taken as possible candidates for display. In an unweighted display, this field specifies banner code; of course, only one database entry with the matching value in the code field should exist.
table     banner The banner table name. The default is reasonable and rarely needs to be changed. my_banner_table can be set to override this value.
r_field     rotate Row in a banner table may include multiple banners in the banner column (separated by specified delimiters). The column specified by r_field is consulted (expecting a boolean value) to determine whether to sequentially rotate banners. This is only used with non-weighted banner display scheme.
b_field     banner Banner descriptor field. In other words, name of the column that will contain actual banner text to display. If a proper delimiter is used, and the r_field column is true, this field may contain multiple banner texts.
c_field     category Specify the column containing banner category. Only banners from the selected category will be taken as possible candidates for display. This is only used with weighted ads.
w_field     weight Specify the table column containing banner weights. This is only used with weighted ads.
separator     : Separator within the table key (the code column), used for multilevel categorized ads. This is only used with unweighted ads.
delimiter     {or} Delimiter that sets different banner texts in the banner field apart. This is only used with unweighted ads.
weighted     0 Use weighted banner system? In a weighted system, the database is expected to contain multiple entries with the same category, and then the banners are selected in regard to their relative weight (more weight = more visibility). The sum of weights can be arbitrary and does not need to equal 1 (obviously - because that would require a manual intervention on every banner addition/remove operation).
once     0 Don't rebuild the banners until the appropriate tmp/Banners/*/total_weight files are manually removed? This is only used with weighted ads.
interpolate     0 interpolate output?
hide     0 Hide the tag return value?


Interchange has a built-in banner display system designed to show ad or other messages, according to optional categories and weighted values. All this functionality is accessible using the [banner] tag.

The weighted system, if used, will pre-built banners in the directory Banners/*/ under the catalog temporary directory (this will happen when the banners are first requested after a catalog reconfiguration or Interchange daemon start). It will build one copy of the banner for every value of weight. If one banner is weighted 7, one 2 and one 1 (in abstract points), then a total of 10 pre-built banners will be made. The first will be displayed 70 percent of the time, the second 20 percent and the third 10 percent, in random fashion. If all banners need to be equal (that is, displayed randomly with the same probability), give each a weight of 1.

Each category has its own separate weighting if categorized display is requested; otherwise all weights enter the same logical "pool".

Note that the term rotation refers to sequentially selecting and displaying banners from the same banner field (keeping a separate counter for each client). This, of course, makes sense in a context where banner contains multiple banner entries, separated by chosen delimiters.


This tag does not appear to be affected by, or affect, the rest of Interchange.


Example: Banner Ads

For the relevant supplemental description and all ready-to-use examples, see the Implement Banner Ads HOW-TO.



banner is available in Interchange versions:

4.6.0-5.9.0 (git-head)


Interchange 5.9.0:

Source: code/SystemTag/banner.coretag
Lines: 119

# Copyright 2002-2007 Interchange Development Group and others
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.  See the LICENSE file for details.
# $Id: banner.coretag,v 1.6 2007-03-30 23:40:49 pajamian Exp $

UserTag banner              Order        category
UserTag banner              addAttr
UserTag banner              PosNumber    1
UserTag banner              Version      $Revision: 1.6 $
UserTag banner              Routine      <<EOR
sub {
my ($place, $opt) = @_;

sub initialize_banner_directory {
  my ($dir, $category, $opt) = @_;
  mkdir $dir, 0777 if ! -d $dir;
  my $t = $opt->{table} || 'banner';
  my $c_field;
  my $append = '';
  if($category) {
    $append = ' AND ';
    $append .= ($opt->{c_field} || 'category');
    $category =~ s/'/''/g;
    $append .= " = '$category'";
  my $db = database_exists_ref($t);
  if(! $db) {
    my $weight_file = "$dir/total_weight";
    return undef if -f $weight_file;
    $t = "no banners db $t\n";
    Vend::Util::writefile( $weight_file, $t, $opt);
    return undef;
  my $w_field = $opt->{w_field} || 'weight';
  my $b_field = $opt->{b_field} || 'banner';
  my $q = "select $w_field, $b_field from $t where $w_field >= 1$append";
  my $banners = $db->query({
    query => $q,
    st => 'db',
  my $i = 0;
  for(@$banners) {
    my ($weight, $text) = @$_;
    for(1 .. $weight) {
      Vend::Util::writefile(">$dir/$i", $text, $opt);
  Vend::Util::writefile(">$dir/total_weight", $i, $opt);

sub tag_weighted_banner {
  my ($category, $opt) = @_;
  my $dir = catfile($Vend::Cfg->{ScratchDir}, 'Banners');
  mkdir $dir, 0777 if ! -d $dir;
  if($category) {
    my $c = $category;
    $c =~ s/\W//g;
    $dir .= "/$c";
  my $statfile = $Vend::Cfg->{ConfDir};
  $statfile .= "/status.$Vend::Cat";
  my $start_time;
  if($opt->{once}) {
    $start_time = 0;
  elsif(! -f $statfile) {
    Vend::Util::writefile( $statfile, "banners initialized " . time() . "\n");
    $start_time = time();
  else {
    $start_time = (stat(_))[9];
  my $weight_file = "$dir/total_weight";
  initialize_banner_directory($dir, $category, $opt)
    if  ( ! -f $weight_file  or  (stat(_))[9] < $start_time );
  my $n = int( rand( readfile($weight_file) ) );
  return Vend::Util::readfile("$dir/$n");
return tag_weighted_banner($place, $opt) if $opt->{weighted};

my $table = $opt->{table}     || 'banner';
my $r_field = $opt->{r_field} || 'rotate';
my $b_field = $opt->{b_field} || 'banner';
my $sep  = $opt->{separator}  || ':';
my $delim = $opt->{delimiter} || "{or}";
$place = 'default' if ! $place;
my $totrot;
do {
  my $banner_data;
  $totrot = tag_data($table, $r_field, $place);
  if(! length $totrot) {
    # No banner present
    unless ($place =~ /$sep/ or $place eq 'default') {
      $place = 'default';
  elsif ($totrot) {
    my $current = $::Scratch->{"rotate_$place"}++ || 0;
    my $data = tag_data($table, $b_field, $place);
    my(@banners) = split /\Q$delim/, $data;
    return '' unless @banners;
    return $banners[$current % scalar(@banners)];
  else {
    return tag_data($table, $b_field, $place);
} while $place =~ s/(.*)$sep.*/$1/;


Interchange Development Group


DocBook! Interchange!