This is Info file pm.info, produced by Makeinfo version 1.68 from the
input file bigpm.texi.


File: pm.info,  Node: OpenInteract/Error,  Next: OpenInteract/Package,  Prev: OpenInteract/DBI,  Up: Module List

Provide central holding location for Interact errors
****************************************************

NAME
====

   OpenInteract::Error - Provide central holding location for Interact
errors

SYNOPSIS
========

     OpenInteract::Error->set( ... );
     $R->throw( ... );

     my $ei = OpenInteract::Error->get;
     print "Last error message: $ei->{system_msg}\n";

DESCRIPTION
===========

   This class provides a central location for error messages from all
Interact modules. The error information collected in these variables is
guaranteed to result from the most recent error generated by Interact.

VARIABLES
=========

   All of these variables are package variables, so you refer to them like
this:

     $OpenInteract::Error::<variable_name>
     $OpenInteract::Error::system_msg

   See the `NOTES' in this node section below for hints on making the
error variables shorter.

   *user_msg* ($)

   A generic message that is suitable for showing a user. When telling a
user something went wrong, you do not want to tell them:

     execute called with 2 bind variables when 1 are needed

   instead, you want to tell them:

     Database query failed to execute

   This variable is identical to the value thrown by the *die()* command,
so you do not normally need to refer to it.

   *system_msg* ($)

   Even though you do not want to show your users details of the error,
you still need to know them! The variable *system_msg* gives you details
regarding the error.

   type ($)

   Interact knows about a few types of errors. Some depend on your Interact
implementation (e.g., DBI, dbm, LDAP, etc.). Others can be:

     -security: There is a security violation and the action could not be
                completed

   package ($)

   Set to the package from where the error was thrown.

   method ($)

   Set to the method from where the error was thrown.

   filename ($)

   Set to the filename from where the error was thrown.

   line ($)

   Set to the line number from where the error was thrown.

   *extra* (\%)

   Different Interact classes have different information related to the
current request. For instance, DBI errors will typically fill the 'sql'
and 'values' keys. Other Interact implementations may use different keys;
see their documentation for details.

METHODS
=======

   clear ()

   Clears the current error saved in the class. Classes outside the
*OpenInteract::* hierarchy should never need to call this.

   No return value.

   get()

   Returns a hashref with all the currently set error values.

   *set( \% )*

   First clears the variables then sets them all in one fell swoop. The
variables that are set are passed in the first argument, a hashref. Also
sets both the package and method variables for you, although you can
override by setting manually.

   No return value;

   *throw( \%params )*

   Throws an error from anywhere in the system. Kept for backward
compatibility - most of the time you will use:

     $R->throw( ... );

   We simply pass the parameters (with any caller info) to the method by
the same name in the error object class.

NOTES
=====

   Some people might find it easier to alias a local package variable to
an Interact error variable. For instance, you can do:

     *err_user_msg   = \$OpenInteract::Error::user_msg;
     *err_system_msg = \$OpenInteract::Error::system_msg;
     *err_type       = \$OpenInteract::Error::type;
     *err_extra      = \%OpenInteract::Error::extra;

   And then refer to the alias in your local package:

     my $obj_list = eval { $obj->fetch_group( { where => 'this = that' } ) };
     if ( $@ ) {
       warn "Error found! Error: $@\n",
            "Error type: $err_type\n",
            "More specific: $err_system_msg\n",
            "Extra stuff:\n",
            "--$err_extra{sql}\n",
            "--$err_extra{values}\n";
     }

TO DO
=====

BUGS
====

COPYRIGHT
=========

   Copyright (c) 2001 intes.net, inc.. All rights reserved.

   This library is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

AUTHORS
=======

   Chris Winters <chris@cwinters.com>


File: pm.info,  Node: OpenInteract/Package,  Next: OpenInteract/PackageRepository,  Prev: OpenInteract/Error,  Up: Module List

Perform actions on individual packages
**************************************

NAME
====

   OpenInteract::Package - Perform actions on individual packages

SYNOPSIS
========

DESCRIPTION
===========

METHODS
=======

   *create_subdirectories( $root_dir, $main_class )*

   Creates subdirectories in a package directory - currently the list of
subdirectories is held in the package lexical @PKG_SUBDIR, plus we also
create the directories:

     $main_class
     $main_class/Handler
     $main_class/SQLInstall

   If there is no $main_class passed in, 'OpenInteract' is assumed.

   *create_package_skeleton( $package_name, $base_install_dir )*

   Creates the skeleton for a package in the current directory. The
skeleton can then be used to for a fully functioning package.

   The skeleton creates the directories found in @PKG_SUBDIR and copies a
number of files from the base OpenInteract installation to the skeleton.
These include:

     Changes
     package.conf
     MANIFEST
     MANIFEST.SKIP
     conf/spops.perl
     conf/action.perl
     doc/package.pod
     doc/titles
     template/dummy.meta
     template/dummy.tmpl
     <PackageName>/SQLInstall/<PackageName>.pm
     <PackageName>/Handler/<PackageName>.pm

   We fill in as much default information as we know in the files above,
and several of the files have helpful hints about the type information
that goes in each.

   *install_distribution*

   Install a package distribution file to the base OpenInteract
installation. We do not need to do any localization work here since we are
just putting the distribution in the base installation, so the operation
is fairly straightforward.

   More work and testing likely needs to be done here to ensure it works
on Win32 systems as well as Unix systems. The use of *Note File/Spec:
File/Spec, and *Note File/Path: File/Path, should help with this, but
there are still issues with the version of *Note Archive/Tar: Archive/Tar,
shipped with ActiveState Perl.

   *install_to_website( \%params )*

   Installs a package from the base OpenInteract installation to a
website. The package must already have defined 'website_name',
'website_dir' and 'package_dir' object_properties. Also, the directory:

     website_dir/pkg/pkg-version

   should not exist, otherwise the method will die.

   Note that we use the routines `_copy_spops_config_file()' and
`_copy_action_config_file()', which localize the `spops.perl' and
`action.perl' configuration files for the website. The localization
consists of changing the relevant class names from 'OpenInteract' to
'MyWebsiteName'.

   *export_package( \%params )*

   Exports the package whose root directory is the current directory into
a distribution file in tarred-gzipped format, also placed into the current
directory.

   Returns: Information about the new package in hashref format with the
following keys:

     name
       Name of package

     version
       Version of package

     file
       Full filename of distribution file created

   Parameters:

     config_file ($) (optional)
       Name of configuration file for package.

     config (\%) (optional)
       Hashref of package configuration file information.

   *read_config( \%params )*

   Reads in a package configuration file. This file is in a simple
name-value format, although the file supports lists and hashes as well.
Whether a parameter supports a list or a hash is defined in the package
lexical variables %CONF_LIST_KEYS and %CONF_HASH_KEYS. The reading goes
like this:

   If a key is not in %CONF_LIST_KEYS or %CONF_HASH_KEYS, it is just a
simple key/value pair; a key in %CONF_LIST_KEYS gets the value pushed onto
a stack, and a key found in %CONF_HASH_KEYS has its value split on
whitespace again and that assigned to the hashref indexed by the original
key. Once we hit the 'description' key, the rest of the file is read in at
once and assigned to the description. Note that comments and blank lines
are skipped until we get to the description when it is all just slurped in.

   Returns: hashref of configuration information with the configuration
keys as hashref keys.

   Parameters:

     file
       Full filename of package file to be read in

     info
       Hashref of package information to read package config from

     directory
       Directory from which to read the package config.

   *replace_and_copy( \%params )*

   Copy a file from one place to another and in the process do a
search-and-replace of certain keys.

   Parameters:

     from_file ($)
       File from which we should read text.

     to_file ($)
       File to which we write changed text.

     from_text (\@)
       List of keys to replace

     to_text (\@)
       Replacement values for each of the keys in 'from_text'

HELPER METHODS
==============

   *_extract_archive( $archive_filename )*

   This method is a wrapper around *Note Archive/Tar: Archive/Tar, to try
and account for some of the differences between versions of the module.
Errors found during extraction will be found in the package lexical
`$ARCHIVE_ERROR'.

   Note that before calling this you should already be in the directory
where the archive will be extracted.

   *_create_manifest*

   Creates a MANIFEST file in the current directory. This file follows the
same rules as found in *Note ExtUtils/Manifest: ExtUtils/Manifest, since
we use the `mkmanifest()' routine from that module.

   Note that we turn on the 'Quiet' and turn off the 'Verbose' parameters
in hopes that the operation will be silent (too confusing), but the
current version of ExtUtils::Manifest does not make its sub-operations
silent. The version shipped with 5.6.1 should take care of this.

   *_remove_directory_tree( $dir )*

   Remove a directory and all files/directories beneath it. Return the
number of removed files.

   *_change_class_name( $old_class, $new_name )*

   Changes the name from 'OpenInteract' to $new_name within $old_class.
For instance:

     my $old_class = 'OpenInteract::Handler::FormProcess';
     my $new_class = $class->_change_class_name( $old_class, 'MyWebsiteName' );
     print "New class is: $new_class\n";

     >> New class is: MyWebsiteName::Handler::FormProcess

   If the method is called from an object and the second argument
($new_name) is not given, we default it to: `$object->{website_name}'.

TO DO
=====

BUGS
====

SEE ALSO
========

   *Note OpenInteract/Package: OpenInteract/Package,

COPYRIGHT
=========

   Copyright (c) 2001 intes.net, inc.. All rights reserved.

   This library is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

AUTHORS
=======

   Chris Winters <chris@cwinters.com>


File: pm.info,  Node: OpenInteract/PackageRepository,  Next: OpenInteract/Request,  Prev: OpenInteract/Package,  Up: Module List

Operations to represent, install, remove and otherwise manipulate package repositories.
***************************************************************************************

NAME
====

   OpenInteract::PackageRepository - Operations to represent, install,
remove and otherwise manipulate package repositories.

SYNOPSIS
========

     # Get a reference to a repository

     my $repository = OpenInteract::PackageRepository->fetch(
                                        undef,
                                        { directory => '/opt/OpenInteract' } );

     # Create a new package, set some properties and save to the repository

     my $pkg_info = {
         name      => 'MyPackage',
         version   => 3.13,
         author    => 'Arthur Dent <arthurd@earth.org>',
         base_dir  => '/path/to/installed/OpenInteract',
         package_dir => 'pkg/mypackage-3.13',
      };
      $repository->save_package_info( $info );

     # Retrieve the latest version of a package

     my $info = eval { $repository->fetch_package_by_name({ name => 'MyPackage' }) };
     unless ( $info ) {
       die "No package found with that name!";
     }

     # Retrieve a specific version

     my $info = eval { $repository->fetch_package_by_name({
                                       name => 'MyPackage',
                                       version => 3.12 }) };
     unless ( $info ) {
       die "No package found with that name and version!";
     }

     # Install a package

     my $info = eval { $repository->install_package({
                           package_file => $OPT_package_file }) };
     if ( $@ ) {
       print "Could not install package! Error: $@";
     }
     else {
       print "Package $info->{name}-$info->{version} installed ok!";
     }

     # Install to website (apply package)

     my $info = eval { $repository->fetch_package_by_name({
                                       name => 'MyPackage',
                                       version => 3.12 }) };
     my $site_repository = OpenInteract::Package->fetch(
                                          undef,
                                          { directory => "/home/MyWebsiteDir" } );
     $info->{website_name} = "MyApp";
     $info->{installed_on}  = $repository->now;
     $site_repository->save_package_info( $info );

     # Create a package skeleton (for when you are developing a new
     # package)

     $repository->create_package_skeleton( $package_name );

     # Export a package into a tar.gz distribution file

     chdir( '/home/MyWebsiteDir' );
     my $status = OpenInteract::Package->export_package();
     print "Package: $status->{name}-$status->{version} ",
           "saved in $status->{file}";
     
     # Find a file in a package

     $repository->find_file({ package => 'MyPackage', file => 'template/mytemplate.tmpl' });
     open( TMPL, $filename ) || die "Cannot open $filename: $!";
     while ( <TMPL> ) { ... }

DESCRIPTION
===========

   This is a different type of module than many others in the
`OpenInteract::' hierarchy. Instead of being created from scratch, the
configuration information is in the class rather than in a configuration
file. It does not use a SQL database for a back end. It does not relate to
any other objects.

   Instead, all we do is represent a package repository. An OpenInteract
package repository is a collection of metadata about files installed to a
particular location, known as a package. The OpenInteract package is a
means of distributing Perl object and handler code, configuration, SQL
structures and data, templates and anything else necessary to implement a
discrete set of functionality.

   A package can exist in two places: in the base installation and in one
or more websites. (You can tell the difference when you are going through
a package information hashref because website packages have the property
'website_dir' defined.)

   The package in the base installation is the master package and should
never be changed. Since you never use it directly, you should never *need*
to change it, either. Every time you create a website the website gets a
customized copy of the master package. The website author can then change
the website package as much as desired without affecting the base
installation master package.

METHODS
=======

   *_class_initialize( $CONFIG )*

   When we initialize the class we want to use the OpenInteract
installation directory for the default package database location.

   *pre_save_action*

   Ensure that before we add a package to a database it has the 'base_dir'
property.

   *fetch_package_by_name( \%params )*

   Retrieve a package by name and/or version. If you ask for a specific
version and that version does not exist, you will get nothing back. If you
do not ask for a version, you will get the latest one available.

   Parameters:

     name ($)
       Package name to retrieve

     version ($ - optional)
       Version of package to retrieve; if you specify a version then
       *only* that version can be returned.

   Example:

     my $pkg = $pkg_class->fetch_by_name( { name => 'zigzag' } );
     if ( $pkg ) {
       print "Latest installed version of zigzag: $pkg->{version}\n";
     }

   *fetch_all_packages()*

   Returns: Arrayref of all package information hashrefs in a particular
repository.

   *verify_package( @package_names )*

   Verify that each of the packages listed in @package_names exists for
this repository.

   Returns: For each package verified a hashref of the package
information. If you pass only one name, you get a single result back;
multiple names get returned in an arrayref.

   *verify_package_list( @package_names )*

   The same as `verify_package()' except we return a list reference of
package instead of a single

   *find_file( @file_list )*

   Pass in one or more possible variations on a filename that you wish to
find within a package. If you pass multiple files, each will be checked in
order. Note that the name must include any directory prefix as well. For
instance:

     $pkg->find_file( 'template/mytemplate', 'template/mytemplate.tmpl' );

   Returns a full filename of an existing file, undef if no existing file
found matching any of the filenames passed in.

   *include_package_dir*

   Put both the base package dir and the website package_dir into  @INC.
Both directories are put onto the front of @INC, the website directory
first and then the base directory. (This enables packages found in the app
to override the base.) Both directories are first tested to ensure they
actually exist.

   Returns: directories that were unshifted onto @INC, in the same order.

   Parameters: none

TO DO
=====

BUGS
====

SEE ALSO
========

   OpenInteract documentation: *Packages in OpenInteract*

COPYRIGHT
=========

   Copyright (c) 2001 intes.net, inc.. All rights reserved.

   This library is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

AUTHORS
=======

   Chris Winters <chris@cwinters.com>

   Christian Lemburg <lemburg@aixonix.de> suffered through early versions
of the package management system and offered insightful feedback,
including a pointer to *Note ExtUtils/Manifest: ExtUtils/Manifest, and the
advice to move to a text-based storage system.


File: pm.info,  Node: OpenInteract/Request,  Next: OpenInteract/SPOPS,  Prev: OpenInteract/PackageRepository,  Up: Module List

container for request info and output
*************************************

NAME
====

   OpenInteract::Request - container for request info and output

SYNOPSIS
========

     # Anywhere in your website

     my $R = OpenInteract::Request->instance;
     my $user_rec = $R->user->fetch( $user_id );
     my $org_recs = $R->org->fetch_group;

     my $db_info = $R->CONFIG->db_info();
     my $dbh  = $R->db;

DESCRIPTION
===========

   The Request object is fairly simple, having only a few methods to it.
But it really ties applications together in the OpenInteract framework,
acting as an object repository, a layer between your object names and
their classes, and as a data store between different parts of the process.
Since it is 'always around' (more later), you can thus have necessary
configuration information, your database handle, cache store, template
parser or other tools at your fingertips.

   This package is designed to run under mod_perl. Since it is a subclass
of *Note Class/Singleton: Class/Singleton,, it maintains the request
object (usually $R) around for the life of the Apache child. The intended
side effect of this allows you to pluck the object from the ether from any
handler or utility you are running by simply doing:

     my $R = OpenInteract::Request->intance;

   That's it. You don't need a 'use OpenInteract::Request;' statement, you
don't need to pass the object around from method to method.

   The other job of the OpenInteract::Request object is to keep track of
where we are: which action we're using, what URL was originally requested.
etc.

PACKAGE LEXICALS
----------------

   *%OBJECTS*

   Hash holding all the general objects needed throughout this request.
Note that finish_request() cleans these up (except for the database handle
and config object) at the end of every request.

   *%ALIAS*

   Holds mapping of alias to class name. When the website is first
started, you should call:

     OpenInteract::Request->setup_aliases();

   Which turns these hash entries into subroutines so you can use the
inline method - $R->user vs $R->user(). The latter one is necessary if you
do everything strictly via AUTOLOAD. (Been there, done that...)

METHODS
=======

   *instance()*

   Returns: a blessed Request object. (Inherited from `Class::Singleton'.)

   initialize()

   Initialize the request. You can override this however you wish.

   *throw( \%params )*

   Make it easy to throw errors from anywhere in the system. All
information gets passed directly to `OpenInteract::Error', although we set
the package, filename and line information so it does not appear to the
error handler that all errors are mysteriously being thrown from
OpenInteract::Request, and all from one line, too... :)

   Note that you do not even need to instantiate an object for this:

     OpenInteract::Request->instance->throw( { ... } );

   See also documentation in *Note OpenInteract/Error: OpenInteract/Error,
and the `OpenInteract::ErrorObject' in this node.

   *stash( 'name', $obj )*

   Adds an object indexed by 'name' to our stash class. If the object
already exists in the stash class, the default behavior is to simply
overwrite. (You can write a more complicated stash class if you like...)

   Examples:

     $R->stash( 'db', $db );
     $R->stash( 'apache', $apr );

   *get_stash( 'name' )*

   Retrieves the value corresponding to 'name' from the current stash
class. Note that many aliases (e.g., "$R->db") actually call 'get_stash'
internally, so you will not see this used very frequently.

   *setup_aliases()*

   Creates subroutines in the symbol table for this class matching up to
the entries in the lexical %ALIAS. You should call this when your website
first starts.

   *lookup_alias( $alias )*

   Returns a class name matching up to $alias. Use this when you do not
know the alias beforehand. You can use it in two different ways:

     1)  $R->$alias()->fetch( $id );

     2)  my $class = $R->lookup_alias( $alias );
         my $obj = $class->fetch( $id );

   *scrib( $level, $msg [, $msg, $msg, ... ] )*

   Centralized notification logging. Check the {DEBUG} key in the config
object, which is stored in the stash class, for the current debugging
level. If that level is less than the level passed in, we do nothing. But
if the value is equal to or greater than the level passed in then we
'warn' with the error.

   Note that you can pass multiple messages at once to the method - the
method join them with a single space between the messages.  e None of the
$msg items need to conetain the filename, package name, subroutine name or
line number - we pull all that from the reference material sent via
`caller' in this node.

   You can use this as a class method as well:

     OpenInteract::Request->instance->scrib( 2, "Boring log message" );

   And you can use either the object or class methods even if you have not
created and registered a config object. If you have not done so, we will
use the value of the package variable $DEBUG in this class.

   This means that setting the value of the package variable $DEBUG will
send all messages from any application at that level or lower to 'warn'.
Be careful or you will get some big honking logfiles! You might want to
use local for setting this value if you really need to.

   Finally, any log messages sent with a debug level of '0' will always go
to the log.. (We have not accounted for any weisenheimers setting the
config/class debug level to a negative number, but who would do such a
thing?)

   *lookup_conductor( [ $action ] )*

   Finds the conductor for a particular action.

   Returns a two-element list: class and method.

   If $action is not passed in, we find the name from
$R->{path}->{current}.

   *lookup_action( [ $action, \%params ] )*

   Finds information associated with a particular action from the
configuration.

   If $action is not passed in, we find the name from
$R->{path}->{current}.

   You have the option of refusing to allow the method to return a default
action if yours is not found. We use this to implement 'static' web pages.
For instance, if there is no action corresponding to the path
'/mod_perl/guide/toc', then we find the default action (which handles page
objects) and return its information.

   Returns either a two-element list (class and method) or with a 'return'
parameter value of 'info', returns a hashref with all known information
about the action.

   Parameters:

     return
       'info' means return all information about the action

     skip_default
       any true value means to *not* use the default action name, even if
       the action name is not found.

   *finish_request()*

   Cleans out this object and tells the stash class associated with the
request to do the same.

TO DO
=====

BUGS
====

COPYRIGHT
=========

   Copyright (c) 2001 intes.net, inc.. All rights reserved.

   This library is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

AUTHORS
=======

   Chris Winters <chris@cwinters.com>

   Christian Lemburg <lemburg@aixonix.de> bugged me about getting a
logging method (scrib) in here.


File: pm.info,  Node: OpenInteract/SPOPS,  Next: OpenInteract/SQLInstall,  Prev: OpenInteract/Request,  Up: Module List

Define common behaviors for all SPOPS objects in the OpenInteract Framework
***************************************************************************

NAME
====

   OpenInteract::SPOPS - Define common behaviors for all SPOPS objects in
the OpenInteract Framework

SYNOPSIS
========

     # In configuration file
     'myobj' => {
        'isa'   => [ qw/ ... Interact::SPOPS ... / ],
     }

DESCRIPTION
===========

   Here we provide some common operations within OpenInteract that are not
implmented within the data abstraction layer itself. One of the reasons
for that is that we want to continue using both separately, so we cannot
embed ideas like a configuration object or a particular cache
implementation within SPOPS. Think of this class as a bridge between the
two.

METHODS
=======

   *log_action( $action, $id )*

   Wrapper for the *log_action_enter* method below, decides whether it
gets called. (Wrapper exists so subclasses can call log_action_enter
directly and not deal with this step.)

   Returns undef on failure, true value on success.

   *log_action_enter( $action, $id )*

   Makes an entry into the 'object_track' table, which logs all object
creations, updates and deletions. We do not note the content that changes,
but we do note who did the action and when it was done.

   Returns undef on failure, true value on success.

   *fetch_creator()*

   Retrieve an arrayref of all user objects who have 'creator' rights to a
particular object.

   *is_creator( $uid )*

   Return 1 if the object was created by $uid, undef if not.

   *fetch_updates()*

   Return an arrayref of arrayrefs, formatted:

     [ uid of updater, date of update ]

   *notify()*

   Either call from an object or from a class passing an arrayref of
objects to send to a user. Calls the as_string() method of the object,
which (if you look in the SPOPS docs), defaults to being a simple property
-> value listing. You can override this with information in your class
configuration which specifies the fields you want to use in the listing
along with associated labels.

   *html_encode( $text )*

   Returns: escaped version of $text (e.g., the character '"' will be
replaced by &quot;)

   *html_decode( $text )*

   Returns: unescaped version of $text (e.g., the entity &quot; will be
replaced by the character '"')

   It may seem silly to have these html_ methods which currently just call
the method of an external module, but we might wish to do more in the
future (for example, screen out javascript>. This way, we have a central
place to change it.

NOTES
=====

TO DO
=====

BUGS
====

COPYRIGHT
=========

   Copyright (c) 2001 intes.net, inc.. All rights reserved.

   This library is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

AUTHORS
=======

   Chris Winters <chris@cwinters.com>


File: pm.info,  Node: OpenInteract/SQLInstall,  Next: OpenInteract/Startup,  Prev: OpenInteract/SPOPS,  Up: Module List

Dispatcher for installing various SQL data from packages to database
********************************************************************

NAME
====

   OpenInteract::SQLInstall - Dispatcher for installing various SQL data
from packages to database

SYNOPSIS
========

     # Define a SQLInstaller for your package
     package OpenInteract::SQLInstall::MyPackage;

     use strict;
     use vars qw( %HANDLERS );
     %HANDLERS = (
       'create_structure' => { Sybase => \&structure_sybase,
                               Oracle => \&structure_oracle,
                               mysql  => \&structure_mysql },
       ...
     );

     sub structure_sybase { ...do stuff...}
     sub structure_oracle { ...do stuff...}
     sub structure_mysql  { ...do stuff...}

     # Use this class in a separate program
     use OpenInteract::SQLInstall;
     use OpenInteract::PackageRepository;
     use OpenInteract::Startup;
     use OpenInteract::DBI;

     my $C   = OpenInteract::Startup->create_config({
                    base_config_file => "$WEBSITE_DIR/conf/base.conf"
               });
     my $dbh = eval { OpenInteract::DBI->connect( $C->{db_info} ) };
     die "Cannot open database handle: $@"  if ( $@ );

     my $repository = OpenInteract::PackageRepository->fetch(
                                       undef, { directory => $WEBSITE_DIR } );
     my $pkg_info = $repository->fetch_package_by_name( { name => 'my_package' } );
     OpenInteract::SQLInstall->require_package_installer( $pkg_info );
     my %args = ( package => $pkg_info, config => $C, db => $dbh );
     OpenInteract::SQLInstall->apply( { %args, action => 'create_structure' } );
     OpenInteract::SQLInstall->apply( { %args, action => 'install_data' } );
     OpenInteract::SQLInstall->apply( { %args, action => 'install_security' } );

DESCRIPTION
===========

   One of the difficulties with developing an application that can
potentially work with so many different databases is that it needs to work
with so many different databases. Many of the differences among databases
are dealt with by the amazing `DBI' in this node module, but enough remain
to warrant some thought.

   This module serves two audiences:

  1. The user of OpenInteract who wants to get packages, run a few commands
     and have them simply work.

  2. The developer of OpenInteract packages who wants to develop for as
     many databases as possible without too much of a hassle.

        This module provides tools for both. The first group (users) does
not need to concern itself with how this module works - running the
various `oi_manage' commands should be sufficient.

   However, OpenInteract developers need a keen understanding of how
things work. This whole endeavor is a work-in-progress - things work, but
there will certainly be new challenges brought on by the wide variety of
applications for which OpenInteract can be used.

USER OVERVIEW
=============

   Every package has a module that has a handful of procedures specified
in such a way that OpenInteract knows what to call and for which database.
Generally, all you need to deal with is the wrapper provided by the
`oi_manage' program. For instance:

     oi_manage install_sql --website_dir=/home/httpd/myOI --package=mypackage

   This will install the SQL for the package 'mypackage' installed to your
website. As long as you have specified your database properly in your
`conf/server.perl' file, everything should flow smooth as silk.

DEVELOPER OVERVIEW
==================

   The SQL installation program of OpenInteract is a kind of mini
framework - you have the freedom to do anything you like in the handlers
for your package. But OpenInteract provides a number of tools for you as
well.

METHODS
=======

   Note that subclasses do not need to define any of these methods. They
simply need to define a package variable %HANDLERS which describes the
work done for particular actions and particular databases and the routines
to accomplish this work.

   *require_package_installer( $package_object )*

   Given a package object, finds and does a `require' in this node on the
library used to install SQL for this package.

   *apply( \%params )*

   Performs a particular SQL action for a particular package.

   Returns: 'ok' if no errors were returned, or the text of the error if
an error occurred.

   Parameters:

     action
       String with name of action to perform.

     db
       DBI database handle

     config
       OpenInteract configuration object (or just a hashref), which should
       have the key 'db_info' with information about the database in it,
       and the key 'website_name' with the name of the website.

     package
       Package object to perform action on

   *read_db_handlers( $driver_name )*

   Finds the handlers to execute for a particular database ($driver_name).
If your handler does not implement a set of handlers for $driver_name, you
can create a generic one (and hope that it works) using the key
'_default_'.

   Of course, your class may implement this however it needs. But the
method implemented in the parent allows you to define a package variable
like so:

     package MyClass::SQLInstall;

     use strict;
     use vars ( %HANDLERS );

     %HANDLERS = (
         create_structure => { that_db  => \&that_db_structure,
                               other_db => \&other_db_structure },
         install_data     => { '_default_' => \&generic_data },
         install_security => {},
     );

   Returns: hashref with the keys as the actions that can be performed and
the values as code references to apply for a database.

   *sql_class_to_website( $website_name, $text, [ $text, ... ] )*

   Does a simple substitution of 'OpenInteract' for the website on each
string you pass to it.

   Returns the strings with the substitution done in the same order.

   Example:

     my @classes = sql_class_to_website( 'MyWebsite',
                                     qw( OpenInteract::News::Handler
                                         OpenInteract::News ) );
     # @classes is now: MyWebsite::News::Handler MyWebsite::News

   *sql_class_to_oi( $website_name, $text, [ $text, ... ] )*

   Does a simple substitution of your website name to 'OpenInteract' for
each string you pass to it.

   Returns the strings with the substitution done in the same order.

   *sql_modify_increment( $driver_name, $sql, [ $sql, ... ] )*

   Modify each SQL statement to use a particular database style for
representing auto-incrementing values.

   Returns the strings with the substitution done, in the same order.

   *process_data_file( \%params )*

   Processes the datafile described by \%params. This is a fairly hefty
method, and its workings are described in `PROCESSING DATA FILES' in this
node.

   *transform_data( \%action, \@data, \%params )*

   Take the first item from a data file (the action) and perform any
indicated tasks necessary.

   Currently, the tasks implemented by this parent class are:

     transform_class_to_website (\@)
       Specify a list of fields that should have the value transformed to
       replace 'OpenInteract' with the website name

   *read_perl_file( $filename )*

   Reads in the file $filename which describes a perl data structure.
Returns the structure.

   *read_file( $filename )*

   Reads in the file $filename and returns a scalar with all the text of
the file.

   *format_status( \%info, \@status )*

PROCESSING DATA FILES
=====================

   We need to be able to pass data from one database to another and be
very flexible as to how we do it. The various data file formats have taken
care of everything I could think of - hopefully you will think up some
more.

   To begin, there are two elements to a data file. The first element
tells the installer what type of data follows - should we create objects
from them? Should we just plug the values into a SQL statement and execute
it against a particular table?

   The second element is the actual data, which is in an order determined
by the first element.

   There are several different ways to process a data file. Both are
described in detail below:

   Object Processing

   Object processing allows you to just specify the field order and the
class, then let SPOPS do the dirty work. This is the preferred way of
transferring data, but it is not always feasible. An example where it is
not feasible include linking tables that SPOPS uses but does not model.

   SQL Processing

   SQL processing allows you to present elements of a SQL statement and
plug in values as many times as necessary. This can be used most anywhere
and for anything.

Object Processing
-----------------

   The first item in the list describes the class you want to use to
create objects and the order the fields that follow are in. Here is a
simple example of the data file used to install initial groups:

     $data_group = [ { spops_class => 'OpenInteract::Group',
                       field_order => [ qw/ group_id name / ] },
                     [ 1, 'admin' ],
                     [ 2, 'public' ],
                     [ 3, 'site admin' ],
     ];

   Important note: when installing to a website, this module will
substitute the name of the website for 'OpenInteract' in 'spops_class'. So
in the above example the class for the group objects created in
'MyWebsite' will be `MyWebsite::Group' - not `OpenInteract::Group'.

   Here is a slightly abbreviated form of what steps would look like if
they were done in code:

     my $website_name = 'MyWebsite';
     my $object_class = 'OpenInteract::Group';
     $object_class =~ s/OpenInteract/$website_name/;
     my %field_num = { group_id => 0, name => 1 };
     foreach my $row ( @{ $data_rows } ) {
       my $object = $object_class->new();
       $object->{group_id} = $row->[ $field_num{group_id} ];
       $object->{name}     = $row->[ $field_num{name} ];
       $object->save({ is_add => 1, skip_security => 1,
                       skip_log => 1, skip_cache => 1 });
     }

   Easy!

   You can also specify operations to perform on the data before they are
saved with the object. The most common operation of this is in security
data:

     $security = [
                   { spops_class => 'OpenInteract::Security',
                     field_order => [ qw/ class oid scope scope_id level / ],
                     transform_class_to_website => [ qw/ class / ] },
                   [ 'OpenInteract::Group', 1, 'w', 'world', 1 ],
                   [ 'OpenInteract::Group', 2, 'w', 'world', 4 ],
                   [ 'OpenInteract::Group', 2, 'g', '2', 4  ],
                   [ 'OpenInteract::Group', 3, 'w', 'world', 4 ],
                   [ 'OpenInteract::Group', 3, 'g', '3', 8 ],
                   [ 'OpenInteract::Group', 2, 'g', '3', 8 ],
                   [ 'OpenInteract::Handler::Group', 0, 'w', 'world', 4 ],
                   [ 'OpenInteract::Handler::Group', 0, 'g', '3', 8 ]
     ];

   So these steps would look like:

     my $website_name = 'MyWebsite';
     my $object_class = 'OpenInteract::Security';
     $object_class =~ s/OpenInteract/$website_name/;
     my %field_num = { class => 0, oid => 1, scope => 2,
                       scope_id => 3, level => 4 };
     foreach my $row ( @{ $data_rows } ) {
       my $object = $object_class->new();
       $object->{class}    = $row->[ $field_num{class} ];
       $object->{oid}      = $row->[ $field_num{oid} ];
       $object->{scope}    = $row->[ $field_num{scope} ];
       $object->{scope_id} = $row->[ $field_num{scope_id} ];
       $object->{level}    = $row->[ $field_num{level} ];
       $object->{class} =~ s/OpenInteract/$website_name/;
       $object->save({ is_add => 1, skip_security => 1,
                       skip_log => 1, skip_cache => 1 });
     }

   There are currently just a few behaviors you can set to transform the
data before it gets saved, but the interface is there to do just about
anything you can imagine.

SQL Processing
--------------

   The actions performed when you just want to insert data into tables is
similar to those performed when you are inserting objects. The only
difference is that you need to specify a little more. Here is an example:

     $data_link = [ { sql_type => 'insert',
                      sql_table => 'sys_group_user',
                      field_order => [ qw/ group_id user_id / ] },
                    [ 1, 1 ]
     ];

   So we specify the action ('insert'), the table to operate on
('sys_group_user'), the order of fields in the data rows ('field_order',
just like with processing objects) and then list the data.

   More work needs to be done on this so, for instance, you could
implement cleanup routines like this:

     $cleanup = [ { sql_type => 'delete',
                    sql_table => 'sys_group_user',
                    where  => "group_id = ? and user_id = ?" },
                    [ 1, 1 ]
     ];

   Or:

     $cleanup = [ { sql_type => 'delete',
                    sql_table => 'sys_security',
                    transform_class_to_website => [ 1, 2 ] },
                    where  => "class = ? OR class = ?" },
                    [ 'OpenInteract::Group', 'OpenInteract::Handler::Group' ]
     ];

BUGS
====

TO DO
=====

   *Setup removals properly*

   See example under `SQL Processing' in this node, above.

   *Other means of abstraction*

   Using something like Alzabo (see http://alzabo.sourceforge.net/) to
provide schema and data translation abilities would be sweet. However,
Alzabo is a whole nother ball of wax on top of OpenInteract...

   *Dumping data for transfer*

   It would be nice if you could do something like:

     oi_manage dump_sql --website_dir=/home/httpd/myOI --package=mypkg

   And get in your `data/dump' directory a series of files that can be
read in by another OpenInteract website for installation. This is the pie
in the sky - developing something like this would be really cool.

   And we can, but only for SPOPS objects. It is quite simple for us to
read data from a flat file, build objects from the data and save them into
a random database - SPOPS was built for this!

   However, structures are a problem with this. Data that are not held in
objects are a problem. And dealing with dependencies is an even bigger
problem.

SEE ALSO
========

   *Note OpenInteract/Package: OpenInteract/Package,, `DBI' in this node

COPYRIGHT
=========

   Copyright (c) 2001 intes.net, inc.. All rights reserved.

   This library is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

AUTHORS
=======

   Chris Winters <chris@cwinters.com>


File: pm.info,  Node: OpenInteract/Startup,  Next: OurNet,  Prev: OpenInteract/SQLInstall,  Up: Module List

Bootstrapper that reads in modules, manipulates @INC, etc.
**********************************************************

NAME
====

   OpenInteract::Startup - Bootstrapper that reads in modules, manipulates
@INC, etc.

SYNOPSIS
========

     # Startup an OpenInteract environment outside Apache and mod_perl

     use strict;
     use OpenInteract::Startup;

     my $R =  OpenInteract::Startup->setup_static_environment(
                                          '/home/httpd/MySite' );

     # For usage inside Apache/mod_perl, see OpenInteract::ApacheStartup

DESCRIPTION
===========

   This module has a number of routines that are (hopefully) independent
of the OpenInteract implementation. One of its primary goals is to make it
simple to initialize OpenInteract not only in a mod_perl context but also
a single-use context. For example, when you create a script to be run as a
cron job to do some sort of data checking (or whatever), you should not be
required to put 50 lines of initialization in your script just to create
the framework.

   This script should also minimize the modules you have to include
yourself, making it easier to add backward-compatible functionality. Most
of the time, you only need to have a 'use' statement for this module which
takes care of everything else.

METHODS
=======

   All methods use the class method invocation syntax, such as:

     OpenInteract::Startup->require_module( { class => [ $my_class ] } );

   *main_initialize( \%params )*

   This will frequently be the only method you call of this class. This
routine goes through a number of common steps:

  1. read in the base configuration file

  2. require the config class, request class and stash class

  3. create the config object

  4. process all packages (see `process_package()' in this node below)

  5. finalize the configuration (see `finalize_configuration()' in this
     node below

  6. set the config object into the stash class

  7. create aliases in the request object (optional)

  8. create/initialize all SPOPS object classes (optional)

        The return value is a list with two members. The first is an
arrayref of all SPOPS object class names that currently exist. The second
is a fully setup *OpenInteract::Config* object. Note that his may change in
the future to be a single return value of the config object with the class
names included as a parameter of it.

   Parameters:

   You must pass in either 'base_config' or 'base_config_file'.

     base_config (\%)
       A hashref with the information from the base configuration file

     base_config_file ($)
       A filename where the base configuration can be found

     alias_init (bool) (optional)
       A true value will initialize aliases within the request class; the
       default is to not perform the initialization.

     spops_init (bool) (optional)
       A true value will create/initialize all SPOPS classes (see
       SPOPS::Configure for more information); the default is to not
       perform the initialization.

     package_extra (\@) (optional)
       A list of packages not included in the filename of packages to read
       in but you want to include anyway (maybe you are testing a new
       package out). The packages included will be put at the end of the
       packages to be read in, although it is feasible that we break this
       into two extra package listings -- those to be read in before
       everything else (but still after 'base') and those to be read in
       after everything else. See if there is a need...

   *setup_static_environment( $website_dir )*

   Sometimes you want to setup OI even when you are not in a web
environment - for instance, you might need to do data reporting, data
import/export, or other tasks.

   With this method, all you need to pass in is the root directory of your
website. It will deal with everything else, including:

   * Reading in the server configuration

   * Reading in all SPOPS and action table configurations - this includes
     setting up @INC properly.

   * Setting up all aliases - SPOPS object and otherwise

   * Creating a database handle

   The only thing it does not do is setup an authentication environment
for you - to get around this (right now), you need to pass in a true value
for 'skip_log' and 'skip_security' whenever you modify and/or retrieve
objects.

   Returns: A "fully-stocked" `OpenInteract::Request' object.

   Example:

     #!/usr/bin/perl

     use strict;
     use OpenInteract::Startup;

     my $R = OpenInteract::Startup->setup_static_environment( '/home/httpd/my' );

     my $news_list = eval { $R->news->fetch_group({
                                   where => 'title like ?',
                                   value => [ '%iraq%' ],
                                   skip_security => 1 }) };
     foreach my $news ( @{ $news_list } ) {
       print "Date:  $news->{posted_on}\n",
             "Title: $news->{title}\n"
             "Story: $news->{news_item}\n";
     }

   Easy!

   *read_package_list( \%params )*

   Reads in a list of packages from a file, one package per line.

   Returns: arrayref of package names.

   Parameters:

   Choose one or the other

     config
       An OpenInteract::Config object which has 'package_list' as a key; this
       file is assumed to be in the 'config' directory, also found on the
       object.

     filename
       A scalar specifying where the packages can be read from.

   *read_base_config( \%params )*

   Reads in the base configuration file, which is a simple per-line
whitespace-separated key-value format.

   Returns a hashref with all information.

   Parameters:

     filename
       A scalar specifying where the file is located; it must have a
       fully-qualified path.

     dir
       A scalar specifying the website directory which has the file
       'conf/base.conf' under it.

   *require_module( \%params )*

   Calls require on one or a number of modules. You can specify a filename
composed of module names (one module per line) and all will be read in.
You can also specify a number of modules to read in.

   Returns: arrayref of modules successfully read in.

   Parameters:

     filename
       Name of file which has modules to read in; one module per line,
       blank lines and lines beginning with a comment (#) are skipped

     class
       Arrayref of classes to read in

   *process_package( \%params )*

   Do initial work to process a particular package. This includes reading
in all external modules and reading both the action configuration and
SPOPS configuration files for inclusion in the config object. We also
include any modules used in the action definition (if you specify a
'class' in a action definition) as well as those in the 'isa' property of
a SPOPS class definition.

   We also add the package directory to @INC, which means any 'use' or
'require' statements that need modules within the package will be able to
work. (See the *OpenInteract Guide to Packages* for more information on
what goes into a package and how it is laid out.)

   Note that we do not create/configure the SPOPS classes yet, since that
process requires that all SPOPS classes to be used exist in the
configuration. (See *Note SPOPS/Configure: SPOPS/Configure, for more
details.)

   Parameters:

     package ($)
       Name of package to be processed; this should correspond to a
       particular directory in the package directory

     config (obj)
       An OpenInteract::Config object

     package_dir (\@ - optional)
       Directories where this package might be kept; if not passed in, it
       will be found from the config object

   *read_action_definition( \%params )*

   Read in a action definition file (a perl data structure) and set its
information in the config object. Multiple actions can be configured, and
we do a 'require' on any actions referenced.

   Parameters:

     filename ($)
       File where the action definion is.

     config (obj)
       OpenInteract::Config object where we set the action information.

     package (\%)
       Hashref with information about a package so we can set name/version
       info.

   *read_spops_definition( \%params )*

   Read in a module definition file (a perl data structure) and set its
information in the config object. Multiple SPOPS objects can be
configured, and we do a 'require' on any modules referenced.

   Parameters:

     filename ($)
       File where the module definion is.

     config (obj)
       OpenInteract::Config object where we set the module information.

     package (obj)
       Hashref with information about a package so we can set name/version
       info.

   *read_perl_file( \%params )*

   Simple routine to read in a file generated by or compatible with a perl
data structure and return its value. For instance, your file could have
something like the following:

     $action = {
                'boxes' => {
                    'class'     => 'OpenInteract::Handler::Boxes',
                    'security'  => 'no',
                }, ...
     };

   And the return value would be the hashref that `$module' is set to. The
actual name of the variable is irrelevant, just the data structure
assigned to it.

   Return value is the data structure in the file, or undef if the file
does not exist or the data structure is formatted incorrectly. If the
latter happens, check your error log (STDERR) and a warning will appear.

   Parameters:

     filename ($)
       File to read data structure from.

   Note: we should modify this to use *Note SPOPS/HashFile:
SPOPS/HashFile,...

   *finalize_configuration( \%params )*

   At this point, all the SPOPS and module information should be read into
the configuration object and we are ready to finish up the configuration
procedure. This means call the final SPOPS configuration, which creates
classes as necessary and puts configuration information and routines to
link the objects together. We also call the various routines in the
'request_class' (usually OpenInteract::Request) to create necessary
aliases for classes from both SPOPS and base system elements.

   Return value is an arrayref of all SPOPS classes that are configured.
Each one needs to be initialized before use, which can handily be done for
you in the `initialize_spops' method.

   Parameters:

     config (obj)
       An OpenInteract::Config configuration object. Note that the keys
       'request_class', 'SPOPS_config_class' and 'stash_class' must be
       defined in the config object prior to calling this routine.

   *initialize_spops( \%params )*

   Call the `class_initialize' method for each of the SPOPS classes
specified. This must be done before the package can be used.

   Returns an arrayref of classes successfully initialized.

   Parameters:

     class (\@)
       An arrayref of classes to initialize.

     config (obj)
       An OpenInteract::Config configuration object, needed by the
       C<class_initialize> method within the SPOPS classes.

TO DO
=====

BUGS
====

COPYRIGHT
=========

   Copyright (c) 2001 intes.net, inc.. All rights reserved.

   This library is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

AUTHORS
=======

   Chris Winters <chris@cwinters.com>


