Changeset 652


Ignore:
Timestamp:
Jun 15, 2005, 12:57:30 PM (17 years ago)
Author:
Earle Martin
Message:

RSS timestamp

Location:
trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/Build.PL

    r651 r652  
    223223        'CGI::Wiki::Plugin::Diff'         => '0.07', # earlier buggy
    224224        'CGI::Wiki::Plugin::Locator::Grid'=> '0.02', # cope with sqlite 3
    225         'CGI::Wiki::Plugin::RSS::ModWiki' => '0.06', # for fancy stuff
     225        'CGI::Wiki::Plugin::RSS::ModWiki' => '0.071', # provides RSS timestamp
    226226        'CGI::Wiki::Plugin::RSS::Reader'  => '1.3',  # earlier versions don't support RSS 2.0
    227227        'Class::Accessor'                 => 0,
  • trunk/Changes

    r651 r652  
    1212          Added owl:sameAs property to RDF output for nodes that are redirects
    1313            to other nodes.
     14          RSS feed now has correct timestamp (matching most recent item) and
     15            matching Last-Modified HTTP header.
    1416        Reorder navigation bar to provide more logical groupings.
    1517        Add "format=plain" option for all-nodes index listing and associated
  • trunk/lib/OpenGuides.pm

    r651 r652  
    171171        $redirect =~ s/\]\]\s*$//;
    172172        # See if this is a valid node, if not then just show the page as-is.
     173
    173174        # Avoid loops by not generating redirects to the same node or the
    174175        # previous node.
     
    620621    my $rdf_writer = OpenGuides::RDF->new( wiki   => $self->wiki,
    621622                                           config => $self->config );
    622     my $output = "Content-type: text/plain\n\n";
     623    my $output = "Content-Type: text/plain\n";
     624    $output .= "Last-Modified: " . $rdf_writer->rss_timestamp( %criteria ) . "\n\n";
    623625    $output .= $rdf_writer->make_recentchanges_rss( %criteria );
    624626    return $output if $return_output;
     
    855857sub redirect_to_node {
    856858    my ($self, $node, $redirect) = @_;
     859   
    857860    my $script_url = $self->config->script_url;
    858861    my $script_name = $self->config->script_name;
    859862    my $formatter = $self->wiki->formatter;
     863
    860864    my $id = $formatter->node_name_to_node_param( $node );
    861    
    862865    my $oldid;
    863866    $oldid = $formatter->node_name_to_node_param( $redirect ) if $redirect;
    864    
     867
    865868    my $redir_param ='';
    866869    $redir_param = "&oldid=$oldid" if $oldid;
    867    
    868     return CGI->redirect( "$script_url$script_name?id=$id$redir_param" );   
     870
     871    return CGI->redirect( "$script_url$script_name?id=$id$redir_param" );
    869872}
    870873
  • trunk/lib/OpenGuides/RDF.pm

    r651 r652  
    99use Time::Piece;
    1010use URI::Escape;
    11 use Carp qw( croak );
     11use Carp 'croak';
     12
     13sub new
     14{
     15  my ($class, @args) = @_;
     16  my $self = {};
     17  bless $self, $class;
     18  $self->_init(@args);
     19}
     20
     21sub _init
     22{
     23  my ($self, %args) = @_;
     24
     25  my $wiki = $args{wiki};
     26 
     27  unless ($wiki && UNIVERSAL::isa($wiki, "CGI::Wiki"))
     28  {
     29    croak "No CGI::Wiki object supplied.";
     30  }
     31  $self->{wiki} = $wiki;
     32
     33  my $config = $args{config};
     34  unless ($config && UNIVERSAL::isa($config, "OpenGuides::Config"))
     35  {
     36    croak "No OpenGuides::Config object supplied.";
     37  }
     38  $self->{config} = $config;
     39
     40  $self->{make_node_url} =
     41    sub
     42    {
     43      my ($node_name, $version) = @_;
     44
     45      my $config = $self->{config};
     46 
     47      my $node_url = $config->script_url . uri_escape($config->script_name) . '?';
     48      $node_url .= 'id=' if defined $version;
     49      $node_url .= uri_escape($self->{wiki}->formatter->node_name_to_node_param($node_name));
     50      $node_url .= '&version=' . uri_escape($version) if defined $version;
     51
     52      $node_url;
     53    }; 
     54  $self->{site_name}        = $config->site_name;
     55  $self->{default_city}     = $config->default_city     || "";
     56  $self->{default_country}  = $config->default_country  || "";
     57  $self->{site_description} = $config->site_desc        || "";
     58
     59  $self;
     60}
     61
     62sub emit_rdfxml
     63{
     64  my ($self, %args) = @_;
     65
     66  my $node_name = $args{node};
     67  my $wiki = $self->{wiki};
     68
     69  my %node_data          = $wiki->retrieve_node( $node_name );
     70  my $phone              = $node_data{metadata}{phone}[0]              || '';
     71  my $fax                = $node_data{metadata}{fax}[0]                || '';
     72  my $website            = $node_data{metadata}{website}[0]            || '';
     73  my $opening_hours_text = $node_data{metadata}{opening_hours_text}[0] || '';
     74  my $postcode           = $node_data{metadata}{postcode}[0]           || '';
     75  my $city               = $node_data{metadata}{city}[0]               || $self->{default_city};
     76  my $country            = $node_data{metadata}{country}[0]            || $self->{default_country};
     77  my $latitude           = $node_data{metadata}{latitude}[0]           || '';
     78  my $longitude          = $node_data{metadata}{longitude}[0]          || '';
     79  my $version            = $node_data{version};
     80  my $username           = $node_data{metadata}{username}[0]           || '';
     81  my $os_x               = $node_data{metadata}{os_x}[0]               || '';
     82  my $os_y               = $node_data{metadata}{os_y}[0]               || '';
     83  my $catrefs            = $node_data{metadata}{category};
     84  my @locales            = @{ $node_data{metadata}{locale} || [] };
     85
     86  # replace any errant characters in data to prevent illegal XML
     87  foreach ($phone, $fax, $website, $opening_hours_text, $postcode, $city, $country,
     88  $latitude, $longitude, $version, $os_x, $os_y, $catrefs, @locales)
     89  {
     90    if ($_)
     91    {
     92      $_ =~ s/&/&/g;
     93      $_ =~ s/</&lt;/g;
     94      $_ =~ s/>/&gt;/g;
     95    }
     96  }
     97 
     98  my ($is_geospatial, $objType);
     99
     100  if ($latitude || $longitude || $postcode || @locales)
     101  {
     102    $is_geospatial = 1;
     103    $objType    = 'geo:SpatialThing';
     104  }
     105  else
     106  {
     107    $objType = 'rdf:Description';
     108  }
     109
     110  my $timestamp = $node_data{last_modified};
     111  # Make a Time::Piece object.
     112  my $timestamp_fmt = $CGI::Wiki::Store::Database::timestamp_fmt;
     113
     114  if ($timestamp)
     115  {
     116    my $time   = Time::Piece->strptime( $timestamp, $timestamp_fmt );
     117    $timestamp = $time->strftime( "%Y-%m-%dT%H:%M:%S" );
     118  }
     119
     120  my $url               = $self->{make_node_url}->($node_name, $version);
     121  my $version_indpt_url = $self->{make_node_url}->($node_name);
     122
     123  my $rdf = qq{<?xml version="1.0"?>
     124  <rdf:RDF
     125  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
     126  xmlns:dc="http://purl.org/dc/elements/1.1/"
     127  xmlns:dcterms="http://purl.org/dc/terms/"
     128  xmlns:foaf="http://xmlns.com/foaf/0.1/"
     129  xmlns:wiki="http://purl.org/rss/1.0/modules/wiki/"
     130  xmlns:chefmoz="http://chefmoz.org/rdf/elements/1.0/"
     131  xmlns:wn="http://xmlns.com/wordnet/1.6/"
     132  xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"
     133  xmlns:os="http://downlode.org/rdf/os/0.1/"
     134  xmlns:owl="http://www.w3.org/2002/07/owl#"
     135  xmlns="http://www.w3.org/2000/10/swap/pim/contact#"
     136  >
     137
     138  <rdf:Description rdf:about="">
     139    <dc:title>} . $self->{site_name} . qq{: $node_name</dc:title>
     140    <dc:date>$timestamp</dc:date>
     141    <dcterms:modified>$timestamp</dcterms:modified>
     142    <dc:contributor>$username</dc:contributor>
     143    <dc:source rdf:resource="$version_indpt_url" />
     144    <wiki:version>$version</wiki:version>
     145    <foaf:topic rdf:resource="#obj" />
     146  </rdf:Description>
     147
     148  <$objType rdf:ID="obj" dc:title="$node_name">
     149};
     150  $rdf .= "\n    <!-- categories -->\n\n" if $catrefs;
     151  $rdf .= "    <dc:subject>$_</dc:subject>\n" foreach @{$catrefs};
     152  $rdf .= "\n    <!-- address and geospatial data -->\n\n" if $is_geospatial;
     153  $rdf .= "    <city>$city</city>\n"                 if $city     && $is_geospatial;
     154  $rdf .= "    <postalCode>$postcode</postalCode>\n" if $postcode && $is_geospatial;
     155  $rdf .= "    <country>$country</country>\n"        if $country  && $is_geospatial;
     156
     157  $rdf .= qq{
     158  <foaf:based_near>
     159       <wn:Neighborhood>
     160         <foaf:name>$_</foaf:name>
     161       </wn:Neighborhood>
     162  </foaf:based_near>\n} foreach @locales;
     163
     164  if ($latitude && $longitude)
     165  {
     166    $rdf .= qq{
     167  <geo:lat>$latitude</geo:lat>
     168  <geo:long>$longitude</geo:long>\n};
     169  }
     170
     171  if ($os_x && $os_y)
     172  {
     173    $rdf .= qq{
     174  <os:x>$os_x</os:x>
     175  <os:y>$os_y</os:y>};
     176  }
     177
     178  $rdf .= "\n\n    <!-- contact information -->\n\n" if ($phone || $fax || $website || $opening_hours_text);
     179  $rdf .= "    <phone>$phone</phone>\n"                              if $phone;
     180  $rdf .= "    <fax>$fax</fax>\n"                                    if $fax;
     181  $rdf .= "    <foaf:homepage rdf:resource=\"$website\" />\n"        if $website;
     182  $rdf .= "    <chefmoz:Hours>$opening_hours_text</chefmoz:Hours>\n" if $opening_hours_text;
     183
     184  if ($node_data{content} =~ /^\#REDIRECT \[\[(.*?)]\]$/)
     185  {
     186    my $redirect = $1;
     187   
     188    $rdf .= qq{    <owl:sameAs rdf:resource="} . $self->{config}->script_url
     189    . uri_escape($self->{config}->script_name) . '?id='
     190    . uri_escape($wiki->formatter->node_name_to_node_param($redirect))
     191    . ';format=rdf#obj';
     192    $rdf .= qq{" />\n};
     193  }
     194 
     195  $rdf .= qq{  </$objType>
     196</rdf:RDF>
     197
     198};
     199
     200  return $rdf;
     201}
     202
     203sub rss_maker
     204{
     205  my $self = shift;
     206
     207  unless ($self->{rss_maker}) # OAOO, please.
     208  {
     209    $self->{rss_maker} = CGI::Wiki::Plugin::RSS::ModWiki->new(
     210      wiki                => $self->{wiki},
     211      site_name           => $self->{site_name},
     212      site_description    => $self->{site_description},
     213      make_node_url       => $self->{make_node_url},
     214      recent_changes_link => $self->{config}->script_url . uri_escape($self->{config}->script_name) . "?RecentChanges"
     215    );
     216  }
     217 
     218  $self->{rss_maker};
     219}
     220
     221sub make_recentchanges_rss
     222{
     223  my ($self, %args) = @_;
     224
     225  $self->rss_maker->recent_changes(%args);
     226}
     227
     228sub rss_timestamp
     229{
     230  my ($self, %args) = @_;
     231 
     232  $self->rss_maker->rss_timestamp(%args);
     233}
    12234
    13235=head1 NAME
     
    31253  my $config = OpenGuides::Config->new( file => "wiki.conf" );
    32254  my $rdf_writer = OpenGuides::RDF->new( wiki   => $wiki,
    33                                          config => $config );
     255                                       config => $config );
    34256
    35257  # RDF version of a node.
    36   print "Content-type: text/plain\n\n";
     258  print "Content-Type: text/plain\n\n";
    37259  print $rdf_writer->emit_rdfxml( node => "Masala Zone, N1 0NU" );
    38260
    39261  # Ten most recent changes.
    40   print "Content-type: text/plain\n\n";
     262  print "Content-Type: text/plain\n";
     263  print "Last-Modified: " . $self->rss_timestamp( items => 10 ) . "\n\n";
    41264  print $rdf_writer->make_recentchanges_rss( items => 10 );
    42265
     
    53276L<OpenGuides::Config> object.  Both arguments mandatory.
    54277
    55 =cut
    56 
    57 sub new {
    58     my ($class, @args) = @_;
    59     my $self = {};
    60     bless $self, $class;
    61     return $self->_init(@args);
    62 }
    63 
    64 sub _init {
    65     my ($self, %args) = @_;
    66 
    67     my $wiki = $args{wiki};
    68     unless ( $wiki && UNIVERSAL::isa( $wiki, "CGI::Wiki" ) ) {
    69         croak "No CGI::Wiki object supplied.";
    70     }
    71     $self->{wiki} = $wiki;
    72 
    73     my $config = $args{config};
    74     unless ( $config && UNIVERSAL::isa( $config, "OpenGuides::Config" ) ) {
    75         croak "No OpenGuides::Config object supplied.";
    76     }
    77     $self->{config} = $config;
    78 
    79     $self->{site_name} = $config->site_name;
    80     $self->{make_node_url} = sub {
    81         my ($node_name, $version) = @_;
    82         if ( defined $version ) {
    83             return $config->script_url . uri_escape($config->script_name) . "?id=" . uri_escape($wiki->formatter->node_name_to_node_param($node_name)) . ";version=" . uri_escape($version);
    84         } else {
    85             return $config->script_url . uri_escape($config->script_name) . "?" . uri_escape($wiki->formatter->node_name_to_node_param($node_name));
    86         }
    87     };
    88     $self->{default_city}     = $config->default_city     || "";
    89     $self->{default_country}  = $config->default_country  || "";
    90     $self->{site_description} = $config->site_desc        || "";
    91 
    92     return $self;
    93 }
    94 
    95 =cut
    96278
    97279=item B<emit_rdfxml>
     
    102284                     { comment  => "New page",
    103285                       username => "Kake",
    104                        locale   => "Islington"
    105                      }
     286                       locale   => "Islington" }
    106287  );
    107288
    108   print "Content-type: text/plain\n\n";
     289  print "Content-Type: text/plain\n\n";
    109290  print $rdf_writer->emit_rdfxml( node => "Masala Zone, N1 0NU" );
    110291
     
    134315=back
    135316
    136 =cut
    137 
    138 sub emit_rdfxml {
    139     my ($self, %args) = @_;
    140 
    141     my $node_name = $args{node};
    142     my $wiki = $self->{wiki};
    143 
    144     my %node_data          = $wiki->retrieve_node( $node_name );
    145     my $phone              = $node_data{metadata}{phone}[0]              || '';
    146     my $fax                = $node_data{metadata}{fax}[0]                || '';
    147     my $website            = $node_data{metadata}{website}[0]            || '';
    148     my $opening_hours_text = $node_data{metadata}{opening_hours_text}[0] || '';
    149     my $postcode           = $node_data{metadata}{postcode}[0]           || '';
    150     my $city               = $node_data{metadata}{city}[0]               || $self->{default_city};
    151     my $country            = $node_data{metadata}{country}[0]            || $self->{default_country};
    152     my $latitude           = $node_data{metadata}{latitude}[0]           || '';
    153     my $longitude          = $node_data{metadata}{longitude}[0]          || '';
    154     my $version            = $node_data{version};
    155     my $username           = $node_data{metadata}{username}[0]           || '';
    156     my $os_x               = $node_data{metadata}{os_x}[0]               || '';
    157     my $os_y               = $node_data{metadata}{os_y}[0]               || '';
    158     my $catrefs            = $node_data{metadata}{category};
    159     my @locales            = @{ $node_data{metadata}{locale} || [] };
    160 
    161     # replace any errant characters in data to prevent illegal XML
    162     foreach ($phone, $fax, $website, $opening_hours_text, $postcode, $city, $country,
    163     $latitude, $longitude, $version, $os_x, $os_y, $catrefs, @locales)
    164     {
    165       if ($_)
    166       {
    167         $_ =~ s/&/&amp;/g;
    168         $_ =~ s/</&lt;/g;
    169         $_ =~ s/>/&gt;/g;
    170       }
    171     }
    172    
    173     my ($is_geospatial, $objType);
    174 
    175     if ($latitude || $longitude || $postcode || @locales)
    176     {
    177       $is_geospatial = 1;
    178       $objType    = 'geo:SpatialThing';
    179     }
    180     else
    181     {
    182       $objType = 'rdf:Description';
    183     }
    184 
    185     my $timestamp = $node_data{last_modified};
    186     # Make a Time::Piece object.
    187     my $timestamp_fmt = $CGI::Wiki::Store::Database::timestamp_fmt;
    188 #        my $timestamp_fmt = $wiki->{store}->timestamp_fmt;
    189     if ( $timestamp ) {
    190         my $time = Time::Piece->strptime( $timestamp, $timestamp_fmt );
    191         $timestamp = $time->strftime( "%Y-%m-%dT%H:%M:%S" );
    192     }
    193 
    194     my $url = $self->{make_node_url}->( $node_name, $version );
    195     my $version_indpt_uri = $self->{make_node_url}->( $node_name );
    196 
    197     my $rdf = qq{<?xml version="1.0"?>
    198   <rdf:RDF
    199     xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    200     xmlns:dc="http://purl.org/dc/elements/1.1/"
    201     xmlns:dcterms="http://purl.org/dc/terms/"
    202     xmlns:foaf="http://xmlns.com/foaf/0.1/"
    203     xmlns:wiki="http://purl.org/rss/1.0/modules/wiki/"
    204     xmlns:chefmoz="http://chefmoz.org/rdf/elements/1.0/"
    205     xmlns:wn="http://xmlns.com/wordnet/1.6/"
    206     xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"
    207     xmlns:os="http://downlode.org/rdf/os/0.1/"
    208     xmlns:owl="http://www.w3.org/2002/07/owl#"
    209     xmlns="http://www.w3.org/2000/10/swap/pim/contact#"
    210   >
    211 
    212   <rdf:Description rdf:about="">
    213     <dc:title>} . $self->{site_name} . qq{: $node_name</dc:title>
    214     <dc:date>$timestamp</dc:date>
    215     <dcterms:modified>$timestamp</dcterms:modified>
    216     <dc:contributor>$username</dc:contributor>
    217     <dc:source rdf:resource="$version_indpt_uri" />
    218     <wiki:version>$version</wiki:version>
    219     <foaf:topic rdf:resource="#obj" />
    220   </rdf:Description>
    221 
    222   <$objType rdf:ID="obj" dc:title="$node_name">
    223 };
    224     $rdf .= "\n    <!-- categories -->\n\n" if $catrefs;
    225     $rdf .= "    <dc:subject>$_</dc:subject>\n" foreach @{$catrefs};
    226     $rdf .= "\n    <!-- address and geospatial data -->\n\n" if $is_geospatial;
    227     $rdf .= "    <city>$city</city>\n"                 if $city     && $is_geospatial;
    228     $rdf .= "    <postalCode>$postcode</postalCode>\n" if $postcode && $is_geospatial;
    229     $rdf .= "    <country>$country</country>\n"        if $country  && $is_geospatial;
    230 
    231     $rdf .= qq{
    232     <foaf:based_near>
    233          <wn:Neighborhood>
    234            <foaf:name>$_</foaf:name>
    235          </wn:Neighborhood>
    236     </foaf:based_near>\n} foreach @locales;
    237 
    238     if ($latitude && $longitude) {
    239       $rdf .= qq{
    240     <geo:lat>$latitude</geo:lat>
    241     <geo:long>$longitude</geo:long>\n};
    242     }
    243 
    244     if ($os_x && $os_y) {
    245       $rdf .= qq{
    246     <os:x>$os_x</os:x>
    247     <os:y>$os_y</os:y>};
    248     }
    249 
    250     $rdf .= "\n\n    <!-- contact information -->\n\n" if ($phone || $fax || $website || $opening_hours_text);
    251     $rdf .= "    <phone>$phone</phone>\n"                              if $phone;
    252     $rdf .= "    <fax>$fax</fax>\n"                                    if $fax;
    253     $rdf .= "    <foaf:homepage rdf:resource=\"$website\" />\n"        if $website;
    254     $rdf .= "    <chefmoz:Hours>$opening_hours_text</chefmoz:Hours>\n" if $opening_hours_text;
    255 
    256     if ($node_data{content} =~ /^\#REDIRECT \[\[(.*?)]\]$/)
    257     {
    258       my $redirect = $1;
    259      
    260       $rdf .= qq{    <owl:sameAs rdf:resource="} . $self->{config}->script_url
    261       . uri_escape($self->{config}->script_name) . '?id='
    262       . uri_escape($wiki->formatter->node_name_to_node_param($redirect))
    263       . ';format=rdf#obj';
    264       $rdf .= qq{" />\n};
    265 
    266     }
    267    
    268     $rdf .= qq{  </$objType>
    269 </rdf:RDF>
    270 
    271 };
    272 
    273     return $rdf;
    274 }
     317=item B<rss_maker>
     318
     319Returns a raw L<CGI::Wiki::Plugin::RSS::ModWiki> object created with the values you
     320invoked this module with.
    275321
    276322=item B<make_recentchanges_rss>
    277323
    278324  # Ten most recent changes.
    279   print "Content-type: text/plain\n\n";
    280   print $rdf_writer->make_recentchanges_rss(
    281                                              items => 10,
    282                                            );
     325  print "Content-Type: text/plain\n";
     326  print "Last-Modified: " . $rdf_writer->rss_timestamp( items => 10 ) . "\n\n";
     327  print $rdf_writer->make_recentchanges_rss( items => 10 );
    283328
    284329  # All the changes made by bob in the past week, ignoring minor edits.
    285   print "Content-type: text/plain\n\n";
    286   print $rdf_writer->make_recentchanges_rss(
    287             days               => 7,
    288             ignore_minor_edits => 1,
    289             filter_on_metadata => { username => "bob" },
    290                                            );
    291 
    292 =cut
    293 
    294 sub make_recentchanges_rss {
    295     my ($self, %args) = @_;
    296 
    297     my $rssmaker = CGI::Wiki::Plugin::RSS::ModWiki->new(
    298         wiki      => $self->{wiki},
    299         site_name => $self->{site_name},
    300         site_description => $self->{site_description},
    301         make_node_url => $self->{make_node_url},
    302         recent_changes_link => $self->{config}->script_url . uri_escape($self->{config}->script_name) . "?RecentChanges"
    303      );
    304 
    305     my %criteria;
    306     $criteria{items} = $args{items} if $args{items};
    307     $criteria{days} = $args{days} if $args{days};
    308     $criteria{ignore_minor_changes} = $args{ignore_minor_edits}
    309                                                   if $args{ignore_minor_edits};
    310     $criteria{filter_on_metadata} = $args{filter_on_metadata}
    311                                                   if $args{filter_on_metadata};
    312     return $rssmaker->recent_changes( %criteria );
    313 }
     330
     331  my %args = (
     332               days               => 7,
     333               ignore_minor_edits => 1,
     334               filter_on_metadata => { username => "bob" },
     335             );
     336
     337  print "Content-Type: text/plain\n";
     338  print "Last-Modified: " . $rdf_writer->rss_timestamp( %args ) . "\n\n";
     339  print $rdf_writer->make_recentchanges_rss( %args );
     340
     341=item B<rss_timestamp>
     342
     343  print "Last-Modified: " . $rdf_writer->rss_timestamp( %args ) . "\n\n";
     344
     345Returns the timestamp of the RSS feed in POSIX::strftime style ("Tue, 29 Feb 2000
     34612:34:56 GMT"), which is equivalent to the timestamp of the most recent item
     347in the feed. Takes the same arguments as make_recentchanges_rss(). You will most
     348likely need this to print a Last-Modified HTTP header so user-agents can determine
     349whether they need to reload the feed or not.
    314350
    315351=back
     
    333369=head1 COPYRIGHT
    334370
    335      Copyright (C) 2003-2005 The OpenGuides Project.  All Rights Reserved.
     371Copyright (C) 2003-2005 The OpenGuides Project.  All Rights Reserved.
    336372
    337373This module is free software; you can redistribute it and/or modify it
Note: See TracChangeset for help on using the changeset viewer.