The Penguin Greetings Content Developers' Guide

Edouard Lagache


Copyright © 2003-2005 (Penguin Greetings 1.0.0 edition)

A introduction to developing content for  the Penguin Greetings web-based ecards system.
Table of Contents:

  1. Introduction to Penguin Greetings Content Development
  2. Penguin Greetings Legalese
  3. Quickstart:

    1. I just want my own pretty cards, what do I do? (sys-admin quickstart)
    2. But I don't like the way my image looks in your template!  (template tweaking)

  4. Penguin Greetings development reference:
    1. Penguin Greetings Operational overview
    2. Penguin Greeting templates and Embperl
    3. Card configuration file and configuration parameter access
    4. Penguin Greetings parameters and CGI variables
    5. Using Embperl to simplify content development
    6. Creating Object-Oriented ecard sites
    7. Error handling and form field checking
    8. Testing and debugging templates: PgTemplateTest
    9. Creating secondary ecard sites
    10. Localizing ecard sites in Penguin Greetings
    11. Implementing CGI "Back" buttons
    12. Using shared template libraries
    13. Developing Penguin Greetings content using HTML::Mason
    14. Troubleshooting and Tips

Introduction to Penguin Greetings Content Development

Penguin Greetings is a Perl CGI tool that manages the information for web-based email greeting cards.  It is a tool in development but is already designed to be quite robust and dependable, while avoiding the complexity and overhead of an SQL database backend.  Penguin Greetings attempts to be a "virtuous" backend program: efficiently handling its duties while not getting in the way of the creative content that it serves. 

Penguin Greetings uses a concept of templates to separate the work of card management from the content to be managed.  The HTML to be used in cards and on the screens used to create a card are stored in separate files called templates.  These can be edited by content developers and can take advantage of any service that is accessible through HTML (and Embperl or Mason - see below.)  Penguin Greetings provides support for these templates with configuration files that can be extended by the developer to provide parameters for the cards and categories of cards.  Penguin Greetings can then be instructed (programmed) to run through a set of templates to assemble a card and then store its contents in databases as well as send a multi-part MIME email to the recipient.

[Table of Contents]

Penguin Greetings Legalese

Penguin Greetings is copyright © Edouard Lagache, 2003-2005. Penguin Greetings is released under the GNU General Public License, Version 2. For more information, see the COPYING file included with this software or visit: http://www.gnu.org/copyleft/gpl.html

The artistic content included with this software is also copyright © Edouard Lagache, 2003-2005. It is released under the Design Science License. For more information, see the DSL.txt file included with the software or visit: http://www.dsl.org/copyleft/.

Powered by Penguin Greetings web badge

If you use Penguin Greetings in your own web cards application.  Please give credit were credit is due by displaying the following web badge or something equivalent on your web site:

Powered by Penguin Greetings

There are now quite a few different badges included in Penguin Greetings with the various secondary ecard sites, so take a look if you want something different.  Better still, if you make a nicer web badge, please share it with me so that I can spread the wealth! :-)

[Table of Contents]

I just want my own pretty cards, what do I do?(development quickstart)

Penguin Greetings now ships with an ever growing collection of greeting cards that are similar in style and tastes to Apple's iCards.  System Administrators for systems large and small may find this a nifty addition to their server (even servers for one! ).  However, even the most un-artistic sys-admin will sooner or later want to add some cards of their own to their system.  Those with more ambitious plans will find a simple example a good point of departure.

The easiest way to get some content is to use the same method used for the existing collection - the ol'good camera.  Film cameras are just as useful for this with the advent of photos on disk which is available by most photo finishers.  Unfortunately, the next step is very much in the hands of the content developer - no matter how many thumbs you have.  Still gimp runs on any system that will run Penguin Greetings, and the simplest greeting cards can be made from photos that are rescaled and to which a caption is added.  You'll need to make a thumbnail image for the present system.  The simpleminded solution is to resize the image by about half (Existing Penguin Greetings sites use 40%) and name it something similar like picture.thb.jpg.

Assuming you've crossed the content hurdle and have a photo that you would like to add to the Penguin Greeting content as it is presently shipped, here are the steps needed to add a card to the system.  The bare-bones change is to add the card to the card.conf configuration file (a portion of which is below.)  Suppose you've created a new birthday card, then you need to add a new card to the list of cards and add that card to the category birthday.

# File: cards.conf
######################################################################
#             Greeting card configuration file for
#
#                ** PENGUIN GREETINGS (pgreet) **
#
# A Perl CGI-based web card application for LINUX and probably any
# other UNIX system supporting standard Perl extensions.
#
#     Edouard Lagache, elagache@canebas.org, April 2003
#
#
# The pgreet system uses the Perl Config::General module for
# reading in configuration file information.  The file format
# is the same as for the Apache web server.  The general
# configuration module contains information for both the
# CGI script and the system daemon.  These are kept in
# separate categories for simplicity of management.
#
######################################################################

# Card category definitions:

# ....



<category Birthday>
    caption = "Birthday Cards"
    cards = Birthday_blooms_on_trail
    cards = Bay_bridge_birthday
    cards = Fishing_Birthday
</category>

# ....

# 21
<card Fishing_Birthday>
    caption = "Fishing around for a way to say: Happy Birthday!"
    template = "picture_frame_card.tpl.html"
    image = "Fishing_Birthday.jpg"
    thumb = "Fishing_Birthday.thb.jpg"
</card>

# ....

Every card in the Penguin Greeting system needs a unique name - in this case the card added is called Fishing_Birthday.  Associated with each card is a template.  Penguin Greetings uses a configuration file system that is basically compatible with Apache-styled configurations.  As shipped, Penguin Greetings uses a template to represent a card which contains a plain text caption for the image, a HTML template to be used to display the card, a thumbnail image name, and the name of the image itself.  With this template, it is possible to "lookup" any piece of the card (via Perl hash references) once you have the name of the card.  Again as shipped, Penguin Greetings uses a template for the card categories.  So all the cards that can be used for birthdays are listed in another template with another plain text caption.  So to add a picture to the system, it suffices to copy the image and thumbnail image to the images directory (which you defined when installing Penguin Greetings.)  Then add an entry in card.conf for the new image as shown above for Fishing_birthday.  Finally add an entry for each category that the card could be used in, and ... you're done!

[Table of Contents]


But I don't like the way my picture looks in your card template!! (Template Tweaking)

Warning: .... a little knowledge is a dangerous thing!! ...

If you manage to add your own images to Penguin Greetings, you may want to start making the cards look different.  No problem, the cards are nothing more than HTML where the contents of your card is substituted from the CGI script.  However,  it does you no good unless you know how to get variables from the CGI script into your template.

The first step to making your own templates is to make a copy of the file picture_frame_card.tpl.html which you'll find in the templates folder of Penguin Greetings.  Don't change the original unless you want to change the appearance of every card that comes with Penguin Greetings.  A copy of this template is shown below:

[#
 # Penguin Greetings HTML/Embperl template for an HTML formatted
 # greeting card. 
 #
 # This template is usually called from within
 # other template either for previewing or displaying a card.
 #
 # E. Lagache, 3/2003
 #]

[# Set Embperl options to avoid interpreting characters that could be URLs #]
[* $ENV{'optRawInput'} = 16; *]
[*  $escmode = 0; *]



[# Interface with Penguin Greeting script (pgreet.pl.cgi) #]
[$ var $trans %card_hash %card $]

[-
  # Retrieve parameters from Penguin Greetings script (pgreet.pl.cgi)
  $trans = shift(@param); # $trans is a hash reference

  # All card configuration data
  %card_hash = %{$trans->{'card_hash'}};

  # "record" for selected card.
  %card = %{$card_hash{'card'}->{$trans->{'card'}}};


 -]
    <center>
      <table cellpadding="2" cellspacing="4" border="2" style=
      "text-align: left; width: 400px;">
        <tbody>
          <tr align="center">
            <td style="vertical-align: top;">
            [# retrieve image URL and image name #]
            <img src="[+ $trans->{'imageurl'} +]/[+ $card{'image'} +]"><br>
             </td>
          </tr>

          <tr>
            <td style="vertical-align: top;">
              <table cellpadding="2" cellspacing="2" border="0"
              style="text-align: left; width: 100%;">
                <tbody>
                  <tr>
                    <td style="vertical-align: top;"><span style=
                    "font-family: times new roman,times,serif; font-style: italic;">
                    [# Insert the message text #]
                    [+ $trans->{'html_message'} +]
                    </span><br>
                     </td>
                  </tr>

                  <tr>
                    <td style="vertical-align: top;">
                      <br>
                       <font size="-1"><br>
                      </font>

                      <table cellpadding="2" cellspacing="2"
                      border="1" style=
                      "text-align: left; width: 100%;">
                        <tbody>
                          <tr>
                            <td style=
                            "vertical-align: top; width: 50%;">
                            <font size="-1">
                            [# Insert the sender's name and email #]
                            From: [+ $trans->{'sender_name'} +]<br>
                           [+ $trans->{'sender_email'} +]</font><br>
                             </td>

                            <td style="vertical-align: top;">
                            <font size="-1">
                            [# Insert recipients name and email #]
                            To: [+ $trans->{'recipient_name'} +]<br>
                           [+ $trans->{'recipient_email'} +]</font></td>
                          </tr>
                        </tbody>
                      </table>
                      <br>
                      <div style="text-align: center;">
                        <a href="http://www.canebas.org/pgreet/">
    <img src="[+ $trans->{'imageurl'} +]/powered_by_pgreet.gif"
                  border="0" align="center"></a>
                      </div>
                    </td>
                  </tr>
                </tbody>
              </table>
            </td>
          </tr>
        </tbody>
      </table>
    </center>


Now the HTML may look like HTML, but what is all this odd looking stuff between the square brackets [ ] ?  Anything between the square brackets is processed by Embperl before being sent to the web server.  This is a very powerful tool as will be seen below, but for now, you can think about it as a way of getting values from the CGI script into your HTML.

The procedure you could use to make a new card template might be something like this:  get the content that you want to use in your new cards and copy it to a single folder (this content will end up in the 'images' folder of Penguin Greetings.)  Next, create a new bit of HTML, using whatever tools you like (even the Mozilla composer would do.) When you are satisfied with your new card template, open it up in a plain-text editor (Emacs, Vi, Pico, whatever) and cut and paste what is needed from the template above into your template.

What's that you say?  Well, for starters you'll need to cut and paste everything before the first HTML tag above (<center>).  That's the code that declares variables and gets the data from the CGI script for you to use.  Then, wherever you want to insert a bit of HTML content (like an image, sound, or flash applet,) put it in like the example of the picture above:

<img src="[+ $trans->{'imageurl'} +]/[+ $card{'image'} +]">

Now there are two parts to this.  The [+ $trans->{'imageurl'} +] part gives the URL to the directory where your good stuff will be found.  The second part [+ $card{'image'} +] comes from the card template in the file card.conf.  The syntax of $card{'a name thingy'} allows you to access any of the 4 fields in the card.conf template.  In general, any of the variables listed below that you want to incorporate into your card can be accessed using the $trans->{'variable name'} syntax.  So you can incorporate the sender's email, recipient's name, etc. in this way.

Now repeat after me - I have no idea how this works!   This is NOT an introduction into how Penguin Greetings really works.  However, if you want to know, keep reading!  If you think you can get on without reading more - well, you've been warned!!


[Table of Contents]


Penguin Greetings Operational overview

Penguin Greetings is Perl CGI application that manages the creation, emailing, storing, and retrieving of HTML-based greeting cards.  Penguin Greetings uses two programs to do this: pgreet.pl.cgi and pgreetd.pl.  However, the content developer can do his/her work as if there was a single program operating to manage the cards.  As noted above, Penguin Greetings tries to remain in the background as much as possible while allowing the content developer the maximum artistic and technical freedom.

Still, a backend program cannot be completely invisible or it wouldn't do anything at all for the developer.  Penguin Greetings employs a Perl-based method for communicating with HTML content developers: Embperl (or  Mason as an alternative.)  Developers can either simply accept the conventions provided here and use their own content generation methods, or they can take advantage of the power of Embedding Perl in their pages by using Embperl directly.  The problem with using Embperl is that it looks - well - rather like Perl.  Developers who aren't a fan of Perl will be forced to "hold their noses" somewhat when getting data from the CGI application to their HTML pages.  For those who are comfortable with Perl though, Embperl offers many advantages for streamlining pages.

Penguin Greetings is a CGI application.  As such it is only run when the user clicks on a hyperlink or presses a submit button.  In between calls, information is stored in state files.  State is maintained for an hour (although this is configurable) so that users can move backwards and forwards without fear of losing their card in progress.  The communications model between Penguin Greetings and the content it serves is simple and robust.  Forms communicate with Penguin Greetings via standard CGI variables.  In return, Penguin Greetings provides data for users to embed in their forms using a simple model available within Embperl.  This gives the developer a way to accumulate the information needed to fill out a card of their own design using as many forms as are needed as shown below:


Penguin Greetings Data cycle

Penguin Greetings preserves all state variables from any given call to the next.  Developers can create as many variables as they need to create their card.  These variables can then be inserted into the next HTML page using the $trans->{'CGI variable name'} syntax shown above in the quick start.  As a result, developers can use a card creation cycle shown below:

Penguin Greetings card creation cycle
Penguin Greetings card creation cycle

A user coming to Penguin Greetings to create a card first needs to login into the system (if system is access restricted.)  This is the login phase.  The next phase is the back and forth with the user to collect all the information needed to send the card.  If the user could type the image name to use in their card, a single HTML form would work just fine.  However, in practice, users need to be guided in their selections.  The Penguin Greetings templates that are shipped with the install provide 3 templates to aid the user in selecting a card.  First, a user selects a card category: love, birthdays, etc.  Then, the user selects a card from that category.  Finally the user is presented with a form to enter the names, email addresses, and other information needed to complete the card.  Some systems may need more levels, other systems, less (such as the secondary ecard sites included with Penguin Greetings.)  Penguin Greetings can support any number of steps to creating the card and any number of templates can be called from any step.  Finally, there is the send phase, where the user can preview the card and send it.

In trying to design a web site that uses Penguin Greetings keep in mind that there are first two kinds of HTML templates which are completely different: there are the cards themselves and then the HTML forms to select and fill in a card (or retrieve and view a card.)  It isn't possible to design the forms to fill in the card until you have designed the cards first.  The simple card template shipped with the default Penguin Greetings web site only needs a picture selected and some text entered.  More complicated cards will require more support for the user.  Also, Penguin Greetings delivers cards as email (plain-text and HTML formatted email.)  There are templates associated with that as well although these are not templates that can afford to be very complicated.  Penguin Greetings permits HTML templates to be included within other HTML templates.  So that a preview and view template can simply include the card template that is also used to generate the HTML that is sent out as email.  Other common bits such as page headers and footers can also be included as a common bit of HTML stored in a single file.

Since version 0.8.8, Penguin Greetings supports secondary ecard sites.  Each site is essentially independent with a few unintrusive exceptions.  Sites need not use the same templates, content, or databases.  See the demonstration secondary sites now included with Penguin Greetings as examples.

[Table of Contents]

Penguin Greeting templates and Embperl


Penguin Greetings shares with its ancestor WebGrams a notion of HTML template files.  These template files are the same as standard HTML files except that they have embedded in them Perl expressions that are evaluated before they are sent to web browser.  In version 0.8.0 (actually 0.7.8) and above, Penguin Greetings moves beyond simply incorporating Perl variables by adopting the Embperl standard for embedding Perl expressions into HTML.  Version 0.9.9 also provides support for HTML::Mason for those who prefer that framework for embedded Perl (see below.)  This means that any Perl expressions can be incorporated into Penguin Greeting templates.  Penguin Greetings doesn't require users to be Perl programmers, but since the incorporation of Embperl, it certainly does help. It is now possible to use any Perl expressions in your Penguin Greetings templates.   It is possible to use Penguin Greetings without any background in Perl, but it will be very easy to make mistakes that will be hard to diagnose without some knowledge of Perl syntax.  Because Penguin Greetings (and this manual) were developed over years when only Embperl was supported, most of the information and examples are provided within the Embperl framework.  However, the transformation to Mason is quite easy.  See the Savoring the sights of Seattle (PgSeattle) secondary ecard site as an example of an Mason implementation of a Penguin Greetings ecard site.

As an example of embedding Perl, if you wish to have the time when a user accesses your greeting card system displayed at some point in your HTML you can do this with this simple expression:

[+ localtime() +]

Embperl uses the [+ +] syntax to wrap any Perl code you wish to include in your HTML page.  Embperl evaluate the expression and substitute the result into the HTML document before sending it (via the Penguin Greetings CGI script) to the web browser.  The expression localtime() is a built-in Perl function that called in this way returns a string similar to this: Fri Mar 21 19:40:36 PST 2003.  If you don't like the date and time formatted in this way ... don't worry, in Perl - you can change it!

The HTML for cards used on the demonstration system are generated using the template shown above for the quick-start example.  For clarity, the Embperl code has been highlighted in bold to set it apart from the HTML.  There are three additional examples of Embperl that are used in this example: [# #] is a comment block, [* *] is a global setting block, and [- -] is a block of Perl code that needs to be evaluated does not produce any output that should be inserted into the HTML page that will be displayed by the browser.  Most Penguin Greetings developers will probably only need the [+ +] block and the [# #] block for comments.   For more details on Embperl syntax, see the Embperl website.  Note that the header in this example:  [- ...   $trans = shift(@param) ... -] is used by Penguin Greetings to move the HTML variables into template page.  This code is required for any HTML template page used by this application.  Another code example that this time expands into plain text is the template used for the text email message shown below:

[* $ENV{'optRawInput'} = 16; *]
[*  $escmode = 0; *]
[-
  # Retrieve parameters from Penguin Greetings script (pgreet.pl.cgi)
  $trans = shift(@param);
 -]
Dear [+ $trans->{'recipient_name'} +],

[+ $trans->{'sender_name'} +] has sent you a Penguin Greeting!  If your email reader doesn't
support email with images, you can view your Penguin Greeting on the world wide
web.  Just point your browser to:

[+ $trans->{'URL_short_cut'} +]

and, if needed,  enter the following information:

Card ID:      [+ $trans->{'number'} +]
Password:    [+ $trans->{'password'} +]

The text of the message from [+ $trans->{'sender_name'} +] is provided below:
-----------------------------------------------------------------

[+ $trans->{'message'} +]

-----------------------------------------------------------------
We hope you enjoy your Penguin Greeting!


Penguin Greetings' HTML page variables are accessed by looking up the variable in the hash reference $trans.  So to return the value of the cgiurl, the expression is: $trans->{'cgiurl'} and to insert that text into your HTML page the required Embperl expression is [+ $trans->{'cgiurl'} +].  Note that while there is a space between each + in the [+ +] square braces, those spaces will not appear in your resulting HTML.  If your imageurl is http://www.myserver.com/images, and pic contains my_picture.gif then the following line:

<img src="[+ $trans->{'imageurl'} +]/[+ $trans->{'pic'} +]">

will produce the following pure HTML:

<img src="http://www.myserver.com/images/my_picture.gif">



[Table of Contents]

Card configuration file and configuration parameter access.


As of Penguin Greetings 0.8.0, there is separate configuration file for card data and each card is represented by a symbolic name.  The configuration file is fairly straightforward to use, but for completeness, an abbreviated example of this file is provided below:

# File: cards.conf
######################################################################
#             Greeting card configuration file for
#
#                ** PENGUIN GREETINGS (pgreet) **
#
# A Perl CGI-based web card application for LINUX and probably any
# other UNIX system supporting standard Perl extensions.
#
#     Edouard Lagache, elagache@canebas.org, April 2003
#
#
# The pgreet system uses the Perl Config::General module for
# reading in configuration file information.  The file format
# is the same as for the Apache web server.  The general
# configuration module contains information for both the
# CGI script and the system daemon.  These are kept in
# separate categories for simplicity of management.
#
######################################################################

# Card category definitions:

<category Encouragement>
    caption = "Cards for encouragement"
    cards = Barrel_of_cheer
    cards = Flower_in_rocks
</category>



# Categories to display at this time.  Provide an entry for each category
# you which to have displayed during current operation.  To change order
# change order in this file.

categories = Encouragement
# categories = St_Patrick
# categories = New_Year

# Card definitions:


# 11
<card Barrel_of_cheer>
    caption = "A barrel of cheerful flowers"
    template = "picture_frame_card.tpl.html"
    image = "Barrel_of_cheer.jpg"
    thumb = "Barrel_of_cheer.thb.jpg"
</card>

# 14
<card Flower_in_rocks>
    caption = "Californian Poppy in rocky ground"
    template = "picture_frame_card.tpl.html"
    image = "Flower_in_rocks.jpg"
    thumb = "Flower_in_rocks.thb.jpg"
</card>


# Array of card names to enable transition and perhaps otherwise useful.
cards = Barrel_of_cheer
cards = Flower_in_rocks

What is more important and less obvious is how to access data from this file in templates.  Penguin Greetings uses the Perl module Config::General to read in configuration files.  Config::General converts this file into a Perl hash data structure that can be easily accessed by standard Perl expressions.  Indeed the above file is converted into the following data structure.

%card_hash = (
                 'card' => {
                            'Flower_in_rocks' => {
                                 'template' => 'picture_frame_card.tpl.html',
                                 'caption' => 'Californian Poppy in rocky ground',
                                 'thumb' => 'Flower_in_rocks.thb.jpg',
                                 'image' => 'Flower_in_rocks.jpg'
                               },
                            'Barrel_of_cheer' => {
                                 'template' => 'picture_frame_card.tpl.html',
                                 'caption' => 'A barrel of cheerful flowers',
                                 'thumb' => 'Barrel_of_cheer.thb.jpg',
                                 'image' => 'Barrel_of_cheer.jpg'
                               },

                           },

                 'cards' => [
                             'Barrel_of_cheer',
                             'Flower_in_rocks',
                            ],
                 'category' => {
                                'Encouragement' => {
                                    'caption' => 'Cards for encouragement',
                                    'cards' => [
                                                'Barrel_of_cheer',
                                                'Flower_in_rocks'
                                               ]
                                                   },

                               },
                 'categories' => [
                                  'Encouragement',
                                 ]
                );


Perl programmers will immediately recognize the various hashes that are produced by the configuration file.  More importantly, the Perl hash %card_hash that is shown above is the same hash that is defined at the top of all the all HTML templates with the statement: %card_hash = %{$trans->{'card_hash'}};.  The table above provides a rough road map to get at elements of the configuration file.  There is an array reference of card categories that can be accessed by: $card_hash{'categories'}.  To get a plain array, just dereference it like this: @{$card_hash{'categories'}} .  The image for the card Barrel_of_cheer requires two levels of lookup.  It makes more sense if you imagine the process as a database lookup.  You are looking for an item in the configuration hash.  It is a card first, it has the name Barrel_of_cheer.  So the Perl expression to get you that card is: $card_hash{'card'}->{'Barrel_of_cheer}.  However, that only gets you to the card, not the image.  If we are at the card, we need the image.  So the final expression is: $card_hash{'card'}->{'Barrel_of_cheer'}->{'image'}.  Each of these lookup items are in bold in the table above.  Using these lookup capabilities, it is possible to get to any piece of information in the configuration file.

There is a special configuration type called Force_to_array that isn't really a configuration variable at all, but a declarative to force values to always be arrays in order to avoid complicated Embperl loops.  This declarative is discussed below.  The card configuration files for secondary ecard sites are completely independent of any information in the primary ecard site, so they must be complete and entirely self-sufficient.  Developers are free to use an entirely different arrangement in each site.

IMPORTANT:  The example here is entirely arbitrary.  In order to implement your own cards you may need other information in your card configuration file.  Penguin Greetings does not assume ANYTHING about the contents of this file.  The configuration structure you create here is only used by your own ecard and web page templates.  Developers should not feel bound by the structures and conventions shown in the demonstration sites shipped with Penguin GreetingsPenguin Greetings in far more flexible than that.  The downside of this is that developers must provide their own tools to deal with their own data-structures.  For example the utility  CardConfTest.pl included with Penguin Greetings only works with the demonstration sites included with Penguin Greetings

[Table of Contents]

Penguin Greetings parameters and CGI variables

Content developers can access any CGI script variable that they define in a template via a form or in a URL get command by the name they assign to it.  All CGI variables can be accessed using the $tran hash reference.  So to access a variable named myvar the Perl expression would be $trans->{'myvar'}.  The only caveat is that name collisions will probably result in unexpected problems.  Penguin Greetings uses CGI.pm to access CGI variables and CGI.pm supports multiple values.  In addition, some of the variables from the configuration files pgreet.conf and card.conf are passed by their configuration file names.
At the moment, the following variables are passed to the HTML templates from the CGI script:

Penguin Greetings' Variable
Variable contents
cgiurl URL to cgi-bin directory on server
imageurl URL to image directory on server
card_hash
Hash reference of the configuration file 'card.conf'
message Text of message to be included in card
html_message
The text of the message to be included in the card formatted in HTML
varstring String of hidden variables to be included in a URL Get request
hiddenfields String of hidden variables formatted as hidden fields of a CGI form
sender_name Name of sender of card
sender_email Email of card sender
recipient_name Name of Recipient
recipient_email Email of Recipient
number Card login (old number in WebGrams)
password Password needed to retrieve card.
login Login of Penguin Greetings user on server.
UserPassword Password of Penguin Greetings user on server.
URL_short_cut URL with card login and password already included for quick access.
SpeedyCGI Boolean that if true indicates that pgreet.pl.scgi is indeed running as a SpeedyCGI persistent server.  The two variables below are only defined this this variable is set to true.
StartTime The UNIX time at which the persistent (SpeedyCGI) process started.  This can be converted using the Localtime function or what-not.
Invocations The number of CGI requests processed by is particular SpeedyCGI process.
BackVarStr A set of state variables encoded in a CGI "GET" request style (ampersands and var=value format) that if invoked in a call to Penguin Greetings will take the user to the previous state of creating their ecard.
BackHiddenFields A set of state variables encoded as "hidden Variables" that if included in a form "POST" request, would take the user to the previous state of creating their ecard.
VERSION
The version number of the CGI application (pgreet.pl.(s)cgi).

In addition, Penguin Greetings expects CGI form variables to be defined for some of its templates.  The recognized CGI variables are:

CGI Variable
Purpose
state_file
Name of state file where user's previous entries are stored on the server
action
Action that pgreet.pl.cgi should do on its next call.  See discussion on action below.
next_template
Name of the next template to be displayed by the CGI script.  Do not include a filename extension, the default extension defined in pgreet.conf is appended to the filename.
message
Text of message
sender_name
Name of sender
sender_email
Email of sender
recipient_name Name of recipient (primary)
recipient_name-2 to 5 Names of any supplemental recipients (up to 4 additional)
recipient_email Email of recipient (primary)
recipient_email-2 to 5 Emails of any supplemental recipients (up to 4 additional)
code
access code for card (old WebGrams number)
password
access password for card
card The name of the card selected
login
User login for accessing Penguin Greetings on this server
UserPassword
User password for accessing Penguin Greetings on this server
schedule
Binary flag - should card be scheduled to emailing later
day
Day to email card later
month
Month to email card later
year
Year to email card later
copy_for_you
Binary flag - do you want a copy of ecard emailed to sender?
site
Name of a secondary ecard site if your server has more than one ecard site running at the same time.
subject
Contains text of a user-supplied subject for the email message.  The subject text will be appended after the identifying tag [pgreet].  If the user does not supply a subject line, the line Penguin Greeting from user name will be used.

Penguin Greetings has a number of actions that can be performed.  This system was outlined in the operational overview.  By specifying one of the actions below and the next template to be displayed, just about any card generation website can be put together.  The available actions are:

Action Notes
<none>
This is the screen that will be displayed if a user accesses Penguin Greetings without any action specified.
build
These are the templates to select and outfit a card.  You can have any number of build stages.  Each time, the CGI script will collect the variables from the previous stage, and output the template specified by the variable next_template.
login
Display login and password page for installations of Penguin Greetings were only authorized users may create cards.
preview
State to review the resulting card and schedule when it will be sent.
send
Confirmation that a Penguin Greeting has been sent.  A link from this page can be added to go back to build state so to allow users to send multiple cards during the same session.
view
State and template for a user who has already received a Penguin Greeting and wants to view the card via the web site.   This is the page that this also displayed if URL_short_cut is enabled.


Versions prior to 0.7.8 of Penguin Greetings provided a  Server Side Include pseudo-declaration.  This has been eliminated in favor of the Execute function provided directly by Embperl.  It is possible to include templates within templates.  The only caveat is that the $trans hash reference isn't automatically passed to the files that are included, therefore the syntax for using Execute with the file my_file.tpl.html is:

[- Execute("myfile.tpl.html", $trans) -]

or you can use the long form:

 [- Execute ({inputfile =>"myfile.tpl.html",
              param => [$trans]
             }
            )
 -]



[Table of Contents]

Using Embperl to simplify content development

It is possible to use nothing more of Embperl to create your website than the facility to move variables from Penguin Greetings into your web pages.  For those who are comfortable with Perl, Embperl provides a powerful development environment for simplifying your web pages and automating the generation of similar HTML.

Programmers will recognize that in the data-structures of the cards and categories in cards.conf are some useful abstractions to simplify the creation of HTML.  In the demonstration web site shipped with Penguin Greetings, a single card thumbnail is shown with the following little snippet of Embperl and HTML:

 <tr>
      <td style="text-align: center; vertical-align: middle;">

              [# The URL will be the general URL above + card selected #]
         <a href="[+ $script_URL +]&card=[+ $card +]">
            [# The image is the image URL + the image name #]
            <img src="[+ $trans->{'imageurl'} +]/[+ $card_data{$card}->{'thumb'} +]"
            >
         </a>
      </td>
    </tr>


There is a whole table with every image within a given category displayed one after the other.  The list of cards in a category is  an array within the category structure.  Following the example in the section on the cards.conf configuration, to get the list of cards in the category Encouragement the following expression will do: @{$card_hash{'category'}->{'Encouragement'}->{'cards'}}.  If you have a list of items in Perl, you can perform a set of common operations with the foreach loop.  Embperl doesn't let down Perl fans, it has its own version of the foreach loop.  The syntax is:

[$ foreach $myvar (@myarray) $]
<my HTML stuff here> ....
[$ endforeach $]

See Embperl documentation for full details.

The demonstration web site displays a sample thumbnail image for each category and then once a user chooses a category, it displays all the thumbnail images for that category.  An old version of the page that does that (card.tpl.html) is shown below (the current version of this template uses Embperl::Object - see below.)

<HTML>
<HEAD>
   <TITLE>Penguin Greetings - Select a picture</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF">

[* $ENV{'optRawInput'} = 16; *]
[*  $escmode = 0; *]
[$ var $trans $script_URL %card_hash %category_hash @cards_list %card_data
       $card $image_ref
 $]
[-
  # Retrieve parameters from Penguin Greetings script (pgreet.pl.cgi)
  $trans = shift(@param);

  # Get the contents of card.conf into a single hash.
  %card_hash = %{$trans->{'card_hash'}};

  # Get the category hash
  %category_hash = %{$card_hash{'category'}};

  # Get the hash of card data
  %card_data = %{$card_hash{'card'}};

  # Get the list of all the card names in the category chosen.
  @cards_list =  @{$category_hash{$trans->{'category'}}->{'cards'}};


  # Build up pieces of URLs to simplify represention.
  $script_URL = join('', $trans->{'cgiurl'}, "/",
                         $trans->{'cgi_script'}, "?action=build&",
                         "next_template=text&",
                         $trans->{'varstring'}
                    );

 -]


<CENTER>
<table cellpadding="2" cellspacing="2" border="0"
 style="text-align: left; width: 640px;">
  <tbody>
    <tr>
      <td style="vertical-align: top;">

      <td style="vertical-align: top;">
      <img src="[+ $trans->{'imageurl'} +]/pgreet_CGI_banner.jpg"
           align="center">
     <img src="[+ $trans->{'imageurl'} +]/pgreet_status_bar-2.jpg"
          align="left"><br>
<center>
<FONT FACE="Arial"><b><small>Please select a picture for your
Penguin Greeting below.</b></small></FONT></CENTER>
<br><br><hr>
<CENTER>

<H3>[+ $category_hash{$trans->{'category'}}->{'caption'}  +]</H3><br>

<table cellpadding="2" cellspacing="2" border="0"
       style="text-align: left; width: 600px;">
  <tbody>

   [# Loop through every card in the category #]
   [$ foreach $card (@cards_list) $]
    <tr>
      <td style="text-align: center; vertical-align: middle;">
        
        [# The URL will be the general URL above + card selected #]
         <a href="[+ $script_URL +]&card=[+ $card +]">
           [# The image is the image URL + the image name #]
            <img src="[+ $trans->{'imageurl'} +]/[+ $card_data{$card}->{'thumb'} +]"
            >
         </a>
      </td>
    </tr>
    [$ endforeach $]

  </tbody>
</table>
</CENTER>
      </td>
    </tr>
  </tbody>
</table>
</center>
[- Execute($trans->{'templatedir'} . "/pgreet_credit.tpl.html", $trans) -]

</BODY>
</HTML>

Using Embperl greatly simplifies the HTML in this example.  Not only does it eliminate making a separate table entry for each card image in an given category, it avoids the need of having a separate file for each category.  Each category is dynamically generated on the fly.  Without using Embperl, this would be difficult to do and essentially impossible to do with the direct integration to the Perl data-structures that are used in the configuration files.  Using Embperl, it is possible to make very compact templates that still expand to elaborate HTML pages.

Unfortunately, there is one wrinkle in using Embperl loops with the data coming from the tool used to read the configuration file Config::General.  There is no explicit mechanism in Config::General to indicate when something is a single value of a list of something.   Instead, if there is only one of something, it is assumed to be a single value until a second one shows up and an array is created for the pair.  This may not seem like a problem until one tries to come up with Embperl code to loop through the values in your configuration file.  Consider what will happen in the foreach loop above if there is category with just one card in it.  If there is just one value, there won't be an array to loop over and Embperl will crash as the attempt to define the array @cards_list.  Of course, this problem can be solved by an explicit programming test, but such tests have to be placed in every template and protect every loop.  This could get very clumsy.

To simplify matters, there is a pseudo configuration type: Force_to_array.  An example is shown below from the Camel Collection secondary ecard site:

# Request that all card entries be array references.
<category Force_to_array>
    fields = cards
</category>

# 01
<category Hello>
    caption = "Hello and general interest cards"
    cards = Camel_racers_unite
    cards = Camel_fun_yet
</category>

# 02
<category Love>
    caption = "Cards for Love and Romance"
    cards = Sweet_camel_two
</category>

By using the pseudo-configuration type Force_to_array in the configuration type category, special processing has been requested of every item that is of the type categoryPenguin Greetings will go through every item that is of category and look for fields named cards.  If anything is returned from Config::General as a single value (scalar), Penguin Greetings will automatically make it into an array of one item.  In the above example, the category hello has two cards Camel_racers_unite and Camel_fun_yet.  These would have been put into an array reference by Config::General.  However, the category Love has only one card Sweet_camel_two.  The Force_to_array declarative will replace that single value with an array reference containing that one items.  It isn't at all necessary to use the Force_to_array declarative, but it can make Embperl coding much simpler.

This is just one example of what is possible using Embperl.  For more information, first, see the Embperl web site and documentation.  Also look at the templates included in the Penguin Greetings demonstration web site for additional ways to use Embperl in your web page templates.


[Table of Contents]

Creating Object-Oriented ecard sites

By using the ability to conclude templates of common code, the Embperl interpreter used by Penguin Greetings allows developers to void duplicating shared code among various the various web pages in their ecard sites.  For simple sites, this can be adequate to avoid needless repetition.  For more complex web sites, however, object-oriented paradigms provide a much more disciplined manner in which to build web sites with shared characteristics that are inherited from hierarchies of templates.  Version 0.9.7 of Penguin Greetings now supports construction of object-oriented ecard web sites by incorporating Object::Embperl (or HTML::Mason) as an alternative template processor.  As of version 0.9.9, Penguin Greetings Included are six examples of object-oriented ecard sites:

  1. The default card site
  2. PgSaint - The Penguin Greetings Holy cards Collection
  3. PgXmas - The Penguin Greetings Christmas card Collection
  4. PgPoppy - The Penguin Greetings California Poppy Collection
  5. Pg4Seasons - The Penguin Greetings Four Seasons Collection.
  6. PgSeattle - The Penguin Greetings Savoring the sights of Seattle Collection (using HTML::Mason)

As with Embperl in general, Penguin Greetings strives to be unobstructive in the execution of object-oriented Embperl pages.  For more information on making object-oriented Embperl websites, see the Embperl::Object documentation.   The main changes to Penguin Greetings are in the configuration files to provide the required parameters needed to make Embperl::Object interpretation possible.   The configuration file for PgPoppy (English-US) site is provided below as an example:

#
# File: PgPoppy.conf
######################################################################
#                 Main configuration file for
#
#   ** PENGUIN GREETINGS (pgreet) California Poppy Collection**
#
# This is a sample configuration file for a secondary pgreet ecard
# web site to be used with Penguin Greetings.  Penguin Greeting is
# a Perl CGI-based web card application for LINUX and probably any
# other UNIX system supporting standard Perl extensions.
#
#     Edouard Lagache, elagache@canebas.org, February 2004
#     For Penguin Greetings 0.9.7 release
#
#
# The pgreet system uses the Perl Config::General module for
# reading in configuration file information.  The file format
# is the same as for the Apache web server.  The general
# configuration module contains information for both the
# CGI script and the system daemon.  These are kept in
# separate categories for simplicity of management.
#
######################################################################
# $Id: PgPoppy.conf,v 1.2 2004/02/05 21:41:02 elagache Exp $


###  Directory and URL defaults

    # Path to image files.
    imagedir = "/home/httpd/htdocs/pgreet/PgPoppy/en-us/images"

    # URL to images on server
    imageurl = "http://www.canebas.org/pgreet/PgPoppy/en-us/images"

    # Path to directory where dbm databases are stored
    datadir = "/home/agent-workspace/PgPoppy/en-us/data"

    # Path to configuration files
    confdir = "/home/agent-workspace/PgPoppy/en-us/conf"

    # Path to HTML templates
    templatedir = "/home/httpd/htdocs/pgreet/PgPoppy/en-us/templates"

    # Parameters for Object-Oriented version of Embperl
    <Embperl_Object>
        object_addpath = "/home/httpd/htdocs/pgreet/PgPoppy"
        object_base = "PgPoppy_template.epl"
        appname = "PgPoppy"
        bypass_object = "Text_email.tpl.html"
    </Embperl_Object>

### Misc CGI script settings.
    # Is Penguin Greetings available only to login/password users?
    login_only = 0

    # Allow encoding of card number and password in URL.
    # (turn off - if you want users to come to your central page)
    allow_quick_views = 1

    # Prevent users from inserting any HTML tags in their messages
    no_html_tags = 1


### Days ecards are kept on server.
     cards_remain = "forever"


The key change is in the new configuration group Embperl_Object.  This provides the additional parameters needed to put a given template into the "context" of an object-oriented hierarchy.  All the parameters in this group come straight from Embperl::Object but one.  They are all explained below:

Parameter
Description
object_addpath A string of directory paths where the base template pages may be found to that are context in which to interpret the page to be converted into HTML.  When more than one directory is to be included, separate each one by a colon in the same manner as a UNIX path statement.
object_base The name of a base template to use as the basis for interpreting the template system.
appname Unique name to differentiate a given site - Putting the site name in this field will avoid conflicts.  Otherwise, the object-oriented hierarchy of one site would be used for another.
bypass_object This is the only setting that is not passed on directly to Embperl.  This allows the developer to specify templates that are to be NOT processed by Embperl::Object, but instead processed by the "flat" Embperl interpreter.  This is provided for templates that should not be created in the same style as the rest of the ecard website.  The most obvious example is the plain-text message sent to the user as part of the MIME multipart email message.  To exclude more than one template, simply add to the <Embperl_Object> configuration group additional bypass template requests (e.g. bypass_object = template1, bypass_object = template2, etc.) each on their own line.  Penguin Greetings will create a list of these and bypass Embperl::Object processing for all in the list.

In addition, any other parameters that you might wish to pass to Embperl::Object::Execute can be added to this composite configuration structure.  Any parameter provided in this group is passed on to Embperl::Object::Execute unaltered but of course the bypass_object parameter as noted above.  See the Embperl documentation for most details on the additional more esoteric options.

The California Poppy collection website provides an example of how Embperl::Object can be used in practice.  In that example, the base object is the file: PgPoppy_template.epl shown below (all Embperl code in bold:)  Note that these templates use the Locale::Maketext localization which is described later in this document.

[# #################################################################### #]
[#                                                                      #]
[#       Penguin Greetings - California Poppy Collection website        #]
[#                                                                      #]
[#  A demonstration ecard website using the Penguin Greetings           #]
[#  Embperl::Object implementation so that the website can be built     #]
[#  out of reusable components.                                         #]
[#                                                                      #]
[#  For more information on Penguin Greetings project go:               #]
[#      http://pgreet.sourceforge.net/                                  #]
[#                                                                      #]
[#  File: PgPoppy_template.epl                                          #]
[#        The file containing the general structure of a web site       #]
[#        page.                                                         #]
[#                                                                      #]
[# #################################################################### #]

[# Set environment to avoid HTML reformatting #]
[* $ENV{'optRawInput'} = 16; *]
[*  $escmode = 0; *]


[# Embperl Variables #]
[$ var $req_obj $trans $LN $pgreet_info_string $pgreet_title_string
 $]

[!
  sub new {
  #
  # Initialization for Object-oriented page structure:
  # Internationalization, etc.
  #
    my $self = shift;
    my $trans = shift;

    # Create language handle for this rendering.
    use Pgreet::I18N;
    $self->{'LN'} = Pgreet::I18N->get_handle($trans->{'lang_code'});
    $self->{'trans'} = $trans;

    return($self);
  }

  sub access {
  #
  # Access methods to store and retrieve items from object
  #
     my $self = shift;
     my $property = shift;
     my $value = shift;

     if ($value) {
        return($self->{$property} = $value);
     } else {
       return($self->{$property});
     }
  }

  sub title {
  #
  # Baseline title
  #
    return("Penguin Greetings - Califorina Poppy collection");

  }

 !]


[# Initialize base page #]
[-
  # Retrieve request object of current page.
  $req_obj = shift;

  # Initialize object-oriented page.
  $req_obj->new(@param);

  # Retrieve parameters from CGI App pgreet.pl.(s)cgi and save in object
  $trans = shift(@param);

  # Retrieve Locale::Maketext handle
  $LN = $req_obj->access('LN');

  # Spare long text in the middle of HTML
  $pgreet_info_string =
   $LN->maketext("For more information on the <i>Penguin Greetings (pgreet)</i> web cards software go to, <a href=\"http://www.canebas.org/pgreet/\">http://www.canebas.org/pgreet/</a>");

  $pgreet_title_string =
    $LN->maketext("Penguin Greetings - Califorina Poppy collection");
 -]

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>
  <head>
    <meta name="generator" content="HTML Tidy, see www.w3.org">
    <meta http-equiv="content-type" content=
    "text/html; charset=ISO-8859-1">

    <title>
[+ $LN->maketext($req_obj->title()) +]</title>
  </head>

  <!-- Colors are for the black background where content will actually be -->
  <body style=
  "color: rgb(255, 204, 0); background-color: rgb(255, 255, 255); background-image: url(
[+ $trans->{'imageurl'} +]/Poppy_wallpaper.jpg);"
   link="#9999ff" vlink="#3333ff" alink="#3366ff">

    <!-- Content appears in a centered black table against poppy wallpaper -->
    <center>
      <table cellpadding="2" cellspacing="2" border="0" style=
      "font-family: times new roman,times,serif; text-align: left; background-color: rgb(51, 0, 51); width: 650px;">
        <tbody>
          <tr>
            <td style="vertical-align: top;">
              <img style="width: 649px; height: 108px;"
              alt="
[+ $pgreet_title_string +]"
              title="
[+ $pgreet_title_string +]"
              src="
[+ $trans->{'imageurl'} +]/Poppy_upper_banner.jpg">
              <br>
             

              [# Insert particular content file here #]
              [- Execute({inputfile =>'*', param => [$trans]}) -]

              
              <!-- Table for giving credit to Pgreet software -->
              <table style=
              "width: 100%; text-align: left; margin-left: auto; margin-right: auto;"
               border="1" cellspacing="2" cellpadding="2">
                <tbody>
                  <tr>
                    <td style="text-align: center;"><font size=
                    "-2">
[+ $pgreet_info_string +]</font>
                    </td>

                    <td style="text-align: right; vertical-align: bottom; width: 90px;"><a href="http://www.canebas.org/pgreet/"><img src=
                    "
[+ $trans->{'imageurl'} +]/powered_by_pgreet.jpg"
                    alt="Powered by pgreet"
                    title="Powered by pgreet" style=
                    "border: 0px solid ; width: 90px; height: 42px;">
                    </a> </td>
                  </tr>
                </tbody>
              </table>
              <img style="width: 649px; height: 27px;"
              alt="
[+ $pgreet_title_string +]"
              title="
[+ $pgreet_title_string +]"
              src="
[+ $trans->{'imageurl'} +]/Poppy_lower_banner.jpg">
            </td>
          </tr>
        </tbody>
      </table>
    </center>
    <br>
    <br>
  </body>
</html>


At first glance, this template looks very similar to other templates included in Penguin Greetings.  However, the object-oriented features become clear with the use of subroutines to perform tasks assigning the title.  The key difference is in the line about 1/2 way in the file:

[# Insert particular content file here #]
[- Execute({inputfile =>'*', param => [$trans]}) -]

The surprise is the asterisk '*' as the name of the input filename.  The asterisk is a placeholder.  The template file you which to actually insert into the baseline template will be inserted right here.  For example, to view an ecard the following template (view.tpl.html) will be inserted into the baseline:

[# #################################################################### #]
[#                                                                      #]
[#       Penguin Greetings - California Poppy Collection website        #]
[#                                                                      #]
[#  A demonstration ecard website using the Penguin Greetings           #]
[#  Embperl::Object implementation so that the website can be built     #]
[#  out of reusable components.                                         #]
[#                                                                      #]
[#  For more information on Penguin Greetings project go:               #]
[#      http://pgreet.sourceforge.net/                                  #]
[#                                                                      #]
[#  File: view.tpl.html                                                 #]
[#        HTML template to that renders the ecard for those seeking     #]
[#        to see their card via the web rather than directly via their  #]
[#        email readers.                                                #]
[#                                                                      #]
[# #################################################################### #]

[# Declare Embperl variables needed to process page #]
[$ var $req_obj $trans $template $card $lang_URL $LN
 $]

[# Set environment to avoid HTML reformatting #]
[* $ENV{'optRawInput'} = 16; *]
[*  $escmode = 0; *]

[# Subroutine declaration #]
[!
  sub title {
  #
  # Override page title
  #
    return("Your Penguin Greeting");

  }

 !]

[# Transfer data from application and create shortcuts #]
[-

  # Retrieve request object of current page.
  $req_obj = shift;

  # Retrieve parameters from Penguin Greetings script (pgreet.pl.cgi)
  $trans = shift(@param); # $trans is a hash reference

  # Retrieve Locale::Maketext handle
  $LN = $req_obj->access('LN');

  # card name
  $card = $trans->{'card'};

  # template to use
  $template = $trans->{'card_hash'}->{'card'}->{$card}->{'template'};

  # Shortcut for URL to card site.
  $lang_URL = join('', $trans->{'cgiurl'}, "/",
                       $trans->{'cgi_script'}, "?site=",
                       $trans->{'site'}
                  );

 -]

[# ##### End of Embperl initialization ##### #]

<!-- Start of ecard - Give a title and state sender -->
<br>
<div style="text-align: center;"><font size="+1"
style="font-style: italic;">[+ $LN->maketext("Your Penguin Greeting") +]
<br><br>
</font><font style="font-style: italic;">
[+ $LN->maketext("A Penguin Greetings ecard.<br>Sent to you from [_1]",
                 $trans->{'sender_name'}) +]</font><br><br>
</div>

      <!-- Template inserted using Embperl -->
      [- Execute ({inputfile => join('', $trans->{'templatedir'},
                                         "/$template"),
                    param => [$trans, $LN]
                  }
                 )
       -]

<br>
<br>
<br>
<div style="text-align: center;"><span style="font-style: italic;">
[+ $LN->maketext("Would you like to send a California Poppy card?") +]
</span><br><br>

<a href="[+ $lang_URL +]"><font size="-1">
[+ $LN->maketext("Click here to access the California Poppy ecard site") +]
</font></a><br>
<br>
<hr style="width: 50%; height: 2px;">
<br>
<br>
<div style="text-align: center;"><font size="-1"><span
style="font-style: italic;">
[+ $LN->maketext("We hope you enjoy your Penguin Greeting!") +]
</span></font></div>
<br>
<br>


In this example, the key advantage of using object-oriented development should be clear.  Note that all the peripheral HTML is not repeated in the view.tpl.html template.  That code is created in one file the baseline template and need not be carved out into separate files for insertion or copied and pasted as in the other example Penguin Greetings ecard sites.  Nonetheless, the view ecard site should have some unique characteristics even in the code surrounding the inserted HTML.  To set a unique title, the subroutine title has been redefined in the view.tpl.html file.  If no such subroutine were provided, Embperl::Object would have followed the chain of hierarchy back to PgPoppy_template.epl and used the "plain vanilla" title subroutine provided there.  By defining subroutines in this way, aspects of the baseline template can be customized by the template that is being interpreted inside of it.  At the same time, it is possible to use Embperl within the templates without object-oriented extensions if that serves your task.  So the template for the actual e-card is shared with the Penguin Greetings demonstration sites unchanged.  The only difference is to call the Execute function with an explicit filename.

For more details on using Penguin Greetings in Embperl::Object mode see any of the four sites using it such as the California Poppy Collection and/or Four Seasons Collection sample websites and the documentation on Embperl::Object.  The Four Seasons ecard site includes some more elaborate uses of Embperl to simplify the creation of more unique ecard sites.

[Table of Contents]

Error handling and form field checking

To err is human, Penguin Greetings tries to cope with errors as gracefully as possible, but like most software, it needs a little help from its friends.  New to Penguin Greetings 0.8.1 is a template based system for reporting errors back to the user.  This is done in two ways.  First, there is a new template that is called in the Penguin Greeting's distribution general_error.tpl.html, but can be assigned anything by setting the configuration variable default_error.  The general error template contains two things, a translation of the error codes to text and the html to be displayed in case of an error.  When Penguin Greetings encounters an unrecoverable error, it call the general_error.tpl.html template (or whatever you call it) and provide an error number as described below.

Error No.
Description of Error
0
'Catch-all' error in case a non-existent error code is given.  This code should never be reached unless "your's truly" accidentally makes an call to the subroutine 'Report_error' with an non-existent error code.



SEVERE UNKNOWN ERRORS (1-19)
1
Unknown operation requested of pgreet.pl.cgi.  Either an action was specified that isn't recognized or some sort of other unknown error.
2
No template to display specified or file is unreadable.  Either the configuration variables 'default_template'  or 'default_login' are in error, or the state variable 'next_template' or 'var_error_template' did not contain a valid template.  As noted in the installation guide, 'default_login' must be set correctly or pgreet.pl.cgi will go into an infinite recursion trying to report an error without any template to report the error with.
3
Penguin Greetings has encountered an error attempting to save your selections in between menu screens.  Possible causes are improperly set ownership and/or permissions on the temporary directory.
4
You are attempting to access a Penguin Greetings e-card site that either no longer exists on this server or has been relocated.  This means either a secondary site is not listed in the primary configuration file or perhaps an attempt has been made to access a secondary site belonging to a user that doesn't exist on the system which makes it impossible to look up a home directory.
5
The secondary ecard site you are trying to access has encountered an unrecoverable configuration error.   One possible way to generate this error is to try to set a parameter in a secondary configuration file that can only be set in the primary configuration file.



FILE SYSTEM RELATED ERRORS (20-39)
20
Some sort of file open error (details in web server error log)
21
Some sort of file close error (details in web server error log)
22
Some sort of file read error (details in web server error log)
23
Some sort of file write error (details in web server error log)
24
Data file corrupted error (details in web server error log)



CARD CONTENT RELATED ERRORS (40-49)
40
The card requested didn't exist in the 'cards' array reference supplied in the cards.conf file (New to version 0.9.8)



DATE RELATED ERRORS (100-109)
100
Unknown date related error (internal failure most likely)
101
Impossible date (e.g. Feb 31.)
102
Date not in the future.



LOGIN RELATED ERRORS (110-119)
110
User login and password do not match
111
Card login and card password do not match



FILE STATE ERRORS (120-129)
120
Attempt to use a state file that does not exist.  Could be a catastrophic failure, but most likely a user has left a card creating session for more than the period allocated to complete a card (default 1 hour) and the system daemon has deleted the associated state file.

Because these codes are not at all language specific, it is possible to use Penguin Greetings to create ecard websites in any language now.  No error messages will be reported to the user in English.  To simplify development, as of version 0.8.8 the error message text is stored in a separate file: error_messages.pl.

There is one error check above that requires some setup.  Because the structure of the card.conf files is completely arbitrary, there is no way to test if a card exists on the system without the developer first indicating what cards exist.  The array reference cards (see configuration file above) in the configuration file gives a way for developers to list which cards are actually available on a given site.  Before a view can a view a card, if the array reference exists, the name of the card to be viewed is tested against that list.  If the name isn't found, then the error 40 message is supplied.  Without the cards array in the configuration file, the attempt to render the card is done directly without any attempt to test if the card actually exists.

Unfortunately, some errors cannot be anticipated in advance.  The contents of forms processed by Penguin Greetings can contain arbitrary information.  Therefore, what constitutes a valid entry for a given field is only known by you the content developer and cannot be "hard coded" into Penguin Greetings in advance.  Of course JavaScript can be used to avoid this problem by catching errors before the forms are sent to the server.  However, users turn off JavaScript for security reasons and not all browsers support JavaScript, so a server-side solution is needed as a fall-back.

To provide content-developers with some way to specify what a field should contain, Penguin Greetings turns to another CPAN Perl module:  String::CheckerString::Checker provides a format for specifying what is expected in a string.  Penguin Greetings expands on this tool by providing a CGI form scheme by which content developers can specify what Penguin Greetings should look for in a given field.  This is done by specifying a hidden field with the same name as the field to be tested and the suffix '+ArgCheck'.  The value of this hidden field is a string that is a comma separated list of expectation tests from String::Checker.  Some of the more useful tests are listed below:

Expectation
Description of Expectation
allow_empty Never fails but if the string is indeed blank, all other tests will be skipped and true will be returned.
disallow_empty Fails if the input string is either undef or empty.
min Fails if the length of the input string is less than the numeric value of it's single argument.
max Fails if the length of the input string is more than the numeric value of it's single argument.
want_int Fails if the input string does not solely consist of numeric characters.
want_float Fails if the argument does not solely consist of numeric characters, plus an optional single '.'.
allow_chars Fails if the input string contains characters other than those in its argument.
disallow_chars Fails if the input string contains any of the characters in its argument.
enum Fails if the input string does not precisely match at least one of the elements of the array reference it takes as an argument.
match Fails if the input string does not match the regular expression it takes as an argument.
want_email Fails if the input string does not match the regular expression: ^\S+\@@[\w-]+\.[\w\.-]+$
want_phone Fails if the input string does not match the regular expression ^[0-9+.()-]*$
want_date Interprets the input string as a date, if possible. This will fail if it can't figure out a date from the input. In addition, it is possible to use this to standardize date input. Pass a formatting string (see the strftime(3) man page) as an argument to this check, and the string will be formatted appropriately if possible. This is based on the Date::Manip(1) module, so that module must be installed if you wish to use that test and that  module's documentation might prove valuable if you're using this check.

Penguin Greetings does not quite implement String::Checker as described in its own documentation.  For the moment, the string manipulation features are not supported.  Also, the allow_empty expectation behaves differently than described.  In String::Checker, it has no effect on the results of other tests.  However, if left in that way, than a blank field that could contain an email address for example would still fail because Penguin Greetings would check in any of the tests had failed and throw up an error template instead of continuing on.  So, the allow_empty expectation "short-circuits" any other tests.  It is the only expectation that does this.  As suggested by this: there is an implied and logic with tests.  If any of the tests fail, Penguin Greetings calls up the error template instead of proceeding on.

An example of specifying form field error checking is shown below in an snippet from the login default template:

 <form method="post"
                  action="[+ $trans->{'cgiurl'} +]/[+ $trans->{'cgi_script'} +]">
              <p>&nbsp;</p>
              <div align="center">
              <center>
              <table border="0">
                <tbody>
                  <tr>
                    <td align="right"><font face="Arial" size="-1">Card Login:</font></td>
                    <td><input type="text" size="20" name="code"value=""></td>
                  </tr>
                  <tr>
                    <td align="right"><font face="Arial" size="-1">Code Word:</font></td>
                    <td><input type="password" size="20" name="password"value=""></td>
                  </tr>
                </tbody>
              </table>
              </center>
              </div>
              <div align="center">
              <center>
              <table border="0">
                <tbody>
                  <tr>
                    <td><input type="submit" value="Submit"></td>
                    <td><input type="reset" value="Reset"></td>
                  </tr>
                </tbody>
              </table>
              </center>
              </div>
              <input type="hidden" name="action" value="view">
              <input type="hidden" name="next_template" value="view">
        <input type="hidden" name="code+ArgCheck" value="disallow_empty">
        <input type="hidden" name="password+ArgCheck" value="disallow_empty">
        <input type="hidden"
               name="var_error_template"
               value="fields_error">

</form>


For each field (such as code) there is a hidden field with the appropriate suffix (for code: code+ArgCheck.)  In this case the test is that the fields not be left blank (disallow_empty.)  To specify multiple tests, separate each test by a comma (leave no whitespace.)  Values that require an argument are specified using the Perl "alternative comma" =>.  So if you were expecting passwords of at least 6 characters the hidden field entry would be this:

        <input type="hidden" name="password+ArgCheck" value="disallow_empty,min=>6">


Even error messages should have your artistic touch (more realistically same look and feel,) so Penguin Greetings allows the developer to specify the template to be used to process the error.  As elsewhere in Penguin Greetings, you can build these templates the "camel way" or use your own tools.  Either way, if an error has occurred, you need to have some indication of what expectation tests failed so that you can advise the user how to correct the errors.  Penguin Greetings defines another value to the hash passed into templates that contains error information: error_hash.  the error_hash hash reference has the name of every field (without the +ArgCheck suffix) that failed an expectation.  The value to the hash is an array reference with the name of every expectation that was failed.  So a sample value of error_hash would look like the following assignment:

$error_hash = {
               code => [ 'disallow_empty', 'min' ]
              };

Handling this hash reference depends on your personal style.  A more Perl intensive solution is shown below and in included in Penguin Greetings 0.8.1 (file - fields_error.tpl.html:)

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>
  <head>
    <meta name="generator" content="HTML Tidy, see www.w3.org">
    <title>Penguin Greeting Error</title>
  </head>

  <body style="background-color: rgb(255, 255, 255);">
[* $ENV{'optRawInput'} = 16; *]
[*  $escmode = 0; *]
[$ var $trans $count %error_hash %check_explanation $field $error $message $]
[-
  # Retrieve parameters from Penguin Greetings script (pgreet.pl.cgi)
  $trans = shift(@param);
 
  %error_hash = %{$trans->{'error_hash'}};

  # Text explanations of the tests from String::Checker
  %check_explanation = (
    disallow_empty => 'Field was left empty',
    min => 'Field was too short',
    max => 'Field was too long',
    want_int => 'Only a number was expected (without a decimal point)',
    want_float => 'Only a number was expected',
    allow_chars => 'Only letter and numbers were expected (no punctuation)',
    disallow_chars => 'No letters or numbers are allowed in this field',
    enum => 'Not one of the allowed values',
    match => 'Field didn\'t match pattern',
    want_email => 'Field not a valid email address',
    want_phone => 'Field not a valid telephone number',
    want_date => 'Field not a valid date'
);


 -]
<center>
<table cellpadding="2" cellspacing="2" border="0"
 style="text-align: left; width: 640px;">
  <tbody>
    <tr>
      <td style="vertical-align: top;">
      <img src="[+ $trans->{'imageurl'} +]/pgreet_CGI_banner.jpg"
           align="center">
      <center>
      <font FACE="Arial"><b><small>Penguin Greeting Error.</font></b>
            </small></center>
      <h1>Error:</h1>
      <h2>missing or incomplete form information</h2>
      <br>
      <p>
         The following fields below are either missing information or
         the supplied information doesn't match what was asked
         for.&nbsp;Please use the back button to correct these fields
         and then resubmit the form.
      </p>

          <dl style="margin-left: 40px;">
             [$ foreach $error (keys(%error_hash)) $]
             <dt><b>[+ $error +]</b></dt>
             [-
               my $expectations = $error_hash{$error};
               my $expectation = @{$expectations}[0];
               $message = $check_explanation{$expectation};
              -]
             <dd>[+ $message +]</dd>
             [$ endforeach $]
          </dl>
      <input type="button" value="Go back and correct errors"
          onClick="parent.history.back();">
      <br>
      <br>
      <br>
      <a href="/">[+ $trans->{'hostname'} +]</a><br>
      <small><i>[+ localtime() +]</i></small>

      </td>
    </tr>
  </tbody>
</table>
</center>

  [- Execute($trans->{'templatedir'} . "/pgreet_credit.tpl.html", $trans) -]
  </body>
</html>


[Table of Contents]

Testing and debugging templates: PgTemplateTest

As a web site developed in Embperl for Penguin Greetings becomes more complex, the risk of error grows.  The utility PgTemplateTest has been updated with the 0.8.3 and 0.8.8 release to allow developers to view their Penguin Greetings templates in as realistic a set of conditions as possible.  The PgTemplateTest utility now installed as a user utility (default location is /usr/local/bin.)  If not already done at the time of installation, developers will probably want to manually edit the program to set the location of the primary configuration file directory.  The line that must be changed are shown below:

# Location of configuration files
# LOCALIZE TO YOUR OWN SETTINGS!!
$conf_dir = "/home/agent-workspace/pgreet/conf/";

PgTemplateTest can also launch a browser of your own choosing with the template that you are evaluating.  The browser launched and the prefix used to load the file can be changed by modifying the configuration file illustrated below.  Also the location of temporary files can be changed.

New to version 0.8.8 is a separate configuration file for PgTemplateTest: PgTemplateTest.conf.  There are a few parameters that can be for the program itself browser path, file prefix and so on, but the main purpose of the configuration file is to provide some way to set the CGI state variables that would normally be set by the running of Penguin Greetings to appropriate values for testing a template.  An example of the configuration file is provided below:

#
# File: PgTemplateTest.conf
######################################################################
#                 PgTemplateTest configuration file for
#
#                ** PENGUIN GREETINGS (pgreet) **
#
# A Perl CGI-based web card application for LINUX and probably any
# other UNIX system supporting standard Perl extensions.
#
#     Edouard Lagache, elagache@canebas.org, September 2003
#     For Penguin Greetings 0.9.2 release
#
# This file allows users to choose the defaults for the CGI state
# variables that are emulated by PgTemplateTest.pl when previewing
# a template.
#
# The pgreet system uses the Perl Config::General module for
# reading in configuration file information.  The file format
# is the same as for the Apache web server.  The general
# configuration module contains information for both the
# CGI script and the system daemon.  These are kept in
# separate categories for simplicity of management.
#
######################################################################
# $Id: pgreet_content_guide.html,v 1.34 2004/03/05 18:08:02 elagache Exp $

# PgTemplateTest configuration parameters
#  mozilla -remote 'openURL("http://www.canebas.org/",new-tab)'
browser = "/usr/bin/mozilla"
prefix = -remote 'openURL("file://
suffix = ", new-tab)'
tmp_path = "/tmp/pgreet/PgTemplateTest"

# CGI State variables that may be needed in the average template.
<pgreet_CGI>
    varstring  =  "PgTemplateTest=true"
    hiddenfields  =  "<!-- Templatetest hiddenfields -->"
    URL_short_cut = "http://www.PgTemplateTest.org/pgreet.cgi?URL_short_cut=true"
    sender_name  =  "Test Sender Name"
    sender_email  =  "test_sender_email@PgTemplateTest.org"
    recipient_name  =  "Test Recipient Name"
    recipient_email  =  "test_recipient_email@PgTemplateTest.org"
    message  =  "This is a test message.  This is a test message.  This is a test message.  This is a test message.  "
    html_message  =  "This is a test message.  This is a test message.<br>  This is a test message.  This is a test message.  "
    login  =  "test_login"
    UserPassword  =  "test_password"
    password  =  "test_card_password"
    number  =  "SWAP-number"
    hostname = "www.PgTemplateTest.org"
    error_no = 11

    # The Error checking hash created using Config::General conventions.
    <error_hash>
        recipient_email = "disallow_empty"
        recipient_email = "want_email"
    </error_hash>

    # SpeedyCGI state variables
    SpeedyCGI = 1
    StartTime = 1058122148
    Invocations = 10

</pgreet_CGI>

These default values are enough to make an initial test of a web site template.  For a complete development project the values can be adjusted to suit the needs at hand.  If you need to load another PgTemplateTest configuration file, use the -t command line option.

This utility can be used in one of two ways: either as a tool to dump the content of all configuration variables and state variables being used to test a page (or setup,) or as a tool to render a template into HTML.  The command line syntax (from the man-page documentation) is as follows:

PgTemplateTest [-b] \
               [-c .../pgreet.conf] \
               [-d] \
               [-p .../cards.conf] \
               [-s SiteName] \
               [-t PgTemplateTest.conf] \
               [-x]
               template.tpl.html var1=value1 var2=value2 ...

In order to allow the developer to set the value of state variables that are unique to their pages, variable, value pairs can be passed to the program separated by an equals sign =. So for example, to process the file MyTemplate.tpl.html with the variable penguin equal to 5 and the variable camel equal to 2_hump the command would be:
    PgTemplateTest MyTemplate.tpl.html penguin=5 camel=2_hump

Variables or values should be quotes as necessary to avoid being misinterpreted by your command shell. Normally, PgTemplateTest displays the dumped variables or the resulting HTML to standard out. If you use the -b option, it will instead save the rendered HTML to a temporary file and launch a browser so that you may view the resulting page.  The command line options are as follows:

Option
Description
-b
Render the template into an HTML page, save as a temporary file, and launch a browser to view the resulting page. In order for this to work you must set the variables: $browser, $prefix, $suffix, and $tmp_path to appropriate values for your system.
-c
Use the specified file instead of the default file for the Penguin Greetings cards.conf card configuration file.
-d
Do not render any template page, just dump what the state variables for this arrangement would be. The -d option will ignore any template filename and process only command line arguments with embedded equals signs as additional state variables.
-p
Use the specified file instead of the default file for the Penguin Greetings pgreet.conf general configuration file.
-s
Load the configuration information of the secondary site SiteName instead of the primary site.  This can be used to test a secondary site using the same configuration file conventions used by the application itself.
-t
Use the specified file instead of the default file for the PgTemplateTest configuration file.
-x
Purge any temporary HTML files that are older than the configuration variable purge_after. Nothing else is done if -x and it provides no output whatsoever,so it can be placed in a cron job to remove files from all users. When not used as root, it will silently only remove files that you have permission to delete.

PgTemplateTest can be used in multiple ways.  To simply test the syntax of the Embperl, standard output can be sent to /dev/null:

    PgTemplateTest MyTemplate.tpl.html > /dev/null

Embperl will send error messages to standard error with line numbers for the offending statements (if any.)  To actually look at the resulting HTML, pipe the output into a file for your inspection.  If you want to actually look at the resulting page, use the -b option:

    PgTemplateTest -b MyTemplate.tpl.html penguin=5 camel=2_hump

This will create a temporary file in $tmp_path and then launch your browser to render the page.   There is one unfortunate "bad habit" in the browser rendering mode.  Since there is no obvious way of knowing when you will be finished with a given temporary file, there is no strategy for deleting them.  Therefore you must "clean up" your $tmp_path directory from time to time (by using the -x option or manually) to avoid clutter.

Since PgTemplateTest uses the identical configuration files that Penguin Greetings uses, you should see your template in the same configuration as it would be seen by a user.  By changing the variables passed to PgTemplateTest as command line arguments, you can simulate any condition your template would encounter on the "live site."  This tool can be used to validate all aspects of a template from raw syntax to the aesthetics of the resulting web page.  For more information on PgTemplateTest, see the PgTemplateTest man page.

Unfortunately, Mozilla version 1.5 no longer automatically finds any existing processes when you attempt to launch another copy.   Version 0.9.2 of Penguin Greetings uses instead the -remote feature of Mozilla to connect to an existing process.  This requires however that Mozilla be already running.  So if you intend to use the -b option of PgTemplateTest with Mozilla, make sure to first launch Mozilla before proceeding.  The default configuration creates the page in a new tab of Mozilla.  If you would prefer something else, edit the prefix and suffix configuration variables accordingly.  For more information on the Mozilla remote feature see the Mozilla documentation page on remote.

Consistent with version 0.9.7 of Penguin Greetings, PgTemplateTest can be used to test templates used in an object-oriented web site.  Simply specify the site of the web site being tested, if necessary, using the -s option.  There is a shared Perl module (Pgreet::CGIUtils) that provides the interface between both PgTemplateTest and CGI application and Embperl.  So you will get the same rendering using PgTemplateTest as will occur when your web site is live.  As one would expect, as of version 0.9.9 of Penguin Greetings, you can use PgTemplateTest to render templates in HTML::Mason just as the CGI application does (see below.) 

[Table of Contents]

Creating secondary ecard sites

New to version 0.8.8 is the ability to have secondary ecard sites that are serviced by the same CGI-Application/daemon pair.  Secondary ecard sites are handled identically as almost all respects so that there is nothing special to be done in creating a secondary site that is different from a primary site.  As noted in the Penguin Greetings Installation Guide, secondary ecard sites need the same organization as a primary site.  They need a configuration, data, and HTML directory.  They could be put together or separately.

It is probably best to try to set up the organization and rough out at least a prototype card configuration file so that you can use PgTemplateTest to test templates easily and in as realistic conditions as possible.  Secondary ecard sites must be added to the primary pgreet.conf file (in the field User_Pgreets) before PgTemplateTest will be able to find the configuration information for the secondary sites.

Note that secondary ecard sites need to have their own templates to handle general errors and CGI field errors (see above.)  If users wish to use the same error handling templates as come with the primary site, they should create symbolic links in the templates directory of the secondary site.  See the section above on object-oriented ecard sites for example secondary ecard site templates or examine any of the six secondary ecard sites that come with Penguin Greetings for more information.  The one site that illustrates a secondary site features without localization issues is the Camel Collection (PgCamel.)

[Table of Contents]

Localizing ecard sites in Penguin Greetings

New to version 0.9.5 of Penguin Greetings is support for localization/Internationalization.  Penguin Greetings does this by providing a separate site for each language and nation to be supported.  These sites, like all secondary ecard sites, need have nothing in common.  However, it is recommended that links be provided to the ecard sites in other languages.  An example below is provided from the PgSaint!en-us site:

[# ###################################################### #]
[#         Penguin Greetings Holy ecard website           #]
[#                                                        #]
[# For more information on Penguin Greetings project go:  #]
[# http://pgreet.sourceforge.net/                         #]
[#                                                        #]
[# File: default.tpl.html - Template for display of card  #]
[#                          image thumbnails.             #]
[# ###################################################### #]
<html>

<head>
   <title>Penguin Greetings - Holy ecard collection</title>
</head>
[* $ENV{'optRawInput'} = 16; *]
[*  $escmode = 0; *]
[$ var $trans $script_URL %card_hash @cards_list $card $sample_URL
       $text_URL %card_data $lang_URL
 $]
[-
  # Retrieve parameters from Penguin Greetings script (pgreet.pl.cgi)
  $trans = shift(@param);

  %card_hash = %{$trans->{'card_hash'}};

  %card_data = %{$card_hash{'card'}};

  @cards_list = @{$card_hash{'cards'}};


  # Build up pieces of URLs to simplify represention.
  $script_URL = join('', $trans->{'cgiurl'}, "/",
                         $trans->{'cgi_script'}, "?action=build&",
                         $trans->{'varstring'}, '&'
                    );

  $lang_URL = join('', $trans->{'cgiurl'}, "/",
                       $trans->{'cgi_script'}, "?site="
                    );

  $sample_URL = $script_URL . "next_template=sample_view";
  $text_URL = $script_URL . "next_template=text";
 -]
<body
 style="background-image: url([+ $trans->{'imageurl'} +]/PgSaints_wallpaper.jpg);">
<center>
<table cellpadding="0" cellspacing="0" border="0"
 style="text-align: left; width: 630px;">
  <tbody>
    <tr>
      <td style="vertical-align: top; text-align: center;" colspan="3"><img
 src="[+ $trans->{'imageurl'} +]/PgSaints_upper_banner.gif" title="" alt=""
 style="width: 642px; height: 38px;"><br>
      <img src="[+ $trans->{'imageurl'} +]/PgSaints_middle_banner.gif" title="" alt=""
 style="width: 641px; height: 73px;"> </td>
    </tr>
    <tr>
      <td
 style="vertical-align: top; background-color: rgb(0, 0, 0); width: 2px;"> <br>
      </td>
      <td style="vertical-align: top;">
      <div style="text-align: center;"> </div>
      <table cellpadding="2" cellspacing="2" border="0"
 style="text-align: left; width: 100%;">
        <tbody>
          <tr>
            <td style="vertical-align: top; text-align: center;">

              <!-- Links to alternative language site -->
              <a href="[+ $lang_URL +]PgSaint!en-us">
               <img
                    src="[+ $trans->{'imageurl'} +]/US_flag.jpg"
                    title="[English-US site]"
                    alt="[English-US site]" align="left"
                    style="width: 39px; height: 24px;" border="0">
              </a>
              <a href="[+ $lang_URL +]PgSaint!fr-fr">
               <img
                    src="[+ $trans->{'imageurl'} +]/French_flag.jpg"
                    title="[French-France Site]"
                    alt="[French-France Site]" align="right"
                    style="width: 39px; height: 24px;" border="0">
              </a>


Note that in the above example the links are to the complete alternative site (e.g. PgSaint!fr-fr.)  This insures that localization information is bypassed.  The option to switch sites should only be provided at the start of an ecard site to avoid confusion.  Users cannot start an ecard in one language and finish it in another (obviously.)  For more information on configuring Penguin Greetings to support multiple languages see the Penguin Greetings Administrators' guide.

Simple websites with only only a small number of translations may find it simplest to "hand code" the text for each language in separate template files.  An example of this is provided in the Holy ecard collection (PgSaint.)  So long as there are not multiple sites or many languages to support, the overhead of additional software management of localization may not be worth it.  Beyond such simple arrangements, explicit software support of localization has clear benefits.  Penguin Greetings does not provide any internal support for any particular localization management scheme since the very nature of an Embedded Perl environment allows developers to easily include one of their own choosing.  As an example of one possible choice, version 0.9.9 of Penguin Greetings uses the module Locale::Maketext module which is now standard with recent versions of Perl 5.8.x.  Four of the five localized sites now use Locale::Maketext.  What follows is a brief description of how to use this particular scheme for localization, but it will be of interest to anyone that needs to localize ecard sites.

The Locale::Maketext localization scheme relies on Perl object inheritance.  For the Penguin Greetings demonstration sites, a new  instance of the module Locale::Maketext was created in Penguin Greetings library module: Pgreet::I18N and associated translation files.  One of the great advantages of the Locale::Maketext approach to localization is that it makes available the full power of Perl in the presentation of the translation information.  For the sake of simplicity and transparency, Penguin Greetings leaves the translated strings in a simple hash.  Anyone seeking to use Locale::Maketext for localization will need to create their own equivalents of Pgreet::I18N.  Please see the Locale::Maketext module documentation and the sample application using that translation scheme File::Findgrep.

The Locale::Maketext module is implemented based on an idea that the project uses one default language.  This has the powerful advantage that the text strings in one language can be used as the keys for the translated strings in other languages.  When used in Penguin Greetings, it means that the templates in the project language (English) will work correctly in any other language, so the templates in alternative language(s) (in this case French) have been replaced with symbolic links to the English templates.  Any template using this localization scheme needs to include the translation modules and use the translation method maketext.  An example of this is shown in the snippet from the California Poppy site (PgPoppy) Text_email.tpl.html template seen below:

.
.
.
[# Embperl Variables #]
[$ var $trans $LN $]
[-
  # Retrieve parameters from Penguin Greetings script (pgreet.pl.cgi)
  $trans = shift(@param);
  use Pgreet::I18N;
  $LN = Pgreet::I18N->get_handle($trans->{'lang_code'});
 -]



[+ $LN->maketext("Dear [_1],",$trans->{'recipient_name'}) +]
.
.
.

The Locale::Maketext module uses a concept of language handles ($LN) that incorporates the normal process of object construction.  So once one uses the particular instance of Locale::Maketext (in this case Pgreet::I18N,) then one creates the language handle.  This requires an argument which is the language code for the site at the moment of calling.  As noted above, this is included in the transfer hash under the key lang_code.  Once one has a language handle, one can go ahead and display text using the maketext method.  The snippet above is a case where the translated text takes an argument (in this case the recipient's name.)  See the Locale::Maketext documentation for details on this feature.  Since there are some idiosyncrasies associated with using Locale::Maketext with Mason (versus Embperl) and in HTML templates versus plain text templates (as above,) Please see any of the four secondary ecard sites using Locale::Maketext (PgXmas, PgPoppy, Pg4Seasons, or PgSeattle) for details on how to implement internationalization/localization using this Perl facility.

[Table of Contents]

Implementing CGI "Back" buttons

Like the majority of Web-Applications, Penguin Greetings finds itself needing to violate one of the tenets of hypertext: that interactions are state-less.  Penguin Greetings has used a "pack rat" solution to this problem: every state generated by users is kept around for a reasonable length of time (default: 1 hour.)  Because of this, Users could "back" to a previous creation page simply by pressing the "back" button on their own browser.  Modern Browsers keep track of what users entered and the state files kept by Penguin Greetings took care of the rest.  Browsers that support JavaScript provide the added luxury of allowing developers to put a "Back" button on the page itself - thereby assuring users that they really could "go back" without losing anything.

Alas, what about browsers that do not support JavaScript?, or those defeated users who turn off JavaScript to avoid banner-ads, viruses, and all the other grim signs of an Internet that has lost its gentility?  New to version 0.9.2 is support for a CGI-based scheme for going back a state.  Unfortunately, because some of the information needed to do this is normally kept on the client side, this turns out to be more complex than one might hope.

On pages where users have not entered any text, going back simply means selecting the previous state file and reseting all the variables that are not included in the state file.   Penguin Greetings does this by providing two new sorts of "hidden fields:" BackVarStr and BackHiddenFields.  By including these in either a  URL-styled "GET" request or a form "POST" request, the Penguin Greetings CGI application would simply go back to a previous state file and allow the user to continue from there.

There is alas a fly in the ointment.  If the user had entered text on that previous screen, that information would be in the browser - not the server.  When the user goes back to the previous state, they'll find it as they had first encountered it - blank, not as they last left it - with text entries and settings.  In truth, the user doesn't want to go back to the previous state, they want to go to results of that previous state.  To accomplish this requires a little more work on the part of you the content developer.

Penguin Greetings takes care of creating state files with the right state variables, so that using BackVarStr and BackHiddenFields works without fuss.  However, you the developer, must change your HTML templates to provide values for any variables that "have been assigned a value" in another life.

This is best illustrated by an example.  In the demonstration web site, the template page that collects most of the information for the ecard includes an entry for setting the subject of the card email.  The HTML snippet is included below:

<center>
    <span style="font-weight: bold;">Email subject</span> (optional)<br>
    Give your recipient a personalized subject<br>
    line for the email they will receive<br>
    <code>[pgreet]</code>&nbsp;
    <input type="text" name="subject" size=45
    value="[+ $trans->{subject} +]">
</center>

The part in bold is the value that will be displayed as a default value when the form is rendered.  The little bit of Embperl inserts into this location the contents of the transfer hash for the key subject.  If there is nothing assigned to this key, there will be no value assigned at all.  When a user arrives at the text entry template for the first time, they will see an empty field.  However, if this page is reached by returning from a successive page, the $trans->{'subject'} will place the users previous entry for her/him.

In order for the user to be able to return to this page, there is now a CGI back button implemented into the succeeding template as shown in the code snippet below.

      <!-- Form to implement CGI "Back" button -->
      <form action="[+ $trans->{'cgiurl'} +]/[+ $trans->{'cgi_script'} +]"
             method="post">

      [# Include the hidden fields to retrieve previous state #]

      [+ $trans->{'BackHiddenFields'} +]

      <input type="submit" value="Back">
</form>

The above example creates a CGI "POST" request in the same fashion used elsewhere in Penguin Greetings.  Penguin Greetings automatically generates the required fields to go back which can be accessed via the $trans->{'BackHiddenFields'} entry.

There is merit to using a JavaScript solution instead of relying on CGI for this job.  For the sake of comparison, The primary demonstration ecard site "shipped" with Penguin Greetings uses CGI for implementing the "Back" button.  All the secondary ecard sites use a simple JavaScript solution instead.  This allows you to compare the two solutions directly.

[Table of Contents]

Using shared template libraries

Even within the collection of secondary ecard sites included with Penguin Greetings there is a lot of common actions that must be performed.  With version 0.9.9 release, a new collection of Embperl templates have been created that can simply be "dropped" into a site to avoid "reinventing the wheel."  These templates can be found in the shared_templates directory of the source code distribution.  This shared templates perform the following duties:


one_col_thumb.tpl.html,
two_col_thumb.tpl.html
templates to display one column or two columns of card thumbnail images with the associated links.
email_text_data.tpl.html
Produces the HTML form entries to enter the textual information needed to send the card (names, email addresses, passwords, etc.)
schedule.tpl.html
HTML forms that allow users to schedule their card to be send sometime in the future.
return_home.tpl.html
Produces the HTML code that allows a user to return to start of the ecard site preserving their login information for password restricted sites so that they don't need to login again to send another card.

The shared templates need to be accessible to the templates that will be using them.  In the demonstration sites this is done by providing symlinks for the shared templates used by that particular site alternatively, these files could be placed in a common location in your web page tree.  Using the templates is very much like calling a subroutine or procedure.  For example, to display a set of thumbnails in a two-column format the call is as follows:

      [# Insert template to display thumbnails #]
      [- Execute({inputfile => $trans->{'templatedir'} .
                               "/two_col_thumb.tpl.html",
                  param => [$trans, $LN, 1]
                 }
                )
       -]
For consistency, these shared templates use the same $trans transfer hash to pass most of the information needed.  The two_col_thumb.tpl.html template requires two other parameters: $LN a language handle used to support Locale::Maketext localization, and a flag indicating if the card caption should be displayed in italics.  The actual code of this shared template is shown below:

[# #################################################################### #]
[#                                                                      #]
[#  Penguin Greetings - Embperl templates of common HTML/Perl code      #]
[#                                                                      #]
[#  These HTML/Embperl templates contain elements that are commonly     #]
[#  within an ecard site (in particular the demonstration sites         #]
[#  included with Penguin greetings.)  This common code is maintained   #]
[#  independently and included into the demonstration sites via         #]
[#  symbolic links to the sites that use the code.                      #]
[#                                                                      #]
[#     Edouard Lagache, elagache@canebas.org, April 2005                #]
[#     For Penguin Greetings 0.9.9 release                              #]
[#                                                                      #]
[#  For more information on Penguin Greetings project go:               #]
[#      http://pgreet.sourceforge.net/                                  #]
[#                                                                      #]
[#                                                                      #]
[#  File: two_col_thumb.tpl.html                                        #]
[#        Template to display thumbnail images of cards in two columns  #]
[#        with the caption above the thumbnail.  Suitable for sites     #]
[#        with a modest number of cards and no categories.              #]
[#                                                                      #]
[# #################################################################### #]

[# Set environment to avoid HTML reformatting #]
[* $ENV{'optRawInput'} = 16; *]
[*  $escmode = 0; *]

[# Declare Embperl variables needed to process page #]
[$ var $trans @lang_codes $default_lang_code $italic $LN $next_thumbs
       $script_URL %card_hash @cards_list $card $sample_URL
       $text_URL %card_data $lang_URL @lang_codes $lang_code
       %send_prompt %sample_prompt $send_string $sample_string
 $]

[! sub new_row {
 #
 #  Helper subroutine create a new row in table of thumbnails
 #  (Use straight Perl to bypass Embperl table tag interpretation.)
 #
   my $next_thumbs = shift;

   if (($next_thumbs % 2) == 0) {
        print OUT "       </tr>";
        print OUT "       <tr>";
   }
 }
 !]

[-
  # Retrieve parameters from Penguin Greetings script (pgreet.pl.cgi)
  $trans = shift(@param);
  $LN = shift(@param); # Locale::Maketext handle
  $italic = shift(@param);

  %card_hash = %{$trans->{'card_hash'}};

  %card_data = %{$card_hash{'card'}};

  @cards_list = @{$card_hash{'cards'}};

  # Build up pieces of URLs to simplify represention.
  $script_URL = join('', $trans->{'cgiurl'}, "/",
                         $trans->{'cgi_script'}, "?action=build&",
                         $trans->{'varstring'}, '&'
                    );

  $next_thumbs = 0;

  $send_string = $LN->maketext("Send this card");
  $sample_string = $LN->maketext("See sample of this card");

  $sample_URL = $script_URL . "next_template=sample_view";
  $text_URL = $script_URL . "next_template=text";
 -]

[# ##### End of Embperl initialization ##### #]


 <!-- Table of card thumbnails -->
   <table cellpadding="2" cellspacing="2" border="0"
    style=
    "text-align: left; width: 100%; margin-left: auto; margin-right: auto;">
         <tbody>
    <tr>
   [# Loop through every card in collection #]
   [$ foreach my $card (@cards_list) $]
     [- $next_thumbs++ -]
      <td style="text-align: center; vertical-align: top;">
       <b>[$ if ($italic) $]<i>[$ endif $]<small>
       [+ $card_data{$card}->{'caption'} +]
       [$ if ($italic) $]</i>[$ endif $]</b><br>
         [# The URL will be the general URL above + card selected #]
         <a href="[+ $sample_URL +]&card=[+ $card +]">
            [# The image is the image URL + the image name #]
            <img src=
             "[+ $trans->{'imageurl'} +]/[+ $card_data{$card}->{'thumb'} +]"
             alt="[+ $card_data{$card}->{'caption'} +]"
             title="[+ $card_data{$card}->{'caption'} +]">
         </a><br>
       <a href="[+ $text_URL +]&card=[+ $card +]">[+ $send_string +]</a><br>
       <a href="[+ $sample_URL +]&card=[+ $card +]">[+ $sample_string +]</a>
          <br>
      </small>
      </td>
    [- new_row($next_thumbs) -]
  [$ endforeach $]
     </tr>
   </tbody>
  </table>
  <!-- End of thumbnails -->

These templates are not intended to serve all possible needs.  For example, the two_col_thumb.tpl.html is intended for use with multilingual sites that use Perl's Locale::Maketext.  The calls to $LN->maketext(...) are performing the localization function.  The shared templates are of two kinds: language independent and localization specific.  The language independent templates are stored directly in the shared_templates folder in the source distribution.  The language specific templates are either in subdirectories reflecting the hard-coded language used (en-us or fr-fr) or in the I18N directory which is reserved for templates using Locale::Maketext Localization.  For the moment, all shared templates are in Embperl only.  As with all such snippets of code, the best way to understand their role is to use at the code itself and the secondary ecard sites that use them: PgSaints, PgXmas, and  PgPoppy.

[Table of Contents]

Developing Penguin Greetings content using HTML::Mason

New to version 0.9.9 of Penguin Greetings is support for the HTML::Mason for templates in addition to Embperl.  Mason is very similar to Embperl in terms of it's purpose and capabilities.  Using Mason and Penguin Greetings is very similar to what has already been described here. 

The Penguin Greetings Installer does not install HTML::Mason as part of the collection of Perl modules since it isn't necessary to get the primary site running (as it uses Embperl.)  Any technique to install a Perl module can be used, but the simplest is to the Perl CPAN installer as illustrated below:

perl -MCPAN -e 'install HTML::Mason'

This manual will not go into any details of how to use Mason, but will instead cover the necessary changes in order to use Mason instead of Embperl.  The best way to see those differences is to view the secondary ecard site using Mason: Savoring the sights of Seattle (PgSeattle.)  By convention, Mason templates have the suffix: mtpl.html, but this can be changed in the configuration file.  Penguin Greetings will assume a site is Embperl unless there is a <Mason_Object> declaration in the site's configuration file.  A sample such declaration is below:

    # Parameters for HTML::Mason
    <Mason_Object>
        comp_root = "/home/httpd/htdocs/pgreet/PgSeattle"
        data_dir = "/var/cache/pgreet/mason-data"
        bypass_object = "Text_email.mtpl.html"
    </Mason_Object>

Two of the three declarations (comp_root and data_dir) are passed directly to the Mason interpreter, the third (bypass_object) serves the same role as elsewhere in Penguin Greetings, allowing templates to bypass Mason autohandlers (mostly for email text only templates.)  The component root (comp_root) is the absolute path to the object-orient tree of Mason component files.  As with  object-oriented Embperl, Mason uses this to determine the top of the Mason component hierarchy.  The other declaration is the location where Mason can store cache and object information (data_dir)  Check with your system administrator to find out what this should be set to.  See the Penguin Greetings Administrators' guide for additional information on this setting.

For the sake of consistency, the same transfer hash $trans is passed to Mason templates as are used in Embperl templates.  However, the syntax for retrieving the hash is slightly different as shown below:

my $trans = shift;

The Perl @_ array is used to move the transfer hash into Mason templates instead of the @param array as is done for Embperl.

An example Mason Penguin Greetings template is shown below.  As with Embperl example in this document, the Mason segments are highlighted in bold.

<%doc>
# #################################################################### #
#                                                                      #
#      Penguin Greetings - Savoring Seattle Collection                 #
#                                                                      #
#  Demonstration ecard site implemented in HTML::Mason as an           #
#  alternative embedded Perl environment and featuring photographs     #
#  Taken in the greater Seattle Washington area.                       #
#                                                                      #
#  For more information on Penguin Greetings project go:               #
#      http://pgreet.sourceforge.net/                                  #
#                                                                      #
#                                                                      #
#  File: default.mtpl.html                                             #
#        The file to present users with thumbnails of images.          #
#        Also login page for retrieving a card.                        #
#                                                                      #
# #################################################################### #
</%doc>

<%method title>
%#
%# Override the text of page title
%#
<%args>
  $LN
</%args>
  <% $LN->maketext("Penguin Greetings - Welcome to the Savoring the sights of Seattle Collection") %>
</%method>

<%perl>
#
# ##### Transfer data from CGI app and create shortcuts  ##### #
#
  my $trans = shift;

  my $add_row = 1;

  my $image_path = $trans->{'imageurl'};

  my %card_hash = %{$trans->{'card_hash'}};

  my %card_data = %{$card_hash{'card'}};

  my @cards_list = @{$card_hash{'cards'}};


  # Build up pieces of URLs to simplify represention.
  my $script_URL = join('', $trans->{'cgiurl'}, "/",
                         $trans->{'cgi_script'}, "?action=build&",
                         $trans->{'varstring'}, '&'
                    );

  my $lang_URL = join('', $trans->{'cgiurl'}, "/",
                       $trans->{'cgi_script'}, "?site="
                    );

  my $sample_URL = $script_URL . "next_template=sample_view";
  my $text_URL = $script_URL . "next_template=text";

  use Pgreet::I18N;
  my $LN = Pgreet::I18N->get_handle($trans->{'lang_code'});

# ##### End initialization  ##### #
</%perl>

            <!-- Template: default.mtpl.html -->

           <!-- Introduction to website -->
           <table style="width: 100%; text-align: center;"
                  border="0">
             <tr>
               <td style="width 40px">
                 <a href="<% $lang_URL %>PgSeattle!en-us">
                   <img src="<% $trans->{'imageurl'} %>/US_flag.jpg"
                        title="<% $LN->maketext("~[English-US site~]") %>"
                        alt="<% $LN->maketext("~[English-US site~]") %>"
                        style="width: 39px; height: 24px;" border="0">
                  </a>
                </td>
                <td>
                   <div align="center">
                   <font style="font-style: italic;" size="-1">
                   <% $LN->maketext("Welcome to a selection of notecards featuring images of the greater Seattle Washington area and powered by Penguin Greetings") %>
                   </font></div>
                </td>
                <td>
                   <a href="<% $lang_URL %>PgSeattle!fr-fr">
                     <img src="<% $trans->{'imageurl'} %>/French_flag.jpg"
                          title=
                           "<% $LN->maketext("~[French-France Site~]") %>"
                          alt=
                           "<% $LN->maketext("~[French-France Site~]") %>"
                          style="width: 39px; height: 24px;" border="0">
                   </a>
                </td>
              </tr>
            </table>

            <p align=center>
            <a href="#Retrieve"><font size="-1">(<%
               $LN->maketext("Received a Savoring Seattle note card?") %>
               <% $LN->maketext("click here to retrieve your card") %>)
               </font></a></p>

            <hr style="width: 100%; height: 2px;">
            <p align="center"><font size="-1">
               <% $LN->maketext("Note: clicking on an image will take to you sample preview of the card.") %>
               <% $LN->maketext("To send a card with that image click on the &quot;Send this card&quot; link below the image.") %>
            </font></p>

            <table style="width: 100%; text-align: left;" border="0"
                   cellpadding="2" cellspacing="2">
              <tbody>
                <tr>
%               foreach my $card (@cards_list) {
%                 my $thumb = $card_data{$card}->{'thumb'};
                  <td style="vertical-align: top; text-align: center;">
                     <font size="-1">
                     <span style="font-family: helvetica,arial,sans-serif;">
                     <% $card_data{$card}->{'caption'} %>
                     </span></font><br>
                     <a href="<% $sample_URL %>&card=<% $card %>">
                        <img alt="<% $card_data{$card}->{'caption'} %>"
                             src="<% $image_path %>/<% $thumb %>"></a><br>
                     <small>
                     <a href="<% $text_URL %>&card=<% $card %>"><%
                        $LN->maketext("Send this card") %></a><br>
                     <a href="<% $sample_URL %>&card=<% $card %>"><%
                        $LN->maketext("See sample of this card")
                        %></a></small><br>
                  </td>
%                if ($add_row % 2 == 0) {
                 </tr>
                 <tr>
%                }
%                $add_row++
%       }
                </tr>
              </tbody>
            </table>
  <br><br>
            <hr style="width: 100%; height: 2px;">

  <!-- Code to handle picking up a card sent by someone previously -->
  <a name="Retrieve"></a>
  <table cellpadding="2" cellspacing="2" border="0"
         style="text-align: left; width: 100%;">
  <tbody>
    <tr>
      <td style="vertical-align: top; text-align: center;">
        <b><% $LN->maketext("Have you received a Penguin Greeting?")
            %></b><br><br>
         <small><small><%
            $LN->maketext("Fill in the information to the right to view your Penguin Greeting!")
         %></small>
         </small>
      </td>
      <td style="vertical-align: top; text-align: right;">
          <form method="post"
                action="<% $trans->{'cgiurl'} %>/<% $trans->{'cgi_script'} %>">
          <table border="0" style="margin-left: auto; margin-right: 0px;">
          <tbody>
            <tr>
              <td align="right"><font size="-1"><% $LN->maketext("Card ID:")
                %></font></td>
              <td><input type="text" size="15" name="code"
                         value=""></td>
              <td style="text-align: right;"><input
                  type="reset" value="<% $LN->maketext("Reset") %>"></td>
           </tr>
           <tr>
             <td align="right"><font size="-1"><%
                 $LN->maketext("Password:") %></font></td>
             <td><input type="password" size="15"
                        name="password" value=""></td>
             <td style="text-align: right;"><input
                        type="submit"
                        value="<% $LN->maketext("Submit") %>"></td>
          </tr>
        </tbody>
        </table>
     <input type="hidden" name="action" value="view">
     <input type="hidden" name="next_template" value="view">
     <input type="hidden" name="code+ArgCheck" value="disallow_empty">
     <input type="hidden" name="password+ArgCheck" value="disallow_empty">
     <input type="hidden"
            name="var_error_template"
            value="fields_error">
     <% $trans->{'hiddenfields'} %>
   </form>
                  </td>
                </tr>
              </tbody>
            </table>
            <!-- End of table to retrieve a card -->
            <hr style="width: 100%; height: 2px;">

The Mason syntax for inserting the result of a Perl expression into the HTML is <% %> and corresponds to Embperl's [+ +] syntax.  Other Perl expressions can be evaluated either if a line starts with a percent symbol % or if wrapped by <%perl> </%perl> tags (serving roughly the role of Embperl's [- -] syntax.)  Another difference between Mason and Embperl is the lack of any page-scoped variables (the [$ var $] declarations.)  Instead local page variables can be declared using the usual Perl my syntax.

Aside from the differences related to the embedded Perl environments, templates using Mason are very similar to those using Embperl.  The template above is very similar to PgPoppy-en-us/templates/default.tpl.html (when the shared template code in shared_templates/I18N/two_col_thumb.tpl.html is taken into account.)  The same mechanisms for retrieving information from Penguin Greetings via the $trans hash work identically and values are returned to Penguin Greetings using the same CGI field mechanisms.  The sections above on using Penguin Greetings with Embperl apply equally to Mason with just a slight bit of revised syntax.  The details on those syntactical differences are worked out in the Savoring the sights of Seattle (PgSeattle) secondary ecard site.  Comparing those templates with say the California Poppy Collection (PgPoppy) site should provide most of the interesting compare/contrasts.

Mason was conceived from an object-oriented perspective, so sites are expected to use some sort of inheritance scheme.  In Mason, this is normally handled through the use of files named autohandlersPenguin Greetings ecard sites using Mason also use autohandler files in the exactly the way that Mason does (by passing the component root information directly to the Mason interpreter object.)  Nonetheless, there are times when templates should not be interpreted within this object hierarchy.  As with the Embperl version of object-oriented templates, a bypass_object declarative is provided to force the Mason interpreter to not nest this template within the autohandler hierarchy.  This makes sense mostly for templates related to emailing the user.

With only one demonstration site using Mason, there are no shared templates in Mason included with version 0.9.9 of Penguin Greetings.  However, this may well change; especially, if there are some contributions from enterprising Mason users of Penguin Greetings.

Embperl vs Mason for Penguin Greetings
Now that there is a choice of embedded Perl environments for Penguin Greetings, inevitably the question will be asked: which environment should I use?  Personal preferences aside, probably the simplest criteria is one of your own needs.  If you are simply trying to use Penguin Greetings, you are better off to stick to Embperl since most of the documentation and examples are written using that environment.  On the other hand, if you have some commitment or interest in using Mason, well - that rather does settle the matter doesn't it! :-) 

[Table of Contents]

Troubleshooting and Tips

Penguin Greetings has grown from a modest tool to very capable system for serving web-based greeting cards in a few months of development.  The Penguin Greetings backend is relatively simple and robust and imposes few constraints on the creative process of content developers.  That however leaves the content developer with few tools to look for problems when they arise.  In addition to  PgTemplateTest, there is one other tool in the utils directory of the source code distribution that can help however:

Here are tips and suggestions to help you get started:

For more ideas on how to create templates, look at the source code included with Penguin Greetings.

[Table of Contents]