0.15_19 commit r0_15_19
authorTony Cook <tony@develop-help.com>
Wed, 20 Jul 2005 09:15:01 +0000 (09:15 +0000)
committertony <tony@45cb6cf1-00bc-42d2-bb5a-07f51df49f94>
Wed, 20 Jul 2005 09:15:01 +0000 (09:15 +0000)
42 files changed:
MANIFEST
Makefile
schema/bse.sql
site/cgi-bin/admin/add.pl
site/cgi-bin/bse.cfg
site/cgi-bin/modules/Article.pm
site/cgi-bin/modules/BSE/AdminSiteUsers.pm
site/cgi-bin/modules/BSE/DB/Mysql.pm
site/cgi-bin/modules/BSE/Dynamic/Article.pm [new file with mode: 0644]
site/cgi-bin/modules/BSE/Dynamic/Catalog.pm [new file with mode: 0644]
site/cgi-bin/modules/BSE/Dynamic/Product.pm [new file with mode: 0644]
site/cgi-bin/modules/BSE/Dynamic/Seminar.pm [new file with mode: 0644]
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/TB/SiteUserGroup.pm [new file with mode: 0644]
site/cgi-bin/modules/BSE/TB/SiteUserGroups.pm [new file with mode: 0644]
site/cgi-bin/modules/BSE/UI/Page.pm [new file with mode: 0644]
site/cgi-bin/modules/DevHelp/Date.pm
site/cgi-bin/modules/DevHelp/Report.pm
site/cgi-bin/modules/Generate/Article.pm
site/cgi-bin/modules/SiteUser.pm
site/cgi-bin/modules/SiteUsers.pm
site/cgi-bin/modules/Util.pm
site/cgi-bin/page.pl [new file with mode: 0755]
site/docs/bse.pod
site/docs/config.pod
site/templates/admin/users/edit.tmpl
site/templates/admin/users/edit_groups.tmpl [new file with mode: 0644]
site/templates/admin/users/groupadd.tmpl [new file with mode: 0644]
site/templates/admin/users/groupdelete.tmpl [new file with mode: 0644]
site/templates/admin/users/groupedit.tmpl [new file with mode: 0644]
site/templates/admin/users/grouplist.tmpl [new file with mode: 0644]
site/templates/admin/users/groupmembers.tmpl [new file with mode: 0644]
site/templates/admin/users/list.tmpl
site/util/initial.pl
t/t00smoke.t
t/t011dhdates.t
t/t20gen.t
t/t21gencat.t
test.cfg

index e19f81bd0d62977cb6ee49310a7715a385b1b0c3..a235f3c9cf33fb5a3173ba7515199543926a9d84 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -56,6 +56,10 @@ 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/Dynamic/Article.pm
+site/cgi-bin/modules/BSE/Dynamic/Catalog.pm
+site/cgi-bin/modules/BSE/Dynamic/Product.pm
+site/cgi-bin/modules/BSE/Dynamic/Seminar.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
@@ -104,6 +108,8 @@ site/cgi-bin/modules/BSE/TB/Seminar.pm
 site/cgi-bin/modules/BSE/TB/SeminarSession.pm
 site/cgi-bin/modules/BSE/TB/SeminarSessions.pm
 site/cgi-bin/modules/BSE/TB/Seminars.pm
+site/cgi-bin/modules/BSE/TB/SiteUserGroup.pm
+site/cgi-bin/modules/BSE/TB/SiteUserGroups.pm
 site/cgi-bin/modules/BSE/TB/Subscription.pm
 site/cgi-bin/modules/BSE/TB/Subscription/Calc.pm
 site/cgi-bin/modules/BSE/TB/Subscriptions.pm
@@ -114,6 +120,7 @@ site/cgi-bin/modules/BSE/UI/AdminSeminar.pm
 site/cgi-bin/modules/BSE/UI/Affiliate.pm
 site/cgi-bin/modules/BSE/UI/Dispatch.pm
 site/cgi-bin/modules/BSE/UI/Formmail.pm
+site/cgi-bin/modules/BSE/UI/Page.pm
 site/cgi-bin/modules/BSE/UI/Shop.pm
 site/cgi-bin/modules/BSE/UI/SiteuserCommon.pm
 site/cgi-bin/modules/BSE/UI/SubAdmin.pm
@@ -161,6 +168,7 @@ site/cgi-bin/modules/Squirrel/Row.pm
 site/cgi-bin/modules/Squirrel/Table.pm
 site/cgi-bin/modules/Squirrel/Template.pm
 site/cgi-bin/modules/Util.pm
+site/cgi-bin/page.pl
 site/cgi-bin/printable.pl
 site/cgi-bin/search.pl
 site/cgi-bin/shop.pl
@@ -325,7 +333,13 @@ site/templates/admin/subscr/list.tmpl
 site/templates/admin/userlist.tmpl
 site/templates/admin/users/add.tmpl
 site/templates/admin/users/edit.tmpl
+site/templates/admin/users/edit_groups.tmpl
 site/templates/admin/users/edit_orders.tmpl
+site/templates/admin/users/groupadd.tmpl
+site/templates/admin/users/groupdelete.tmpl
+site/templates/admin/users/groupedit.tmpl
+site/templates/admin/users/grouplist.tmpl
+site/templates/admin/users/groupmembers.tmpl
 site/templates/admin/users/list.tmpl
 site/templates/admin/xbase.tmpl
 site/templates/affiliate.tmpl
index 8f83df0613594680aacb150673851de15564ecb6..1352551c72235d661f55f004eb6948f45c453e1c 100755 (executable)
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-VERSION=0.15_18
+VERSION=0.15_19
 DISTNAME=bse-$(VERSION)
 DISTBUILD=$(DISTNAME)
 DISTTAR=../$(DISTNAME).tar
index 60672b3ba4e6e02acdcf6062601cfe243b3b11a4..4f2336c9ef856de8e964d1cf9d077ee14364903b 100644 (file)
@@ -80,6 +80,10 @@ CREATE TABLE article (
   author varchar(255) default '' not null,
   pageTitle varchar(255) default '' not null,
 
+  force_dynamic integer not null default 0,
+  cached_dynamic integer not null default 0,
+  inherit_siteuser_rights integer not null default 1,
+
   PRIMARY KEY (id),
 
   -- if we keep id in the indexes MySQL will sometimes be able to
@@ -740,3 +744,17 @@ create table bse_seminar_bookings (
   primary key(session_id, siteuser_id),
   index (siteuser_id)
 );
+
+drop table if exists bse_siteuser_groups;
+create table bse_siteuser_groups (
+  id integer not null auto_increment primary key,
+  name varchar(80) not null
+);
+
+drop table if exists bse_siteuser_membership;
+create table bse_siteuser_membership (
+  group_id integer not null,
+  siteuser_id integer not null,
+  primary key(group_id, siteuser_id),
+  index(siteuser_id)
+);
index 94af56b55eaccfbd5eb9c0c3206ffcca885292f6..7791aa320b1611e4cc9789c0bdd2fc9dbd6d039a 100755 (executable)
@@ -9,6 +9,7 @@ use Article;
 use BSE::DB;
 use BSE::Request;
 use BSE::Template;
+use BSE::Edit::Base;
 use Carp qw'verbose';
 use Carp 'confess';
 
@@ -33,7 +34,7 @@ if (defined $id && $id =~ /\d/ && $id == -1) {
     or die "Cannot get sections class";
   $result = $obj->edit_sections($req, $articles);
 }
-elsif (my ($obj, $article) = article_class($id, $articles, $cfg)) {
+elsif (my ($obj, $article) = BSE::Edit::Base->article_class_id($id, $articles, $cfg)) {
   $result = $obj->article_dispatch($req, $article, $articles);
 }
 else {
@@ -47,7 +48,7 @@ else {
   unless ($obj) {
     my $parentid = $cgi->param('parentid');
     my $parent;
-    if (($obj, $parent) = article_class($parentid, $articles, $cfg)) {
+    if (($obj, $parent) = BSE::Edit::Base->article_class_id($parentid, $articles, $cfg)) {
       if (my ($class) = $obj->child_types($parent)) {
        $obj = get_class($class, $cfg);
       }
@@ -93,18 +94,3 @@ sub get_class {
   return $class->new(cfg=>$cfg, db=>BSE::DB->single);
 }
 
-sub article_class {
-  my ($id, $articles, $cfg) = @_;
-
-  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 ($obj, $article);
-}
index dafad35715f55f962478885ce342270656a629b5..eca91655cc795ecf0a4200a46bbf2fa44e4a188c 100644 (file)
@@ -12,6 +12,7 @@ access_control=0
 ; the following needs to be set to a path writable by the BSE processes
 downloads = /somewhere
 siteuser_images = $(paths/downloads)
+dynamic_cache=$(paths/downloads)/../cache
 
 [pregenerate]
 
@@ -239,6 +240,7 @@ descendants=1
 I=Index even if hidden
 N=Don't index even if listed
 C=Don't index this article or it's descendants
+R=Regenerate even if we're using JIT regen (dynamic pages only)
 
 [article 1]
 extra_templates=index.tmpl
index d0dc4bf5fa7bf227d225b2991336fa3716b01866..cc057ad63786b4369c54ca845be4cc69a75dbb18 100644 (file)
@@ -4,6 +4,7 @@ use strict;
 use Squirrel::Row;
 use vars qw/@ISA/;
 @ISA = qw/Squirrel::Row/;
+use Carp 'confess';
 
 sub columns {
   return qw/id parentid displayOrder title titleImage body
@@ -12,7 +13,8 @@ sub columns {
     summaryLength generator level listed lastModified flags
     customDate1 customDate2 customStr1 customStr2
     customInt1 customInt2 customInt3 customInt4 
-    lastModifiedBy created createdBy author pageTitle/;
+    lastModifiedBy created createdBy author pageTitle
+    force_dynamic cached_dynamic inherit_siteuser_rights/;
 }
 
 sub numeric {
@@ -124,4 +126,26 @@ sub parent {
   return Articles->getByPkey($self->{parentid});
 }
 
+sub update_dynamic {
+  my ($self, $cfg) = @_;
+
+  $cfg && $cfg->can('entry')
+    or confess 'update_dynamic called without $cfg';
+
+  # conditional in case something strange is in the config file
+  my $dynamic = $cfg->entry('basic', 'all_dynamic', 0) ? 1 : 0;
+
+  $dynamic or $dynamic = $self->{force_dynamic};
+
+  unless ($dynamic) {
+    # check for groups, etc
+  }
+
+  $self->{cached_dynamic} = $dynamic;
+}
+
+sub is_dynamic {
+  $_[0]{cached_dynamic};
+}
+
 1;
index 6f2eecff2286a250697954d0e263d23db813ccc4..88c599ae164a27968a4d1feca5930e4f251e5f2a 100644 (file)
@@ -9,6 +9,7 @@ use BSE::Util::DynSort qw(sorter tag_sorthelp);
 use BSE::Util::SQL qw/now_datetime/;
 use BSE::SubscriptionTypes;
 use BSE::CfgInfo qw(custom_class);
+use constant SITEUSER_GROUP_SECT => 'BSE Siteuser groups validation';
 
 my %actions =
   (
@@ -17,6 +18,15 @@ my %actions =
    save=>1,
    addform=>1,
    add=>1,
+   grouplist=>1,
+   addgroupform=>1,
+   addgroup => 1,
+   editgroup=>1,
+   savegroup => 1,
+   deletegroupform =>1,
+   deletegroup=>1,
+   groupmemberform => 1,
+   savegroupmembers => 1,
   );
 
 my @donttouch = qw(id userId password email confirmed confirmSecret waitingForConfirmation flags affiliate_name previousLogon); # flags is saved separately
@@ -148,6 +158,20 @@ sub iter_orders {
   return $siteuser->orders;
 }
 
+sub iter_groups {
+  require BSE::TB::SiteUserGroups;
+
+  BSE::TB::SiteUserGroups->all;
+}
+
+sub tag_ifUserMember {
+  my ($user, $rgroup) = @_;
+
+  $$rgroup or return 0;
+
+  $user->is_member_of($$rgroup);
+}
+
 sub req_edit {
   my ($class, $req, $msg, $errors) = @_;
 
@@ -184,6 +208,7 @@ sub req_edit {
   require BSE::SubscribedUsers;
   my @usersubs = BSE::SubscribedUsers->getBy(userId=>$siteuser->{id});
   my %usersubs = map { $_->{subId}, $_ } @usersubs;
+  my $current_group;
   my %acts;
   %acts =
     (
@@ -203,6 +228,9 @@ sub req_edit {
      $it->make_iterator([ \&iter_orders, $siteuser ], 
                        'userorder', 'userorders' ),
      $class->_edit_tags($siteuser, $req->cfg),
+     $it->make_iterator(\&iter_groups, 'group', 'groups', 
+                       undef, undef, undef, \$current_group),
+     ifMember => [ \&tag_ifUserMember, $siteuser, \$current_group ],
     );  
 
   my $template = 'admin/users/edit';
@@ -343,6 +371,26 @@ sub req_save {
     if $cgi->param('saveDisabled') && !defined $cgi->param('disabled');
   $user->save;
 
+  # save group membership
+  my @save_ids = $cgi->param('set_group_id');
+  if (@save_ids) {
+    my %member_of = map { $_ => 1 } $user->group_ids;
+    my %new_ids = map { $_ => 1 } $cgi->param('group_id');
+    require BSE::TB::SiteUserGroups;
+    my %all_groups = map { $_->{id} => $_ } BSE::TB::SiteUserGroups->all;
+    
+    for my $id (@save_ids) {
+      my $group = $all_groups{$id} 
+       or next;
+      if ($member_of{$id} and !$new_ids{$id}) {
+       $group->remove_member($user->{id});
+      }
+      elsif (!$member_of{$id} and $new_ids{$id}) {
+       $group->add_member($user->{id});
+      }
+    }
+  }
+
   if ($cgi->param('checkedsubs')) {
     $class->save_subs($req, $user);
   }
@@ -660,4 +708,219 @@ sub _validate_affiliate_name {
   return;
 }
 
+sub _get_group {
+  my ($req, $msg) = @_;
+
+  my $id = $req->cgi->param('id');
+  defined $id && $id =~ /^\d+$/
+    or do { $$msg = "Missing or invalid group id"; return };
+  require BSE::TB::SiteUserGroups;
+  my $group = BSE::TB::SiteUserGroups->getByPkey($id);
+  $group
+    or do { $$msg = "Unknown group id"; return };
+
+  $group;
+}
+
+sub req_grouplist {
+  my ($class, $req, $errors) = @_;
+
+  require BSE::TB::SiteUserGroups;
+  my @groups = BSE::TB::SiteUserGroups->all;
+
+  my $msg = $req->message($errors);
+
+  my $it = BSE::Util::Iterate->new;
+  my %acts;
+  %acts =
+    (
+     BSE::Util::Tags->basic(undef, $req->cgi, $req->cfg),
+     BSE::Util::Tags->secure($req),
+     BSE::Util::Tags->admin(undef, $req->cfg),
+     msg=>$msg,
+     message=>$msg,
+     $it->make_iterator(undef, 'group', 'groups', \@groups),
+    );
+
+  return $req->response('admin/users/grouplist', \%acts);
+}
+
+sub req_addgroupform {
+  my ($class, $req, $errors) = @_;
+
+  my $msg = $req->message($errors);
+
+  my %acts;
+  %acts =
+    (
+     BSE::Util::Tags->basic(undef, $req->cgi, $req->cfg),
+     BSE::Util::Tags->secure($req),
+     BSE::Util::Tags->admin(undef, $req->cfg),
+     msg=>$msg,
+     message=>$msg,
+     error_img => [ \&tag_error_img, $req->cfg, $errors ],
+    );
+
+  return $req->response('admin/users/groupadd', \%acts);
+}
+
+sub req_addgroup {
+  my ($class, $req) = @_;
+
+  require BSE::TB::SiteUserGroups;
+  my %fields = BSE::TB::SiteUserGroup->valid_fields;
+  my %rules = BSE::TB::SiteUserGroup->valid_rules;
+
+  my %errors;
+  $req->validate(errors=>\%errors,
+                fields=>\%fields,
+                rules=>\%rules,
+                section=>SITEUSER_GROUP_SECT)
+    or return $class->req_addgroupform($req, \%errors);
+
+  my $name = $req->cgi->param('name');
+  my $group = BSE::TB::SiteUserGroups->add($name);
+
+  return BSE::Template->get_refresh("$ENV{SCRIPT_NAME}?a_grouplist=1&m=Group+added", 
+                                   $req->cfg);  
+}
+
+sub req_editgroup {
+  my ($class, $req, $errors) = @_;
+
+  return $class->_common_group($req, $errors, 'admin/users/groupedit');
+}
+
+sub req_savegroup {
+  my ($class, $req) = @_;
+
+  my $msg;
+  my $group = _get_group($req, \$msg)
+    or return $class->req_grouplist($req, { id => $msg });
+
+  my %fields = BSE::TB::SiteUserGroup->valid_fields;
+  my %rules = BSE::TB::SiteUserGroup->valid_rules;
+
+  my %errors;
+  $req->validate(errors=>\%errors,
+                fields=>\%fields,
+                rules=>\%rules,
+                section=>SITEUSER_GROUP_SECT)
+    or return $class->req_editgroup($req, \%errors);
+  
+  $group->{name} = $req->cgi->param('name');
+  $group->save;
+
+  return BSE::Template->get_refresh("$ENV{SCRIPT_NAME}?a_grouplist=1&m=Group+saved", $req->cfg);
+}
+
+sub _common_group {
+  my ($class, $req, $errors, $template) = @_;
+
+  my $msg;
+  my $group = _get_group($req, \$msg)
+    or return $class->req_grouplist($req, { id=> $msg });
+
+  $msg = $req->message($errors);
+
+  my %acts;
+  %acts =
+    (
+     BSE::Util::Tags->basic(undef, $req->cgi, $req->cfg),
+     BSE::Util::Tags->secure($req),
+     BSE::Util::Tags->admin(undef, $req->cfg),
+     msg=>$msg,
+     message=>$msg,
+     error_img => [ \&tag_error_img, $req->cfg, $errors ],
+     group => [ \&tag_hash, $group ],
+    );
+
+  return $req->response($template, \%acts);
+}
+
+sub req_deletegroupform {
+  my ($class, $req, $errors) = @_;
+
+  return $class->_common_group($req, $errors, 'admin/users/groupdelete');
+}
+
+sub req_deletegroup {
+  my ($class, $req) = @_;
+
+  my $msg;
+  my $group = _get_group($req, \$msg)
+    or return $class->req_grouplist($req, { id=>$msg });
+
+  $group->remove;
+
+  return BSE::Template->get_refresh("$ENV{SCRIPT_NAME}?a_grouplist=1&m=Group+deleted", $req->cfg);
+}
+
+sub tag_ifMember {
+  my ($ruser, $members) = @_;
+
+  $$ruser or return 0;
+  exists $members->{$$ruser->{id}};
+}
+
+sub req_groupmemberform {
+  my ($class, $req, $errors) = @_;
+
+  my $msg;
+  my $group = _get_group($req, \$msg)
+    or return $class->req_grouplist($req, { id=>$msg });
+
+  $msg = $req->message($errors);
+
+  my %members = map { $_=> 1 } $group->member_ids;
+  my @siteusers = SiteUsers->all;
+
+  my $user;
+
+  my $it = BSE::Util::Iterate->new;
+  my %acts;
+  %acts =
+    (
+     BSE::Util::Tags->basic(undef, $req->cgi, $req->cfg),
+     BSE::Util::Tags->secure($req),
+     BSE::Util::Tags->admin(undef, $req->cfg),
+     msg=>$msg,
+     message=>$msg,
+     error_img => [ \&tag_error_img, $req->cfg, $errors ],
+     group => [ \&tag_hash, $group ],
+     $it->make_iterator(undef, 'siteuser', 'siteusers', \@siteusers, 
+                       undef, undef, \$user),
+     ifMember => [ \&tag_ifMember, \$user, \%members ],
+    );
+
+  return $req->response('admin/users/groupmembers', \%acts);
+}
+
+sub req_savegroupmembers {
+  my ($class, $req) = @_;
+
+  my $msg;
+  my $group = _get_group($req, \$msg)
+    or return $class->req_grouplist($req, { id=>$msg });
+
+  my $cgi = $req->cgi;
+  my %current_ids = map { $_ => 1 } $group->member_ids;
+  my @to_be_set = $cgi->param('set_is_member');
+  my %set_ids = map { $_ => 1 } $cgi->param('is_member');
+  my %all_ids = map { $_ => 1 } SiteUsers->all_ids;
+
+  for my $id (@to_be_set) {
+    next unless $all_ids{$id};
+
+    if ($set_ids{$id} && !$current_ids{$id}) {
+      $group->add_member($id);
+    }
+    elsif (!$set_ids{$id} && $current_ids{$id}) {
+      $group->remove_member($id);
+    }
+  }
+
+  return BSE::Template->get_refresh("$ENV{SCRIPT_NAME}?a_grouplist=1&m=Membership+saved", $req->cfg);
+}
+
 1;
index add47352906b198655d37e53f8b38dfba557ba5a..55efbe7edb247be2f6f007bdced9d8b0f47413ef 100644 (file)
@@ -18,9 +18,9 @@ my %statements =
   (
    Articles => 'select * from article',
    replaceArticle =>
-     'replace article values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',
+     'replace article values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',
    addArticle =>  
-     'insert article values (null, ?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',
+     'insert article values (null, ?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',
    deleteArticle => 'delete from article where id = ?',
    getArticleByPkey => 'select * from article where id = ?',
    
@@ -146,6 +146,7 @@ select distinct su.*
   where su.id = od.siteuser_id and od.id = oi.orderId 
         and oi.subscription_id <> -1
 SQL
+   siteuserAllIds => 'select id from site_users',
    getBSESiteuserImage => <<SQL,
 select * from bse_siteuser_images
   where siteuser_id = ? and image_id = ?
@@ -448,10 +449,36 @@ update bse_seminar_bookings
   set roll_present = ?
   where session_id = ? and siteuser_id = ?
 SQL
-  userSeminarSessionBookings => <<SQL,
+   userSeminarSessionBookings => <<SQL,
 select session_id 
   from bse_seminar_bookings sb, bse_seminar_sessions ss
 where ss.seminar_id = ? and ss.id = sb.session_id and siteuser_id = ?
+SQL
+   SiteUserGroups => 'select * from bse_siteuser_groups',
+   addSiteUserGroup => 'insert bse_siteuser_groups values(null,?)',
+   replaceSiteUserGroup => 'replace bse_siteuser_groups values(?,?)',
+   deleteSiteUserGroup => 'delete from bse_siteuser_groups where id = ?',
+   getSiteUserGroupByPkey => 'select * from bse_siteuser_groups where id = ?',
+   siteuserGroupMemberIds => <<SQL,
+select siteuser_id as "id" 
+from bse_siteuser_membership 
+where group_id = ?
+SQL
+   siteuserGroupAddMember => <<SQL,
+insert bse_siteuser_membership values(?,?)
+SQL
+   siteuserGroupDeleteMember => <<SQL,
+delete from bse_siteuser_membership where group_id = ? and siteuser_id = ?
+SQL
+    siteuserGroupDeleteAllMembers => <<SQL,
+delete from bse_siteuser_membership where group_id = ?
+SQL
+    siteuserMemberOfGroup => <<SQL,
+select * from bse_siteuser_membership 
+where siteuser_id = ? and group_id = ?
+SQL
+    siteuserGroupsForUser => <<SQL,
+select group_id as "id" from bse_siteuser_membership where siteuser_id = ?
 SQL
   );
 
diff --git a/site/cgi-bin/modules/BSE/Dynamic/Article.pm b/site/cgi-bin/modules/BSE/Dynamic/Article.pm
new file mode 100644 (file)
index 0000000..c9519db
--- /dev/null
@@ -0,0 +1,45 @@
+package BSE::Dynamic::Article;
+use strict;
+use BSE::Util::Tags qw(tag_hash);
+use BSE::Template;
+
+sub new {
+  my ($class, $req) = @_;
+
+  return bless { req=> $req }, $class;
+}
+
+sub generate {
+  my ($self, $article, $template) = @_;
+
+  my %acts = $self->tags($article);
+
+  my $result =
+    {
+     content => BSE::Template->replace($template, $self->{req}->cfg, \%acts),
+     type => BSE::Template->get_type($self->{req}->cfg, $article->{template}),
+    };
+
+  my @headers = "Content-Length: ".length $result->{content};
+  $result->{headers} = \@headers;
+
+  return $result;
+}
+
+sub tags {
+  my ($self, $article) = @_;
+
+  return
+    (
+     dynarticle => [ \&tag_hash, $article ],
+     BSE::Util::Tags->basic({}, $self->{req}->cgi, $self->{req}->cfg),
+    );
+}
+
+sub get_real_article {
+  my ($self, $article) = @_;
+
+  return $article;
+}
+
+1;
diff --git a/site/cgi-bin/modules/BSE/Dynamic/Catalog.pm b/site/cgi-bin/modules/BSE/Dynamic/Catalog.pm
new file mode 100644 (file)
index 0000000..835b290
--- /dev/null
@@ -0,0 +1,7 @@
+package BSE::Dynamic::Catalog;
+use strict;
+use base 'BSE::Dynamic::Article';
+
+# no specific behavious yet
+
+1;
diff --git a/site/cgi-bin/modules/BSE/Dynamic/Product.pm b/site/cgi-bin/modules/BSE/Dynamic/Product.pm
new file mode 100644 (file)
index 0000000..065a286
--- /dev/null
@@ -0,0 +1,12 @@
+package BSE::Dynamic::Product;
+use strict;
+use base 'BSE::Dynamic::Article';
+use Products;
+
+sub get_real_article {
+  my ($self, $article) = @_;
+
+  Products->getByPkey($article->{id});
+}
+
+1;
diff --git a/site/cgi-bin/modules/BSE/Dynamic/Seminar.pm b/site/cgi-bin/modules/BSE/Dynamic/Seminar.pm
new file mode 100644 (file)
index 0000000..50b4821
--- /dev/null
@@ -0,0 +1,12 @@
+package BSE::Dynamic::Seminar;
+use strict;
+use base 'BSE::Dynamic::Product;
+use BSE::TB::Seminars;
+
+sub get_real_article {
+  my ($self, $article) = @_;
+
+  BSE::TB::Seminars->getByPkey($article->{id});
+}
+
+1;
index 1cd9465cbcd4acc316894b2fb7eafc14a0990223..8f83c9a2ca91b7c4c364ec7e81a547c29bb01a05 100644 (file)
@@ -1311,6 +1311,10 @@ sub default_link_path {
 sub make_link {
   my ($self, $article) = @_;
 
+  if ($article->is_dynamic) {
+    return "/cgi-bin/page.pl?id=$article->{id}&title=".escape_uri($article->{title});
+  }
+
   my $article_uri = $self->link_path($article);
   my $link = "$article_uri/$article->{id}.html";
   my $link_titles = $self->{cfg}->entryBool('basic', 'link_titles', 0);
@@ -1390,6 +1394,9 @@ sub save_new {
   $data{createdBy} = $user ? $user->{logon} : '';
   $data{lastModifiedBy} = $user ? $user->{logon} : '';
   $data{created} =  now_sqldatetime();
+  $data{force_dynamic} = 0;
+  $data{cached_dynamic} = 0;
+  $data{inherit_siteuser_rights} = 1;
 
   $self->fill_new_data($req, \%data, $articles);
   for my $col (qw(titleImage imagePos template keyword)) {
@@ -1457,7 +1464,8 @@ sub save {
   $req->user_can(edit_save => $article)
     or return $self->edit_form($req, $article, $articles,
                               "You don't have access to save this article");
-  
+
+  my $old_dynamic = $article->is_dynamic;
   my $cgi = $req->cgi;
   my %data;
   for my $name ($article->columns) {
@@ -1533,6 +1541,12 @@ sub save {
     if defined $cgi->param('expire') && 
       $req->user_can('edit_field_edit_expire', $article);
   $article->{lastModified} =  now_sqldatetime();
+  if ($cgi->param('save_force_dynamic')) {
+    $article->{force_dynamic} = $cgi->param('force_dynamic') ? 1 : 0;
+  }
+
+  # this need to go last
+  $article->update_dynamic($self->{cfg});
   if ($article->{link} && 
       !$self->{cfg}->entry('protect link', $article->{id})) {
     my $article_uri = $self->make_link($article);
@@ -1545,12 +1559,54 @@ sub save {
 
   $article->save();
 
+  # if we changed dynamic status, we need to update it for the kids too
+  my @extra_regen;
+  if ($article->is_dynamic != $old_dynamic) {
+    @extra_regen = $self->update_child_dynamic($article, $articles, $req);
+  }
+
   use Util 'generate_article';
-  generate_article($articles, $article) if $Constants::AUTO_GENERATE;
+  if ($Constants::AUTO_GENERATE) {
+    generate_article($articles, $article);
+    for my $regen_id (@extra_regen) {
+      my $regen = $articles->getByPkey($regen_id);
+      Util::generate_low($articles, $article, $self->{cfg});
+    }
+  }
 
   return $self->refresh($article, $cgi, undef, 'Article saved');
 }
 
+sub update_child_dynamic {
+  my ($self, $article, $articles, $req) = @_;
+
+  my $cfg = $req->cfg;
+  my @stack = $article->children;
+  my @regen;
+  while (@stack) {
+    my $workart = pop @stack;
+    my $old_dynamic = $workart->is_dynamic; # before update
+    $workart->update_dynamic($cfg);
+    if ($old_dynamic != $workart->is_dynamic) {
+      # update the link
+      if ($article->{link} && !$cfg->entry('protect link', $workart->{id})) {
+       my $editor;
+       ($editor, $workart) = $self->article_class($workart, $articles, $cfg);
+
+       my $uri = $editor->make_link($workart);
+       $workart->setLink($uri);
+      }
+
+      # save dynamic cache change and link if that changed
+      $workart->save;
+    }
+    push @stack, $workart->children;
+    push @regen, $workart->{id};
+  }
+
+  @regen;
+}
+
 sub sql_date {
   my $str = shift;
   my ($year, $month, $day);
index ca647f0b8a6dcbcd68427b98e18e4f485db38508..964d8508f87f4fbc56590561d2203c257e65789a 100644 (file)
@@ -8,4 +8,42 @@ sub new {
   return bless \%parms, $class;
 }
 
+sub article_class_id {
+  my ($class, $id, $articles, $cfg) = @_;
+
+  return unless defined $id and $id =~ /^\d+$/;
+
+  my $article = $articles->getByPkey($id)
+    or return;
+  
+  return $class->article_class($article, $articles, $cfg);
+}
+
+sub article_class {
+  my ($class, $article, $articles, $cfg) = @_;
+
+  my $editclass = "BSE::Edit::Article";
+  $editclass = $article->{generator};
+  $editclass =~ s/^(?:BSE::)?Generate::/BSE::Edit::/;
+  my $obj = _get_class($editclass, $cfg);
+  if ($obj) {
+    $article = $obj->get_article($articles, $article);
+  }
+  return ($obj, $article);
+}
+
+sub _get_class {
+  my ($class, $cfg) = @_;
+
+  (my $file = $class . ".pm") =~ s!::!/!g;
+  eval {
+    require $file;
+  };
+  if ($@) {
+    print STDERR "Loading $class: $@\n";
+    return;
+  }
+  return $class->new(cfg=>$cfg, db=>BSE::DB->single);
+}
+
 1;
index 83adb47d82440fb3c1fa985afe6f98343a5e1d69..270d93775609b3d6b269c29f3c170badf6871f01 100644 (file)
@@ -87,8 +87,13 @@ sub default_link_path {
 sub make_link {
   my ($self, $article) = @_;
 
-  my $shop_uri = $self->link_path($article);
   my $urlbase = $self->{cfg}->entryVar('site', 'secureurl');
+
+  if ($article->is_dynamic) {
+    return "$urlbase/cgi-bin/page.pl?id=$article->{id}&title=".escape_uri($article->{title});
+  }
+
+  my $shop_uri = $self->link_path($article);
   return $urlbase.$shop_uri."/shop$article->{id}.html";
 }
 
index 515e1c4e677680e13613c08093e1a0e0e7ecf966..fad1b86fc774a568a330ccd7cf02951c559fd8d5 100644 (file)
@@ -233,8 +233,13 @@ sub default_link_path {
 sub make_link {
   my ($self, $article) = @_;
 
-  my $shop_uri = $self->link_path($article);
   my $urlbase = $self->{cfg}->entryVar('site', 'secureurl');
+
+  if ($article->is_dynamic) {
+    return "$urlbase/cgi-bin/page.pl?id=$article->{id}&title=".escape_uri($article->{title});
+  }
+
+  my $shop_uri = $self->link_path($article);
   return $urlbase.$shop_uri."/shop$article->{id}.html";
 }
 
diff --git a/site/cgi-bin/modules/BSE/TB/SiteUserGroup.pm b/site/cgi-bin/modules/BSE/TB/SiteUserGroup.pm
new file mode 100644 (file)
index 0000000..db3fa57
--- /dev/null
@@ -0,0 +1,69 @@
+package BSE::TB::SiteUserGroup;
+use strict;
+use base 'Squirrel::Row';
+
+sub columns {
+  qw(id name);
+}
+
+sub primary { 'id' }
+
+sub valid_fields {
+  my ($class) = @_;
+
+  return
+    (
+     name =>
+     {
+      description=>'Group Name',
+      rules=>'required;no_star_at_front',
+     },
+    );
+}
+
+sub valid_rules {
+  my ($class) = @_;
+
+  return
+    (
+     no_star_at_front =>
+     {
+      nomatch => qr/^\*/,
+      error=>'Group names may not start with *',
+     },
+    );
+}
+
+sub remove {
+  my ($self) = @_;
+
+  # remove any permissions and members for this group
+  print STDERR "** FIXME ", __FILE__, " ", __LINE__, "\n";
+
+  # remove any members
+  BSE::DB->single->run(siteuserGroupDeleteAllMembers => $self->{id});
+
+  $self->SUPER::remove();
+}
+
+sub member_ids {
+  my ($self) = @_;
+
+  map $_->{id}, BSE::DB->single->query(siteuserGroupMemberIds => $self->{id});
+}
+
+sub add_member {
+  my ($self, $id) = @_;
+
+  eval {
+    BSE::DB->single->run(siteuserGroupAddMember => $self->{id}, $id);
+  };
+}
+
+sub remove_member {
+  my ($self, $id) = @_;
+
+  BSE::DB->single->run(siteuserGroupDeleteMember => $self->{id}, $id);
+}
+
+1;
diff --git a/site/cgi-bin/modules/BSE/TB/SiteUserGroups.pm b/site/cgi-bin/modules/BSE/TB/SiteUserGroups.pm
new file mode 100644 (file)
index 0000000..5265deb
--- /dev/null
@@ -0,0 +1,8 @@
+package BSE::TB::SiteUserGroups;
+use strict;
+use base 'Squirrel::Table';
+use BSE::TB::SiteUserGroup;
+
+sub rowClass { 'BSE::TB::SiteUserGroup' }
+
+1;
diff --git a/site/cgi-bin/modules/BSE/UI/Page.pm b/site/cgi-bin/modules/BSE/UI/Page.pm
new file mode 100644 (file)
index 0000000..d45fa62
--- /dev/null
@@ -0,0 +1,90 @@
+package BSE::UI::Page;
+use strict;
+use Articles;
+
+# we don't do anything fancy on dispatch yet, so don't use the 
+# dispatch classes
+sub dispatch {
+  my ($class, $req) = @_;
+
+  my $cgi = $req->cgi;
+  my $id = $cgi->param('id');
+  $id && $id =~ /^\d+$/
+    or return $class->error($req, "required id parameter not present or invalid");
+  my $article = Articles->getByPkey($id)
+    or return $class->error($req, "unknown article id $id");
+
+  # get the dynamic generate for this article type
+  my $gen_class = $article->{generator};
+  $gen_class =~ s/.*\W//;
+  $gen_class = "BSE::Dynamic::".$gen_class;
+  (my $gen_file = $gen_class . ".pm") =~ s!::!/!g;
+  eval {
+    require $gen_file;
+  };
+  $@ and return $class->error($req, $@);
+  my $gen = $gen_class->new($req);
+  $article = $gen->get_real_article($article)
+    or return $class->error($req, "Cannot get full article for $id");
+
+  $article->is_dynamic
+    or print STDERR "** page.pl called for non-dynamic article $id\n";
+
+  my $cfg = $req->cfg;
+  my $dynamic_path = $cfg->entryVar('paths', 'dynamic_cache');
+  my $debug_jit = $cfg->entry('debug', 'jit_dynamic_regen');
+  my $srcname = $dynamic_path . "/" . $article->{id} . ".html";
+  my $dynamic_pregen = $cfg->entry('basic', 'jit_dynamic_pregen');
+  my $template;
+  if (-e $srcname) {
+    if (open SRC, "< $srcname") {
+      local $/;
+      $template = <SRC>;
+      close SRC;
+    }
+    else {
+      print STDERR "** PAGE: $id - page file exists but isn't readable\n";
+    }
+  }
+  unless (defined $template) {
+    $debug_jit && !$dynamic_pregen
+      and print STDERR "** JIT: $id - pregen page not found but JIT off\n";
+    
+    $template = $class->_generate_pregen($req, $article, $srcname);
+  }
+
+  return $gen->generate($article, $template);
+}
+
+# returns an error page
+sub error {
+  my ($class, $req, $msg) = @_;
+
+  require BSE::UI::Dispatch;
+  return BSE::UI::Dispatch->error($req, { error => $msg });
+}
+
+sub _generate_pregen {
+  my ($class, $req, $article, $srcname) = @_;
+
+  my $articles = 'Articles';
+  my $genname = $article->{generator};
+  eval "use $genname";
+  $@ && die $@;
+  my $gen = $genname->new(articles=>$articles, cfg=>$req->cfg, top=>$article);
+
+  my $content = $gen->generate($article, $articles);
+
+  if (open CONTENT, "> $srcname") {
+    binmode CONTENT;
+    print CONTENT $content;
+    close CONTENT;
+  }
+  else {
+    print STDERR "** PAGE: $article->{id} - cannot create $srcname: $!\n";
+  }
+
+  $content;
+}
+
+1;
index c050f4266724e35fc94872ba462c77787fd74b6c..4f800f06d750fe73485683cf1472aabee496e179 100644 (file)
@@ -4,7 +4,8 @@ require Exporter;
 use vars qw(@EXPORT_OK %EXPORT_TAGS @ISA);
 @ISA = qw(Exporter);
 @EXPORT_OK = 
-  qw(dh_parse_date dh_parse_date_sql dh_parse_time dh_parse_time_sql);
+  qw(dh_parse_date dh_parse_date_sql dh_parse_time dh_parse_time_sql
+     dh_parse_sql_date dh_parse_sql_datetime dh_strftime_sql_datetime);
 %EXPORT_TAGS =
   (
    all => \@EXPORT_OK,
@@ -128,4 +129,38 @@ sub dh_parse_time_sql {
   sprintf("%02d:%02d:%02d", $hour, $min, $sec);
 }
 
+sub dh_parse_sql_date {
+  my ($date) = @_;
+
+  $date =~ /^(\d+)\D+(\d+)\D+(\d+)/
+    or return;
+
+  return (0+$1, 0+$2, 0+$3);
+}
+
+sub dh_parse_sql_datetime {
+  my ($datetime) = @_;
+
+  my ($year, $month, $day, $hour, $min, $sec) = 
+    ($datetime =~ /^(\d+)\D+(\d+)\D+(\d+)\D+(\d+)\D+(\d+)\D+(\d+)/)
+      or return;
+
+  return ($year, 0+$month, 0+$day, 0+$hour, 0+$min, 0+$sec);
+}
+
+sub dh_strftime_sql_datetime {
+  my ($format, $datetime) = @_;
+
+  my ($year, $month, $day, $hour, $min, $sec) = 
+    dh_parse_sql_datetime($datetime)
+      or return;
+
+  $year -= 1900;
+  --$month;
+
+  require POSIX;
+  return POSIX::strftime($format, $sec, $min, $hour, $day, $month, $year, 
+                        0, 0);
+}
+
 1;
index 733a412263bc04698d89530102d4cb21ed0d1e73..2460097315c53286d3b634f6c601b5f2276855e0 100644 (file)
@@ -473,7 +473,7 @@ sub show_tags {
      ([ \&iter_levelN_links, 0, $report->{sql}[0]{links}, $work[0], 
        \$index[0] ], 
       'level1_link', 'level1_links', undef, undef, 'NoCache'),
-     level1_col => [ \&tag_levelN_col, \$level1_row ],
+     level1_byname => [ \&tag_levelN_col, \$level1_row ],
      level1_sum => 
      [ \&tag_levelN_sum, $results[0]{rows}, $results[0]{names_hash} ],
      report => [ \&tag_hash, $report ],
index 39e05602edaa0d9999063ab8e19a82b93429fe30..1a4536275f861e2b53bdf6d17b3fdd8c359689c9 100644 (file)
@@ -476,6 +476,8 @@ HTML
      BSE::Util::Tags->make_iterator(\@allkids, 'allkid', 'allkids', \$allkids_index),
      BSE::Util::Tags->make_iterator(\@stepparents, 'stepparent', 'stepparents'),
      top => [ \&tag_top, $self, $article ],
+     ifDynamic => 
+     scalar(UNIVERSAL::isa($top, 'Article') ? $top->is_dynamic : 0),
     );
 
   if ($abs_urls) {
index 68930d817d29a15a5928967a4b01840d09885cf6..96af9ffbb5763a25d4a3a9051ba6ca175b5c0d85 100644 (file)
@@ -403,4 +403,20 @@ sub seminar_sessions_booked {
     BSE::DB->query(userSeminarSessionBookings => $seminar_id, $self->{id});
 }
 
+sub is_member_of {
+  my ($self, $group) = @_;
+
+  my $group_id = ref $group ? $group->{id} : $group;
+
+  my @result = BSE::DB->query(siteuserMemberOfGroup => $self->{id}, $group_id);
+
+  return scalar(@result);
+}
+
+sub group_ids {
+  my ($self) = @_;
+
+  map $_->{id}, BSE::DB->query(siteuserGroupsForUser => $self->{id});
+}
+
 1;
index 3e8e6d266672ee206186feffc2f9d0e35885dbba..68daa811f86160f5fdee71415b74127f12f8e758 100644 (file)
@@ -15,4 +15,10 @@ sub all_subscribers {
   $class->getSpecial('allSubscribers');
 }
 
+sub all_ids {
+  my ($class) = @_;
+
+  map $_->{id}, BSE::DB->query('siteuserAllIds');
+}
+
 1;
index c672dd369d41c7735e20e00f86089d135f20390d..24705c11da58bd06c73641383e755fae03ec0e0d 100644 (file)
@@ -33,16 +33,33 @@ sub generate_low {
   my ($articles, $article, $cfg) = @_;
 
   $cfg ||= BSE::Cfg->new;
+
+  my $outname;
+  if ($article->is_dynamic) {
+    my $debug_jit = $cfg->entry('debug', 'jit_dynamic_regen');
+    my $dynamic_path = $cfg->entryVar('paths', 'dynamic_cache');
+    $outname = $dynamic_path . "/" . $article->{id} . ".html";
+    if ($article->{flags} !~ /R/ && 
+       $cfg->entry('basic', 'jit_dynamic_pregen')) {
+      $debug_jit and print STDERR "JIT: $article->{id} - deleting $outname\n";
+      # just delete the file, page.pl will make it if needed
+      unlink $outname;
+      return;
+    }
+  }
+  else {
+    $outname = $article->{link};
+    $outname =~ s!/\w*$!!;
+    $outname =~ s{^\w+://[\w.-]+(?::\d+)?}{};
+    $outname = $CONTENTBASE . $outname;
+    $outname =~ s!//+!/!;
+  }
+
   my $genname = $article->{generator};
   eval "use $genname";
   $@ && die $@;
   my $gen = $genname->new(articles=>$articles, cfg=>$cfg, top=>$article);
 
-  my $outname = $article->{link};
-  $outname =~ s!/\w*$!!;
-  $outname =~ s{^\w+://[\w.-]+(?::\d+)?}{};
-  $outname = $CONTENTBASE . $outname;
-  $outname =~ s!//+!/!;
   my $content = $gen->generate($article, $articles);
   my $tempname = $outname . ".work";
   unlink $tempname;
diff --git a/site/cgi-bin/page.pl b/site/cgi-bin/page.pl
new file mode 100755 (executable)
index 0000000..0af05cf
--- /dev/null
@@ -0,0 +1,19 @@
+#!/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 BSE::DB;
+use BSE::Request;
+use BSE::Template;
+use Carp 'confess';
+use BSE::UI::Page;
+
+$SIG{__DIE__} = sub { confess $@ };
+
+my $req = BSE::Request->new;
+
+my $result = BSE::UI::Page->dispatch($req);
+
+BSE::Template->output_result($req, $result);
index e547cf231db8974dff944f39b08f57e984d5f507..4bb269f11767b8f1547e6973bfd75676b5dc54d0 100644 (file)
@@ -10,6 +10,31 @@ Maybe I'll add some other bits here.
 
 =head1 CHANGES
 
+=head2 0.15_19
+
+Minor release so I can patch some other things.
+
+=over
+
+=item *
+
+renamed the new level1_col tag to level1_byname to prevent a conflict
+with the older level1_col tag from the level1_cols iterator.
+
+=item *
+
+pages can now be generated dynamically, though I'm still working on
+the UI side of making them flagged dynamic.  Structure exists to make
+it possible to add extra dynamic tags on per article type basis, but
+isn't used yet.
+
+=item *
+
+you can create site user groups, and put users in them, and delete
+them.
+
+=back
+
 =head2 0.15_18
 
 =over
index 87a60f8377ef22c206a2a3003e59d68d5d18d6db..84f22a0caa2594864c9e9abeba453d01a9e19a8d 100644 (file)
@@ -91,6 +91,11 @@ Where uploaded siteuser images are stored.  This must be set in the
 config file.  The default bse.cfg include an entry to use the current
 values of [paths].downloads
 
+=item dynamic_cache
+
+Pregenerated dynamic article pages are stored here.  This must be
+defined if you site contains any dynamicly generated pages.
+
 =back
 
 =head2 [extensions]
@@ -233,6 +238,11 @@ The name of the custom class for your site.  This is currently only
 used for article editing customizations.  This class should derive
 from BSE::CustomBase.  Default: BSE::Custom.
 
+=item jit_dynamic_regen
+
+If this is true, then pre-generation for dynamic pages will be delayed
+until the page is displayed to a user.  Default: off.
+
 =back
 
 =head2 [mail]
@@ -679,6 +689,11 @@ from the database.
 If non-zero then subscription expiry date calculations are dumped to
 STDERR.
 
+=item jit_dynamic_regen
+
+If non-zero then information about jit_dynamic_regen is sent to
+STDERR.
+
 =back
 
 =head2 [uri]
index 82e598d0e082f72169411225b235c6578342ffb2..a9ffd33e2d2ce99ab017e9a35efe5ff2cd827eb2 100644 (file)
@@ -6,6 +6,7 @@
 <a href="mailto:<:siteuser email:>">Email</a>
 <:ifUserorders:>| <a href="/cgi-bin/admin/siteusers.pl?a_edit=1&amp;id=<:siteuser id:>&amp;_t=orders">Orders</a><:or:><:eif:> |
 <a href="/cgi-bin/admin/admin_seminar.pl?a_addattendseminar=1&amp;siteuser_id=<:siteuser id:>">Add to seminar</a> |
+<a href="<:script:>?a_edit=1&amp;id=<:siteuser id:>&amp;_t=groups">Groups</a> |
 </p>
 
 <:ifMessage:>
diff --git a/site/templates/admin/users/edit_groups.tmpl b/site/templates/admin/users/edit_groups.tmpl
new file mode 100644 (file)
index 0000000..cd33380
--- /dev/null
@@ -0,0 +1,43 @@
+<:wrap admin/xbase.tmpl title=>"Edit Site Member - Groups":>
+<h1>Edit Site Member - Groups</h1>
+<p>
+| <a href="/cgi-bin/admin/menu.pl">Admin menu</a> |
+<a href="/cgi-bin/admin/siteusers.pl">Site Members</a> |
+<a href="mailto:<:siteuser email:>">Email</a>
+<:ifUserorders:>| <a href="/cgi-bin/admin/siteusers.pl?a_edit=1&amp;id=<:siteuser id:>&amp;_t=orders">Orders</a><:or:><:eif:> |
+<a href="/cgi-bin/admin/admin_seminar.pl?a_addattendseminar=1&amp;siteuser_id=<:siteuser id:>">Add to seminar</a> |
+</p>
+
+<:ifMessage:>
+<p><b><:message:></b></p>
+<:or:><:eif:> 
+
+<form method="post" action="<:script:>">
+<input type="hidden" name="id" value="<:siteuser id:>" />
+        <table cellpadding="6" border="0" cellspacing="1">
+  <tr>
+    <th>Group Name</th>
+    <th>Member</th>
+  </tr>
+<:if Groups:>
+<:iterator begin groups:>
+  <tr>
+   <td><:group name:></td>
+   <td>
+  <input type="hidden" name="set_group_id" value="<:group id:>" />
+  <input type="checkbox" name="group_id" value="<:group id:>" <:ifMember:>checked="checked"<:or:><:eif:> />
+   </td>
+  </tr>
+<:iterator end groups:>
+<:or Groups:>
+  <tr>
+    <td colspan="2">There are no groups defined</td>
+  </tr>
+<:eif Groups:>
+          <tr> 
+            <td colspan="2" align="right"> 
+              <input type="submit" name="a_save" value="  Save User  " />
+            </td>
+          </tr>
+        </table>
+</form>
diff --git a/site/templates/admin/users/groupadd.tmpl b/site/templates/admin/users/groupadd.tmpl
new file mode 100644 (file)
index 0000000..77983df
--- /dev/null
@@ -0,0 +1,26 @@
+<:wrap admin/xbase.tmpl title=>"Add Member Group":>
+<h2>Add Member Group</h2>
+<p>
+| 
+<a href="/cgi-bin/admin/menu.pl">Admin menu</a>
+|
+<a href="<:script:>">Member List</a>
+|
+<a href="<:script:>?a_grouplist=1">Group List</a>
+|
+</p>
+<:ifMsg:><p><:msg:></p><:or:><:eif:>
+
+<form method="post" action="<:script:>" name="addgroup">
+<table>
+<tr>
+  <th>Group Name:</th>
+  <td><input type="text" name="name" value="<:old name:>" /></td>
+  <td><:error_img name:></td>
+</tr>
+<tr>
+  <td colspan="2"><input type="submit" name="a_addgroup" value="Add Group" /></td>
+  <td>&nbsp;</td>
+</tr>
+</table>
+</form>
\ No newline at end of file
diff --git a/site/templates/admin/users/groupdelete.tmpl b/site/templates/admin/users/groupdelete.tmpl
new file mode 100644 (file)
index 0000000..bd18f1e
--- /dev/null
@@ -0,0 +1,25 @@
+<:wrap admin/xbase.tmpl title=>"Delete Member Group":>
+<h2>Add Member Group</h2>
+<p>
+| 
+<a href="/cgi-bin/admin/menu.pl">Admin menu</a>
+|
+<a href="<:script:>">Member List</a>
+|
+<a href="<:script:>?a_grouplist=1">Group List</a>
+|
+</p>
+<:ifMsg:><p><:msg:></p><:or:><:eif:>
+
+<form method="post" action="<:script:>" name="deletegroup">
+<input type="hidden" name="id" value="<:group id:>" />
+<table>
+<tr>
+  <th>Group Name:</th>
+  <td><:group name:></td>
+</tr>
+<tr>
+  <td colspan="2"><input type="submit" name="a_deletegroup" value="Delete Group" /></td>
+</tr>
+</table>
+</form>
\ No newline at end of file
diff --git a/site/templates/admin/users/groupedit.tmpl b/site/templates/admin/users/groupedit.tmpl
new file mode 100644 (file)
index 0000000..16f44a1
--- /dev/null
@@ -0,0 +1,27 @@
+<:wrap admin/xbase.tmpl title=>"Edit Member Group":>
+<h2>Add Member Group</h2>
+<p>
+| 
+<a href="/cgi-bin/admin/menu.pl">Admin menu</a>
+|
+<a href="<:script:>">Member List</a>
+|
+<a href="<:script:>?a_grouplist=1">Group List</a>
+|
+</p>
+<:ifMsg:><p><:msg:></p><:or:><:eif:>
+
+<form method="post" action="<:script:>" name="editgroup">
+<input type="hidden" name="id" value="<:group id:>" />
+<table>
+<tr>
+  <th>Group Name:</th>
+  <td><input type="text" name="name" value="<:old name group name:>" /></td>
+  <td><:error_img name:></td>
+</tr>
+<tr>
+  <td colspan="2"><input type="submit" name="a_savegroup" value="Save Group" /></td>
+  <td>&nbsp;</td>
+</tr>
+</table>
+</form>
\ No newline at end of file
diff --git a/site/templates/admin/users/grouplist.tmpl b/site/templates/admin/users/grouplist.tmpl
new file mode 100644 (file)
index 0000000..c13a2e5
--- /dev/null
@@ -0,0 +1,35 @@
+<:wrap admin/xbase.tmpl title=>"Member Groups":>
+<h2>Member Groups</h2>
+<p>
+| 
+<a href="/cgi-bin/admin/menu.pl">Admin menu</a>
+|
+<a href="<:script:>">Member List</a>
+|
+<a href="<:script:>?a_addgroupform=1">Add New Group</a>
+|
+</p>
+<:ifMsg:><p><b><:msg:></b></p><:or:><:eif:>
+
+<table>
+<tr>
+  <th>Group</th>
+  <td>&nbsp;</td>
+</tr>
+<:if Groups:>
+<:iterator begin groups:>
+<tr>
+  <td><:group name:></td>
+  <td>
+    <a href="<:script:>?a_editgroup=1&amp;id=<:group id:>">Edit</a>
+    <a href="<:script:>?a_deletegroupform=1&amp;id=<:group id:>">Delete</a>
+    <a href="<:script:>?a_groupmemberform=1&amp;id=<:group id:>">Members</a>
+  </td>
+</tr>
+<:iterator end groups:>
+<:or Groups:>
+<tr>
+  <td colspan="2">No member groups defined</td>
+</tr>
+<:eif Groups:>
+</table>
diff --git a/site/templates/admin/users/groupmembers.tmpl b/site/templates/admin/users/groupmembers.tmpl
new file mode 100644 (file)
index 0000000..fda2d03
--- /dev/null
@@ -0,0 +1,38 @@
+<:wrap admin/xbase.tmpl title=>"Group Members":>
+<h1>Group Members: <:group name:></h1>
+<p>
+| <a href="/cgi-bin/admin/menu.pl">Admin menu</a> | <a href="<:script:>?a_grouplist=1">Groups</a> |
+<:if UserCan siteuser_add :>
+<a href="<:script:>?a_addform=1">Add Member</a> |<:or UserCan:><:eif UserCan:>
+<:ifMessage:>
+<p><b><:message:></b></p>
+<:or:><:eif:> 
+
+<form method="post" action="<:script:>">
+<input type="hidden" name="id" value="<:group id:>" />
+<table cellpadding="6" border="0" cellspacing="1">
+  <tr> 
+            <th bgcolor="#FFFFFF" nowrap> Logon</th>
+            <th bgcolor="#FFFFFF" nowrap> First Name</th>
+            <th bgcolor="#FFFFFF" nowrap> Last Name</th>
+           <th bgcolor="#FFFFFF" nowrap>Member</th>
+          </tr>
+          <:if Siteusers:> <: iterator begin siteusers :> 
+          <tr bgcolor="#FFFFFF"> 
+            <td nowrap> <a href="<:script:>?a_edit=1&id=<:siteuser id:>"><:siteuser userId:></a></td>
+            <td valign="top"><:siteuser name1:></td>
+            <td valign="top"><:siteuser name2:></td>
+            <td valign="top"><input type="hidden" name="set_is_member" value="<:siteuser id:>" /><input type="checkbox" name="is_member" value="<:siteuser id:>" <:ifMember:>checked="checked"<:or:><:eif:> /></td>
+          </tr>
+          <: iterator end siteusers :> 
+  <tr>
+   <td colspan="4"><input type="submit" name="a_savegroupmembers" value="Save Group Membership" /></td>
+  </tr>
+          <:or Siteusers:> 
+          <tr bgcolor="#FFFFFF"> 
+            <td colspan="4" align="center">Your system has no users.</td>
+          </tr>
+          <:eif Siteusers:> 
+</table>
+</form>
+
index 7ee11031cf7362146fea1b9b3cd1cf532cc85056..e258eab09acddcac14504193497638fe74098d04 100644 (file)
@@ -1,7 +1,7 @@
 <:wrap admin/xbase.tmpl title=>"Admin Site Members":>
 <h1>Admin Site Members</h1>
 <p>
-| <a href="/cgi-bin/admin/menu.pl">Admin menu</a> |
+| <a href="/cgi-bin/admin/menu.pl">Admin menu</a> | <a href="<:script:>?a_grouplist=1">Groups</a> |
 <:if UserCan siteuser_add :>
 <a href="<:script:>?a_addform=1">Add Member</a> |<:or UserCan:><:eif UserCan:>
 <:ifMessage:>
index 6414c40fddea7af080d2617d12170a1ef342c2ec..0d41be0218cb95992787c8434c32a1a9605e681f 100644 (file)
@@ -46,6 +46,9 @@ my @prebuilt =
     createdBy=>'',
     author=>'',
     pageTitle=>'',
+    force_dynamic => 0,
+    cached_dynamic => 0,
+    inherit_siteuser_rights => 1,
    },
    {
     # the invisible subsection for what's hot
@@ -77,6 +80,9 @@ my @prebuilt =
     createdBy=>'',
     author=>'',
     pageTitle=>'',
+    force_dynamic => 0,
+    cached_dynamic => 0,
+    inherit_siteuser_rights => 1,
    },
    {
     id=>3,
@@ -107,6 +113,9 @@ my @prebuilt =
     createdBy=>'',
     author=>'',
     pageTitle=>'',
+    force_dynamic => 0,
+    cached_dynamic => 0,
+    inherit_siteuser_rights => 1,
    },
    {
     id=>4,
@@ -137,6 +146,9 @@ my @prebuilt =
     createdBy=>'',
     author=>'',
     pageTitle=>'',
+    force_dynamic => 0,
+    cached_dynamic => 0,
+    inherit_siteuser_rights => 1,
    },
    {
     id=>5,
@@ -167,6 +179,9 @@ my @prebuilt =
     createdBy=>'',
     author=>'',
     pageTitle=>'',
+    force_dynamic => 0,
+    cached_dynamic => 0,
+    inherit_siteuser_rights => 1,
    },
    {
     id=>6,
@@ -427,6 +442,9 @@ EOS
     createdBy=>'',
     author=>'',
     pageTitle=>'',
+    force_dynamic => 0,
+    cached_dynamic => 0,
+    inherit_siteuser_rights => 1,
    },
    {
     id=>7,
@@ -461,6 +479,9 @@ EOS
     createdBy=>'',
     author=>'',
     pageTitle=>'',
+    force_dynamic => 0,
+    cached_dynamic => 0,
+    inherit_siteuser_rights => 1,
    },
    {
     id=>8,
@@ -495,6 +516,9 @@ EOS
     createdBy=>'',
     author=>'',
     pageTitle=>'',
+    force_dynamic => 0,
+    cached_dynamic => 0,
+    inherit_siteuser_rights => 1,
    },
   );
 
index bb36cfba0f7d5fb692f7580dc27659aca2d85d8c..708b357622fb5c9b92935c3d87606bf65aabb4b8 100644 (file)
@@ -3,7 +3,7 @@ use strict;
 use BSE::Test qw(make_ua ok fetch_ok base_url config);
 
 ++$|;
-print "1..60\n";
+print "1..62\n";
 my $baseurl = base_url;
 ok($baseurl =~ /^http:/, "basic check of base url");
 my $ua = make_ua;
@@ -70,3 +70,5 @@ fetch_ok($ua, "reorder", "$baseurl/cgi-bin/admin/reorder.pl",
 
 fetch_ok($ua, 'fmail', "$baseurl/cgi-bin/fmail.pl",
         qr!name="form"!);
+fetch_ok($ua, 'page.pl?id=1', "$baseurl/cgi-bin/page.pl?id=1",
+        qr!welcome\s+to\stest\s+server!i);
index 1e16d0d3b26e5264b867cb9517d6cb735eba64e3..8790e5d3db3bc7ed3267fc02481c83d241bce241 100644 (file)
@@ -1,13 +1,13 @@
 #!perl -w
 use strict;
-use Test::More tests=>37;
+use Test::More tests=>42;
 
 my $gotmodule;
 BEGIN { $gotmodule = use_ok('DevHelp::Date', ':all'); }
 
 SKIP:
 {
-  skip "couldn't load module", 36 unless $gotmodule;
+  skip "couldn't load module", 41 unless $gotmodule;
   my $msg;
   is_deeply([ dh_parse_time("10:00", \$msg) ], [ 10, 0, 0 ], "parse 10:00");
   is($msg, undef, "no error");
@@ -68,4 +68,16 @@ SKIP:
   undef $msg;
   is(dh_parse_time_sql("2:30pm"), "14:30:00", "2:30pm to sql");
   is($msg, undef, "no error");
+
+  # parse SQL date
+  is_deeply([ dh_parse_sql_date("2005-07-12") ], [ 2005, 7, 12 ],
+           "simple sql date parse");
+  is_deeply([ dh_parse_sql_date("20") ], [ ],
+           "invalid sql date parse");
+  is_deeply([ dh_parse_sql_datetime("2005-06-30 12:00:05") ],
+           [ 2005, 6, 30, 12, 0, 5 ], "parse SQL date time");
+  is_deeply([ dh_parse_sql_datetime("2005-06-30 12") ],
+           [ ], "invalid parse SQL date time");
+  is(dh_strftime_sql_datetime("%d/%m/%Y", "2005-06-30 12:00:05"),
+     "30/06/2005", "dh_strftime_sql_datetime");
 }
index 9c67aae8517732d74fc73ba3da4d4ef70a7e6df6..e0a86ae48648e0537983d8995789ef2a4c76da21 100644 (file)
@@ -262,6 +262,7 @@ sub add_article {
      level => 1, listed=>1, lastModified => sql_datetime(time), flags=>'',
      lastModifiedBy=>'t20gen', created=>sql_datetime(time),
      createdBy=>'t20gen', author=>'', pageTitle=>'',
+     cached_dynamic => 0, force_dynamic=>0, inherit_siteuser_rights => 1,
     );
   for my $key (%defaults) {
     unless (exists $parms{$key}) {
index 2c8521e8ec3189ed9b16bd69de55c8db4b2cabcb..f24abff22345c6974f07348a264bd9d14c9c30e2 100644 (file)
@@ -267,6 +267,7 @@ sub add_article {
      level => 1, listed=>1, lastModified => sql_datetime(time), flags=>'',
      lastModifiedBy=>'t21gencat', created=>sql_datetime(time),
      createdBy=>'t21gencat', author=>'', pageTitle=>'',
+     cached_dynamic => 0, force_dynamic=>0, inherit_siteuser_rights => 1,
     );
   for my $key (%defaults) {
     unless (exists $parms{$key}) {
index 20186472ca8f0baaaa208da966d399700855bcd9..1ea286533b4ff93bd4b6c7e937888e30bfa7b989 100644 (file)
--- a/test.cfg
+++ b/test.cfg
@@ -11,7 +11,7 @@ dbclass = BSE::DB::Mysql
 sessionclass = Apache::Session::MySQL
 # the location of mysql
 mysql = mysql
-basic.access_control=1
+basic.access_control=0
 #basic.htusers = /home/httpd/bsetest/htdocs/images/.htusers
 #article uris.9 = /test
 paths.htpasswd=/home/httpd/bsetest/data/htusers