0.12_02 commit r0_12_02
authorTony Cook <tony@develop-help.com>
Fri, 9 Aug 2002 09:27:06 +0000 (09:27 +0000)
committertony <tony@45cb6cf1-00bc-42d2-bb5a-07f51df49f94>
Fri, 9 Aug 2002 09:27:06 +0000 (09:27 +0000)
40 files changed:
MANIFEST
Makefile
schema/bse.sql
site/cgi-bin/admin/add.pl
site/cgi-bin/admin/shopadmin.pl
site/cgi-bin/admin/subs.pl
site/cgi-bin/bse.cfg
site/cgi-bin/modules/Article.pm
site/cgi-bin/modules/BSE/DB/Mysql.pm
site/cgi-bin/modules/BSE/Edit/Article.pm [new file with mode: 0644]
site/cgi-bin/modules/BSE/Edit/Base.pm [new file with mode: 0644]
site/cgi-bin/modules/BSE/Edit/Catalog.pm [new file with mode: 0644]
site/cgi-bin/modules/BSE/Edit/Product.pm [new file with mode: 0644]
site/cgi-bin/modules/BSE/Request.pm [new file with mode: 0644]
site/cgi-bin/modules/BSE/Template.pm
site/cgi-bin/modules/BSE/Util/Tags.pm
site/cgi-bin/modules/BSE/Util/Valid.pm
site/cgi-bin/modules/Generate/Catalog.pm
site/cgi-bin/modules/Generate/Product.pm
site/cgi-bin/modules/Image.pm
site/cgi-bin/modules/Images.pm
site/cgi-bin/modules/Products.pm
site/cgi-bin/modules/Squirrel/Template.pm
site/cgi-bin/shop.pl
site/docs/bse.pod
site/docs/config.pod
site/templates/admin/add_product.tmpl
site/templates/admin/article_img.tmpl
site/templates/admin/catalog.tmpl
site/templates/admin/edit_0.tmpl
site/templates/admin/edit_1.tmpl
site/templates/admin/edit_2.tmpl
site/templates/admin/edit_catalog.tmpl
site/templates/admin/edit_product.tmpl
site/templates/admin/edit_steps.tmpl
site/templates/admin/filelist.tmpl
site/templates/admin/product_list.tmpl
site/templates/test/children.tmpl [new file with mode: 0644]
t/BSE/Test.pm
t/t20gen.t

index 6eb8d40..4041f1e 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -39,15 +39,20 @@ site/cgi-bin/modules/BSE/CustomBase.pm
 site/cgi-bin/modules/BSE/DB.pm
 site/cgi-bin/modules/BSE/DB/MSSQL.pm
 site/cgi-bin/modules/BSE/DB/Mysql.pm
+site/cgi-bin/modules/BSE/Edit/Article.pm
+site/cgi-bin/modules/BSE/Edit/Base.pm
+site/cgi-bin/modules/BSE/Edit/Catalog.pm
+site/cgi-bin/modules/BSE/Edit/Product.pm
 site/cgi-bin/modules/BSE/EmailBlackEntry.pm
 site/cgi-bin/modules/BSE/EmailBlacklist.pm
 site/cgi-bin/modules/BSE/EmailRequest.pm
 site/cgi-bin/modules/BSE/EmailRequests.pm
-site/cgi-bin/modules/BSE/FileEditor.pm
+site/cgi-bin/modules/BSE/FileEditor.pm
 site/cgi-bin/modules/BSE/Mail.pm
 site/cgi-bin/modules/BSE/Mail/SMTP.pm
 site/cgi-bin/modules/BSE/Mail/Sendmail.pm
 site/cgi-bin/modules/BSE/Message.pm
+site/cgi-bin/modules/BSE/Request.pm
 site/cgi-bin/modules/BSE/Session.pm
 site/cgi-bin/modules/BSE/Shop/Util.pm
 site/cgi-bin/modules/BSE/Sort.pm
@@ -82,7 +87,7 @@ site/cgi-bin/modules/Products.pm
 site/cgi-bin/modules/SiteUser.pm
 site/cgi-bin/modules/SiteUsers.pm
 site/cgi-bin/modules/Squirrel/GPG.pm
-site/cgi-bin/modules/Squirrel/ImageEditor.pm
+#site/cgi-bin/modules/Squirrel/ImageEditor.pm
 site/cgi-bin/modules/Squirrel/PGP5.pm
 site/cgi-bin/modules/Squirrel/PGP6.pm
 site/cgi-bin/modules/Squirrel/Row.pm
@@ -220,6 +225,7 @@ site/templates/shop_help.tmpl
 site/templates/shop_sect.tmpl
 site/templates/shopitem.tmpl
 site/templates/sidebar/logon.tmpl
+site/templates/test/children.tmpl
 site/templates/textemail/basic.tmpl
 site/templates/user/alreadyblacklisted_base.tmpl
 site/templates/user/blacklistdone_base.tmpl
index ed986a3..439835b 100755 (executable)
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-VERSION=0.12_01
+VERSION=0.12_02
 DISTNAME=bse-$(VERSION)
 DISTBUILD=$(DISTNAME)
 DISTTAR=../$(DISTNAME).tar
index 8d2b667..c82e50a 100644 (file)
@@ -96,6 +96,7 @@ CREATE TABLE image (
   width smallint(5) unsigned,
   height smallint(5) unsigned,
   url varchar(255),
+  displayOrder integer not null default 0,
 
   PRIMARY KEY (id)
 );
index e296429..ce4ff86 100755 (executable)
 #!/usr/bin/perl -w
-
+# -d:ptkdb
+BEGIN { $ENV{DISPLAY} = '192.168.32.15:0.0' }
 use strict;
 use FindBin;
 use lib "$FindBin::Bin/../modules";
-use Constants qw(:edit $CGI_URI $IMAGES_URI);
-
 use Articles;
 use Article;
-use Images;
-use Image;
-use Squirrel::Template;
-use CGI qw(:standard);
-use CGI::Cookie;
 use BSE::DB;
-use Carp 'verbose';
-use Squirrel::ImageEditor;
-use BSE::Cfg;
-use BSE::Session;
-use BSE::Util::Tags;
-
-my $cfg = BSE::Cfg->new;
-my %session;
-BSE::Session->tie_it(\%session, $cfg);
-
-my $urlbase = $cfg->entryVar('site', 'url');
-my $securlbase = $cfg->entryVar('site', 'secureurl');
-use Constants qw(%LEVEL_DEFAULTS $SHOPID $PRODUCTPARENT $LINK_TITLES);
-my %levels = %LEVEL_DEFAULTS;
-
-# what to do
-my %steps =
-  (
-   save=>\&save,
-   remove=>\&remove,
-   add_stepkid=>\&add_stepkid,
-   del_stepkid=>\&del_stepkid,
-   save_stepkids => \&save_stepkids,
-   add_stepparent => \&add_stepparent,
-   del_stepparent => \&del_stepparent,
-   save_stepparents => \&save_stepparents,
-  );
-
-my $level = param('level') || 3;
-
-#my $articles = Articles->new;
-my $articles = 'Articles';
-my $article;
-my $id = param('id');
-
-my %acts;
-
-my $imageEditor = Squirrel::ImageEditor->new(session=>\%session, 
-                                            extras=>\%acts,
-                                            keep=>[ qw/id level parentid/ ],
-                                            cfg=>$cfg);
-
-if (defined $id && $id) {
-  if ($id != -1) {
-    $article = $articles->getByPkey($id);
-    if ($article) {
-      $level = $article->{level};
-      # get the image state
-      if (!$session{imagesid} || $session{imagesid} != $id) {
-        my @images = sort { $a->{id} <=> $b->{id} }
-         Images->getBy('articleId', $article->{id});
-        for my $image (@images) {
-          # make a new hash rather than just using the object - we will be
-          # doing non-OO things to it (like saving them in a session)
-         $image = { map { $_=>$image->{$_} } $image->columns };
-       }
-        $imageEditor->set(\@images, $article->{imagePos});
-        $session{imagesid} = $id;
-      }
-    }
-  }
-  else {
-    # dummy parent to sections
-    my @columns = Article->columns;
-    @$article{@columns} = ('') x @columns;
-    $article->{listed} = 1;
-    $article->{id} = -1;
-    $article->{parentid} = undef;
-    $article->{level} = 0;
-    $level = 0;
-  }
-}
-if (!$article) {
-  # dummy article
-  my @columns = Article->columns;
-  @$article{@columns} = ('') x @columns;
-  $article->{listed} = 1;
-  $article->{body} = '<maximum of 64Kb>';
-
-  # allows a button to add to this section/subsection/article
-  if (defined(my $parentid = param('parentid'))) {
-    if ($parentid && $parentid != -1) {
-      my $parent = $articles->getByPkey($parentid);
-      if ($parent) {
-        $level = $parent->{level}+1;
-        $article->{parentid} = $parentid;
-      }
-    }
-    else {
-      $level = 1;
-      $article->{parentid} = -1;
-    }
-  }
-  if (!exists $session{imagesid} || $session{imagesid}) {
-    # the images are from editing some article - don't use them
-    $session{imagesid} = '';
-    $imageEditor->set([], 'tr');
-  }
-}
-my $parent;
-
-if ($article && $article->{parentid} && $article->{parentid} > 0) {
-  $parent = $articles->getByPkey($article->{parentid});
-  $level = $parent->{level}+1;
-}
-
-my @images;
-my $message = param('message') || ''; # for displaying error message
-my @children;
-if (defined $id) {
-  @children = sort { #$b->{listed} <=> $a->{listed} ||
-     $b->{displayOrder} <=> $a->{displayOrder} }
-    $articles->children($id);
-}
-my @title_images;
-if (opendir TITLE_IMAGES, "$IMAGEDIR/titles") {
-  push(@title_images, 
-       grep -f "$IMAGEDIR/titles/$_" && /\.(gif|jpeg|jpg)$/i, 
-       readdir TITLE_IMAGES);
-  closedir TITLE_IMAGES;
-}
-
-my @templates;
-if (should_be_catalog($article, $parent, $articles)) {
-  @templates = @{$levels{catalog}{templates}}
-    if $levels{catalog}{templates};
-  if (opendir CAT_TEMPL, "$TMPLDIR/catalog") {
-    push(@templates, sort map "catalog/$_", 
-         grep -f "$TMPLDIR/catalog/$_" && /\.tmpl/i, readdir CAT_TEMPL);
-    closedir CAT_TEMPL;
-  }
-  push(@templates, "catalog.tmpl")
-    if $article->{parentid} && $article->{parentid} == $SHOPID 
-      && -e "$TMPLDIR/catalog.tmpl";
+use BSE::Request;
+use BSE::Template;
+use Carp 'confess';
+
+$SIG{__DIE__} = sub { confess $@ };
+
+my $req = BSE::Request->new;
+my $cgi = $req->cgi;
+my $cfg = $req->cfg;
+my $id = $cgi->param('id');
+my $articles = 'Articles'; # for a later switch to proper objects, I hope
+my $result;
+if ($id == -1) {
+  my $obj = get_class('BSE::Edit::Article', $cfg)
+    or die "Cannot get sections class";
+  $result = $obj->edit_sections($req, $articles);
+}
+elsif (my ($obj, $article) = article_class($id, $articles, $cfg)) {
+  $result = $obj->article_dispatch($req, $article, $articles);
 }
 else {
-  # build a list of templates
-  @templates = [ @{$levels{$level}{templates}} ]
-    if $levels{$level}{templates};
-  for my $where ($level, 'common') {
-    if (opendir SECT_TEMPL, "$TMPLDIR/$where") {
-      push(@templates, sort map { "$where/$_" } 
-           grep -f "$TMPLDIR/$where/$_" && /\.tmpl$/i, readdir SECT_TEMPL);
-      closedir SECT_TEMPL;
-    }
-  }
-
-  if ($id) {
-    # allow special templates for the index
-    push(@templates, "index.tmpl") 
-      if $id == 1 && -e "$TMPLDIR/index.tmpl";
-    push(@templates, "index2.tmpl") 
-      if $id == 2 && -e "$TMPLDIR/index2.tmpl";
-
-    # allow special templates for the shop
-    push(@templates, "shop_sect.tmpl")
-      if $id == $SHOPID && -e "$TMPLDIR/shop_sect.tmpl";
-  }
-}
-my $def_template;
-if ($parent && $parent->{id}) {
-  my $section = "children of $parent->{id}";
-  $def_template = $cfg->entry($section, "template");
-  if (my $dirs = $cfg->entry($section, "template_dirs")) {
-    for my $dir (split /,/, $dirs) {
-      next unless $dir && -d "$TMPLDIR/$dir";
-      if (opendir CUST_TEMPL, "$TMPLDIR/$dir") {
-       push(@templates, sort map "$dir/$_",
-            grep -f "$TMPLDIR/$dir/$_" && /\.tmpl$/i, readdir CUST_TEMPL);
-       closedir CUST_TEMPL;
-      }
-    }
-  }
-}
-
-my @files;
-if ($article->{id} && $article->{id} > 0) {
-  require 'ArticleFiles.pm';
-  @files = ArticleFiles->getBy(articleId=>$article->{id});
-}
-my $file_index;
-
-use OtherParents;
-my @stepkids = OtherParents->getBy(parentId=>$article->{id}) if $article->{id};
-my %stepkids = map { $_->{childId} => $_ } @stepkids;
-my @allkids = $article->allkids if $article->{id} && $article->{id} > 0;
-my $allkids_index = -1;
-my @possibles;
-my @stepparents = OtherParents->getBy(childId=>$article->{id})
-  if $article->{id} && $article->{id} > 0;
-my @stepparent_targets = $article->step_parents 
-  if $article->{id} && $article->{id} > 0;
-my %stepparent_targets = map { $_->{id}, $_ } @stepparent_targets;
-my @stepparent_possibles = grep !$stepparent_targets{$_->{id}}, $articles->all;
-my $stepparent_index;
-
-my $child_index = -1;
-%acts =
-  (
-   BSE::Util::Tags->basic(\%acts, $CGI::Q, $cfg),
-   BSE::Util::Tags->admin(\%acts, $cfg),
-   #iterate_image=>\&iterate_image,
-   image=>\&image,
-   article=>\&article,
-   articleType => sub { $levels{$level}{display} },
-   parentType=> sub { $levels{$level-1}{display} },
-   ifart=> sub { $level == 3 },
-   ifnew=> sub { !defined $article || !defined $article->{id} || !length $article->{id}},
-   list=>\&list,
-   #imgtype => sub { $imgtype },
-   script=>sub { $ENV{SCRIPT_NAME} },
-   level => sub { $level },
-   checked => \&checked,
-   iterate_images => \&image_iterator,
-   message => sub { $message },
-   
-   # maintain the children (articles/responses)
-   iterate_children =>
-   sub {
-     return ++$child_index < @children;
-   },
-   child =>
-   sub {
-     return CGI::escapeHTML($children[$child_index]{$_[0]});
-   },
-   ifchildren=>sub { scalar @children },
-   ifNextChild => sub { $child_index < $#children },
-   ifPrevChild => sub { $child_index > 0 },
-   iflisted => 
-   sub {
-     return $acts{$_[0]}->('listed');
-   },
-   childtype => sub { $levels{$level+1}{display} },
-   ifHaveChildType => sub { exists $levels{$level+1} },
-   is => sub {
-     my ($func, $args) = split ' ', $_[0], 2;
-     return $acts{$func}->($args) ? "Yes" : "No";
-   },
-   templates=>
-   sub {
-     return popup_menu(-name=>'template', -values=>\@templates,
-                       -default=>$id?$article->{template}:
-                      ($def_template || $levels{$level}{template}));
-   },
-   titleImages=>
-   sub {
-     return popup_menu(-name=>'titleImage', -values=>[ '', @title_images ],
-                       -labels=>{ ''=>'None', map { $_, $_ } @title_images },
-                       -default=>$id?$article->{titleImage} : '',
-                       -override);
-   },
-   editParent =>
-   sub {
-     if ($id && $id != -1) {
-       return <<HTML;
-<a href="$ENV{SCRIPT_NAME}?id=$article->{parentid}">Edit parent</a> |
-HTML
-     }
-     else {
-       return '';
-     }
-   },
-   ifType => sub {
-     my ($which, $type) = split ' ', $_[0];
-     $acts{$which} or return 0;
-     $acts{$which}->('generator') eq "Generate::$type";
-   },
-   edit => \&edit_link,
-   adminMenu => sub { $ROOT_URI . "admin/"; },
-   iterate_kids_reset => sub { $allkids_index = -1 },
-   iterate_kids => sub { ++$allkids_index < @allkids },
-   ifKids => sub { @allkids },
-   kid => 
-   sub { 
-     my $value = $allkids[$allkids_index]{$_[0]};
-     defined $value or $value = '';
-     CGI::escapeHTML($value)
-   },
-   ifStepKid => sub { exists $stepkids{$allkids[$allkids_index]{id}} },
-   stepkid =>
-   sub {
-     my $value = $stepkids{$allkids[$allkids_index]{id}}{$_[0]};
-     defined $value or $value = '';
-     CGI::escapeHTML($value);
-   },
-   movestepkid =>
-   sub {
-     my $html = '';
-     my $url = $ENV{SCRIPT_NAME} . "?id=$article->{id}";
-     if (param('_t')) {
-       $url .= "&_t=".param('_t');
-     }
-     $url .= "#step";
-     my $refreshto = CGI::escape($url);
-     if ($allkids_index < $#allkids) {
-       $html .= <<HTML
-<a href="$CGI_URI/admin/move.pl?stepparent=$article->{id}&d=swap&id=$allkids[$allkids_index]{id}&other=$allkids[$allkids_index+1]{id}&refreshto=$refreshto"><img src="$IMAGES_URI/admin/move_down.gif" width="17" height="13" border="0" alt="Move Down" align="absbottom"></a>
-HTML
-     }
-     if ($allkids_index > 0) {
-       $html .= <<HTML
-<a href="$CGI_URI/admin/move.pl?stepparent=$article->{id}&d=swap&id=$allkids[$allkids_index]{id}&other=$allkids[$allkids_index-1]{id}&refreshto=$refreshto"><img src="$IMAGES_URI/admin/move_up.gif" width="17" height="13" border="0" alt="Move Up" align="absbottom"></a>
-HTML
-     }
-     return $html;
-   },
-   date =>
-   sub {
-     my ($func, $args) = split ' ', $_[0], 2;
-     $acts{$func} or return "** function $func not defined **";
-     use BSE::Util::SQL qw/sql_to_date/;
-     sql_to_date($acts{$func}->($args));
-   },
-   possible_stepkids =>
-   sub {
-#       @possibles =
-#         sort { $a->{title} cmp $b->{title} }
-#         grep $_->{generator} eq 'Generate::Product' && !$stepkids{$_->{id}},
-#         $articles->all()
-#       unless @possibles;
-     @possibles = possible_stepkids($articles, \%stepkids)
-       unless @possibles;
-     my %labels = map { $_->{id}, "$_->{title} ($_->{id})" } @possibles;
-     CGI::popup_menu(-name=>'stepkid',
-                    -values => [ map $_->{id}, @possibles ],
-                    -labels => \%labels);
-   },
-   ifPossibles =>
-   sub {
-     @possibles = possible_stepkids($articles, \%stepkids)
-       unless @possibles;
-#       @possibles =
-#         sort { $a->{title} cmp $b->{title} }
-#         grep $_->{generator} eq 'Generate::Product' && !$stepkids{$_->{id}},
-#         $articles->all()
-#       unless @possibles;
-     @possibles;
-   },
-   ifStepParents => sub { @stepparents },
-   iterate_stepparents_reset => sub { $stepparent_index = -1; },
-   iterate_stepparents => sub { ++$stepparent_index < @stepparents },
-   stepparent => sub { CGI::escapeHTML($stepparents[$stepparent_index]{$_[0]}) },
-   stepparent_targ =>
-   sub {
-     CGI::escapeHTML($stepparent_targets[$stepparent_index]{$_[0]});
-   },
-   movestepparent =>
-   sub {
-     my $html = '';
-     my $url = $ENV{SCRIPT_NAME} . "?id=$article->{id}";
-     if (param('_t')) {
-       $url .= "&_t=".param('_t');
-     }
-     $url .= "#stepparents";
-     my $refreshto = CGI::escape($url);
-     if ($stepparent_index < $#stepparents) {
-        $html .= <<HTML;
-<a href="$CGI_URI/admin/move.pl?stepchild=$article->{id}&id=$stepparents[$stepparent_index]{parentId}&d=swap&other=$stepparents[$stepparent_index+1]{parentId}&refreshto=$refreshto&all=1"><img src="$IMAGES_URI/admin/move_down.gif" width="17" height="13" border="0" alt="Move Down" align="absbottom"></a>
-HTML
-       }
-       if ($stepparent_index > 0) {
-        $html .= <<HTML;
-<a href="$CGI_URI/admin/move.pl?stepchild=$article->{id}&id=$stepparents[$stepparent_index]{parentId}&d=swap&other=$stepparents[$stepparent_index-1]{parentId}&refreshto=$refreshto&all=1"><img src="$IMAGES_URI/admin/move_up.gif" width="17" height="13" border="0" alt="Move Up" align="absbottom"></a>
-HTML
-       }
-       return $html;
-     },
-     ifStepparentPossibles => sub { @stepparent_possibles },
-     stepparent_possibles => sub {
-       popup_menu(-name=>'stepparent',
-                 -values=>[ map $_->{id}, @stepparent_possibles ],
-                 -labels=>{ map { $_->{id}, "$_->{title} ($_->{id})" } 
-                            @stepparent_possibles });
-     },
-     BSE::Util::Tags->
-     make_iterator(\@files, 'file', 'files', \$file_index),
-  );
-
-if ($imageEditor->action($CGI::Q)) {
-  exit;
-}
-
-use BSE::FileEditor;
-my $file_editor = 
-  BSE::FileEditor->new(session=>\%session, cgi=>$CGI::Q, cfg=>$cfg,
-                      backopts=>{ });
-if ($file_editor->process_files()) {
-  exit;
-}
-
-for my $step (keys %steps) {
-  if (defined param($step)) {
-    $steps{$step}->();
-    exit;
-  }
-}
-
-start();
-
-# the startup page for a new article/subsection
-sub start {
-  $message = shift if @_;
-  # just substitute empty defaults into the blank page
-  my $base;
-  if (should_be_catalog($article, $parent, $articles)) {
-    $base = 'catalog';
-  }
-  else {
-    $base = $level;
-  }
-  if (param('_t')) {
-    $base = param('_t');
-    $base =~ s/\W//g;
-  }
-  my $template = $cfg->entry('admin templates', $base, 
-                           "admin/edit_$base.tmpl");
-  page($template);
-}
-
-sub save_new_article {
-  my %data;
-  my @columns = Article->columns;
-  use AdminUtil 'save_thumbnail';
-  save_thumbnail(undef, \%data);
-  for my $name (@columns) {
-    $data{$name} = param($name) if defined param($name);
-  }
-  if ($data{parentid} eq '') {
-    $message = "You need to select a valid section";
-    start();
-    exit;
-  }
-  
-  if (exists $data{template} &&
-      $data{template} =~ m#\.\.#) {
-    $message = "Please only select templates from the list provided";
-    start();
-    exit;
-  }
-  my $parent = $articles->getByPkey($data{parentid});
-  $data{displayOrder} ||= time;
-  $data{titleImage} ||= '';
-  $data{imagePos} = $imageEditor->imagePos();
-  $data{release} = sql_date($data{release}) || epoch_to_sql(time);
-  $data{expire} = sql_date($data{expire}) || $D_99;
-  $data{template} ||= $levels{$level}{template};
-  $data{link} ||= '';
-  $data{admin} ||= '';
-  if ($parent) {
-    $data{threshold} = $parent->{threshold}
-      if !defined $data{threshold} || $data{threshold} =~ /^\s*$/;
-    $data{summaryLength} = $parent->{summaryLength}
-      if !defined $data{summaryLength} || $data{summaryLength} =~ /^\s*$/;
-  }
-  else {
-    $data{threshold} = $levels{$level}{threshold}
-      if !defined $data{threshold} || $data{threshold} =~ /^\s*$/;
-    $data{summaryLength} = 200
-      if !defined $data{summaryLength} || $data{summaryLength} =~ /^\s*$/;
-  }
-  $data{generator} = should_be_catalog($article, $parent, $articles) 
-    ? 'Generate::Catalog' : 'Generate::Article';
-  $data{lastModified} = epoch_to_sql(time);
-
-  shift @columns;
-  $article = $articles->add(@data{@columns});
-
-  # we now have an id - generate the links
-  my $link;
-  if ($data{generator} eq 'Generate::Article') {
-    $link = "$ARTICLE_URI/$article->{id}.html";
-    if ($LINK_TITLES) {
-      (my $extra = lc $article->{title}) =~ tr/a-z0-9/_/sc;
-      $link .= "/".$extra;
-    }
-  }
-  else {
-    $link = $urlbase.$SHOP_URI."/shop$article->{id}.html";
-  }
-  $article->setAdmin("$CGI_URI/admin/admin.pl?id=$article->{id}");
-  $article->setLink($link);
-  $article->save();
-
-  # save the images
-  my @images = $imageEditor->images();
-  my @imcols = Image->columns;
-  splice(@imcols, 0, 2);
-  for my $image (@images) {
-    my $obj = Images->add($article->{id}, @$image{@imcols});
-  }
-  $imageEditor->clear();
-  delete $session{imagesid};
-
-  return $article;
-}
-
-sub save_old_article {
-  for my $name ($article->columns) {
-    $article->{$name} = param($name) 
-      if defined(param($name)) and $name ne 'id' && $name ne 'parentid';
-  }
-  if (exists $article->{template} &&
-      $article->{template} =~ m#\.\.#) {
-    $message = "Please only select templates from the list provided";
-    start();
-    exit;
-  }
-
-  # reparenting
-  my $newparentid = param('parentid');
-  if ($newparentid == $article->{parentid}) {
-    # nothing to do
-  }
-  elsif ($newparentid != -1) {
-    print STDERR "Reparenting...\n";
-    my $newparent = $articles->getByPkey($newparentid);
-    if ($newparent) {
-      if ($newparent->{level} != $article->{level}-1) {
-       # the article cannot become a child of itself or one of it's 
-       # children
-       if ($id == $newparentid 
-           || is_descendant($article->{id}, $newparentid)) {
-         $message = "Cannot become a child of itself or of a descendant";
-         start();
-         exit;
-       }
-       if (is_descendant($article->{id}, $SHOPID)) {
-         $message = "Cannot become a descendant of the shop";
-         start();
-         exit;
-       }
-       reparent($article, $newparentid);
+  # look for a type
+  my $obj;
+  my $type = $cgi->param('type');
+  if ($type && $type !~ /\W/) {
+    my $class = "BSE::Edit::$type";
+    $obj = get_class($class, $cfg);
+  }
+  unless ($obj) {
+    my $parentid = $cgi->param('parentid');
+    my $parent;
+    if (($obj, $parent) = article_class($parentid, $articles, $cfg)) {
+      if (my ($class) = $obj->child_types($parent)) {
+       $obj = get_class($class, $cfg);
       }
       else {
-       # stays at the same level, nothing special
-       $article->{parentid} = $newparentid;
+       undef $obj;
       }
     }
-    # else ignore it
   }
-  else {
-    # becoming a section
-    reparent($article, -1);
-  }
-
-  
-  $article->{imagePos} = $imageEditor->imagePos();
-  $article->{listed} = param('listed');
-  $article->{release} = sql_date(param('release')) || $D_00;
-  $article->{expire} = sql_date(param('expire')) || $D_99;
-  $article->{lastModified} =  epoch_to_sql(time);
-  if ($article->{id} != 1 && $article->{link} && $LINK_TITLES) {
-    (my $extra = lc $article->{title}) =~ tr/a-z0-9/_/sc;
-    $article->{link} = "$ARTICLE_URI/$article->{id}.html/$extra";
-  }
-  use AdminUtil 'save_thumbnail';
-  save_thumbnail($article, $article);
-
-  $article->save();
-
-  # save the image list
-  my @images = $imageEditor->images();
-  my $images = Images->new;
-  my %imagefiles = map { $_->{image}, 1 } @images;
-
-  # out with the old
-  my @oldimages = $images->getBy('articleId', $id);
-  for my $image (@oldimages) {
-    unless ($imagefiles{$image->{image}}) {
-      # image not used anymore
-      unlink "$IMAGEDIR/$image->{image}";
-    }
-    $image->remove();
-  }
-  # in with the new
-  my @imcols = Image->columns;
-  splice(@imcols, 0, 2);
-  for my $image (@images) {
-    Images->add($article->{id}, @$image{@imcols});
-  }
-
-  $imageEditor->clear();
-  delete $session{imagesid};
-}
-
-# saves the article and it's image list
-sub save {
-  if (defined $article->{id} and length $article->{id}) {
-    save_old_article();
-  }
-  else {
-    $article = save_new_article();
-  }
-
-  use Util 'generate_article';
-  generate_article($articles, $article) if $AUTO_GENERATE;
-
-  print "Refresh: 0; url=\"$urlbase$article->{admin}\"\n";
-  print "Content-type: text/html\n\n<HTML></HTML>\n";
-}
-
-sub remove {
-  my $deleteid = param('deleteid');
-  if (defined $deleteid) {
-    # you can't delete an article with children
-    if ($articles->children($deleteid)) {
-      $message = "This article has children.  You must delete the children first (or change their parents)";
-    }
-    if (grep ($_ == $deleteid, @NO_DELETE )) {
-      $message = "Sorry, these pages are essential to the site structure - they cannot be deleted";
-    }
-    my $delart = $articles->getByPkey($deleteid);
-    if ($delart->{generator} eq 'Generate::Product' ) {
-      $message = "Sorry, you can only delete articles, not products";
-    }
-    if ($deleteid == $SHOPID) {
-      $message = "Sorry, these pages are essential to the store - they cannot be deleted - you may want to hide the the store instead.";
-    }
-    if ($message) {
-      start();
-      exit;
-    }
-
-    # first lose the images
-    my @images = Images->getBy(articleId=>$deleteid);
-    for my $image (@images) {
-      unlink("$IMAGEDIR/$image->{image}");
-      $image->remove();
-    }
-
-    # remove any step(child|parent) links
-    require 'OtherParents.pm';
-    my @steprels = OtherParents->anylinks($deleteid);
-    for my $link (@steprels) {
-      $link->remove();
-    }
-    
-    $delart->remove();
-    $articles = Articles->new(1);
-    use Util 'generate_article';
-    generate_article($articles, $article) if $AUTO_GENERATE;
-    @children = grep { $_->{id} != $deleteid } @children;
-    start();
-  }
-}
-
-sub add_stepkid {
-  require 'BSE/Admin/StepParents.pm';
-  eval {
-    my $childId = param('stepkid');
-    defined $childId
-      or die "No stepkid supplied to add_stepkid";
-    int($childId) eq $childId
-      or die "Invalid stepkid supplied to add_stepkid";
-    my $child = $articles->getByPkey($childId)
-      or die "Article $childId not found";
-    
-    my $release = param('release');
-    defined $release
-      or $release = "01/01/2000";
-    use BSE::Util::Valid qw/valid_date/;
-    $release eq '' or valid_date($release)
-      or die "Invalid release date";
-    my $expire = param('expire');
-    defined $expire
-      or $expire = '31/12/2999';
-    $expire eq '' or valid_date($expire)
-      or die "Invalid expire data";
-  
-    my $newentry = 
-      BSE::Admin::StepParents->add($article, $child, $release, $expire);
-  };
-  if ($@) {
-    $message = $@;
-    return start();
-  }
-  refresh('step');
-}
-
-sub del_stepkid {
-  require 'BSE/Admin/StepParents.pm';
-
-  eval {
-    my $childId = param('stepkid');
-    defined $childId
-      or die "No stepkid supplied to add_stepkid";
-    int($childId) eq $childId
-      or die "Invalid stepkid supplied to add_stepkid";
-    require 'Products.pm';
-    my $child = $articles->getByPkey($childId)
-      or die "Article $childId not found";
-    
-    BSE::Admin::StepParents->del($article, $child);
-  };
-  
-  if ($@) {
-    $message = $@;
-    return start();
+  unless ($obj) {
+    # last try
+    $obj = get_class("BSE::Edit::Article", $cfg)
+      or die "Cannot get article class!!";
   }
-  refresh('step');
+  $result = $obj->noarticle_dispatch($req, $articles);
 }
 
-sub save_stepkids {
-  require 'BSE/Admin/StepParents.pm';
-  my @stepcats = OtherParents->getBy(parentId=>$article->{id});
-  my %stepcats = map { $_->{parentId}, $_ } @stepcats;
-  my %datedefs = ( release => '2000-01-01', expire=>'2999-12-31' );
-  for my $stepcat (@stepcats) {
-    for my $name (qw/release expire/) {
-      my $date = param($name.'_'.$stepcat->{childId});
-      if (defined $date) {
-       if ($date eq '') {
-         $date = $datedefs{$name};
-       }
-       elsif (valid_date($date)) {
-         use BSE::Util::SQL qw/date_to_sql/;
-         $date = date_to_sql($date);
-       }
-       else {
-         return refresh('', "Invalid date '$date'");
-       }
-       $stepcat->{$name} = $date;
-      }
-    }
-    eval {
-      $stepcat->save();
-    };
-    $@ and return refresh('', $@);
-  }
-  refresh('step');
+$| = 1;
+push @{$result->{headers}}, "Content-Type: $result->{type}";
+push @{$result->{headers}}, $req->extra_headers;
+if (exists $ENV{GATEWAY_INTERFACE}
+    && $ENV{GATEWAY_INTERFACE} =~ /^CGI-Perl\//) {
+  use Apache;
+  my $r = Apache->request or die;
+  $r->send_cgi_header(join("\n", @{$result->{headers}})."\n");
 }
-
-#####################
-# Step parents
-
-sub add_stepparent {
-  require 'BSE/Admin/StepParents.pm';
-  #my $productid = param('id');
-
-  $article->{id} && $article->{id} > 0
-    or return start("No id supplied to add_stepproduct");
-  
-  eval {
-    my $step_parent_id = param('stepparent');
-    defined($step_parent_id)
-      or die "No stepparent supplied to add_stepparent";
-    int($step_parent_id) eq $step_parent_id
-      or die "Invalid stepcat supplied to add_stepcat";
-    my $step_parent = Articles->getByPkey($step_parent_id)
-      or die "Parnet $step_parent_id not found\n";
-
-    my $release = param('release');
-    defined $release
-      or $release = "01/01/2000";
-    use BSE::Util::Valid qw/valid_date/;
-    $release eq '' or valid_date($release)
-      or die "Invalid release date";
-    my $expire = param('expire');
-    defined $expire
-      or $expire = '31/12/2999';
-    $expire eq '' or valid_date($expire)
-      or die "Invalid expire data";
-  
-    my $newentry = 
-      BSE::Admin::StepParents->add($step_parent, $article, $release, $expire);
-  };
-  $@ and return refresh('step', $@);
-
-  return refresh('stepparents');
+else {
+  print "$_\n" for @{$result->{headers}};
+  print "\n";
 }
+print $result->{content};
 
-sub del_stepparent {
-  require 'BSE/Admin/StepParents.pm';
-
-  $article->{id} && $article->{id} > 0
-    or return refresh('', 'No article id supplied');
-  my $step_parent_id = param('stepparent');
-  defined($step_parent_id)
-    or return refresh('stepparents', "No stepparent supplied to add_stepcat");
-  int($step_parent_id) eq $step_parent_id
-    or return refresh('stepparents', "Invalid stepparent supplied to add_stepparent");
-  my $step_parent = $articles->getByPkey($step_parent_id)
-    or return refresh('stepparent', "Catalog $step_parent_id not found");
+sub get_class {
+  my ($class, $cfg) = @_;
 
+  (my $file = $class . ".pm") =~ s!::!/!g;
   eval {
-    BSE::Admin::StepParents->del($step_parent, $article);
+    require $file;
   };
-  $@ and return refresh('stepparents', $@);
-
-  return refresh('stepparents');
-}
-
-sub save_stepparents {
-  require 'BSE/Admin/StepParents.pm';
-
-  $article->{id} && $article->{id} > 0
-    or return refresh('', 'No article id supplied');
-    
-  my @stepparents = OtherParents->getBy(childId=>$article->{id});
-  my %stepparents = map { $_->{parentId}, $_ } @stepparents;
-  my %datedefs = ( release => '2000-01-01', expire=>'2999-12-31' );
-  for my $stepparent (@stepparents) {
-    for my $name (qw/release expire/) {
-      my $date = param($name.'_'.$stepparent->{parentId});
-      if (defined $date) {
-       if ($date eq '') {
-         $date = $datedefs{$name};
-       }
-       elsif (valid_date($date)) {
-         use BSE::Util::SQL qw/date_to_sql/;
-         $date = date_to_sql($date);
-       }
-       else {
-         return refresh("Invalid date '$date'");
-       }
-       $stepparent->{$name} = $date;
-      }
-    }
-    eval {
-      $stepparent->save();
-    };
-    $@ and return refresh('', $@);
-  }
-
-  refresh('stepparents');
-}
-
-sub refresh {
-  my ($name, $message) = @_;
-
-  my $url = "$urlbase$ENV{SCRIPT_NAME}?id=$article->{id}";
-  $url .= "&message=" . CGI::escape($message) if $message;
-  if (param('_t')) {
-    $url .= "&_t=".CGI::escape(param('_t'));
-  }
-  $url .= "#$name" if $name;
-  print STDERR "Url $url\n";
-
-  print "Refresh: 0; url=\"$url\"\n";
-  print "Content-type: text/html\n\n<HTML></HTML>\n";
-}
-
-sub page {
-  my ($page) = @_;
-  print header, Squirrel::Template->new->show_page($TMPLDIR, $page, \%acts);
-}
-
-sub article {
-  my ($name) = @_;
-  return display_date($article->{$name}) || ''
-    if $name eq 'expire' || $name eq 'release';
-  return CGI::escapeHTML($article->{$name});
-}
-
-
-sub list {
-  my $what = shift;
-
-  if ($what eq 'listed') {
-    my @values = qw(0 1);
-    my %labels = ( 0=>"No", 1=>"Yes");
-    if ($level <= 2) {
-      $labels{2} = "In Sections, but not menu";
-      push(@values, 2);
-    }
-    else {
-      $labels{2} = "In content, but not menus";
-      push(@values, 2);
-    }
-    return popup_menu(-name=>'listed',
-                     -values=>\@values,
-                     -labels=>\%labels,
-                     -default=>$article->{listed});
-  }
-  else {
-    my @values;
-    my %labels;
-
-    # articles that this article could become a child of
-    if (should_be_catalog($article, $parent, $articles)) {
-      # the parents of a catalog can be other catalogs or the shop
-      my $shop = $articles->getByPkey($SHOPID);
-      my @work = [ $SHOPID, $shop->{title} ];
-      while (@work) {
-       my ($id, $title) = @{pop @work};
-       push(@values, $id);
-       $labels{$id} = $title;
-       push @work, map [ $_->{id}, $title.' / '.$_->{title} ],
-       sort { $b->{displayOrder} <=> $a->{displayOrder} }
-         grep $_->{generator} eq 'Generate::Catalog', 
-         $articles->getBy(parentid=>$id);
-      }
-    }
-    else {
-      my @parents = $articles->getBy('level', $level-1);
-      @parents = grep { $_->{generator} eq 'Generate::Article' 
-                         && $_->{id} != $SHOPID } @parents;
-      
-      
-      @values = ( map {$_->{id}} @parents );
-      %labels = ( map { $_->{id} => "$_->{title} ($_->{id})" } @parents );
-      
-      if ($level == 1) {
-       push @values, -1;
-       $labels{-1} = "No parent - this is a section";
-      }
-      
-      if ($id && reparent_updown()) {
-       # we also list the siblings and grandparent (if any)
-       my @siblings = grep $_->{id} != $id && $_->{id} != $SHOPID,
-       $articles->getBy(parentid => $article->{parentid});
-       push @values, map $_->{id}, @siblings;
-       @labels{map $_->{id}, @siblings} =
-         map { "-- move down a level -- $_->{title} ($_->{id})" } @siblings;
-       
-       if ($article->{parentid} != -1) {
-         my $parent = $articles->getByPkey($article->{parentid});
-         if ($parent->{parentid} != -1) {
-           my $gparent = $articles->getByPkey($parent->{parentid});
-           push @values, $gparent->{id};
-           $labels{$gparent->{id}} =
-             "-- move up a level -- $gparent->{title} ($gparent->{id})";
-         }
-         else {
-           push @values, -1;
-           $labels{-1} = "-- move up a level -- become a section";
-         }
-       }
-      }
-    }
-    my $html;
-    if (defined $article->{parentid}) {
-      $html = popup_menu(-name=>'parentid',
-                        -values=> \@values,
-                        -labels => \%labels,
-                        -default => $article->{parentid},
-                        -override=>1);
-    }
-    else {
-      $html = popup_menu(-name=>'parentid',
-                        -values=> \@values,
-                        -labels => \%labels,
-                        -override=>1);
-    }
-
-    # munge the html - we display a default value, so we need to wrap the 
-    # default <select /> around this one
-    $html =~ s!^<select[^>]+>|</select>!!gi;
-    return $html;
-  }
-}
-
-sub edit_link {
-  my ($args) = @_;
-  my ($which, $name) = split / /, $args, 2;
-  $name ||= 'Edit';
-  my $gen_class;
-  if ($acts{$which} && ($gen_class = $acts{$which}->('generator'))) {
-    eval "use $gen_class";
-    unless ($@) {
-      my $gen = $gen_class->new;
-      my $link = $gen->edit_link($acts{$which}->('id'));
-      return qq!<a href="$link">$name</a>!;
-    }
-  }
-  return '';
-}
-
-sub checked {
-  my ($func, $name, $value) = split ' ', $_[0];
-  return $acts{$func}->($name) eq $value ? 'checked' : ''
-}
-
-# image iteration
-
-{
-  my $image;
-  my @iter_images;
-  
-  # used to iterate over images in the article editor
-  # maybe these should be combined
-  sub image_iterator {
-    my ($ignore, $name) = @_;
-    if (!$image) {
-      @iter_images = $imageEditor->images();
-    }
-    $image = shift @iter_images;
-    return $image;
-  }
-  
-  sub image {
-    return $image->{$_[0]};
-  }
-}
-
-# convert a user entered date from dd/mm/yyyy to ANSI sql format
-# we try to parse flexibly here
-sub sql_date {
-  my $str = shift;
-  my ($year, $month, $day);
-
-  # look for a date
-  if (($day, $month, $year) = ($str =~ m!(\d+)/(\d+)/(\d+)!)) {
-    $year += 2000 if $year < 100;
-
-    return sprintf("%04d-%02d-%02d", $year, $month, $day);
-  }
-  return undef;
-}
-
-# convert a data from ANSI sql format to .au format
-sub display_date {
-  my ($date) = @_;
-  
-  if ( my ($year, $month, $day) = 
-       ($date =~ /^(\d+)-(\d+)-(\d+)/)) {
-    return sprintf("%02d/%02d/%04d", $day, $month, $year);
-  }
-  return undef;
-}
-
-# convert an epoch time to sql format
-sub epoch_to_sql {
-  use POSIX 'strftime';
-  my ($time) = @_;
-
-  return strftime('%Y-%m-%d', localtime $time);
+  return if $@;
+  return $class->new(cfg=>$cfg, db=>BSE::DB->single);
 }
 
-# can the user reparent to a different level?
-sub reparent_updown {
-  if (ref $REPARENT_UPDOWN) {
-    return $REPARENT_UPDOWN->();
-  }
-  else {
-    return $REPARENT_UPDOWN;
-  }
-}
+sub article_class {
+  my ($id, $articles, $cfg) = @_;
 
-# tests if $desc is a descendant of $art
-# where both are article ids
-sub is_descendant {
-  my ($art, $desc) = @_;
-  
-  my @check = ($art);
-  while (@check) {
-    my $parent = shift @check;
-    $parent == $desc and return 1;
-    my @kids = $articles->getBy(parentid=>$parent);
-    push @check, map $_->{id}, @kids;
+  return unless defined $id and $id =~ /^\d+$/;
+  my $class = "BSE::Edit::Article";
+  my $article = $articles->getByPkey($id)
+    or return;
+  $class = $article->{generator};
+  $class =~ s/^(?:BSE::)?Generate::/BSE::Edit::/;
+  my $obj = get_class($class, $cfg);
+  if ($obj) {
+    $article = $obj->get_article($articles, $article);
   }
-
-  return 0;
+  return ($obj, $article);
 }
-
-# reparent an article
-# this includes fixing the level number of the article's children
-sub reparent {
-  my ($article, $newparentid) = @_;
-
-  my $newlevel;
-  if ($newparentid == -1) {
-    $newlevel = 1;
-  }
-  else {
-    my $parent = $articles->getByPkey($newparentid);
-    unless ($parent) {
-      $message = "Cannot get new parent article";
-      start();
-      exit;
-    }
-    $newlevel = $parent->{level} + 1;
-  }
-  # the caller will save this one
-  $article->{parentid} = $newparentid;
-  $article->{level} = $newlevel;
-  $article->{displayOrder} = time;
-
-  my @change = ( [ $article->{id}, $newlevel ] );
-  while (@change) {
-    my $this = shift @change;
-    my ($art, $level) = @$this;
-
-    my @kids = $articles->getBy(parentid=>$art);
-    push @change, map { [ $_->{id}, $level+1 ] } @kids;
-
-    for my $kid (@kids) {
-      $kid->{level} = $level+1;
-      $kid->save;
-    }
-  }
-}
-
-sub should_be_catalog {
-  my ($article, $parent, $articles) = @_;
-
-  if ($article->{parentid} && (!$parent || $parent->{id} != $article->{parentid})) {
-    $parent = $articles->getByPkey($article->{id});
-  }
-
-  return $article->{parentid} && $parent &&
-    ($article->{parentid} == $SHOPID || 
-     $parent->{generator} eq 'Generate::Catalog');
-}
-
-sub possible_stepkids {
-  my ($articles, $stepkids) = @_;
-
-  #@possibles =
-  #  sort { $a->{title} cmp $b->{title} }
-  #    grep $_->{generator} eq 'Generate::Product' && !$stepkids{$_->{id}},
-  #    $articles->all();
-
-  return sort { $a->{title} cmp $b->{title} }
-    grep !$stepkids->{$_->{id}}, $articles->all;
-}
-
-__END__
-
-=head1 NAME
-
-add.pl - article editing tool
-
-=head1 SYNOPSYS
-
-A CGI script.
-
-=head1 DESCRIPTION
-
-add.pl is used to add and edit articles.
-
-=head1 TAGS
-
-=over 4
-
-=item edit I<which> I<label>
-
-Inserts a link to a page to edit the specified article.
-
-=back
-
-=cut
index 9a694d0..3419627 100755 (executable)
@@ -16,7 +16,7 @@ use OrderItems;
 use OrderItem;
 use Constants qw($TMPLDIR);
 use Squirrel::Template;
-use Squirrel::ImageEditor;
+#use Squirrel::ImageEditor;
 use Constants qw(:shop $SHOPID $PRODUCTPARENT 
                  $SHOP_URI $CGI_URI $IMAGES_URI $AUTO_GENERATE);
 use Images;
@@ -41,16 +41,16 @@ my %what_to_do =
    order_list_unpaid => \&order_list_unpaid,
    order_detail=>\&order_detail,
    order_filled=>\&order_filled,
-   edit_product=>\&edit_product,
-   add_product=>\&add_product,
-   save_product=>\&save_product,
+#     edit_product=>\&edit_product,
+#     add_product=>\&add_product,
+#     save_product=>\&save_product,
    delete_product=>\&delete_product,
    undelete_product=>\&undelete_product,
    product_detail=>\&product_detail,
-   add_stepcat=>\&add_stepcat,
-   del_stepcat=>\&del_stepcat,
-   save_stepcats => \&save_stepcats,
-   back=>\&img_return,
+#     add_stepcat=>\&add_stepcat,
+#     del_stepcat=>\&del_stepcat,
+#     save_stepcats => \&save_stepcats,
+#     back=>\&img_return,
   );
 
 my @modifiable = qw(body retailPrice wholesalePrice gst release expire 
@@ -84,22 +84,22 @@ my %acts;
    level => sub { 3; }, # doesn't really matter here
   );
 
-my $imageEditor = Squirrel::ImageEditor->new(session=>\%session,
-                                            extras=>\%acts,
-                                            keep => [ qw/id parentid/ ],
-                                            cfg=>$cfg);
-
-if ($imageEditor->action($CGI::Q)) {
-  exit;
-}
-
-use BSE::FileEditor;
-my $file_editor = 
-  BSE::FileEditor->new(session=>\%session, cgi=>$CGI::Q, cfg=>$cfg,
-                      backopts=>{ edit_product=> 1});
-if ($file_editor->process_files()) {
-  exit;
-}
+#  my $imageEditor = Squirrel::ImageEditor->new(session=>\%session,
+#                                           extras=>\%acts,
+#                                           keep => [ qw/id parentid/ ],
+#                                           cfg=>$cfg);
+
+#  if ($imageEditor->action($CGI::Q)) {
+#    exit;
+#  }
+
+#  use BSE::FileEditor;
+#  my $file_editor = 
+#    BSE::FileEditor->new(session=>\%session, cgi=>$CGI::Q, cfg=>$cfg,
+#                     backopts=>{ edit_product=> 1});
+#  if ($file_editor->process_files()) {
+#    exit;
+#  }
 
 while (my ($key, $func) = each %what_to_do) {
   if (param($key)) {
index ce7f87c..a637c38 100755 (executable)
@@ -217,6 +217,7 @@ sub addsave {
     for my $field (@fields) {
       $subs{$field} = $q->param($field) if defined $q->param($field);
     }
+    $subs{archive} = () = $q->param('archive');
     $subs{visible} = 0 + defined $q->param('visible');
     $subs{lastSent} = '0000-00-00 00:00';
     my $sub = BSE::SubscriptionTypes->add(@subs{@fields});
@@ -252,6 +253,7 @@ sub editsave {
     for my $field (@fields) {
       $sub->{$field} = $q->param($field) if defined $q->param($field);
     }
+    $sub->{archive} = () = $q->param('archive');
     $sub->{visible} = 0 + defined $q->param('visible');
     $sub->save();
     _refresh_list($cfg);
index 572d64d..f1fa94b 100644 (file)
@@ -76,3 +76,13 @@ mysqldump = /usr/local/mysql/bin/mysqldump
 # will be available when sending a subscription
 override=1
 
+[level names]
+0=Your Site
+1=Section
+2=Subsect Lev1
+3=Subsect Lev2
+4=Subsect Lev3
+5=Subsect Lev4
+
+[articles]
+shop=3
index b20a658..ffa0407 100644 (file)
@@ -95,4 +95,24 @@ sub all_visible_kids {
   return @kids{ sort { $order{$b} <=> $order{$a} } keys %kids };
 }
 
+sub images {
+  my ($self) = @_;
+  require Images;
+  Images->getBy(articleId=>$self->{id});
+}
+
+sub children {
+  my ($self) = @_;
+
+  return sort { $b->{displayOrder} <=> $b->{displayOrder} } 
+    Articles->children($self->{id});
+}
+
+sub files {
+  my ($self) = @_;
+
+  require ArticleFiles;
+  return ArticleFiles->getBy(articleId=>$self->{id});
+}
+
 1;
index 889cc2c..08d2ac9 100644 (file)
@@ -44,10 +44,10 @@ EOS
 
    Images => 'select * from image',
    replaceImage =>
-     'replace image values (?,?,?,?,?,?,?)',
-   addImage => 'insert image values(null, ?, ?, ?, ?, ?, ?)',
+     'replace image values (?,?,?,?,?,?,?,?)',
+   addImage => 'insert image values(null, ?, ?, ?, ?, ?, ?, ?)',
    deleteImage => 'delete from image where id = ?',
-   getImageByArticleId => 'select * from image where articleId = ? order by id',
+   getImageByArticleId => 'select * from image where articleId = ? order by displayOrder',
    
    dropIndex => 'delete from searchindex',
    insertIndex => 'insert searchindex values(?, ?, ?, ?)',
diff --git a/site/cgi-bin/modules/BSE/Edit/Article.pm b/site/cgi-bin/modules/BSE/Edit/Article.pm
new file mode 100644 (file)
index 0000000..2805db7
--- /dev/null
@@ -0,0 +1,1863 @@
+package BSE::Edit::Article;
+use strict;
+use HTML::Entities;
+use base qw(BSE::Edit::Base);
+use BSE::Util::Tags;
+use BSE::Util::SQL qw(now_sqldate);
+
+sub article_dispatch {
+  my ($self, $request, $article, $articles) = @_;
+  
+  my $cgi = $request->cgi;
+  my $action;
+  my %actions = $self->article_actions;
+  for my $check (keys %actions) {
+    if ($cgi->param($check) || $cgi->param("$check.x")) {
+      $action = $check;
+      last;
+    }
+  }
+  my @extraargs;
+  unless ($action) {
+    ($action, @extraargs) = $self->other_article_actions($cgi);
+  }
+  $action ||= 'edit';
+  my $method = $actions{$action};
+  return $self->$method($request, $article, $articles, @extraargs);
+}
+
+sub noarticle_dispatch {
+  my ($self, $request, $articles) = @_;
+
+  my $cgi = $request->cgi;
+  my $action = 'add';
+  my %actions = $self->noarticle_actions;
+  for my $check (keys %actions) {
+    if ($cgi->param($check) || $cgi->param("$check.x")) {
+      $action = $check;
+      last;
+    }
+  }
+  my $method = $actions{$action};
+  return $self->$method($request, $articles);
+}
+
+sub edit_sections {
+  my ($self, $req, $articles) = @_;
+
+  my %article;
+  my @cols = Article->columns;
+  @article{@cols} = ('') x @cols;
+  $article{id} = '-1';
+  $article{parentid} = -1;
+  $article{level} = 0;
+  $article{body} = '';
+  $article{listed} = 0;
+  $article{generator} = $self->generator;
+
+  return $self->low_edit_form($req, \%article, $articles);
+}
+
+sub article_actions {
+  my ($self) = @_;
+
+  return
+    (
+     edit => 'edit_form',
+     save => 'save',
+     add_stepkid => 'add_stepkid',
+     del_stepkid => 'del_stepkid',
+     save_stepkids => 'save_stepkids',
+     add_stepparent => 'add_stepparent',
+     del_stepparent => 'del_stepparent',
+     save_stepparents => 'save_stepparents',
+     artimg => 'save_image_changes',
+     addimg => 'add_image',
+     showimages => 'show_images',
+     process => 'save_image_changes',
+     removeimg => 'remove_img',
+     moveimgup => 'move_img_up',
+     moveimgdown => 'move_img_down',
+     filelist => 'filelist',
+     fileadd => 'fileadd',
+     fileswap => 'fileswap',
+     filedel => 'filedel',
+     filesave => 'filesave',
+    );
+}
+
+sub other_article_actions {
+  my ($self, $cgi) = @_;
+
+  for my $param ($cgi->param) {
+    if ($param =~ /^removeimg_(\d+)(\.x)?$/) {
+      return ('removeimg', $1 );
+    }
+  }
+
+  return;
+}
+
+sub noarticle_actions {
+  return
+    (
+     add => 'add_form',
+     save => 'save_new',
+    );
+}
+
+sub get_parent {
+  my ($self, $parentid, $articles) = @_;
+
+  if ($parentid == -1) {
+    return 
+      {
+       id => -1,
+       title=>'All Sections',
+       level => 0,
+       listed => 0,
+       parentid => undef,
+      };
+  }
+  else {
+    return $articles->getByPkey($parentid);
+  }
+}
+
+sub tag_hash {
+  my ($object, $args) = @_;
+
+  my $value = $object->{$args};
+  defined $value or $value = '';
+  encode_entities($value);
+}
+
+sub tag_art_type {
+  my ($level, $cfg) = @_;
+
+  encode_entities($cfg->entry('level names', $level, 'Article'));
+}
+
+sub tag_if_new {
+  my ($article) = @_;
+
+  !$article->{id};
+}
+
+sub reparent_updown {
+  return 1;
+}
+
+sub should_be_catalog {
+  my ($self, $article, $parent, $articles) = @_;
+
+  if ($article->{parentid} && (!$parent || $parent->{id} != $article->{parentid})) {
+    $parent = $articles->getByPkey($article->{id});
+  }
+
+  my $shopid = $self->{cfg}->entryErr('articles', 'shop');
+
+  return $article->{parentid} && $parent &&
+    ($article->{parentid} == $shopid || 
+     $parent->{generator} eq 'Generate::Catalog');
+}
+
+sub possible_parents {
+  my ($self, $article, $articles) = @_;
+
+  my %labels;
+  my @values;
+
+  my $shopid = $self->{cfg}->entryErr('articles', 'shop');
+  my @parents = $articles->getBy('level', $article->{level}-1);
+  @parents = grep { $_->{generator} eq 'Generate::Article' 
+                     && $_->{id} != $shopid } @parents;
+  
+  @values = ( map {$_->{id}} @parents );
+  %labels = ( map { $_->{id} => "$_->{title} ($_->{id})" } @parents );
+  
+  if ($article->{level} == 1) {
+    push @values, -1;
+    $labels{-1} = "No parent - this is a section";
+  }
+  
+  if ($article->{id} && $self->reparent_updown($article)) {
+    # we also list the siblings and grandparent (if any)
+    my @siblings = grep $_->{id} != $article->{id} && $_->{id} != $shopid,
+    $articles->getBy(parentid => $article->{parentid});
+    push @values, map $_->{id}, @siblings;
+    @labels{map $_->{id}, @siblings} =
+      map { "-- move down a level -- $_->{title} ($_->{id})" } @siblings;
+    
+    if ($article->{parentid} != -1) {
+      my $parent = $articles->getByPkey($article->{parentid});
+      if ($parent->{parentid} != -1) {
+       my $gparent = $articles->getByPkey($parent->{parentid});
+       push @values, $gparent->{id};
+       $labels{$gparent->{id}} =
+         "-- move up a level -- $gparent->{title} ($gparent->{id})";
+      }
+      else {
+       push @values, -1;
+       $labels{-1} = "-- move up a level -- become a section";
+      }
+    }
+  }
+
+  return (\@values, \%labels);
+}
+
+sub tag_list {
+  my ($self, $article, $articles, $cgi, $what) = @_;
+
+  if ($what eq 'listed') {
+    my @values = qw(0 1);
+    my %labels = ( 0=>"No", 1=>"Yes");
+    if ($article->{level} <= 2) {
+      $labels{2} = "In Sections, but not menu";
+      push(@values, 2);
+    }
+    else {
+      $labels{2} = "In content, but not menus";
+      push(@values, 2);
+    }
+    return $cgi->popup_menu(-name=>'listed',
+                           -values=>\@values,
+                           -labels=>\%labels,
+                           -default=>$article->{listed});
+  }
+  else {
+    my ($values, $labels) = $self->possible_parents($article, $articles);
+    my $html;
+    if (defined $article->{parentid}) {
+      $html = $cgi->popup_menu(-name=>'parentid',
+                              -values=> $values,
+                              -labels => $labels,
+                              -default => $article->{parentid},
+                              -override=>1);
+    }
+    else {
+      $html = $cgi->popup_menu(-name=>'parentid',
+                              -values=> $values,
+                              -labels => $labels,
+                              -override=>1);
+    }
+
+    # munge the html - we display a default value, so we need to wrap the 
+    # default <select /> around this one
+    $html =~ s!^<select[^>]+>|</select>!!gi;
+    return $html;
+  }
+}
+
+sub tag_checked {
+  my ($arg, $acts, $funcname, $templater) = @_;
+  my ($func, $args) = split ' ', $arg, 2;
+  return $templater->perform($acts, $func, $args) ? 'checked' : '';
+}
+
+sub iter_get_images {
+  my ($article) = @_;
+
+  $article->{id} or return;
+  $article->images;
+}
+
+sub iter_get_kids {
+  my ($article, $articles) = @_;
+
+  $article->{id} or return;
+  if (UNIVERSAL::isa($article, 'Article')) {
+    $article->children;
+  }
+  elsif ($article->{id}) {
+    return $articles->children($article->{id});
+  }
+  else {
+    return;
+  }
+}
+
+sub tag_if_have_child_type {
+  my ($level, $cfg) = @_;
+
+  defined $cfg->entry("level names", $level+1);
+}
+
+sub tag_is {
+  my ($args, $acts, $isname, $templater) = @_;
+
+  my ($func, $funcargs) = split ' ', $args, 2;
+  return $templater->perform($acts, $func, $funcargs) ? 'Yes' : 'No';
+}
+
+sub tag_templates {
+  my ($self, $article, $cfg, $cgi) = @_;
+
+  my @templates = sort $self->templates($article);
+  my $default;
+  if ($article->{template} && grep $_ eq $article->{template}, @templates) {
+    $default = $article->{template};
+  }
+  else {
+    $default = $templates[0];
+  }
+  return $cgi->popup_menu(-name=>'template',
+                         -values=>\@templates,
+                         -default=>$default,
+                         -override=>1);
+}
+
+sub title_images {
+  my ($self, $article) = @_;
+
+  my @title_images;
+  my $imagedir = $self->{cfg}->entry('paths', 'images', $Constants::IMAGEDIR);
+  if (opendir TITLE_IMAGES, "$imagedir/titles") {
+    @title_images = sort 
+      grep -f "$imagedir/titles/$_" && /\.(gif|jpeg|jpg|png)$/i,
+      readdir TITLE_IMAGES;
+    closedir TITLE_IMAGES;
+  }
+
+  @title_images;
+}
+
+sub tag_title_images  {
+  my ($self, $article, $cfg, $cgi) = @_;
+
+  my @images = $self->title_images($article);
+  my @values = ( '', @images );
+  my %labels = ( '' => 'None', map { $_ => $_ } @images );
+  return $cgi->
+    popup_menu(-name=>'titleImage',
+              -values=>\@values,
+              -labels=>\%labels,
+              -default=>$article->{id} ? $article->{titleImage} : '',
+              -override=>1);
+}
+
+sub base_template_dirs {
+  return ( "common" );
+}
+
+sub template_dirs {
+  my ($self, $article) = @_;
+
+  my @dirs = $self->base_template_dirs;
+  if (my $parentid = $article->{parentid}) {
+    my $section = "children of $parentid";
+    if (my $dirs = $self->{cfg}->entry($section, 'template_dirs')) {
+      push @dirs, split /,/, $dirs;
+    }
+  }
+  if (my $id = $article->{id}) {
+    my $section = "article $id";
+    if (my $dirs = $self->{cfg}->entry($section, 'template_dirs')) {
+      push @dirs, split /,/, $dirs;
+    }
+  }
+
+  @dirs;
+}
+
+sub templates {
+  my ($self, $article) = @_;
+
+  my @dirs = $self->template_dirs($article);
+  my @templates;
+  my $basedir = $self->{cfg}->entry('paths', 'templates', $Constants::TMPLDIR);
+  for my $dir (@dirs) {
+    my $path = File::Spec->catdir($basedir, $dir);
+    if (-d $path) {
+      if (opendir TEMPLATE_DIR, $path) {
+       push(@templates, sort map "$dir/$_",
+            grep -f "$path/$_" && /\.(tmpl|html)$/i, readdir TEMPLATE_DIR);
+       closedir TEMPLATE_DIR;
+      }
+    }
+  }
+  return (@templates, $self->extra_templates($article));
+}
+
+sub extra_templates {
+  my ($self, $article) = @_;
+
+  my $basedir = $self->{cfg}->entry('paths', 'templates', $Constants::TMPLDIR);
+  my @templates;
+  if (my $id = $article->{id}) {
+    push @templates, 'index.tmpl'
+      if $id == 1 && -f "$basedir/index.html";
+    push @templates, 'index2.tmpl'
+      if $id == 2 && -f "$basedir/index2.html";
+    my $shopid = $self->{cfg}->entryErr('articles', 'shop');
+    push @templates, "shop_sect.tmpl"
+      if $id == $shopid && -f "$basedir/shop_sect.tmpl";
+    my $section = "article $id";
+    my $extras = $self->{cfg}->entry($section, 'extra_templates');
+    push @templates, grep /\.(tmpl|html)$/i, split /,/, $extras
+      if $extras;
+  }
+
+  @templates;
+}
+
+sub edit_parent {
+  my ($article) = @_;
+
+  return '' unless $article->{id} && $article->{id} != -1;
+  return <<HTML;
+<a href="$ENV{SCRIPT_NAME}?id=$article->{parentid}">Edit parent</a> |
+HTML
+}
+
+sub iter_allkids {
+  my ($article) = @_;
+
+  return unless $article->{id} && $article->{id} > 0;
+  $article->allkids;
+}
+
+sub _load_step_kids {
+  my ($article, $step_kids) = @_;
+
+  my @stepkids = OtherParents->getBy(parentId=>$article->{id}) if $article->{id};
+  %$step_kids = map { $_->{childId} => $_ } @stepkids;
+  use Data::Dumper;
+  print STDERR "stepkids:\n", Dumper($step_kids);
+  $step_kids->{loaded} = 1;
+}
+
+sub tag_if_step_kid {
+  my ($article, $allkids, $rallkid_index, $step_kids) = @_;
+
+  _load_step_kids($article, $step_kids) unless $step_kids->{loaded};
+
+  my $kid = $allkids->[$$rallkid_index]
+    or return;
+  exists $step_kids->{$kid->{id}};
+}
+
+sub tag_step_kid {
+  my ($article, $allkids, $rallkid_index, $step_kids, $arg) = @_;
+
+  _load_step_kids($article, $step_kids) unless $step_kids->{loaded};
+
+  my $kid = $allkids->[$$rallkid_index]
+    or return '';
+  print STDERR "found kid (want $arg): ", Dumper $kid;
+  encode_entities($step_kids->{$kid->{id}}{$arg});
+}
+
+sub tag_move_stepkid {
+  my ($self, $cgi, $article, $allkids, $rallkids_index) = @_;
+
+  my $cgi_uri = $self->{cfg}->entry('uri', 'cgi', '/cgi-bin');
+  my $images_uri = $self->{cfg}->entry('uri', 'images', '/images');
+  my $html = '';
+  my $url = $ENV{SCRIPT_NAME} . "?id=$article->{id}";
+  if ($cgi->param('_t')) {
+    $url .= "&_t=".$cgi->param('_t');
+  }
+  $url .= "#step";
+  my $refreshto = CGI::escape($url);
+  if ($$rallkids_index < $#$allkids) {
+    $html .= <<HTML;
+<a href="$cgi_uri/admin/move.pl?stepparent=$article->{id}&d=swap&id=$allkids->[$$rallkids_index]{id}&other=$allkids->[$$rallkids_index+1]{id}&refreshto=$refreshto"><img src="$images_uri/admin/move_down.gif" width="17" height="13" border="0" alt="Move Down" align="absbottom"></a>
+HTML
+  }
+  if ($$rallkids_index > 0) {
+    $html .= <<HTML;
+<a href="$cgi_uri/admin/move.pl?stepparent=$article->{id}&d=swap&id=$allkids->[$$rallkids_index]{id}&other=$allkids->[$$rallkids_index-1]{id}&refreshto=$refreshto"><img src="$images_uri/admin/move_up.gif" width="17" height="13" border="0" alt="Move Up" align="absbottom"></a>
+HTML
+  }
+  return $html;
+}
+
+sub possible_stepkids {
+  my ($articles, $stepkids) = @_;
+
+  return sort { lc $a->{title} cmp lc $b->{title} }
+    grep !$stepkids->{$_->{id}}, $articles->all;
+}
+
+
+
+sub tag_possible_stepkids {
+  my ($step_kids, $article, $possstepkids, $articles, $cgi) = @_;
+
+  _load_step_kids($article, $step_kids) unless $step_kids->{loaded};
+  @$possstepkids = possible_stepkids($articles, $step_kids)
+    unless @$possstepkids;
+  my %labels = map { $_->{id} => "$_->{title} ($_->{id})" } @$possstepkids;
+  return
+    $cgi->popup_menu(-name=>'stepkid',
+                    -values=> [ map $_->{id}, @$possstepkids ],
+                    -labels => \%labels);
+}
+
+sub tag_if_possible_stepkids {
+  my ($step_kids, $article, $possstepkids, $articles, $cgi) = @_;
+
+  _load_step_kids($article, $step_kids) unless $step_kids->{loaded};
+  @$possstepkids = possible_stepkids($articles, $step_kids)
+    unless @$possstepkids;
+  
+  @$possstepkids;
+}
+
+sub iter_get_stepparents {
+  my ($article) = @_;
+
+  return unless $article->{id} && $article->{id} > 0;
+
+  OtherParents->getBy(childId=>$article->{id});
+}
+
+sub tag_ifStepParents {
+  my ($args, $acts, $funcname, $templater) = @_;
+
+  return $templater->perform($acts, 'ifStepparents', '');
+}
+
+sub tag_stepparent_targ {
+  my ($article, $targs, $rindex, $arg) = @_;
+
+  if ($article->{id} && $article->{id} > 0 && !@$targs) {
+    @$targs = $article->step_parents;
+  }
+  encode_entities($targs->[$$rindex]{$arg});
+}
+
+sub tag_move_stepparent {
+  my ($self, $cgi, $article, $stepparents, $rindex) = @_;
+
+  my $cgi_uri = $self->{cfg}->entry('uri', 'cgi', '/cgi-bin');
+  my $images_uri = $self->{cfg}->entry('uri', 'images', '/images');
+  my $html = '';
+  my $url = $ENV{SCRIPT_NAME} . "?id=$article->{id}";
+  if ($cgi->param('_t')) {
+    $url .= "&_t=".$cgi->param('_t');
+  }
+  $url .= "#stepparents";
+  my $refreshto = CGI::escape($url);
+  if ($$rindex < $#$stepparents) {
+    $html .= <<HTML;
+<a href="$cgi_uri/admin/move.pl?stepchild=$article->{id}&id=$stepparents->[$$rindex]{parentId}&d=swap&other=$stepparents->[$$rindex+1]{parentId}&refreshto=$refreshto&all=1"><img src="$images_uri/admin/move_down.gif" width="17" height="13" border="0" alt="Move Down" align="absbottom"></a>
+HTML
+  }
+  if ($$rindex > 0) {
+    $html .= <<HTML;
+<a href="$cgi_uri/admin/move.pl?stepchild=$article->{id}&id=$stepparents->[$$rindex]{parentId}&d=swap&other=$stepparents->[$$rindex-1]{parentId}&refreshto=$refreshto&all=1"><img src="$images_uri/admin/move_up.gif" width="17" height="13" border="0" alt="Move Up" align="absbottom"></a>
+HTML
+  }
+  return $html;
+}
+
+sub tag_if_stepparent_possibles {
+  my ($article, $articles, $targs, $possibles) = @_;
+
+  if ($article->{id} && $article->{id} > 0) {
+    @$targs = $article->step_parents unless @$targs;
+    my %targs = map { $_->{id}, 1 } @$targs;
+    @$possibles = grep !$targs{$_->{id}}, $articles->all;
+  }
+  scalar @$possibles;
+}
+
+sub tag_stepparent_possibles {
+  my ($cgi, $article, $articles, $targs, $possibles) = @_;
+
+  if ($article->{id} && $article->{id} > 0) {
+    @$targs = $article->step_parents unless @$targs;
+    my %targs = map { $_->{id}, 1 } @$targs;
+    @$possibles = sort { lc $a->{title} cmp lc $b->{title} }
+      grep !$targs{$_->{id}}, $articles->all;
+  }
+  $cgi->popup_menu(-name=>'stepparent',
+                  -values => [ map $_->{id}, @$possibles ],
+                  -labels => { map { $_->{id}, "$_->{title} ($_->{id})" }
+                               @$possibles });
+}
+
+sub iter_files {
+  my ($article) = @_;
+
+  return unless $article->{id} && $article->{id} > 0;
+
+  return $article->files;
+}
+
+sub tag_edit_parent {
+  my ($article) = @_;
+
+  return '' unless $article->{id} && $article->{id} != -1;
+
+  return <<HTML;
+<a href="$ENV{SCRIPT_NAME}?id=$article->{parentid}">Edit parent</a> |
+HTML
+}
+
+sub tag_if_children {
+  my ($args, $acts, $funcname, $templater) = @_;
+
+  return $templater->perform($acts, 'ifChildren', '');
+}
+
+sub tag_movechild {
+  my ($self, $kids, $rindex) = @_;
+
+  $$rindex >=0 && $$rindex < @$kids
+    or return '** movechild can only be used in the children iterator **';
+
+  my $cgi_uri = $self->{cfg}->entry('uri', 'cgi', '/cgi-bin');
+  my $images_uri = $self->{cfg}->entry('uri', 'images', '/images');
+  my $html = '';
+  my $nomove = '<img src="/images/trans_pixel.gif" width="17" height="13" border="0" alt="" align="absbottom">';
+  my $id = $kids->[$$rindex]{id};
+  if ($$rindex < $#$kids) {
+    $html .= <<HTML;
+<a href="$cgi_uri/admin/move.pl?id=$id&d=down&edit=1&all=1"><img src="$images_uri/admin/move_down.gif" width="17" height="13" alt="Move Down" border="0" align="absbottom"></a>
+HTML
+  }
+  else {
+    $html .= $nomove;
+  }
+  if ($$rindex > 0) {
+    $html .= <<HTML;
+<a href="$cgi_uri/admin/move.pl?id=$id&d=up&edit=1&all=1"><img src="$images_uri/admin/move_up.gif" width="17" height="13" alt="Move Up" border="0" align="absbottom"></a>
+HTML
+  }
+  else {
+    $html .= $nomove;
+  }
+  $html =~ tr/\n//d;
+
+  $html;
+}
+
+sub tag_edit_link {
+  my ($args, $acts, $funcname, $templater) = @_;
+  my ($which, $name) = split / /, $args, 2;
+  $name ||= 'Edit';
+  my $gen_class;
+  if ($acts->{$which} 
+      && ($gen_class = $templater->perform($acts, $which, 'generator'))) {
+    eval "use $gen_class";
+    unless ($@) {
+      my $gen = $gen_class->new;
+      my $link = $gen->edit_link($templater->perform($acts, $which, 'id'));
+      return qq!<a href="$link">$name</a>!;
+    }
+  }
+  return '';
+}
+
+sub tag_imgmove {
+  my ($article, $rindex, $images) = @_;
+
+  $$rindex >= 0 && $$rindex < @$images 
+    or return '** imgmove can only be used in image iterator **';
+
+  my $html = '';
+  my $nomove = '<img src="/images/trans_pixel.gif" width="17" height="13" border="0" alt="" align="absbottom">';
+  my $image = $images->[$$rindex];
+  if ($$rindex > 0) {
+    $html .= <<HTML
+<a href="$ENV{SCRIPT_NAME}?id=$article->{id}&moveimgup=1&imageid=$image->{id}"><img src="/images/admin/move_up.gif" width="17" height="13" border="0" alt="Move Up" align="absbottom"></a>
+HTML
+  }
+  else {
+    $html .= $nomove;
+  }
+  if ($$rindex < $#$images) {
+    $html .= <<HTML
+<a href="$ENV{SCRIPT_NAME}?id=$article->{id}&moveimgdown=1&imageid=$image->{id}"><img src="/images/admin/move_down.gif" width="17" height="13" border="0" alt="Move Down" align="absbottom"></a>
+HTML
+  }
+  else {
+    $html .= $nomove;
+  }
+  return $html;
+}
+
+sub tag_movefiles {
+  my ($self, $article, $files, $rindex) = @_;
+
+  my $html = '';
+
+  $$rindex >= 0 && $$rindex < @$files
+    or return '** movefiles can only be used in the files iterator **';
+
+  my $nomove = '<img src="/images/trans_pixel.gif" width="17" height="13" border="0" alt="" align="absbottom">';
+  my $images_uri = $self->{cfg}->entry('uri', 'images', '/images');
+  
+  if ($$rindex < $#$files) {
+    $html .= <<HTML;
+<a href="$ENV{SCRIPT_NAME}?fileswap=1&id=$article->{id}&file1=$files->[$$rindex]{id}&file2=$files->[$$rindex+1]{id}"><img src="$images_uri/admin/move_down.gif" width="17" height="13" border="0" alt="Move Down" align="absbottom"></a>
+HTML
+  }
+  else {
+    $html .= $nomove;
+  }
+  if ($$rindex > 0) {
+    $html .= <<HTML;
+<a href="$ENV{SCRIPT_NAME}?fileswap=1&id=$article->{id}&file1=$files->[$$rindex]{id}&file2=$files->[$$rindex-1]{id}"><img src="$images_uri/admin/move_up.gif" width="17" height="13" border="0" alt="Move Up" align="absbottom"></a>
+HTML
+  }
+  else {
+    $html .= $nomove;
+  }
+  $html =~ tr/\n//d;
+  $html;
+}
+
+sub tag_old {
+  my ($article, $cgi, $args, $acts, $funcname, $templater) = @_;
+
+  my ($col, $func, $funcargs) = split ' ', $args, 3;
+  my $value = $cgi->param($col);
+  if (defined $value) {
+    return encode_entities($value);
+  }
+  else {
+    if ($func) {
+      return $templater->perform($acts, $func, $funcargs);
+    }
+    else {
+      $value = $article->{$args};
+      defined $value or $value = '';
+      return encode_entities($value);
+    }
+  }
+}
+
+sub tag_error_img {
+  my ($self, $errors, $args) = @_;
+
+  return '' unless $errors->{$args};
+  my $images_uri = $self->{cfg}->entry('uri', 'images', '/images');
+  my $encoded = encode_entities($errors->{$args});
+  return qq!<img src="$images_uri/admin/error.gif" alt="$encoded" title="$encoded" border="0" align="top">!; 
+}
+
+sub low_edit_tags {
+  my ($self, $acts, $request, $article, $articles, $msg, $errors) = @_;
+
+  my $cgi = $request->cgi;
+  $msg ||= '';
+  $errors ||= {};
+  if (keys %$errors && !$msg) {
+    # try to get the errors in the same order as the table
+    my @cols = $self->table_object($articles)->rowClass->columns;
+    my %work = %$errors;
+    my @out = grep defined, delete @work{@cols};
+
+    $msg = join "<br>", @out, values %work;
+  }
+  my @images;
+  my $image_index;
+  my @children;
+  my $child_index;
+  my %stepkids;
+  my $cfg = $self->{cfg};
+  my @allkids;
+  my $allkid_index;
+  my @possstepkids;
+  my @stepparents;
+  my $stepparent_index;
+  my @stepparent_targs;
+  my @stepparentpossibles;
+  my @files;
+  my $file_index;
+  return
+    (
+     BSE::Util::Tags->basic($acts, $cgi, $cfg),
+     BSE::Util::Tags->admin($acts, $cfg),
+     article => [ \&tag_hash, $article ],
+     old => [ \&tag_old, $article, $cgi ],
+     articleType => [ \&tag_art_type, $article->{level}, $cfg ],
+     parentType => [ \&tag_art_type, $article->{level}-1, $cfg ],
+     ifnew => [ \&tag_if_new, $article ],
+     list => [ \&tag_list, $self, $article, $articles, $cgi ],
+     script => $ENV{SCRIPT_NAME},
+     level => $article->{level},
+     checked => \&tag_checked,
+     DevHelp::Tags->make_iterator2
+     ([ \&iter_get_images, $article ], 'image', 'images', \@images, 
+      \$image_index),
+     imgmove => [ \&tag_imgmove, $article, \$image_index, \@images ],
+     message => $msg,
+     DevHelp::Tags->make_iterator2
+     ([ \&iter_get_kids, $article, $articles ], 
+      'child', 'children', \@children, \$child_index),
+     ifchildren => \&tag_if_children,
+     childtype => [ \&tag_art_type, $article->{level}+1, $cfg ],
+     ifHaveChildType => [ \&tag_if_have_child_type, $article->{level}, $cfg ],
+     movechild => [ \&tag_movechild, $self, \@children, \$child_index],
+     is => \&tag_is,
+     templates => [ \&tag_templates, $self, $article, $cfg, $cgi ],
+     titleImages => [ \&tag_title_images, $self, $article, $cfg, $cgi ],
+     editParent => [ \&tag_edit_parent, $article ],
+     DevHelp::Tags->make_iterator2
+     ([ \&iter_allkids, $article ], 'kid', 'kids', \@allkids, \$allkid_index),
+     ifStepKid => 
+     [ \&tag_if_step_kid, $article, \@allkids, \$allkid_index, \%stepkids ],
+     stepkid => [ \&tag_step_kid, $article, \@allkids, \$allkid_index, 
+                 \%stepkids ],
+     movestepkid => 
+     [ \&tag_move_stepkid, $self, $cgi, $article, \@allkids, \$allkid_index ],
+     possible_stepkids =>
+     [ \&tag_possible_stepkids, \%stepkids, $article, \@possstepkids, 
+       $articles, $cgi ],
+     ifPossibles => 
+     [ \&tag_if_possible_stepkids, \%stepkids, $article, \@possstepkids, 
+       $articles, $cgi ],
+     DevHelp::Tags->make_iterator2
+     ( [ \&iter_get_stepparents, $article ], 'stepparent', 'stepparents', 
+       \@stepparents, \$stepparent_index),
+     ifStepParents => \&tag_ifStepParents,
+     stepparent_targ => 
+     [ \&tag_stepparent_targ, $article, \@stepparent_targs, 
+       \$stepparent_index ],
+     movestepparent => 
+     [ \&tag_move_stepparent, $self, $cgi, $article, \@stepparents, 
+       \$stepparent_index ],
+     ifStepparentPossibles =>
+     [ \&tag_if_stepparent_possibles, $article, $articles, \@stepparent_targs, 
+       \@stepparentpossibles, ],
+     stepparent_possibles =>
+     [ \&tag_stepparent_possibles, $cgi, $article, $articles, 
+       \@stepparent_targs, \@stepparentpossibles, ],
+     DevHelp::Tags->make_iterator2
+     ([ \&iter_files, $article ], 'file', 'files', \@files, \$file_index ),
+     movefiles => [ \&tag_movefiles, $self, $article, \@files, \$file_index ],
+     edit => \&tag_edit_link,
+     error => [ \&tag_hash, $errors ],
+     error_img => [ \&tag_error_img, $self, $errors ],
+    );
+}
+
+sub edit_template {
+  my ($self, $article, $cgi) = @_;
+
+  my $base = $article->{level};
+  my $t = $cgi->param('_t');
+  if ($t && $t =~ /^\w+$/) {
+    $base = $t;
+  }
+  return $self->{cfg}->entry('admin templates', $base, 
+                            "admin/edit_$base");
+}
+
+sub add_template {
+  my ($self, $article, $cgi) = @_;
+
+  $self->edit_template($article, $cgi);
+}
+
+sub low_edit_form {
+  my ($self, $request, $article, $articles, $msg, $errors) = @_;
+
+  my $cgi = $request->cgi;
+  my %acts;
+  %acts = $self->low_edit_tags(\%acts, $request, $article, $articles, $msg,
+                             $errors);
+  my $template = $article->{id} ? 
+    $self->edit_template($article, $cgi) : $self->add_template($article, $cgi);
+
+  return BSE::Template->get_response($template, $request->cfg, \%acts);
+}
+
+sub edit_form {
+  my ($self, $request, $article, $articles, $msg, $errors) = @_;
+
+  return $self->low_edit_form($request, $article, $articles, $msg, $errors);
+}
+
+sub add_form {
+  my ($self, $request, $articles, $msg, $errors) = @_;
+
+  my $level;
+  my $cgi = $request->cgi;
+  my $parentid = $cgi->param('parentid');
+  if ($parentid) {
+    if ($parentid =~ /^\d+$/) {
+      if (my $parent = $self->get_parent($parentid, $articles)) {
+       $level = $parent->{level}+1;
+      }
+      else {
+       $parentid = undef;
+      }
+    }
+    elsif ($parentid eq "-1") {
+      $level = 1;
+    }
+  }
+  unless (defined $level) {
+    $level = $cgi->param('level');
+    undef $level unless defined $level && $level =~ /^\d+$/
+      && $level > 0 && $level < 100;
+    defined $level or $level = 3;
+  }
+  
+  my %article;
+  my @cols = Article->columns;
+  @article{@cols} = ('') x @cols;
+  $article{id} = '';
+  $article{parentid} = $parentid;
+  $article{level} = $level;
+  $article{body} = '<maximum of 64Kb>';
+  $article{listed} = 1;
+  $article{generator} = $self->generator;
+
+  return $self->low_edit_form($request, \%article, $articles, $msg, $errors);
+}
+
+sub generator { 'Generate::Article' }
+
+sub _validate_common {
+  my ($self, $data, $articles, $errors) = @_;
+
+  if (defined $data->{parentid} && $data->{parentid} =~ /^(?:-1|\d+)$/) {
+    unless ($data->{parentid} == -1 or 
+           $articles->getByPkey($data->{parentid})) {
+      $errors->{parentid} = "Selected parent article doesn't exist";
+    }
+  }
+  else {
+    $errors->{parentid} = "You need to select a valid parent";
+  }
+
+  if (exists $data->{template} && $data->{template} =~ /\.\./) {
+    $errors->{template} = "Please only select templates from the list provided";
+  }
+  
+}
+
+sub validate {
+  my ($self, $data, $articles, $rmsg, $errors) = @_;
+
+  $self->_validate_common($data, $articles, $errors);
+
+  return !keys %$errors;
+}
+
+sub validate_old {
+  my ($self, $data, $articles, $rmsg, $errors) = @_;
+
+  $self->_validate_common($data, $articles, $errors);
+
+  return !keys %$errors;
+}
+
+sub validate_parent {
+  1;
+}
+
+sub fill_new_data {
+  my ($self, $req, $data, $articles) = @_;
+
+  1;
+}
+
+sub make_link {
+  my ($self, $article) = @_;
+
+  my $article_uri = $self->{cfg}->entry('uri', 'articles', '/a');
+  my $link = "$article_uri/$article->{id}.html";
+  my $link_titles = $self->{cfg}->entryBool('basic', 'link_titles', 0);
+  if ($link_titles) {
+    (my $extra = lc $article->{title}) =~ tr/a-z0-9/_/sc;
+    $link .= "/".$extra;
+  }
+
+  $link;
+}
+
+sub save_new {
+  my ($self, $req, $articles) = @_;
+  
+  my $cgi = $req->cgi;
+  my %data;
+  my $table_object = $self->table_object($articles);
+  my @columns = $table_object->rowClass->columns;
+  $self->save_thumbnail($cgi, undef, \%data);
+  for my $name (@columns) {
+    $data{$name} = $cgi->param($name) if defined $cgi->param($name);
+  }
+
+  my $msg;
+  my %errors;
+  $self->validate(\%data, $articles, \$msg, \%errors)
+    or return $self->add_form($req, $articles, $msg, \%errors);
+
+  my $parent;
+  if ($data{parentid} > 0) {
+    $parent = $articles->getByPkey($data{parentid}) or die;
+  }
+
+  $self->validate_parent(\%data, $articles, $parent, \$msg)
+    or return $self->add_form($req, $articles, $msg);
+
+  $self->fill_new_data($req, \%data, $articles);
+  my $level = $parent ? $parent->{level}+1 : 1;
+  $data{displayOrder} ||= time;
+  $data{titleImage} ||= '';
+  $data{imagePos} = 'tr';
+  $data{release} = sql_date($data{release}) || now_sqldate();
+  $data{expire} = sql_date($data{expire}) || $Constants::D_99;
+  unless ($data{template}) {
+    $data{template} ||= 
+      $self->{cfg}->entry("children of $data{parentid}", 'template');
+    $data{template} ||=
+      $self->{cfg}->entry("level $level", 'template');
+  }
+  $data{link} ||= '';
+  $data{admin} ||= '';
+  if ($parent) {
+    $data{threshold} = $parent->{threshold}
+      if !defined $data{threshold} || $data{threshold} =~ /^\s*$/;
+    $data{summaryLength} = $parent->{summaryLength}
+      if !defined $data{summaryLength} || $data{summaryLength} =~ /^\s*$/;
+  }
+  else {
+    $data{threshold} = $self->{cfg}->entry("level $level", 'threshold', 5)
+      if !defined $data{threshold} || $data{threshold} =~ /^\s*$/;
+    $data{summaryLength} = 200
+      if !defined $data{summaryLength} || $data{summaryLength} =~ /^\s*$/;
+  }
+  $data{generator} = $self->generator;
+  $data{lastModified} = now_sqldate();
+  $data{level} = $level;
+  $data{listed} = 1 unless defined $data{listed};
+
+  shift @columns;
+  my $article = $table_object->add(@data{@columns});
+
+  # we now have an id - generate the links
+
+  my $cgi_uri = $self->{cfg}->entry('uri', 'cgi', '/cgi-bin');
+  $article->setAdmin("$cgi_uri/admin/admin.pl?id=$article->{id}");
+  $article->setLink($self->make_link($article));
+  $article->save();
+
+  my $urlbase = $self->{cfg}->entryVar('site', 'url');
+  return BSE::Template->get_refresh($urlbase . $article->{admin}, 
+                                   $self->{cfg});
+}
+
+sub fill_old_data {
+  my ($req, $article, $data) = @_;
+
+  for my $col (Article->columns) {
+    $article->{$col} = $data->{$col}
+      if exists $data->{$col} && $col ne 'id' && $col ne 'parentid';
+  }
+
+  return 1;
+}
+
+sub save {
+  my ($self, $req, $article, $articles) = @_;
+  
+  my $cgi = $req->cgi;
+  my %data;
+  for my $name ($article->columns) {
+    $data{$name} = $cgi->param($name) 
+      if defined($cgi->param($name)) and $name ne 'id' && $name ne 'parentid';
+  }
+  my %errors;
+  $self->validate_old($article, \%data, $articles, \%errors)
+    or return $self->edit_form($req, $article, $articles, undef, \%errors);
+  $self->fill_old_data($req, $article, \%data);
+  if (exists $article->{template} &&
+      $article->{template} =~ m|\.\.|) {
+    my $msg = "Please only select templates from the list provided";
+    return $self->edit_form($req, $article, $articles, $msg);
+  }
+
+  # reparenting
+  my $newparentid = $cgi->param('parentid');
+  if ($newparentid == $article->{parentid}) {
+    # nothing to do
+  }
+  elsif ($newparentid != -1) {
+    print STDERR "Reparenting...\n";
+    my $newparent = $articles->getByPkey($newparentid);
+    if ($newparent) {
+      if ($newparent->{level} != $article->{level}-1) {
+       # the article cannot become a child of itself or one of it's 
+       # children
+       if ($article->{id} == $newparentid 
+           || $self->is_descendant($article->{id}, $newparentid, $articles)) {
+         my $msg = "Cannot become a child of itself or of a descendant";
+         return $self->edit_form($req, $article, $articles, $msg);
+       }
+       my $shopid = $self->{cfg}->entryErr('articles', 'shop');
+       if ($self->is_descendant($article->{id}, $shopid, $articles)) {
+         my $msg = "Cannot become a descendant of the shop";
+         return $self->edit_form($req, $article, $articles, $msg);
+       }
+       my $msg;
+       $self->reparent($article, $newparentid, $articles, \$msg)
+         or return $self->edit_form($req, $article, $articles, $msg);
+      }
+      else {
+       # stays at the same level, nothing special
+       $article->{parentid} = $newparentid;
+      }
+    }
+    # else ignore it
+  }
+  else {
+    # becoming a section
+    my $msg;
+    $self->reparent($article, -1, $articles, \$msg)
+      or return $self->edit_form($req, $article, $articles, $msg);
+  }
+
+  $article->{listed} = $cgi->param('listed') if defined $cgi->param('listed');
+  $article->{release} = sql_date($cgi->param('release'));
+  $article->{expire} = sql_date($cgi->param('expire')) || $Constants::D_99;
+  $article->{lastModified} =  now_sqldate();
+  my $link_titles = $self->{cfg}->entryBool('basic', 'link_titles', 0);
+  if ($article->{id} != 1 && $article->{link} && $link_titles) {
+    (my $extra = lc $article->{title}) =~ tr/a-z0-9/_/sc;
+    my $article_uri = $self->{cfg}->entry('uri', 'articles', '/a');
+    $article->{link} = "$article_uri/$article->{id}.html/$extra";
+  }
+
+  $article->save();
+  my $urlbase = $self->{cfg}->entryVar('site', 'url');
+  return BSE::Template->get_refresh($urlbase . $article->{admin}, 
+                                   $self->{cfg});
+}
+
+sub sql_date {
+  my $str = shift;
+  my ($year, $month, $day);
+
+  # look for a date
+  if (($day, $month, $year) = ($str =~ m!(\d+)/(\d+)/(\d+)!)) {
+    $year += 2000 if $year < 100;
+
+    return sprintf("%04d-%02d-%02d", $year, $month, $day);
+  }
+  return undef;
+}
+
+sub reparent {
+  my ($self, $article, $newparentid, $articles, $rmsg) = @_;
+
+  my $newlevel;
+  if ($newparentid == -1) {
+    $newlevel = 1;
+  }
+  else {
+    my $parent = $articles->getByPkey($newparentid);
+    unless ($parent) {
+      $$rmsg = "Cannot get new parent article";
+      return;
+    }
+    $newlevel = $parent->{level} + 1;
+  }
+  # the caller will save this one
+  $article->{parentid} = $newparentid;
+  $article->{level} = $newlevel;
+  $article->{displayOrder} = time;
+
+  my @change = ( [ $article->{id}, $newlevel ] );
+  while (@change) {
+    my $this = shift @change;
+    my ($art, $level) = @$this;
+
+    my @kids = $articles->getBy(parentid=>$art);
+    push @change, map { [ $_->{id}, $level+1 ] } @kids;
+
+    for my $kid (@kids) {
+      $kid->{level} = $level+1;
+      $kid->save;
+    }
+  }
+
+  return 1;
+}
+
+# tests if $desc is a descendant of $art
+# where both are article ids
+sub is_descendant {
+  my ($self, $art, $desc, $articles) = @_;
+  
+  my @check = ($art);
+  while (@check) {
+    my $parent = shift @check;
+    $parent == $desc and return 1;
+    my @kids = $articles->getBy(parentid=>$parent);
+    push @check, map $_->{id}, @kids;
+  }
+
+  return 0;
+}
+
+sub save_thumbnail {
+  my ($self, $cgi, $original, $newdata) = @_;
+
+  unless ($original) {
+    @$newdata{qw/thumbImage thumbWidth thumbHeight/} = ('', 0, 0);
+  }
+  my $imagedir = $self->{cfg}->entry('paths', 'images', $Constants::IMAGEDIR);
+  if ($cgi->param('remove_thumb') && $original && $original->{thumbImage}) {
+    unlink("$imagedir/$original->{thumbImage}");
+    @$newdata{qw/thumbImage thumbWidth thumbHeight/} = ('', 0, 0);
+  }
+  my $image = $cgi->param('thumbnail');
+  if ($image && -s $image) {
+    # where to put it...
+    my $name = '';
+    $image =~ /([\w.-]+)$/ and $name = $1;
+    my $filename = time . "_" . $name;
+
+    use Fcntl;
+    my $counter = "";
+    $filename = time . '_' . $counter . '_' . $name
+      until sysopen( OUTPUT, "$imagedir/$filename", 
+                     O_WRONLY| O_CREAT| O_EXCL)
+        || ++$counter > 100;
+
+    fileno(OUTPUT) or die "Could not open image file: $!";
+    binmode OUTPUT;
+    my $buffer;
+
+    #no strict 'refs';
+
+    # read the image in from the browser and output it to our 
+    # output filehandle
+    print STDERR "\$image ",ref $image,"\n";
+    seek $image, 0, 0;
+    print OUTPUT $buffer while sysread $image, $buffer, 1024;
+
+    close OUTPUT
+      or die "Could not close image output file: $!";
+
+    use Image::Size;
+
+    if ($original && $original->{thumbImage}) {
+      #unlink("$imagedir/$original->{thumbImage}");
+    }
+    @$newdata{qw/thumbWidth thumbHeight/} = imgsize("$imagedir/$filename");
+    $newdata->{thumbImage} = $filename;
+  }
+}
+
+sub child_types {
+  my ($self, $article) = @_;
+
+  my $shopid = $self->{cfg}->entryErr('articles', 'shop');
+  if ($article && $article->{id} && $article->{id} == $shopid) {
+    return ( 'BSE::Edit::Catalog' );
+  }
+  return ( 'BSE::Edit::Article' );
+}
+
+sub add_stepkid {
+  my ($self, $req, $article, $articles) = @_;
+
+  my $cgi = $req->cgi;
+  require 'BSE/Admin/StepParents.pm';
+  eval {
+    my $childId = $cgi->param('stepkid');
+    defined $childId
+      or die "No stepkid supplied to add_stepkid";
+    $childId =~ /^\d+$/
+      or die "Invalid stepkid supplied to add_stepkid";
+    my $child = $articles->getByPkey($childId)
+      or die "Article $childId not found";
+    
+    use BSE::Util::Valid qw/valid_date/;
+    my $release = $cgi->param('release');
+    valid_date($release) or $release = undef;
+    my $expire = $cgi->param('expire');
+    valid_date($expire) or $expire = undef;
+  
+    my $newentry = 
+      BSE::Admin::StepParents->add($article, $child, $release, $expire);
+  };
+  if ($@) {
+    return $self->edit_form($req, $article, $articles, $@);
+  }
+  return $self->refresh($article, $cgi, 'step');
+}
+
+sub del_stepkid {
+  my ($self, $req, $article, $articles) = @_;
+
+  my $cgi = $req->cgi;
+  require 'BSE/Admin/StepParents.pm';
+  eval {
+    my $childId = $cgi->param('stepkid');
+    defined $childId
+      or die "No stepkid supplied to add_stepkid";
+    $childId =~ /^\d+$/
+      or die "Invalid stepkid supplied to add_stepkid";
+    my $child = $articles->getByPkey($childId)
+      or die "Article $childId not found";
+    
+    BSE::Admin::StepParents->del($article, $child);
+  };
+  
+  if ($@) {
+    return $self->edit_form($req, $article, $articles, $@);
+  }
+  return $self->refresh($article, $cgi, 'step');
+}
+
+sub save_stepkids {
+  my ($self, $req, $article, $articles) = @_;
+
+  my $cgi = $req->cgi;
+  require 'BSE/Admin/StepParents.pm';
+  my @stepcats = OtherParents->getBy(parentId=>$article->{id});
+  my %stepcats = map { $_->{parentId}, $_ } @stepcats;
+  my %datedefs = ( release => '2000-01-01', expire=>'2999-12-31' );
+  for my $stepcat (@stepcats) {
+    for my $name (qw/release expire/) {
+      my $date = $cgi->param($name.'_'.$stepcat->{childId});
+      if (defined $date) {
+       if ($date eq '') {
+         $date = $datedefs{$name};
+       }
+       elsif (valid_date($date)) {
+         use BSE::Util::SQL qw/date_to_sql/;
+         $date = date_to_sql($date);
+       }
+       else {
+         return $self->refresh($article, $cgi, '', "Invalid date '$date'");
+       }
+       $stepcat->{$name} = $date;
+      }
+    }
+    eval {
+      $stepcat->save();
+    };
+    $@ and return $self->refresh($article, $cgi, '', $@);
+  }
+  return $self->refresh($article, $cgi, 'step');
+}
+
+sub add_stepparent {
+  my ($self, $req, $article, $articles) = @_;
+
+  my $cgi = $req->cgi;
+  require 'BSE/Admin/StepParents.pm';
+  eval {
+    my $step_parent_id = $cgi->param('stepparent');
+    defined($step_parent_id)
+      or die "No stepparent supplied to add_stepparent";
+    int($step_parent_id) eq $step_parent_id
+      or die "Invalid stepcat supplied to add_stepcat";
+    my $step_parent = $articles->getByPkey($step_parent_id)
+      or die "Parnet $step_parent_id not found\n";
+
+    my $release = $cgi->param('release');
+    defined $release
+      or $release = "01/01/2000";
+    use BSE::Util::Valid qw/valid_date/;
+    $release eq '' or valid_date($release)
+      or die "Invalid release date";
+    my $expire = $cgi->param('expire');
+    defined $expire
+      or $expire = '31/12/2999';
+    $expire eq '' or valid_date($expire)
+      or die "Invalid expire data";
+  
+    my $newentry = 
+      BSE::Admin::StepParents->add($step_parent, $article, $release, $expire);
+  };
+  $@ and return $self->refresh($article, $cgi, 'step', $@);
+
+  return $self->refresh($article, $cgi, 'stepparents');
+}
+
+sub del_stepparent {
+  my ($self, $req, $article, $articles) = @_;
+
+  my $cgi = $req->cgi;
+  require 'BSE/Admin/StepParents.pm';
+  my $step_parent_id = $cgi->param('stepparent');
+  defined($step_parent_id)
+    or return $self->refresh($article, $cgi, 'stepparents', 
+                            "No stepparent supplied to add_stepcat");
+  int($step_parent_id) eq $step_parent_id
+    or return $self->refresh($article, $cgi, 'stepparents', 
+                            "Invalid stepparent supplied to add_stepparent");
+  my $step_parent = $articles->getByPkey($step_parent_id)
+    or return $self->refresh($article, $cgi, 'stepparent', 
+                            "Stepparent $step_parent_id not found");
+
+  eval {
+    BSE::Admin::StepParents->del($step_parent, $article);
+  };
+  $@ and return $self->refresh($article, $cgi, 'stepparents', $@);
+
+  return $self->refresh($article, $cgi, 'stepparents');
+}
+
+sub save_stepparents {
+  my ($self, $req, $article, $articles) = @_;
+
+  my $cgi = $req->cgi;
+
+  require 'BSE/Admin/StepParents.pm';
+  my @stepparents = OtherParents->getBy(childId=>$article->{id});
+  my %stepparents = map { $_->{parentId}, $_ } @stepparents;
+  my %datedefs = ( release => '2000-01-01', expire=>'2999-12-31' );
+  for my $stepparent (@stepparents) {
+    for my $name (qw/release expire/) {
+      my $date = $cgi->param($name.'_'.$stepparent->{parentId});
+      if (defined $date) {
+       if ($date eq '') {
+         $date = $datedefs{$name};
+       }
+       elsif (valid_date($date)) {
+         use BSE::Util::SQL qw/date_to_sql/;
+         $date = date_to_sql($date);
+       }
+       else {
+         return $self->refresh($article, $cgi, "Invalid date '$date'");
+       }
+       $stepparent->{$name} = $date;
+      }
+    }
+    eval {
+      $stepparent->save();
+    };
+    $@ and return $self->refresh($article, $cgi, '', $@);
+  }
+
+  return $self->refresh($article, $cgi, 'stepparents');
+}
+
+sub refresh {
+  my ($self, $article, $cgi, $name, $message, $extras) = @_;
+
+  my $urlbase = $self->{cfg}->entryVar('site', 'url');
+  my $url = "$urlbase$ENV{SCRIPT_NAME}?id=$article->{id}";
+  $url .= "&message=" . CGI::escape($message) if $message;
+  if ($cgi->param('_t')) {
+    $url .= "&_t=".CGI::escape($cgi->param('_t'));
+  }
+  $url .= $extras if defined $extras;
+  $url .= "#$name" if $name;
+
+  return BSE::Template->get_refresh($url, $self->{cfg});
+}
+
+sub show_images {
+  my ($self, $req, $article, $articles, $msg) = @_;
+
+  my %acts;
+  %acts = $self->low_edit_tags(\%acts, $req, $article, $articles, $msg);
+  my $template = 'admin/article_img';
+
+  return BSE::Template->get_response($template, $req->cfg, \%acts);
+}
+
+sub save_image_changes {
+  my ($self, $req, $article, $articles) = @_;
+
+  my $cgi = $req->cgi;
+  my $image_pos = $cgi->param('imagePos');
+  if ($image_pos 
+      && $image_pos =~ /^(?:tl|tr|bl|br)$/
+      && $image_pos ne $article->{imagePos}) {
+    $article->{imagePos} = $image_pos;
+    $article->save;
+  }
+  my @images = $article->images;
+
+  my $changed;
+  my @alt = $cgi->param('alt');
+  if (@alt) {
+    ++$changed;
+    for my $index (0..$#images) {
+      $index < @alt or last;
+      $images[$index]{alt} = $alt[$index];
+    }
+  }
+  my @urls = $cgi->param('url');
+  if (@urls) {
+    ++$changed;
+    for my $index (0..$#images) {
+      $index < @urls or next;
+      $images[$index]{url} = $urls[$index];
+    }
+  }
+  if ($changed) {
+    for my $image (@images) {
+      $image->save;
+    }
+  }
+  return $self->refresh($article, $cgi, undef, undef, '&showimage=1');
+}
+
+sub add_image {
+  my ($self, $req, $article, $articles) = @_;
+
+  my $cgi = $req->cgi;
+
+  my $image = $cgi->param('image');
+  unless ($image) {
+    return $self->show_images($req, $article, $articles,
+                             'Enter or select the name of an image file on your machine');
+  }
+  if (-z $image) {
+    return $self->show_images($req, $article, $articles,
+                             'Image file is empty');
+  }
+  my $imagename = $image;
+  $imagename .= ''; # force it into a string
+  my $basename = '';
+  $imagename =~ /([\w.-]+)$/ and $basename = $1;
+
+  # create a filename that we hope is unique
+  my $filename = time. '_'. $basename;
+
+  # for the sysopen() constants
+  use Fcntl;
+
+  my $imagedir = $req->cfg->entry('paths', 'images', $Constants::IMAGEDIR);
+  # loop until we have a unique filename
+  my $counter="";
+  $filename = time. '_' . $counter . '_' . $basename 
+    until sysopen( OUTPUT, "$imagedir/$filename", O_WRONLY| O_CREAT| O_EXCL)
+      || ++$counter > 100;
+
+  fileno(OUTPUT) or die "Could not open image file: $!";
+
+  # for OSs with special text line endings
+  binmode OUTPUT;
+
+  my $buffer;
+
+  no strict 'refs';
+
+  # read the image in from the browser and output it to our output filehandle
+  print OUTPUT $buffer while read $image, $buffer, 1024;
+
+  # close and flush
+  close OUTPUT
+    or die "Could not close image file $filename: $!";
+
+  use Image::Size;
+
+
+  my($width,$height) = imgsize("$imagedir/$filename");
+
+  my $alt = $cgi->param('altIn');
+  defined $alt or $alt = '';
+  my $url = $cgi->param('url');
+  defined $url or $url = '';
+  my %image =
+    (
+     articleId => $article->{id},
+     image => $filename,
+     alt=>$alt,
+     width=>$width,
+     height => $height,
+     url => $url,
+     displayOrder=>time,
+    );
+  require Images;
+  my @cols = Image->columns;
+  shift @cols;
+  my $imageobj = Images->add(@image{@cols});
+  
+  return $self->refresh($article, $cgi, undef, undef, '&showimage=1');
+}
+
+# remove an image
+sub remove_img {
+  my ($self, $req, $article, $articles, $imageid) = @_;
+
+  $imageid or die;
+
+  my @images = $article->images();
+  my ($image) = grep $_->{id} == $imageid, @images
+    or return $self->show_images($req, $article, $articles, "No such image");
+  my $imagedir = $req->cfg->entry('paths', 'images', $Constants::IMAGEDIR);
+  unlink "$imagedir$image->{image}" if !$image->{id};
+  $image->remove;
+
+  return $self->refresh($article, $req->cgi, undef, undef, '&showimage=1');
+}
+
+sub move_img_up {
+  my ($self, $req, $article, $articles) = @_;
+
+  my $imageid = $req->cgi->param('imageid');
+  my @images = $article->images;
+  my ($imgindex) = grep $images[$_]{id} == $imageid, 0..$#images
+    or return $self->show_images($req, $article, $articles, "No such image");
+  $imgindex > 0
+    or return $self->show_images($req, $article, $articles, "Image is already at the top");
+  my ($to, $from) = @images[$imgindex-1, $imgindex];
+  ($to->{displayOrder}, $from->{displayOrder}) =
+    ($from->{displayOrder}, $to->{displayOrder});
+  $to->save;
+  $from->save;
+
+  return $self->refresh($article, $req->cgi, undef, undef, '&showimage=1');
+}
+
+sub move_img_down {
+  my ($self, $req, $article, $articles) = @_;
+
+  my $imageid = $req->cgi->param('imageid');
+  my @images = $article->images;
+  my ($imgindex) = grep $images[$_]{id} == $imageid, 0..$#images
+    or return $self->show_images($req, $article, $articles, "No such image");
+  $imgindex < $#images
+    or return $self->show_images($req, $article, $articles, "Image is already at the end");
+  my ($to, $from) = @images[$imgindex+1, $imgindex];
+  ($to->{displayOrder}, $from->{displayOrder}) =
+    ($from->{displayOrder}, $to->{displayOrder});
+  $to->save;
+  $from->save;
+
+  return $self->refresh($article, $req->cgi, undef, undef, '&showimage=1');
+}
+
+sub get_article {
+  my ($self, $articles, $article) = @_;
+
+  return $article;
+}
+
+sub table_object {
+  my ($self, $articles) = @_;
+
+  $articles;
+}
+
+my %types =
+  (
+   qw(
+   pdf  application/pdf
+   txt  text/plain
+   htm  text/html
+   html text/html
+   gif  image/gif
+   jpg  image/jpeg
+   jpeg image/jpeg
+   doc  application/msword
+   rtf  application/rtf
+   zip  application/zip
+   png  image/png
+   bmp  image/bmp
+   tif  image/tiff
+   tiff image/tiff
+   sgm  text/sgml
+   sgml text/sgml
+   xml  text/xml
+   mov  video/quicktime
+   )
+  );
+
+sub _refresh_filelist {
+  my ($self, $req, $article) = @_;
+
+  return $self->refresh($article, $req->cgi, undef, undef, '&filelist=1');
+}
+
+sub filelist {
+  my ($self, $req, $article, $articles, $msg) = @_;
+
+  my %acts;
+  %acts = $self->low_edit_tags(\%acts, $req, $article, $articles, $msg);
+  my $template = 'admin/filelist';
+
+  return BSE::Template->get_response($template, $req->cfg, \%acts);
+}
+
+sub fileadd {
+  my ($self, $req, $article, $articles) = @_;
+
+  my %file;
+  my $cgi = $req->cgi;
+  require ArticleFile;
+  my @cols = ArticleFile->columns;
+  shift @cols;
+  for my $col (@cols) {
+    if (defined $cgi->param($col)) {
+      $file{$col} = $cgi->param($col);
+    }
+  }
+  
+  $file{forSale} = 0 + exists $file{forSale};
+  $file{articleId} = $article->{id};
+  $file{download} = 0 + exists $file{download};
+  $file{requireUser} = 0 + exists $file{requireUser};
+
+  my $downloadPath = $self->{cfg}->entryVar('paths', 'downloads');
+
+  # build a filename
+  my $file = $cgi->param('file');
+  unless ($file) {
+    return $self->filelist($req, $article, $articles,
+                          "Enter or select the name of a file on your machine");
+  }
+  if (-z $file) {
+    return $self->filelist($req, $article, $articles,
+                          message=>"File is empty");
+  }
+
+  unless ($file{contentType}) {
+    unless ($file =~ /\.([^.]+)$/) {
+      $file{contentType} = "application/octet-stream";
+    }
+    unless ($file{contentType}) {
+      my $ext = lc $1;
+      my $type = $types{$ext};
+      unless ($type) {
+       $type = $self->{cfg}->entry('extensions', $ext)
+         || $self->{cfg}->entry('extensions', ".$ext")
+           || "application/octet-stream";
+      }
+      $file{contentType} = $type;
+    }
+  }
+  
+  my $basename = '';
+  $file =~ /([\w.-]+)$/ and $basename = $1;
+
+  my $filename = time. '_'. $basename;
+
+  # for the sysopen() constants
+  use Fcntl;
+
+  # loop until we have a unique filename
+  my $counter="";
+  $filename = time. '_' . $counter . '_' . $basename 
+    until sysopen( OUTPUT, "$downloadPath/$filename", 
+                  O_WRONLY| O_CREAT| O_EXCL)
+      || ++$counter > 100;
+
+  fileno(OUTPUT) or die "Could not open file: $!";
+
+  # for OSs with special text line endings
+  binmode OUTPUT;
+
+  my $buffer;
+
+  no strict 'refs';
+
+  # read the image in from the browser and output it to our output filehandle
+  print OUTPUT $buffer while read $file, $buffer, 8192;
+
+  # close and flush
+  close OUTPUT
+    or die "Could not close file $filename: $!";
+
+  use BSE::Util::SQL qw/now_datetime/;
+  $file{filename} = $filename;
+  $file{displayName} = $basename;
+  $file{sizeInBytes} = -s $file;
+  $file{displayOrder} = time;
+  $file{whenUploaded} = now_datetime();
+
+  require ArticleFiles;
+  my $fileobj = ArticleFiles->add(@file{@cols});
+
+  $self->_refresh_filelist($req, $article);
+}
+
+sub fileswap {
+  my ($self, $req, $article, $articles) = @_;
+
+  my $cgi = $req->cgi;
+  my $id1 = $cgi->param('file1');
+  my $id2 = $cgi->param('file2');
+
+  if ($id1 && $id2) {
+    my @files = $article->files;
+    
+    my ($file1) = grep $_->{id} == $id1, @files;
+    my ($file2) = grep $_->{id} == $id2, @files;
+    
+    if ($file1 && $file2) {
+      ($file1->{displayOrder}, $file2->{displayOrder})
+       = ($file2->{displayOrder}, $file1->{displayOrder});
+      $file1->save;
+      $file2->save;
+    }
+  }
+
+  $self->_refresh_filelist($req, $article);
+}
+
+sub filedel {
+  my ($self, $req, $article, $articles) = @_;
+
+  my $cgi = $req->cgi;
+  my $fileid = $cgi->param('file');
+  if ($fileid) {
+    my @files = $article->files;
+
+    my ($file) = grep $_->{id} == $fileid, @files;
+
+    if ($file) {
+      my $downloadPath = $req->cfg->entryErr('paths', 'downloads');
+      my $filename = $downloadPath . "/" . $file->{filename};
+      my $debug_del = $req->cfg->entryBool('debug', 'file_unlink', 0);
+      if ($debug_del) {
+       unlink $filename
+         or print STDERR "Error deleting $filename: $!\n";
+      }
+      else {
+       unlink $filename;
+      }
+      $file->remove();
+    }
+  }
+
+  $self->_refresh_filelist($req, $article);
+}
+
+sub filesave {
+  my ($self, $req, $article) = @_;
+
+  my @files = $article->files;
+
+  my $cgi = $req->cgi;
+  for my $file (@files) {
+    if (defined $cgi->param("description_$file->{id}")) {
+      $file->{description} = $cgi->param("description_$file->{id}");
+      if (my $type = $cgi->param("contentType_$file->{id}")) {
+       $file->{contentType} = $type;
+      }
+      $file->{download} = 0 + defined $cgi->param("download_$file->{id}");
+      $file->{forSale} = 0 + defined $cgi->param("forSale_$file->{id}");
+      $file->{requireUser} = 0 + defined $cgi->param("requireUser_$file->{id}");
+      $file->save;
+    }
+  }
+
+  $self->_refresh_filelist($req, $article);
+}
+
+1;
+
+=head1 NAME
+
+  BSE::Edit::Article - editing functionality for BSE articles
+
+=head1 AUTHOR
+
+Tony Cook <tony@develop-help.com>
+
+=head1 REVISION 
+
+$Revision$
+
+=cut
diff --git a/site/cgi-bin/modules/BSE/Edit/Base.pm b/site/cgi-bin/modules/BSE/Edit/Base.pm
new file mode 100644 (file)
index 0000000..ca647f0
--- /dev/null
@@ -0,0 +1,11 @@
+package BSE::Edit::Base;
+use strict;
+
+# one day I might put something useful here
+sub new {
+  my ($class, %parms) = @_;
+
+  return bless \%parms, $class;
+}
+
+1;
diff --git a/site/cgi-bin/modules/BSE/Edit/Catalog.pm b/site/cgi-bin/modules/BSE/Edit/Catalog.pm
new file mode 100644 (file)
index 0000000..db157d9
--- /dev/null
@@ -0,0 +1,87 @@
+package BSE::Edit::Catalog;
+use strict;
+use base 'BSE::Edit::Article';
+
+sub base_template_dirs {
+  return ( "catalog" );
+}
+
+sub extra_templates {
+  my ($self, $article) = @_;
+
+  my @extras = $self->SUPER::extra_templates($article);
+  my $basedir = $self->{cfg}->entry('paths', 'templates', $Constants::TMPLDIR);
+  push @extras, 'catalog.tmpl' if -f "$basedir/catalog.tmpl";
+
+  return @extras;
+}
+
+#  sub low_edit_tags {
+#    my ($self, $acts, $req, $article, $articles, $msg) = @_;
+
+#    return 
+#      (
+#       $self->SUPER::low_edit_tags($acts, $req, $article, $articles, $msg),
+#      );
+#  }
+
+sub edit_template { 
+  my ($self, $article, $cgi) = @_;
+
+  my $base = 'catalog';
+  my $t = $cgi->param('_t');
+  if ($t && $t =~ /^\w+$/) {
+    $base = $t;
+  }
+  return $self->{cfg}->entry('admin templates', $base, 
+                            "admin/edit_$base");
+}
+
+sub generator { "Generate::Catalog" }
+
+sub validate_parent {
+  my ($self, $data, $articles, $parent, $rmsg) = @_;
+
+  my $shopid = $self->{cfg}->entryErr('articles', 'shop');
+  unless ($parent && 
+         ($parent->{id} == $shopid 
+          || $parent->{generator} == 'Generate::Catalog')) {
+    $$rmsg = "Catalogs must be in the shop";
+    return;
+  }
+
+  return $self->SUPER::validate_parent($data, $articles, $parent, $rmsg);
+}
+
+sub possible_parents {
+  my ($self, $article, $articles) = @_;
+
+  my %labels;
+  my @values;
+
+  my $shopid = $self->{cfg}->entryErr('articles', 'shop');
+  # the parents of a catalog can be other catalogs or the shop
+  my $shop = $articles->getByPkey($shopid);
+  my @work = [ $shopid, $shop->{title} ];
+  while (@work) {
+    my ($id, $title) = @{pop @work};
+    push(@values, $id);
+    $labels{$id} = $title;
+    push @work, map [ $_->{id}, $title.' / '.$_->{title} ],
+    sort { $b->{displayOrder} <=> $a->{displayOrder} }
+      grep $_->{generator} eq 'Generate::Catalog', 
+      $articles->getBy(parentid=>$id);
+  }
+
+  return (\@values, \%labels);
+}
+
+sub make_link {
+  my ($self, $article) = @_;
+
+  my $shop_uri = $self->{cfg}->entry('uri', 'shop', '/shop');
+  my $urlbase = $self->{cfg}->entryVar('site', 'secureurl');
+  return $urlbase.$shop_uri."/shop$article->{id}.html";
+}
+
+1;
diff --git a/site/cgi-bin/modules/BSE/Edit/Product.pm b/site/cgi-bin/modules/BSE/Edit/Product.pm
new file mode 100644 (file)
index 0000000..bed4664
--- /dev/null
@@ -0,0 +1,206 @@
+package BSE::Edit::Product;
+use strict;
+use base 'BSE::Edit::Article';
+use Products;
+use HTML::Entities;
+
+my %money_fields =
+  (
+   retailPrice => "Retail price",
+   wholesalePrice => "Wholesale price",
+   gst => "GST",
+  );
+
+sub generator { 'Generate::Product' }
+
+sub base_template_dirs {
+  return ( "products" );
+}
+
+sub extra_templates {
+  my ($self, $article) = @_;
+
+  my @extras = $self->SUPER::extra_templates($article);
+  my $basedir = $self->{cfg}->entry('paths', 'templates', $Constants::TMPLDIR);
+  push @extras, 'shopitem.tmpl' if -f "$basedir/shopitem.tmpl";
+
+  return @extras;
+}
+
+sub hash_tag {
+  my ($article, $arg) = @_;
+
+  my $value = $article->{$arg};
+  defined $value or $value = '';
+
+  return encode_entities($value);
+}
+
+sub low_edit_tags {
+  my ($self, $acts, $req, $article, $articles, $msg, $errors) = @_;
+  return 
+    (
+     product => [ \&hash_tag, $article ],
+     $self->SUPER::low_edit_tags($acts, $req, $article, $articles, $msg,
+                               $errors),
+     alloptions => join(",", sort keys %Constants::SHOP_PRODUCT_OPTS),
+    );
+}
+
+sub edit_template { 
+  my ($self, $article, $cgi) = @_;
+
+  my $base = 'product';
+  my $t = $cgi->param('_t');
+  if ($t && $t =~ /^\w+$/) {
+    $base = $t;
+  }
+  return $self->{cfg}->entry('admin templates', $base, 
+                            "admin/edit_$base");
+}
+
+sub add_template { 
+  my ($self, $article, $cgi) = @_;
+
+  return $self->{cfg}->entry('admin templates', 'add_product', 
+                            'admin/add_product');
+}
+
+sub validate_parent {
+  my ($self, $data, $articles, $parent, $rmsg) = @_;
+
+  my $shopid = $self->{cfg}->entryErr('articles', 'shop');
+  unless ($parent && 
+         $parent->{generator} eq 'Generate::Catalog') {
+    $$rmsg = "Products must be in a catalog (not $parent->{generator})";
+    return;
+  }
+
+  return $self->SUPER::validate_parent($data, $articles, $parent, $rmsg);
+}
+
+sub _validate_common {
+  my ($self, $data, $articles, $errors) = @_;
+
+  for my $col (keys %money_fields) {
+    my $value = $data->{$col};
+    unless ($value =~ /^\d+(\.\d{1,2})?\s*/) {
+      $errors->{$col} = "$money_fields{$col} invalid";
+    }
+  }
+
+  my @bad_opts =grep !$Constants::SHOP_PRODUCT_OPTS{$_}, 
+  split /,/, $data->{options};
+  if (@bad_opts) {
+    $errors->{options} = "Bad product options '". join(",", @bad_opts)."' entered";
+  }
+
+  return !keys %$errors;
+}
+
+sub validate {
+  my ($self, $data, $articles, $rmsg, $errors) = @_;
+
+  my $ok = $self->SUPER::validate($data, $articles, $rmsg, $errors);
+  $self->_validate_common($data, $articles, $errors);
+
+  for my $field (qw(title summary body)) {
+    unless ($data->{$field} =~ /\S/) {
+      $errors->{$field} = "No $field entered";
+    }
+  }
+
+  return $ok && !keys %$errors;
+}
+
+sub validate_old {
+  my ($self, $article, $data, $articles, $rmsg, $errors) = @_;
+
+  $self->SUPER::validate($data, $articles, $rmsg, $errors)
+    or return;
+  
+  return !keys %$errors;
+}
+
+sub possible_parents {
+  my ($self, $article, $articles) = @_;
+
+  my %labels;
+  my @values;
+
+  my $shopid = $self->{cfg}->entryErr('articles', 'shop');
+  # the parents of a catalog can be other catalogs or the shop
+  my $shop = $articles->getByPkey($shopid);
+  my @work = [ $shopid, $shop->{title} ];
+  while (@work) {
+    my ($id, $title) = @{pop @work};
+    push(@values, $id);
+    $labels{$id} = $title;
+    push @work, map [ $_->{id}, $title.' / '.$_->{title} ],
+    sort { $b->{displayOrder} <=> $a->{displayOrder} }
+      grep $_->{generator} eq 'Generate::Catalog', 
+      $articles->getBy(parentid=>$id);
+  }
+  shift @values;
+  delete $labels{$shopid};
+  return (\@values, \%labels);
+}
+
+sub table_object {
+  my ($self, $articles) = @_;
+
+  'Products';
+}
+
+sub get_article {
+  my ($self, $articles, $article) = @_;
+
+  return Products->getByPkey($article->{id});
+}
+
+sub make_link {
+  my ($self, $article) = @_;
+
+  my $shop_uri = $self->{cfg}->entry('uri', 'shop', '/shop');
+  my $urlbase = $self->{cfg}->entryVar('site', 'secureurl');
+  return $urlbase.$shop_uri."/shop$article->{id}.html";
+}
+
+sub _fill_product_data {
+  my ($self, $req, $data, $src) = @_;
+
+  for my $money_col (qw(retailPrice wholesalePrice gst)) {
+    if (exists $src->{$money_col}) {
+      if ($src->{$money_col} =~ /^\d+(\.\d\d)?\s*/) {
+       $data->{$money_col} = 100 * $src->{$money_col};
+      }
+      else {
+       $data->{$money_col} = 0;
+      }
+    }
+  }
+  if (exists $src->{leadTime}) {
+    $src->{leadTime} =~ /^\d+\s*$/
+      or $src->{leadTime} = 0;
+    $data->{leadTime} = $src->{leadTime};
+  }
+}
+
+sub fill_new_data {
+  my ($self, $req, $data, $articles) = @_;
+
+  $self->_fill_product_data($req, $data, $data);
+
+  return $self->SUPER::fill_new_data($req, $data, $articles);
+}
+
+sub fill_old_data {
+  my ($self, $req, $article, $src) = @_;
+
+  $self->_fill_product_data($req, $article, $src);
+
+  return $self->SUPER::fill_old_data($req, $article, $src);
+}
+
+1;
diff --git a/site/cgi-bin/modules/BSE/Request.pm b/site/cgi-bin/modules/BSE/Request.pm
new file mode 100644 (file)
index 0000000..c212ace
--- /dev/null
@@ -0,0 +1,43 @@
+package BSE::Request;
+use strict;
+use BSE::Session;
+use CGI;
+use BSE::Cfg;
+
+sub new {
+  my ($class) = @_;
+
+  my $self =
+    bless {
+          cfg => BSE::Cfg->new,
+          cgi => CGI->new,
+         }, $class;
+  my %session;
+  BSE::Session->tie_it(\%session, $self->{cfg});
+  $self->{session} = \%session;
+
+  $self;
+}
+
+sub cgi {
+  return $_[0]{cgi};
+}
+
+sub cfg {
+  return $_[0]{cfg};
+}
+
+sub session {
+  return $_[0]{session};
+}
+
+sub extra_headers { return }
+
+sub DESTROY {
+  my ($self) = @_;
+  if ($self->{session}) {
+    undef $self->{session};
+  }
+}
+
+1;
index ec01ebc..110eb33 100644 (file)
@@ -15,16 +15,46 @@ sub get_page {
   $obj->show_page($base, $file, $acts);
 }
 
-sub show_page {
-  my ($class, $template, $cfg, $acts) = @_;
+sub html_type {
+  my ($class, $cfg) = @_;
 
   my $type = "text/html";
   my $charset = $cfg->entry('html', 'charset');
   $charset = 'iso-8859-1' unless defined $charset;
-  $type .= "; charset=$charset";
+  return $type . "; charset=$charset";
+}
+
+sub show_page {
+  my ($class, $template, $cfg, $acts) = @_;
+
+  my $type = $class->html_type($cfg);
 
   print "Content-Type: $type\n\n";
   print $class->get_page($template, $cfg, $acts);
 }
 
+sub get_response {
+  my ($class, $template, $cfg, $acts) = @_;
+
+  my $result =
+    {
+     type => $class->html_type($cfg),
+     content => scalar($class->get_page($template, $cfg, $acts)),
+    };
+  push @{$result->{headers}}, "Content-Length: ".length($result->{content});
+
+  $result;
+}
+
+sub get_refresh {
+  my ($class, $url, $cfg) = @_;
+
+  return
+    {
+     type=>$class->html_type($cfg),
+     content=>"<html></html>",
+     headers=>[ qq/Refresh: 0; url="$url"/ ],
+    };
+}
+
 1;
index a821c97..89c94bc 100644 (file)
@@ -89,6 +89,7 @@ sub static {
        my ($arg, $acts, $name, $templater) = @_;
        (my ($left, $right) = DevHelp::Tags->get_parms($arg, $acts, $templater)) == 2
         or die; # leaves if in place
+       #print STDERR "ifEq >$left< >$right<\n";
        $left eq $right;
      },
      ifMatch =>
index dcc844b..c2c8de1 100644 (file)
@@ -2,12 +2,25 @@ package BSE::Util::Valid;
 use strict;
 use vars qw(@EXPORT_OK @ISA);
 require 'Exporter.pm';
-@EXPORT_OK = qw/valid_date/;
+@EXPORT_OK = qw/valid_date convert_date_to_sql/;
 @ISA = qw/Exporter/;
 
 sub valid_date {
   $_[0] =~ m!^\d+[/-]\d+[/-]\d+$!;
 }
 
+sub convert_date_to_sql {
+  my ($date) = @_;
+  my ($day, $month, $year) = $date =~  m!^\s*(\d+)[/-](\d+)[/-](\d+)$!
+    or return;
+
+  if ($year < 100) {
+    $year += $year < 50 ? 2000 : 1900;
+  }
+
+  print STDERR "date: $year-$month-$day\n";
+  return "$year-$month-$day";
+}
+
 1;
 
index ef9f76b..1e1d5c6 100644 (file)
@@ -59,8 +59,7 @@ sub generate_low {
 <td><form action="$ADMIN_URI">
 <input type=submit value="Admin menu">
 </form></td>
-<td><form action="$CGI_URI/admin/shopadmin.pl">
-<input type=hidden name="add_product" value=1>
+<td><form action="$CGI_URI/admin/add.pl">
 <input type=hidden name="parentid" value="$article->{id}">
 <input type=submit value="Add product"></form></td>
 <td><form action="$CGI_URI/admin/shopadmin.pl">
index c514beb..f1996aa 100644 (file)
@@ -10,7 +10,7 @@ use Carp qw(confess);
 
 sub edit_link {
   my ($self, $id) = @_;
-  return "/cgi-bin/admin/shopadmin.pl?id=$id&edit_product=1";
+  return "/cgi-bin/admin/add.pl?id=$id";
 }
 
 sub generate {
@@ -44,8 +44,8 @@ sub baseActs {
        my $html = <<HTML;
 <table>
 <tr>
-<td><form action="$CGI_URI/admin/shopadmin.pl">
-<input type=hidden name="edit_product" value=1>
+<td><form action="$CGI_URI/admin/add.pl">
+<input type=hidden name="edit" value=1>
 <input type=hidden name=id value=$product->{id}>
 <input type=submit value="Edit Product"></form></td>
 <td><form action="$ADMIN_URI">
index 0173964..bb8655f 100644 (file)
@@ -6,7 +6,7 @@ use vars qw/@ISA/;
 @ISA = qw/Squirrel::Row/;
 
 sub columns {
-  return qw/id articleId image alt width height url/;
+  return qw/id articleId image alt width height url displayOrder/;
 }
 
 1;
index 3b97349..d7cd601 100644 (file)
@@ -3,6 +3,7 @@ package Images;
 use Squirrel::Table;
 use vars qw(@ISA $VERSION);
 @ISA = qw(Squirrel::Table);
+use Image;
 
 sub rowClass {
   return 'Image';
index 430cc31..d01f1cd 100644 (file)
@@ -3,6 +3,7 @@ package Products;
 use Squirrel::Table;
 use vars qw(@ISA $VERSION);
 @ISA = qw(Squirrel::Table);
+use Product;
 
 sub rowClass {
   return 'Product';
index db018dd..ea8694c 100644 (file)
@@ -43,6 +43,10 @@ sub perform {
     else {
       $value = $action;
     }
+    unless (defined $value) {
+      use Carp 'cluck';
+      cluck "** undefined value returned by $func $args tag";
+    }
     return $fmt ? $acts->{_format}->($value, $fmt) : $value;
   }
   for my $match (keys %$acts) {
@@ -65,7 +69,7 @@ sub perform {
     my ($newfunc, $newargs) = split /\s+/, $temp, 2;
     $newargs = '' if !defined $newargs;
     if (exists $acts->{$newfunc}
-       and defined(my $value = $acts->{$newfunc}->($newargs))) {
+       and defined(my $value = $self->perform($acts, $newfunc, $newargs))) {
       # work out a summary
       return $value if length($value) < $size;
       $value = substr($value, 0, $size);
@@ -139,6 +143,7 @@ sub cond {
     my $msg = $@;
     $msg =~ /^ENOIMPL\b/
       and return $orig;
+    print STDERR "Eval error in cond: $msg\n";
     $msg =~ s/([<>&])/"&#".ord($1).";"/ge;
     return "<!-- ** $msg ** -->";
   }
@@ -146,6 +151,14 @@ sub cond {
   return $result;
 }
 
+sub tag_param {
+  my ($params, $arg) = @_;
+
+  exists $params->{$arg} or return '';
+
+  $params->{$arg};
+}
+
 sub replace_template {
   my ($self, $template, $acts, $iter) = @_;
 
@@ -174,14 +187,16 @@ sub replace_template {
         $wraptext =~ s/<:\s*wrap\s+here\s*:>/$template/i
           and $template = $wraptext
             or last;
-       
-       while ($params =~ s/^\s*(\w+)\s*=>\s*\"([^\"]+)\"//
-              || $params =~ s/^\s*(\w+)\s*=>\s*([^\s,]+)//) {
-         $params{$1} = $2;
-         $params =~ s/\s*,\s*//;
+
+       unless (defined $params) {
+         while ($params =~ s/^\s*(\w+)\s*=>\s*\"([^\"]+)\"//
+                || $params =~ s/^\s*(\w+)\s*=>\s*([^\s,]+)//) {
+           $params{$1} = $2;
+           $params =~ s/\s*,\s*//;
+         }
+         $params =~ /^\s*$/
+           or print STDERR "WARNING: Extra data after parameters '$params'\n";
        }
-       $params =~ /^\s*$/
-         or print STDERR "WARNING: Extra data after parameters '$params'\n";
       }
       else {
        print "ERROR: Unable to load wrapper $wrapper: $!\n";
@@ -189,6 +204,8 @@ sub replace_template {
     }
   }
 
+  $acts->{param} = [ \&tag_param, \%params ];
+
   # the basic iterator
   if ($iter && 
       (my ($before, $row, $after) =
@@ -235,8 +252,9 @@ sub replace_template {
     $self->perform($acts, $2, $3, $1) /segx;
 
   # replace any wrap parameters
-  $template =~ s/(<:\s*param\s+(\w+)\s*:>)/
-    exists $params{$2} ? $params{$2} : $1 /eg;
+  # now done elsewhere
+  #$template =~ s/(<:\s*param\s+(\w+)\s*:>)/
+  #  exists $params{$2} ? $params{$2} : $1 /eg;
 
   return $template;
 }
index c872485..a15c27e 100755 (executable)
@@ -7,7 +7,6 @@ use Products;
 use Product;
 use Constants qw(:shop $TMPLDIR $CGI_URI);
 use Squirrel::Template;
-use Squirrel::ImageEditor;
 use CGI::Cookie;
 use BSE::Custom;
 use BSE::Mail;
index 329d151..0137823 100644 (file)
@@ -10,6 +10,115 @@ Maybe I'll add some other bits here.
 
 =head1 CHANGES
 
+=head2 0.12_02
+
+When installing this release over earlier releases:
+
+=over
+
+=item 1.
+
+run mysql_upgrade.pl to upgrade the database schema
+
+=item 2.
+
+start the mysql shell and run the following query in your database:
+
+  update image set displayOrder = id;
+
+to preserve image ordering.
+
+=item *
+
+the <:if art:> conditional tag is no longer available
+
+=back
+
+This is a development release, intended to test the new code in BSE,
+in preparation for adding security mechanisms.  This is not intended
+for production use yet.
+
+Please see the known problems list.
+
+=over
+
+=item *
+
+was parsing wrap parameters when there weren't any, which caused
+warnings in the error log
+
+=item *
+
+you couldn't create a new subscription with the archiving switched off
+
+=item *
+
+if you edited a subscription, and unset the archive check, the save
+left it as archived.
+
+=item *
+
+article editing is being almost completely rewritten to allow more
+code re-use.
+
+=item *
+
+article image management has been completely rewritten so that the
+changes you make are immediate, rather than relying on the user to
+save the article after making changes.  This prevents the problem that
+sometimes crops up where the images you're editing are for the wrong
+article, or left over from an aberted attempt to create a new article.
+
+This change also means you can't add images to an article until the
+article has been created.
+
+=item *
+
+previously the order of images would change at the whim of the
+database, which happened pretty rarely but still happened.  Added a
+displayOrder field to images, and we base the ordering (and
+re-ordering) on that instead of whatever the database chooses to give
+us today.
+
+=item *
+
+products and catalogs use the new editing code
+
+=item *
+
+new tags on edit pages to give access to validation errors (most
+noticable on the add product page for now),
+
+=item *
+
+added an "old" tag to take a value from either the cgi parameters or a
+given other tag.  This makes error reporting more natural
+
+=back
+
+Known issues:
+
+=over
+
+=item *
+
+the code allows the product title and summary to be changed.  The new
+security code will correct this (and allow them to be changed, or the
+product to be deleted, for unused products)
+
+=item *
+
+only add_product.tmpl has the new error reporting and old value access
+tags
+
+=item *
+
+the delete/undelete links for products don't work.  Currently the same
+functionality is available through the Listed drop-down, but it might
+be desirable to keep the links, comments welcome.
+
+=back
+
 =head2 0.12_01
 
 Some derived tags may not work with the new fields, since I've
index 5fc1104..310f6cf 100644 (file)
@@ -37,6 +37,11 @@ directory under $TMPLDIR.
 Directory base for most templates.  Note: this is not completely
 implemented for now, so assume the default.  Default: $TMPLDIR.
 
+=item images
+
+Where uploaded images are stored.  This is not yet completely
+implemented.  Default: $IMAGEDIR.
+
 =back
 
 =head2 [extensions]
@@ -148,6 +153,13 @@ false (0).  This has the effect that anyone could send you an unsigned
 message encrypted with your public key, though this may not be a
 security threat.  Default: True.
 
+=item link_titles
+
+If this is true then the links to your articles within BSE will be
+followed by a / and then by a simplified version of the article title.
+The aim is to include at least some title information in the URL
+without modifying the name of the HTML file.  Default: False.
+
 =back
 
 =head2 [mail]
@@ -180,7 +192,7 @@ rather than sent immediately.
 
 =back
 
-=head2 [children of id]
+=head2 [children of I<id>]
 
 Where I<id> is the identifier for an article.
 
@@ -197,6 +209,25 @@ for templates that can be used for children of the given parent article.
 
 =back
 
+=head2 [article I<id>]
+
+Where I<id> is the identifier of an article.
+
+=over
+
+=item template_dirs
+
+A comma-separated list of extra directories under $TMPLDIR to search
+for templates that can be used for children of the given parent
+article.
+
+=item extra_templates
+
+A comma-separated list of extra templates under $TMPLDIR that can be
+used for the given article.
+
+=back
+
 =head2 [messages]
 
 This can be used to control translation of error messages.  Each key
@@ -338,7 +369,7 @@ Controls the interest.pl script.
 
 Email address that is notified of the interest.  Defaults to $SHOP_FROM.
 
-=bacl
+=back
 
 =head2 [debug]
 
@@ -372,6 +403,30 @@ removed.
 Reports debugging information to standard error while encrypting your
 mail.
 
+=back
+
+=head2 [uri]
+
+Contains various URIs.
+
+This is underused, so don't rely on it yet.
+
+=over
+
+=item cgi
+
+The URI to the CGI directory.  Default: /cgi-bin
+
+=item images
+
+The URI where images are kept.  Default: /images
+
+=item shop
+
+=item articles
+
+
+
 =back
 
 =head1 AUTHOR
index 2005c47..2c14d18 100644 (file)
   | </p>
 <h2>Manage Images</h2>
 <form action="<:script:>" enctype="multipart/form-data" method=POST>
+<input type=hidden name=type value="Product">
   <p>
     <input type=submit name=showimages value="Image Wizard">
   </p>
-  <h2><:action:> Product</h2>
+  <h2>Add New Product</h2>
   <input type="hidden" name="id" value="<:product id:>">
   <table border="0" cellspacing="0" cellpadding="0" bgcolor="#000000" class="table">
     <tr>
           <tr> 
             <th nowrap align="left" bgcolor="#FFFFFF">Title*:</th>
             <td nowrap bgcolor="#FFFFFF"> 
-              <input type="text" name="title" value="<:product title:>" size=60>
+              <input type="text" name="title" value="<:old title:>" size=60><:error_img title:>
             </td>
             <td nowrap bgcolor="#FFFFFF"><:help product title:></td>
           </tr>
           <tr> 
             <th nowrap align="left" bgcolor="#FFFFFF">Summary*:</th>
             <td nowrap bgcolor="#FFFFFF"> 
-              <input type="text" name="summary" value="<:product summary:>" size=60>
+              <input type="text" name="summary" value="<:old summary:>" size=60><:error_img summary:>
             </td>
             <td nowrap bgcolor="#FFFFFF"><:help product summary:></td>
           </tr>
           <tr> 
             <th nowrap align="left" bgcolor="#FFFFFF">Catalog:</th>
-            <td nowrap bgcolor="#FFFFFF"> <:catalogs:> </td>
+            <td nowrap bgcolor="#FFFFFF"> <select name="parentid"><:list:></select><:error_img parentid:> </td>
             <td nowrap bgcolor="#FFFFFF"><:help product catalog:></td>
           </tr>
+          <tr> 
+            <th nowrap align="left" bgcolor="#FFFFFF">Template:</th>
+            <td nowrap bgcolor="#FFFFFF"><:templates:><:error_img template:></td>
+            <td nowrap bgcolor="#FFFFFF"><:help product template:></td>
+          </tr>
           <tr> 
             <th nowrap align="left" bgcolor="#FFFFFF" valign="top"> Body:</th>
-            <td nowrap bgcolor="#FFFFFF"> 
-              <textarea name="body" rows=15 cols=60 wrap=virtual><:product body:></textarea>
+            <td nowrap bgcolor="#FFFFFF" valign="top"
+              <textarea name="body" rows=15 cols=60 wrap=virtual><:old body:></textarea> <:error_img body:>
             </td>
             <td nowrap bgcolor="#FFFFFF" valign="top"><:help body body:></td>
           </tr>
           <tr> 
             <th nowrap align="left" bgcolor="#FFFFFF">Lead time:</th>
             <td nowrap bgcolor="#FFFFFF"> 
-              <input type="text" name="leadTime" value="<:product leadTime:>" size=5>
-              days</td>
+              <input type="text" name="leadTime" value="<:old leadTime:>" size=5>
+              days <:error_img leadTime:></td>
             <td nowrap bgcolor="#FFFFFF"><:help product leadtime:></td>
           </tr>
           <tr> 
             <th nowrap align="left" bgcolor="#FFFFFF">Retail price:</th>
             <td nowrap bgcolor="#FFFFFF">$ 
-              <input type="text" name="retailPrice" value="<:money retailPrice:>" size=7>
-              (0.00)</td>
+              <input type="text" name="retailPrice" value="<:old retailPrice money product retailPrice:>" size=7>
+              (0.00) <:error_img retailPrice:></td>
             <td nowrap bgcolor="#FFFFFF"><:help product retail:></td>
           </tr>
           <tr> 
             <th nowrap align="left" bgcolor="#FFFFFF">Wholesale price:</th>
             <td nowrap bgcolor="#FFFFFF">$ 
-              <input type="text" name="wholesalePrice" value="<:money wholesalePrice:>" size=7>
-              (0.00)</td>
+              <input type="text" name="wholesalePrice" value="<:old wholesalePrice money product wholesalePrice:>" size=7>
+              (0.00) <:error_img wholesalePrice:></td>
             <td nowrap bgcolor="#FFFFFF"><:help product wholesale:></td>
           </tr>
           <tr> 
             <th nowrap align="left" bgcolor="#FFFFFF">GST:</th>
             <td nowrap bgcolor="#FFFFFF">$ 
-              <input type="text" name="gst" value="<:money gst:>" size=7>
+              <input type="text" name="gst" value="<:old gst money product gst:>" size=7> <:error_img gst:>
               (0.00)</td>
             <td nowrap bgcolor="#FFFFFF"><:help product gst:></td>
           </tr>
     </tr>
   </table>
   <p><font size="-1">*These fields cannot be modified once you save this form.</font></p>
-  <input type=submit name="save_product" value="Save product">
+  <input type=submit name="save" value="Save product">
 </form>
 <p><font size="-1">BSE Release <:release:></font></p>
 </body>
index 4513e44..660d518 100644 (file)
             <th valign="top" bgcolor="#FFFFFF" colspan="3">First Image Position</th>
           </tr>
           <tr> 
-            <td align="center" bgcolor="#FFFFFF" colspan="3"> <input type="radio" name="imagePos" value="tl" <: 
-              checked tl :>>Top Left &nbsp;<input type="radio" name="imagePos" value="tr" <: 
-              checked tr :>>Top Right &nbsp; <input type="radio" name="imagePos" value="bl" <: 
-              checked bl :>>Bottom Left &nbsp; <input type="radio" name="imagePos" value="br" <: 
-              checked br :>>Bottom Right </td>
+            <td align="center" bgcolor="#FFFFFF" colspan="3"> <input type="radio" name="imagePos" value="tl" <: ifEq [article imagePos] "tl":>checked<:or:><:eif:>
+             >Top Left &nbsp;<input type="radio" name="imagePos" value="tr"  <: ifEq [article imagePos] "tr":>checked<:or:><:eif:>
+             >Top Right &nbsp; <input type="radio" name="imagePos" value="bl"  <: ifEq [article imagePos] "bl":>checked<:or:><:eif:>
+             >Bottom Left &nbsp; <input type="radio" name="imagePos" value="br"  <: ifEq [article imagePos] "br":>checked<:or:><:eif:>
+             >Bottom Right
+           </td>
           </tr>
           <tr bgcolor="#FFFFFF"> 
             <th colspan="3">Image</th>
           </tr>
-          <: iterator begin image :> 
+          <: iterator begin images :> 
           <tr bgcolor="#FFFFFF"> 
-            <td align="center" colspan="3"> <img src="/images/<: image image :>" alt="<: image alt :>"<: 
-              image height :><: image width :>></td>
+            <td align="center" colspan="3"> <img src="/images/<: image image :>" alt="<: image alt :>" width="<: 
+              image height :>" height="<: image width :>"></td>
           </tr>
           <tr bgcolor="#FFFFFF"> 
             <th width="50%"> Alt Text</th>
               <input type="text" name="url" value="<: image url :>" size="32">
             </td>
             <td valign="bottom" nowrap> 
-              <input type="submit" name="removeimg_<: image order :>" value="Delete" onClick="return window.alert('Remember to update the article after deletion is complete')" >
+              <input type="submit" name="removeimg_<: image id :>" value="Delete" onClick="return window.alert('Remember to update the article after deletion is complete')" >
               <:imgmove:> </td>
           </tr>
-          <: iterator separator image :> 
+          <: iterator separator images :> 
           <tr bgcolor="#FFFFFF"> 
             <td colspan="3">&nbsp;</td>
           </tr>
-          <: iterator end image :> 
+          <: iterator end images :> 
           <tr> 
             <td align="right" bgcolor="#FFFFFF" colspan="3"> 
               <input type="submit" name="process" value="Save changes">
index f16f8fa..1710b67 100644 (file)
@@ -30,7 +30,7 @@
           <td align=right nowrap>$<:money retailPrice:></td>
           <td align=right nowrap>$<:money wholesalePrice:></td>
           <td align=right nowrap>$<:money gst:></td>
-          <td nowrap><a href="<:script:>?id=<:product id:>&edit_product=1">Edit</a> 
+          <td nowrap><a href="/cgi-bin/admin/add.pl?id=<:product id:>">Edit</a> 
             <:if Product listed:> <a href="<:script:>?id=<:product id:>&delete_product=1">Hide</a> 
             <:or Product:> <a href="<:script:>?id=<:product id:>&undelete_product=1">Show</a> 
             <:eif Product:> <:move:> </td>
@@ -42,8 +42,8 @@
   </tr>
 </table>
 <:or Products:><:eif Products:>
-<form action="<:script:>" method="post">
-  <p><input type=hidden name=parentid value="<:catalog id:>"><input type=submit name="add_product" value="Add Product"></p></form>
+<form action="/cgi-bin/admin/add.pl">
+  <p><input type=hidden name=parentid value="<:catalog id:>"><input type=hidden name=type value="Product"><input type=submit name="add_product" value="Add Product"></p></form>
 <h4>Sub catalogs</h4>
 <h4><:if Subcats :> <:iterator begin subcats:> </h4>
 <ul>
@@ -53,6 +53,6 @@
 </ul>
 <:iterator end subcats:>
 <:or Subcats:><:eif Subcats:>
-<form action="/cgi-bin/admin/add.pl" method="post">
+<form action="/cgi-bin/admin/add.pl"><input type=hidden name=type value="Catalog">
   <input type=hidden name=parentid value="<:catalog id:>"><input type=submit value="Add Sub-catalog"></form>
 
index 5ad98b0..7d2280a 100644 (file)
@@ -37,9 +37,7 @@
           <td bgcolor="#FFFFFF" align="center"><: is child listed :></td>
           <td nowrap> <a href="/cgi-bin/admin/add.pl?id=<:child id:>">Edit</a> 
             <a href="/cgi-bin/admin/add.pl?id=<:article id:>&deleteid=<:child id:>&remove=1" onClick="return window.confirm('Are you sure you want to delete this Section')">Delete</a> 
-            <:if NextChild:><a href="/cgi-bin/admin/move.pl?id=<:child id:>&d=down&edit=1&all=1"><img src="/images/admin/move_down.gif" width="17" height="13" alt="Move Down" border="0"></a><:or 
-            NextChild:><:eif NextChild:><:if PrevChild:><a href="/cgi-bin/admin/move.pl?id=<:child id:>&d=up&edit=1&all=1"><img src="/images/admin/move_up.gif" width="17" height="13" alt="Move Up" border="0"></a><:or 
-            PrevChild:><:eif PrevChild:> </td>
+            <:movechild:> </td>
         </tr>
         <:iterator separator children:> <:iterator end children:> 
       </table>
index 2f29469..ae643ff 100644 (file)
 <h1>Administration Page</h1>
 <:ifMessage:> 
 <p><b><:message:></b></p>
-<:or:><:eif:> 
+<:or:><:eif:>
 <p>| <a href="/admin/">Admin menu</a> | <:ifnew:><:or:><:if Eq [article id] 3:><a href="<:article admin:>">See 
   shop</a><:or Eq:><a href="<:article admin:>">See article</a><:eif Eq:> | <:eif:><:editParent:> 
   <:if Eq [article id] 3:><a href="/cgi-bin/admin/shopadmin.pl">Manage catalogs</a> 
   |<:or Eq:><:eif Eq:><:if new:><:or new:> <a href="<:script:>?id=<:article id:>&_t=steps">Manage 
   step children/parents</a> | <:eif new:></p>
-<h2>Manage Images</h2>
+<:if new:><:or new:><h2>Manage Images</h2>
 
   <form enctype="multipart/form-data" method="POST" action="<:script:>">
 
   <p>
-  <input type="submit" name="artimg" value="<:articleType:> Image Wizard">
+  <input type="submit" name="showimages" value="<:articleType:> Image Wizard">
   </p>
-
+<:eif new:>
 
   <h2><:articleType:> Details</h2>
 
@@ -42,8 +42,7 @@
             <th nowrap bgcolor="#FFFFFF" align="left"> <:parentType:>: </th>
             <td bgcolor="#FFFFFF" width="100%"> 
               <select name="parentid">
-                <option value="">Please select a Section <: ifart :><: list subsections 
-                :><:or:><:list sections:><:eif:> 
+                <option value="">Please select a <:parentType:><: list:>
               </select>
             </td>
             <td bgcolor="#FFFFFF"><:help edit section:> </td>
           <tr> 
             <th nowrap bgcolor="#FFFFFF" align="left"> Release date: </th>
             <td bgcolor="#FFFFFF" width="100%"> 
-              <input type="text" name="release" value="<: article release :>" size="10" maxlength="10">
+              <input type="text" name="release" value="<: date "%d/%m/%Y" article release :>" size="10" maxlength="10">
               (dd/mm/yyyy<: ifnew :> - default is today<: or :><: eif :>)</td>
             <td bgcolor="#FFFFFF"><:help edit release:></td>
           </tr>
           <tr> 
             <th nowrap bgcolor="#FFFFFF" align="left"> Expiry date: </th>
             <td bgcolor="#FFFFFF" width="100%"> 
-              <input type="text" name="expire" value="<: article expire :>" size="10" maxlength="10">
+              <input type="text" name="expire" value="<: date "%d/%m/%Y" article expire :>" size="10" maxlength="10">
               (dd/mm/yyyy - <: ifnew :>default is never, <: or :><: eif :>blank 
               for never expires)</td>
             <td bgcolor="#FFFFFF"><:help edit expire:></td>
@@ -93,8 +92,7 @@
             <th nowrap bgcolor="#FFFFFF" align="left"> Summary length: </th>
             <td bgcolor="#FFFFFF" width="100%"> 
               <input type="text" name="summaryLength" size="10" maxlength="10" value="<: article summaryLength :>">
-              (in characters - <: ifnew :>default inherited from section<: if 
-              art :> or sub-section<: or art :><: eif art :>, <: or :><: eif :> 
+              (in characters - <: ifnew :>default inherited from <:parentType:>, <: or :><: eif :> 
               zero for no summary)</td>
             <td bgcolor="#FFFFFF"><:help edit summary:></td>
           </tr>
             </td>
             <td nowrap bgcolor="#FFFFFF" valign="top"><:help edit files:></td>
           </tr>
-          <:or Article:><:eif Article:> 
           <tr> 
             <th valign="top" nowrap bgcolor="#FFFFFF" align="left"> Uploaded images: 
             </th>
               <: iterator end images :> &nbsp; <br></td>
             <td bgcolor="#FFFFFF" valign="top"><:help edit images:> </td>
           </tr>
+          <:or Article:><:eif Article:> 
         </table>
 </td>
 </tr>
           <td align="center"><: is child listed :></td>
           <td nowrap> <a href="/cgi-bin/admin/add.pl?id=<:child id:>">Edit</a> 
             <a href="/cgi-bin/admin/add.pl?id=<:article id:>&deleteid=<:child id:>&remove=1" onClick="return window.confirm('Are you sure you want to delete this <:ifEq [article id] 3:>Catalog<:or:><: childtype :><:eif:>')">Delete</a> 
-            <:if NextChild:><a href="/cgi-bin/admin/move.pl?id=<:child id:>&d=down&edit=1&all=1"><img src="/images/admin/move_down.gif" width="17" height="13" alt="Move Down" border="0"></a><:or 
-            NextChild:><:eif NextChild:><:if PrevChild:><a href="/cgi-bin/admin/move.pl?id=<:child id:>&d=up&edit=1&all=1"><img src="/images/admin/move_up.gif" width="17" height="13" alt="Move Up" border="0"></a><:or 
-            PrevChild:><:eif PrevChild:> </td>
+            <:movechild:> </td>
         </tr>
         <:iterator separator children:> <:iterator end children:> 
       </table>
 <:if HaveChildType:>
 <:if new:>
 <:or new:>
-<form action="/cgi-bin/admin/add.pl" method="POST">
+<form action="/cgi-bin/admin/add.pl">
 <input type=hidden name="parentid" value="<:article id:>">
   <p> 
     <input type=submit value="Add <:ifEq [article id] 3:>Catalog<:or:><: childtype :><:eif:>">
index ceff207..a2bf8fc 100644 (file)
 <p>| <a href="/admin/">Admin menu</a> | <:ifnew:><:or:><a href="<:article admin:>">See 
   article</a> | <:eif:><:editParent:> <:if new:><:or new:><a href="<:script:>?id=<:article id:>&_t=steps">Manage 
   step children/parents</a> | <:eif new:></p>
-<h2>Manage Images</h2>
+<:ifnew:><:or:><h2>Manage Images</h2><:eif:>
 
   <form enctype="multipart/form-data" method="POST" action="<:script:>">
 
-  <p>
-  <input type="submit" name="artimg" value="<:articleType:> Image Wizard">
-  </p>
+  <:ifnew:><:or:><p>
+  <input type="submit" name="showimages" value="<:articleType:> Image Wizard">
+  </p><:eif:>
 
 
   <h2><:articleType:> Details</h2>
@@ -40,8 +40,7 @@
             <th nowrap bgcolor="#FFFFFF" align="left"> <:parentType:>: </th>
             <td bgcolor="#FFFFFF" width="100%"> 
               <select name="parentid">
-                <option value="">Please select a Section <: ifart :><: list subsections 
-                :><:or:><:list sections:><:eif:> 
+                <option value="">Please select a <:parentType:> <: list :> 
               </select>
             </td>
             <td bgcolor="#FFFFFF"><:help edit section:> </td>
           <tr> 
             <th nowrap bgcolor="#FFFFFF" align="left"> Release date: </th>
             <td bgcolor="#FFFFFF" width="100%"> 
-              <input type="text" name="release" value="<: article release :>" size="10" maxlength="10">
+              <input type="text" name="release" value="<: date "%d/%m/%Y" article release :>" size="10" maxlength="10">
               (dd/mm/yyyy<: ifnew :> - default is today<: or :><: eif :>)</td>
             <td bgcolor="#FFFFFF"><:help edit release:></td>
           </tr>
           <tr> 
             <th nowrap bgcolor="#FFFFFF" align="left"> Expiry date: </th>
             <td bgcolor="#FFFFFF" width="100%"> 
-              <input type="text" name="expire" value="<: article expire :>" size="10" maxlength="10">
+              <input type="text" name="expire" value="<: date "%d/%m/%Y" article expire :>" size="10" maxlength="10">
               (dd/mm/yyyy - <: ifnew :>default is never, <: or :><: eif :>blank 
               for never expires)</td>
             <td bgcolor="#FFFFFF"><:help edit expire:></td>
@@ -91,8 +90,7 @@
             <th nowrap bgcolor="#FFFFFF" align="left"> Summary length: </th>
             <td bgcolor="#FFFFFF" width="100%"> 
               <input type="text" name="summaryLength" size="10" maxlength="10" value="<: article summaryLength :>">
-              (in characters - <: ifnew :>default inherited from section<: if 
-              art :> or sub-section<: or art :><: eif art :>, <: or :><: eif :> 
+              (in characters - <: ifnew :>default inherited from <:parentType:> <: or :><: eif :> 
               zero for no summary)</td>
             <td bgcolor="#FFFFFF"><:help edit summary:></td>
           </tr>
             </td>
             <td nowrap bgcolor="#FFFFFF" valign="top"><:help edit files:></td>
           </tr>
-          <:or Article:><:eif Article:> 
           <tr> 
             <th valign="top" nowrap bgcolor="#FFFFFF" align="left"> Uploaded images: 
             </th>
               <: iterator end images :> &nbsp; <br></td>
             <td bgcolor="#FFFFFF" valign="top"><:help edit images:> </td>
           </tr>
+          <:or Article:><:eif Article:> 
         </table>
 </td>
 </tr>
           <td align="center"><: is child listed :></td>
           <td nowrap> <a href="/cgi-bin/admin/add.pl?id=<:child id:>">Edit</a> 
             <a href="/cgi-bin/admin/add.pl?id=<:article id:>&deleteid=<:child id:>&remove=1" onClick="return window.confirm('Are you sure you want to delete this article')">Delete</a> 
-            <:if NextChild:><a href="/cgi-bin/admin/move.pl?id=<:child id:>&d=down&edit=1&all=1"><img src="/images/admin/move_down.gif" width="17" height="13" alt="Move Down" border="0"></a><:or 
-            NextChild:><:eif NextChild:><:if PrevChild:><a href="/cgi-bin/admin/move.pl?id=<:child id:>&d=up&edit=1&all=1"><img src="/images/admin/move_up.gif" width="17" height="13" alt="Move Up" border="0"></a><:or 
-            PrevChild:><:eif PrevChild:> </td>
+            <:movechild:> </td>
         </tr>
         <:iterator separator children:> <:iterator end children:> 
       </table>
 <:if HaveChildType:>
 <:if new:>
 <:or new:>
-<form action="/cgi-bin/admin/add.pl" method="POST">
+<form action="/cgi-bin/admin/add.pl">
 <input type=hidden name="parentid" value="<:article id:>">
   <p> 
     <input type=submit value="Add <:ifEq [article id] 3:>Catalog<:or:><: childtype :><:eif:>">
index 79c25c2..82bc743 100644 (file)
   catalog</a> | <:eif:><:editParent:> <a href="/cgi-bin/admin/shopadmin.pl">Manage 
   catalogs</a> | <:if new:><:or new:><a href="<:script:>?id=<:article id:>&_t=steps">Manage 
   step children/parents</a> | <:eif new:></p>
+  <:ifnew:><:or:><p>
 <h2>Manage Images</h2>
 
   <form enctype="multipart/form-data" method="POST" action="<:script:>">
+  <input type=hidden name=type value="Catalog">
 
   <!--input type="hidden" name="return_url" value="<:script:>?level=<: level :>&id=<:article id:>" -->
 
-  <p>
   <input type="submit" name="artimg" value="Catalog Image Wizard">
-  </p>
+  </p><:eif:>
 
 
   <h2>Catalog Details</h2>
           <tr> 
             <th bgcolor="#FFFFFF" nowrap align="left"> Release date: </th>
             <td bgcolor="#FFFFFF" width="100%"> 
-              <input type="text" name="release" value="<: article release :>" size="10" maxlength="10">
+              <input type="text" name="release" value="<: date "%d/%m/%Y" article release :>" size="10" maxlength="10">
               (dd/mm/yyyy<: ifnew :> - default is today<: or :><: eif :>)</td>
             <td bgcolor="#FFFFFF"><:help catalog release:></td>
           </tr>
           <tr> 
             <th bgcolor="#FFFFFF" nowrap align="left"> Expiry date: </th>
             <td bgcolor="#FFFFFF" width="100%"> 
-              <input type="text" name="expire" value="<: article expire :>" size="10" maxlength="10">
+              <input type="text" name="expire" value="<: date "%d/%m/%Y" article expire :>" size="10" maxlength="10">
               (dd/mm/yyyy - <: ifnew :>default is never, <: or :><: eif :>blank 
               for never expires)</td>
             <td bgcolor="#FFFFFF"><:help catalog expire:></td>
@@ -86,8 +87,7 @@
             <th bgcolor="#FFFFFF" nowrap align="left"> Summary length: </th>
             <td bgcolor="#FFFFFF" width="100%"> 
               <input type="text" name="summaryLength" size="10" maxlength="10" value="<: article summaryLength :>">
-              (in characters - <: ifnew :>default inherited from section<: if 
-              art :> or sub-section<: or art :><: eif art :>, <: or :><: eif :>zero 
+              (in characters - default inherited from parent catalog or shop, zero 
               for no summary)</td>
             <td bgcolor="#FFFFFF"><:help catalog summary:></td>
           </tr>
               Remove<:or:><:eif:></td>
             <td bgcolor="#FFFFFF" valign="top"><:help catalog thumb:></td>
           </tr>
+          <:if Article id:> 
+          <tr> 
+            <th nowrap bgcolor="#FFFFFF" align="left" valign="top"><a name="files"></a>Files:</th>
+            <td nowrap bgcolor="#FFFFFF" width="100%"> <:if Files:> 
+              <table cellpadding="0" cellspacing="0" border="0" bgcolor="#333333">
+                <tr><td> 
+                    <table cellpadding="5" cellspacing="1" border="0">
+                      <tr bgcolor="#FFFFFF"> 
+                        <th>Filename</th>
+                        <th>Size</th>
+                        <th>Type</th>
+                        <th>D/l</th>
+                        <th>Pay</th>
+                        <th>User</th>
+                      </tr>
+                      <:iterator begin files:> 
+                      <tr bgcolor="#FFFFFF"> 
+                        <td><:file displayName:></td>
+                        <td align="center"><:kb file sizeInBytes:></td>
+                        <td><:file contentType:></td>
+                        <td align=center><:ifFile download:>Yes<:or:>No<:eif:></td>
+                        <td align=center><:ifFile forSale:>Yes<:or:>No<:eif:></td>
+                        <td align=center><:ifFile requireUser:>Yes<:or:>No<:eif:></td>
+                      </tr>
+                      <:iterator end files:> 
+                    </table>
+              </td></tr></table>
+              <:or Files:><p>No files are attached to this article.<:eif Files:> 
+                <a href="<:script:>?filelist=1&id=<:article id:>"><b>Manage Files</b></a></p>
+            </td>
+            <td nowrap bgcolor="#FFFFFF" valign="top"><:help edit files:></td>
+          </tr>
+          <:or Article:><:eif Article:> 
           <tr> 
             <th valign="top" bgcolor="#FFFFFF" nowrap align="left"> Uploaded images: 
             </th>
           <td width="70%"><: summary child body :></td>
           <td align="center"><: is child listed :></td>
           <td nowrap> <: edit child Edit :> <:if Match [child generator] "Catalog":><a href="/cgi-bin/admin/add.pl?id=<:article id:>&deleteid=<:child id:>&remove=1" onClick="return window.confirm('Are you sure you want to delete this Sub-catalog')">Delete</a><:or 
-            Match:><:eif Match:><:if NextChild:><a href="/cgi-bin/admin/move.pl?id=<:child id:>&d=down&edit=1&all=1"><img src="/images/admin/move_down.gif" width="17" height="13" alt="Move Down" border="0"></a><:or 
-            NextChild:><:eif NextChild:><:if PrevChild:><a href="/cgi-bin/admin/move.pl?id=<:child id:>&d=up&edit=1&all=1"><img src="/images/admin/move_up.gif" width="17" height="13" alt="Move Up" border="0"></a><:or 
-            PrevChild:><:eif PrevChild:> </td>
+            Match:><:eif Match:><:movechild:> </td>
         </tr>
         <:iterator separator children:> <:iterator end children:> 
       </table>
 <:if HaveChildType:>
 <:if new:>
 <:or new:>
-<form action="/cgi-bin/admin/add.pl" method="POST">
+<table><tr>
+<td><form action="/cgi-bin/admin/add.pl" method="POST">
 <input type=hidden name="parentid" value="<:article id:>">
+<input type=hidden name="type" value="Catalog">
 <p>
     <input type=submit value="Add Sub-catalog">
   </p>
-</form>
+</form></td>
+<td><form action="/cgi-bin/admin/add.pl" method="POST">
+<input type=hidden name="parentid" value="<:article id:>">
+<input type=hidden name="parentid" value="Product">
+<p>
+    <input type=submit value="Add Product">
+  </p>
+</form></td></tr></table>
 <:eif new:> <:or HaveChildType:> <:eif HaveChildType:> 
 <p><font size="-1">BSE Release <:release:></font></p>
 </body
index 8fd548e..c49b150 100644 (file)
   | <a href="<:script:>?id=<:product id:>&delete_product=1">Hide product</a> <:or 
   Product:> | <a href="<:script:>?id=<:product id:>&undelete_product=1">Show product</a> 
   <:eif Product:>| <a href="/cgi-bin/admin/add.pl?id=<:product id:>&_t=steps">Manage 
-  step parents</a> | <:hiddenNote:></p>
+  step parents</a> | <:ifProduct listed:><:or:>Hidden<:eif:></p>
 <h2>Manage Images</h2>
 <form action="<:script:>" enctype="multipart/form-data" method="POST">
   <p>
     <input type=submit name=showimages value="Image Wizard">
   </p>
-  <h2><:action:> Product</h2>
+  <h2>Edit Product</h2>
   <p>
     <input type="hidden" name="id" value="<:product id:>">
   </p>
           </tr>
           <tr> 
             <th align="left" bgcolor="#FFFFFF">Catalog:</th>
-            <td bgcolor="#FFFFFF"><:catalogs:></td>
+            <td bgcolor="#FFFFFF"><select name="parentid"><:list:></select></td>
             <td nowrap bgcolor="#FFFFFF"><:help product catalog:></td>
           </tr>
+          <tr> 
+            <th nowrap align="left" bgcolor="#FFFFFF">Template:</th>
+            <td nowrap bgcolor="#FFFFFF"><:templates:></td>
+            <td nowrap bgcolor="#FFFFFF"><:help product template:></td>
+          </tr>
           <tr> 
             <th align="left" bgcolor="#FFFFFF" valign="top"> Body:</th>
             <td bgcolor="#FFFFFF"> 
           <tr> 
             <th align="left" bgcolor="#FFFFFF">Retail price:</th>
             <td bgcolor="#FFFFFF">$ 
-              <input type="text" name="retailPrice" value="<:money retailPrice:>" size=7>
+              <input type="text" name="retailPrice" value="<:money product retailPrice:>" size=7>
               (0.00)</td>
             <td nowrap bgcolor="#FFFFFF"><:help product retail:></td>
           </tr>
           <tr> 
             <th align="left" bgcolor="#FFFFFF">Wholesale price:</th>
             <td bgcolor="#FFFFFF">$ 
-              <input type="text" name="wholesalePrice" value="<:money wholesalePrice:>" size=7>
+              <input type="text" name="wholesalePrice" value="<:money product wholesalePrice:>" size=7>
               (0.00)</td>
             <td nowrap bgcolor="#FFFFFF"><:help product wholesale:></td>
           </tr>
           <tr> 
             <th align="left" bgcolor="#FFFFFF">GST:</th>
             <td bgcolor="#FFFFFF">$ 
-              <input type="text" name="gst" value="<:money gst:>" size=7>
+              <input type="text" name="gst" value="<:money product gst:>" size=7>
               (0.00)</td>
             <td nowrap bgcolor="#FFFFFF"><:help product gst:></td>
           </tr>
           <tr> 
             <th align="left" bgcolor="#FFFFFF">Release date:</th>
             <td bgcolor="#FFFFFF"> 
-              <input type="text" name="release" value="<:date product release:>" size=11>
+              <input type="text" name="release" value="<:date "%d/%m/%Y" product release:>" size=11>
               (dd/mm/yyyy)</td>
             <td nowrap bgcolor="#FFFFFF"><:help product release:></td>
           </tr>
           <tr> 
             <th align="left" bgcolor="#FFFFFF">Expiry date:</th>
             <td bgcolor="#FFFFFF"> 
-              <input type="text" name="expire" value="<:date product expire:>" size=11>
+              <input type="text" name="expire" value="<:date "%d/%m/%Y" product expire:>" size=11>
               (dd/mm/yyyy)</td>
             <td nowrap bgcolor="#FFFFFF"><:help product expire:></td>
           </tr>
     </tr>
   </table>
   <p>
-    <input type=submit name="save_product" value="Save changes">
+    <input type=submit name="save" value="Save changes">
   </p>
 </form>
 
index 1f9c800..3433cfc 100644 (file)
@@ -13,7 +13,7 @@
 <h1>Manage step <:ifMatch [article generator] "Product":><:or:> children/<:eif:>parents</h1>
 
 <p>| <a href="/admin/">Admin menu</a> | <:ifMatch [article generator] "Product":><a href="/cgi-bin/admin/shopadmin.pl">Manage 
-  catalogs</a> | <a href="/cgi-bin/admin/shopadmin.pl?id=<:article id:>&edit_product=1">Edit 
+  catalogs</a> | <a href="/cgi-bin/admin/add.pl?id=<:article id:>">Edit 
   product</a><:or:><a href="/cgi-bin/admin/add.pl?id=<:article id:>"><:if Eq [article 
   parentid] 3:>Edit catalog<:or Eq:>Edit article<:eif Eq:></a><:eif:> |</p>
 <:if new:><:or new:> <:if Match [article generator] "Product":><:or Match:> 
             <td align="center" nowrap><:kid id:></td>
             <td width="100%"><:kid title:></td>
             <td><:ifStepKid:> 
-              <input type="text" name="release_<:stepkid childId:>" value="<:date stepkid release:>" size=10>
+              <input type="text" name="release_<:stepkid childId:>" value="<:date "%d/%m/%Y" stepkid release:>" size=10>
               <:or:>&nbsp;<:eif:></td>
             <td><:ifStepKid:> 
-              <input type="text" name="expire_<:stepkid childId:>" value="<:date stepkid expire:>" size=10>
+              <input type="text" name="expire_<:stepkid childId:>" value="<:date "%d/%m/%Y" stepkid expire:>" size=10>
               <:or:>&nbsp;<:eif:></td>
             <td nowrap><:edit kid Edit:> <:ifStepKid:><a href="<:script:>?del_stepkid=1&id=<:stepkid parentId:>&stepkid=<:stepkid childId:>&_t=steps">Delete</a><:or:><:eif:> 
               <:movestepkid:></td>
               parentId:></a></td>
             <td width="100%"><:stepparent_targ title:></td>
             <td> 
-              <input type="text" name="release_<:stepparent parentId:>" value="<:date stepparent release:>" size="10">
+              <input type="text" name="release_<:stepparent parentId:>" value="<:date "%d/%m/%Y" stepparent release:>" size="10">
             </td>
             <td> 
-              <input type="text" name="expire_<:stepparent parentId:>" value="<:date stepparent expire:>" size="10">
+              <input type="text" name="expire_<:stepparent parentId:>" value="<:date "%d/%m/%Y" stepparent expire:>" size="10">
             </td>
             <td nowrap><a href="/cgi-bin/admin/add.pl?id=<:stepparent parentId:>">Edit</a> 
               <a href="<:script:>?del_stepparent=1&id=<:stepparent childId:>&stepparent=<:stepparent parentId:>&_t=steps">Delete</a> 
index 70e9b2a..1898a6b 100644 (file)
                     <:ifFile requireUser:> checked<:or:><:eif:> />Require login 
                     &nbsp;&nbsp;&nbsp;</td>
                   <td nowrap align="right" width="100%"> <b><a href="<:script:>?filedel=1&amp;id=<:article id:>&amp;file=<:file id:>">Remove</a></b> 
-                    <:move:></td>
+                    <:movefiles:></td>
                 </tr>
               </table>
             </td>
 </td>
 </tr>
 </table>
+</form>
 
-  <p>
-    <input type="submit" name="back" value=" &lt;&lt;  Back  " /></p>
-
+<form action="<:script:>">
+<input type=hidden name=id value="<:article id:>">
+  <p><input type="submit" name="back" value=" &lt;&lt;  Back  " /></p>
 </form>
 
 <p><font size="-1">BSE Release <:release:></font></p>
index 9d1ef9e..89850ec 100644 (file)
@@ -18,7 +18,7 @@
 <p>&nbsp; </p>
 <hr noshade size="1">
 <:iterator end catalogs:> 
-<form action="/cgi-bin/admin/add.pl" method="post">
+<form action="/cgi-bin/admin/add.pl"><input type=hidden name=type value="Catalog">
   <input type=hidden name=parentid value=3><input type=submit value="Add Catalog"></form>
 <p><font size="-1">BSE Release <:release:></font></p>
 </body>
diff --git a/site/templates/test/children.tmpl b/site/templates/test/children.tmpl
new file mode 100644 (file)
index 0000000..35ecc04
--- /dev/null
@@ -0,0 +1,3 @@
+<:iterator begin children:><:
+child title:>
+<:iterator end children:>
index 867c9f8..b8fed8e 100644 (file)
@@ -299,7 +299,7 @@ sub check_form {
          ok(1, "$note - $textname - field exists");
          if (defined $todo{$textname}[0]) {
            my $tvalue = $todo{$textname}[0];
-           ok($tvalue eq $fvalue, "$note - $textname - checking value");
+           ok($tvalue eq $fvalue, "$note - $textname - checking value($tvalue vs $fvalue)");
          }
          if (defined $todo{$textname}[1]) {
            ok ($todo{$textname}[1] eq 'textarea',
index c23a54b..03f21bc 100644 (file)
@@ -1,7 +1,7 @@
 #!perl -w
 use strict;
 use BSE::Test ();
-use Test::More tests=>25;
+use Test::More tests=>31;
 use File::Spec;
 use FindBin;
 my $cgidir = File::Spec->catdir(BSE::Test::base_dir, 'cgi-bin');
@@ -63,6 +63,27 @@ TEMPLATE
 No
 EXPECTED
 
+template_test "children", $parent, <<TEMPLATE, <<EXPECTED;
+<:iterator begin children:><:
+child title:>
+<:iterator end children:>
+TEMPLATE
+Three
+Two
+One
+
+EXPECTED
+
+template_test "embed children", $top, <<TEMPLATE, <<EXPECTED;
+<:embed $parent->{id} test/children.tmpl:>
+TEMPLATE
+Three
+Two
+One
+
+
+EXPECTED
+
 for my $kid (reverse @kids) {
   my $name = $kid->{title};
   $kid->remove();