ACLs: finish group edit (agents + rights) & browse
authorivan <ivan>
Sun, 18 Jun 2006 12:54:49 +0000 (12:54 +0000)
committerivan <ivan>
Sun, 18 Jun 2006 12:54:49 +0000 (12:54 +0000)
16 files changed:
FS/FS/AccessRight.pm
FS/FS/access_group.pm
FS/FS/access_groupagent.pm
FS/FS/m2name_Common.pm [new file with mode: 0644]
FS/FS/part_pkg.pm
FS/MANIFEST
htetc/handler.pl
httemplate/browse/access_group.html
httemplate/browse/access_user.html
httemplate/edit/access_group.html
httemplate/edit/elements/edit.html
httemplate/edit/part_pkg.cgi
httemplate/edit/process/access_group.html
httemplate/edit/process/elements/process.html
httemplate/elements/checkboxes-table-name.html [new file with mode: 0644]
httemplate/elements/checkboxes-table.html

index 01d63e3..5229e1e 100644 (file)
@@ -1,7 +1,7 @@
 package FS::AccessRight;
 
 use strict;
 package FS::AccessRight;
 
 use strict;
-user vars qw(@rights %rights);
+use vars qw(@rights); # %rights);
 use Tie::IxHash;
 
 =head1 NAME
 use Tie::IxHash;
 
 =head1 NAME
@@ -19,59 +19,94 @@ assigned to users and/or groups.
 
 =cut
 
 
 =cut
 
+#@rights = (
+#  'Reports' => [
+#    '_desc' => 'Access to high-level reporting',
+#  ],
+#  'Configuration' => [
+#    '_desc' => 'Access to configuration',
+#
+#    'Settings' => {},
+#
+#    'agent' => [
+#      '_desc' => 'Master access to reseller configuration',
+#      'agent_type'  => {},
+#      'agent'       => {},
+#    ],
+#
+#    'export_svc_pkg' => [
+#      '_desc' => 'Access to export, service and package configuration',
+#      'part_export' => {},
+#      'part_svc'    => {},
+#      'part_pkg'    => {},
+#      'pkg_class'   => {},
+#    ],
+#
+#    'billing' => [
+#      '_desc' => 'Access to billing configuration',
+#      'payment_gateway'  => {},
+#      'part_bill_event'  => {},
+#      'prepay_credit'    => {},
+#      'rate'             => {},
+#      'cust_main_county' => {},
+#    ],
+#
+#    'dialup' => [
+#      '_desc' => 'Access to dialup configuraiton',
+#      'svc_acct_pop' => {},
+#    ],
+#
+#    'broadband' => [
+#      '_desc' => 'Access to broadband configuration',
+#      'router'     => {},
+#      'addr_block' => {},
+#    ],
+#
+#    'misc' => [
+#      'part_referral'      => {},
+#      'part_virtual_field' => {},
+#      'msgcat'             => {},
+#      'inventory_class'    => {},
+#    ],
+#
+#  },
+#
+#);
+#
+##turn it into a more hash-like structure, but ordered via IxHash
+
+#well, this is what we have for now.  could be ordered better, could be lots of
+# things better, but this ACL system does 99% of what folks need and the UI
+# isn't *that* bad
 @rights = (
 @rights = (
-  'Reports' => [
-    '_desc' => 'Access to high-level reporting',
-  ],
-  'Configuration' => [
-    '_desc' => 'Access to configuration',
-
-    'Settings' => {},
-
-    'agent' => [
-      '_desc' => 'Master access to reseller configuration',
-      'agent_type'  => {},
-      'agent'       => {},
-    ],
-
-    'export_svc_pkg' => [
-      '_desc' => 'Access to export, service and package configuration',
-      'part_export' => {},
-      'part_svc'    => {},
-      'part_pkg'    => {},
-      'pkg_class'   => {},
-    ],
-
-    'billing' => [
-      '_desc' => 'Access to billing configuration',
-      'payment_gateway'  => {},
-      'part_bill_event'  => {},
-      'prepay_credit'    => {},
-      'rate'             => {},
-      'cust_main_county' => {},
-    ],
-
-    'dialup' => [
-      '_desc' => 'Access to dialup configuraiton',
-      'svc_acct_pop' => {},
-    ],
-
-    'broadband' => [
-      '_desc' => 'Access to broadband configuration',
-      'router'     => {},
-      'addr_block' => {},
-    ],
-
-    'misc' => [
-      'part_referral'      => {},
-      'part_virtual_field' => {},
-      'msgcat'             => {},
-      'inventory_class'    => {},
-    ],
-
-  },
+  'New customer',
+  'View customer',
+  #'View Customer | View tickets',
+  'Edit customer',
+  'Cancel customer',
+  'Delete customer',
+
+  'Order customer package',
+  'Change customer package',
+  'Edit customer package dates',
+  'Customize customer package',
+  'Suspend customer package',
+  'Unsuspend customer package',
+  'Cancel customer package immediately',
+  'Cancel customer package later',
+
+  'Provision service',
+  'Unprovision service',
+  #legacy link stuff
+
+  'Post payment',
+  'Process payment',
+  'Post credit',
+  #more financial stuff
 
 );
 
 
 );
 
-#turn it into a more hash-like structure, but ordered via IxHash
+sub rights {
+  @rights;
+}
 
 
index 9d870e5..2519040 100644 (file)
@@ -3,8 +3,11 @@ package FS::access_group;
 use strict;
 use vars qw( @ISA );
 use FS::Record qw( qsearch qsearchs );
 use strict;
 use vars qw( @ISA );
 use FS::Record qw( qsearch qsearchs );
+use FS::m2name_Common;
+use FS::access_groupagent;
+use FS::access_right;
 
 
-@ISA = qw(FS::Record);
+@ISA = qw(FS::m2m_Common FS::m2name_Common FS::Record);
 
 =head1 NAME
 
 
 =head1 NAME
 
@@ -27,15 +30,14 @@ FS::access_group - Object methods for access_group records
 
 =head1 DESCRIPTION
 
 
 =head1 DESCRIPTION
 
-An FS::access_group object represents an example.  FS::access_group inherits from
+An FS::access_group object represents an access group.  FS::access_group inherits from
 FS::Record.  The following fields are currently supported:
 
 =over 4
 
 =item groupnum - primary key
 
 FS::Record.  The following fields are currently supported:
 
 =over 4
 
 =item groupnum - primary key
 
-=item groupname - 
-
+=item groupname - Access group name
 
 =back
 
 
 =back
 
@@ -45,7 +47,7 @@ FS::Record.  The following fields are currently supported:
 
 =item new HASHREF
 
 
 =item new HASHREF
 
-Creates a new example.  To add the example to the database, see L<"insert">.
+Creates a new access group.  To add the access group to the database, see L<"insert">.
 
 Note that this stores the hash reference, not a distinct copy of the hash it
 points to.  You can ask the object for a copy with the I<hash> method.
 
 Note that this stores the hash reference, not a distinct copy of the hash it
 points to.  You can ask the object for a copy with the I<hash> method.
@@ -84,7 +86,7 @@ returns the error, otherwise returns false.
 
 =item check
 
 
 =item check
 
-Checks all fields to make sure this is a valid example.  If there is
+Checks all fields to make sure this is a valid access group.  If there is
 an error, returns the error, otherwise returns false.  Called by the insert
 and replace methods.
 
 an error, returns the error, otherwise returns false.  Called by the insert
 and replace methods.
 
@@ -105,12 +107,51 @@ sub check {
   $self->SUPER::check;
 }
 
   $self->SUPER::check;
 }
 
+=item access_groupagent
+
+Returns all associated FS::access_groupagent records.
+
+=cut
+
+sub access_groupagent {
+  my $self = shift;
+  qsearch('access_groupagent', { 'groupnum' => $self->groupnum } );
+}
+
+=item access_rights
+
+Returns all associated FS::access_right records.
+
+=cut
+
+sub access_rights {
+  my $self = shift;
+  qsearch('access_right', { 'righttype'   => 'FS::access_group',
+                            'rightobjnum' => $self->groupnum 
+                          }
+         );
+}
+
+=item access_right RIGHTNAME
+
+Returns the specified FS::access_right record.  Can be used as a boolean, to
+test if this group has the given RIGHTNAME.
+
+=cut
+
+sub access_right {
+  my( $self, $name ) = shift;
+  qsearchs('access_right', { 'righttype'   => 'FS::access_group',
+                             'rightobjnum' => $self->groupnum,
+                             'rightname'   => $name,
+                           }
+          );
+}
+
 =back
 
 =head1 BUGS
 
 =back
 
 =head1 BUGS
 
-The author forgot to customize this manpage.
-
 =head1 SEE ALSO
 
 L<FS::Record>, schema.html from the base documentation.
 =head1 SEE ALSO
 
 L<FS::Record>, schema.html from the base documentation.
index 6b5def1..3de8fee 100644 (file)
@@ -3,6 +3,7 @@ package FS::access_groupagent;
 use strict;
 use vars qw( @ISA );
 use FS::Record qw( qsearch qsearchs );
 use strict;
 use vars qw( @ISA );
 use FS::Record qw( qsearch qsearchs );
+use FS::agent;
 
 @ISA = qw(FS::Record);
 
 
 @ISA = qw(FS::Record);
 
@@ -27,7 +28,7 @@ FS::access_groupagent - Object methods for access_groupagent records
 
 =head1 DESCRIPTION
 
 
 =head1 DESCRIPTION
 
-An FS::access_groupagent object represents an example.  FS::access_groupagent inherits from
+An FS::access_groupagent object represents an group reseller virtualization.  FS::access_groupagent inherits from
 FS::Record.  The following fields are currently supported:
 
 =over 4
 FS::Record.  The following fields are currently supported:
 
 =over 4
@@ -47,7 +48,7 @@ FS::Record.  The following fields are currently supported:
 
 =item new HASHREF
 
 
 =item new HASHREF
 
-Creates a new example.  To add the example to the database, see L<"insert">.
+Creates a new group reseller virtualization.  To add the record to the database, see L<"insert">.
 
 Note that this stores the hash reference, not a distinct copy of the hash it
 points to.  You can ask the object for a copy with the I<hash> method.
 
 Note that this stores the hash reference, not a distinct copy of the hash it
 points to.  You can ask the object for a copy with the I<hash> method.
@@ -86,7 +87,7 @@ returns the error, otherwise returns false.
 
 =item check
 
 
 =item check
 
-Checks all fields to make sure this is a valid example.  If there is
+Checks all fields to make sure this is a valid group reseller virtualization.  If there is
 an error, returns the error, otherwise returns false.  Called by the insert
 and replace methods.
 
 an error, returns the error, otherwise returns false.  Called by the insert
 and replace methods.
 
@@ -100,20 +101,29 @@ sub check {
 
   my $error = 
     $self->ut_numbern('groupagentnum')
 
   my $error = 
     $self->ut_numbern('groupagentnum')
-    || $self->ut_number('groupnum')
-    || $self->ut_number('agentnum')
+    || $self->ut_foreign_key('groupnum', 'access_group', 'groupnum')
+    || $self->ut_foreign_key('agentnum', 'agent',        'agentnum')
   ;
   return $error if $error;
 
   $self->SUPER::check;
 }
 
   ;
   return $error if $error;
 
   $self->SUPER::check;
 }
 
+=item agent
+
+Returns the associated FS::agent object.
+
+=cut
+
+sub agent {
+  my $self = shift;
+  qsearchs('agent', { 'agentnum' => $self->agentnum } );
+}
+
 =back
 
 =head1 BUGS
 
 =back
 
 =head1 BUGS
 
-The author forgot to customize this manpage.
-
 =head1 SEE ALSO
 
 L<FS::Record>, schema.html from the base documentation.
 =head1 SEE ALSO
 
 L<FS::Record>, schema.html from the base documentation.
diff --git a/FS/FS/m2name_Common.pm b/FS/FS/m2name_Common.pm
new file mode 100644 (file)
index 0000000..7c9637e
--- /dev/null
@@ -0,0 +1,95 @@
+package FS::m2name_Common;
+
+use strict;
+use vars qw( @ISA $DEBUG );
+use FS::Schema qw( dbdef );
+use FS::Record qw( qsearch qsearchs ); #dbh );
+
+@ISA = qw( FS::Record );
+
+$DEBUG = 0;
+
+=head1 NAME
+
+FS::m2name_Common - Base class for tables with a related table listing names
+
+=head1 SYNOPSIS
+
+use FS::m2name_Common;
+
+@ISA = qw( FS::m2name_Common );
+
+=head1 DESCRIPTION
+
+FS::m2name_Common is intended as a base class for classes which have a
+related table that lists names.
+
+=head1 METHODS
+
+=over 4
+
+=item process_m2name
+
+=cut
+
+sub process_m2name {
+  my( $self, %opt ) = @_;
+
+  my $self_pkey = $self->dbdef_table->primary_key;
+  my $link_sourcekey = $opt{'num_col'} || $self_pkey;
+
+  my $link_table = $self->_load_table($opt{'link_table'});
+
+  my $link_static = $opt{'link_static'} || {};
+
+  foreach my $name ( @{ $opt{'names_list'} } ) {
+
+    my $obj = qsearchs( $link_table, {
+        $link_sourcekey  => $self->$self_pkey(),
+        $opt{'name_col'} => $name,
+        %$link_static,
+    });
+
+    if ( $obj && ! $opt{'params'}->{"$link_table.$name"} ) {
+
+      my $d_obj = $obj; #need to save $obj for below.
+      my $error = $d_obj->delete;
+      die "error deleting $d_obj for $link_table.$name: $error" if $error;
+
+    } elsif ( $opt{'params'}->{"$link_table.$name"} && ! $obj ) {
+
+      #ok to clobber it now (but bad form nonetheless?)
+      #$obj = new "FS::$link_table" ( {
+      $obj = "FS::$link_table"->new( {
+        $link_sourcekey  => $self->$self_pkey(),
+        $opt{'name_col'} => $name,
+        %$link_static,
+      });
+      my $error = $obj->insert;
+      die "error inserting $obj for $link_table.$name: $error" if $error;
+    }
+
+  }
+
+  '';
+}
+
+sub _load_table {
+  my( $self, $table ) = @_;
+  eval "use FS::$table";
+  die $@ if $@;
+  $table;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>
+
+=cut
+
+1;
+
index 05dc599..de4d047 100644 (file)
@@ -1,7 +1,7 @@
 package FS::part_pkg;
 
 use strict;
 package FS::part_pkg;
 
 use strict;
-use vars qw( @ISA %freq %plans $DEBUG );
+use vars qw( @ISA %plans $DEBUG );
 use Carp qw(carp cluck confess);
 use Tie::IxHash;
 use FS::Conf;
 use Carp qw(carp cluck confess);
 use Tie::IxHash;
 use FS::Conf;
@@ -571,6 +571,32 @@ sub is_free {
   }
 }
 
   }
 }
 
+
+sub freqs_href {
+  #method, class method or sub? #my $self = shift;
+
+  tie my %freq, 'Tie::IxHash', 
+    '0'  => '(no recurring fee)',
+    '1h' => 'hourly',
+    '1d' => 'daily',
+    '1w' => 'weekly',
+    '2w' => 'biweekly (every 2 weeks)',
+    '1'  => 'monthly',
+    '2'  => 'bimonthly (every 2 months)',
+    '3'  => 'quarterly (every 3 months)',
+    '6'  => 'semiannually (every 6 months)',
+    '12' => 'annually',
+    '24' => 'biannually (every 2 years)',
+    '36' => 'triannually (every 3 years)',
+    '48' => '(every 4 years)',
+    '60' => '(every 5 years)',
+    '120' => '(every 10 years)',
+  ;
+
+  \%freq;
+
+}
+
 =item freq_pretty
 
 Returns an english representation of the I<freq> field, such as "monthly",
 =item freq_pretty
 
 Returns an english representation of the I<freq> field, such as "monthly",
@@ -578,29 +604,14 @@ Returns an english representation of the I<freq> field, such as "monthly",
 
 =cut
 
 
 =cut
 
-tie %freq, 'Tie::IxHash', 
-  '0'  => '(no recurring fee)',
-  '1h' => 'hourly',
-  '1d' => 'daily',
-  '1w' => 'weekly',
-  '2w' => 'biweekly (every 2 weeks)',
-  '1'  => 'monthly',
-  '2'  => 'bimonthly (every 2 months)',
-  '3'  => 'quarterly (every 3 months)',
-  '6'  => 'semiannually (every 6 months)',
-  '12' => 'annually',
-  '24' => 'biannually (every 2 years)',
-  '36' => 'triannually (every 3 years)',
-  '48' => '(every 4 years)',
-  '60' => '(every 5 years)',
-  '120' => '(every 10 years)',
-;
-
 sub freq_pretty {
   my $self = shift;
   my $freq = $self->freq;
 sub freq_pretty {
   my $self = shift;
   my $freq = $self->freq;
-  if ( exists($freq{$freq}) ) {
-    $freq{$freq};
+
+  my $freqs_href = $self->freqs_href;
+
+  if ( exists($freqs_href->{$freq}) ) {
+    $freqs_href->{$freq};
   } else {
     my $interval = 'month';
     if ( $freq =~ /^(\d+)([hdw])$/ ) {
   } else {
     my $interval = 'month';
     if ( $freq =~ /^(\d+)([hdw])$/ ) {
index 9e3285d..098fe4a 100644 (file)
@@ -342,3 +342,6 @@ t/access_right.t
 FS/m2m_Common.pm
 FS/pay_batch.pm
 t/pay_batch.t
 FS/m2m_Common.pm
 FS/pay_batch.pm
 t/pay_batch.t
+FS/ConfDefaults.pm
+t/ConfDefaults.t
+FS/m2name_Common.pm
index d400a55..1dfa137 100644 (file)
@@ -182,6 +182,10 @@ sub handler
       use FS::pkg_class;
       use FS::access_user;
       use FS::access_group;
       use FS::pkg_class;
       use FS::access_user;
       use FS::access_group;
+      use FS::access_usergroup;
+      use FS::access_groupagent;
+      use FS::access_right;
+      use FS::AccessRight;
 
       if ( %%%RT_ENABLED%%% ) {
         eval '
 
       if ( %%%RT_ENABLED%%% ) {
         eval '
index 6ba89ea..9ebb2b8 100644 (file)
@@ -4,6 +4,45 @@ my $html_init =
   "Internal access groups control access to the back-office interface.<BR><BR>".
   qq!<A HREF="${p}edit/access_group.html"><I>Add an internal access group</I></A><BR><BR>!;
 
   "Internal access groups control access to the back-office interface.<BR><BR>".
   qq!<A HREF="${p}edit/access_group.html"><I>Add an internal access group</I></A><BR><BR>!;
 
+#false laziness w/access_user.html & agent_type.cgi
+my $agents_sub = sub {
+  my $access_group = shift;
+
+  [ map {
+          my $access_groupagent = $_;
+          my $agent = $access_groupagent->agent;
+          [
+            {
+              'data'  => $agent->agent,
+              'align' => 'left',
+              'link'  => $p. 'edit/agent.cgi?'. $agent->agentnum,
+            },
+          ];
+        }
+    grep { $_->agent } #?
+    $access_group->access_groupagent,
+
+  ];
+  
+};
+
+my $rights_sub = sub {
+  my $access_group = shift;
+
+  [ map { my $access_right = $_;
+          [
+            { 
+              'data'  => $access_right->rightname,
+              'align' => 'left',
+            },
+          ];
+        }
+    $access_group->access_rights,
+
+  ];
+
+};
+
 my $count_query = 'SELECT COUNT(*) FROM access_group';
 
 my $link = [ $p.'edit/access_group.html?', 'groupnum' ];
 my $count_query = 'SELECT COUNT(*) FROM access_group';
 
 my $link = [ $p.'edit/access_group.html?', 'groupnum' ];
@@ -22,12 +61,18 @@ my $link = [ $p.'edit/access_group.html?', 'groupnum' ];
                  'count_query' => $count_query,
                  'header'      => [ '#',
                                     'Group name',
                  'count_query' => $count_query,
                  'header'      => [ '#',
                                     'Group name',
+                                    'Agents',
+                                    'Rights',
                                   ],
                  'fields'      => [ 'groupnum',
                                     'groupname',
                                   ],
                  'fields'      => [ 'groupnum',
                                     'groupname',
+                                    $agents_sub,
+                                    $rights_sub,
                                   ],
                  'links'       => [ $link,
                                     $link,
                                   ],
                  'links'       => [ $link,
                                     $link,
+                                    '',
+                                    '',
                                   ],
              )
 %>
                                   ],
              )
 %>
index 38d5430..be11bf8 100644 (file)
@@ -4,7 +4,7 @@ my $html_init =
   "Internal users have access to the back-office interface.  Typically, this is your employees and contractors, but in a VISP setup, you can also add accounts for your reseller's employees.  It is <B>highly recommended</B> to add a <B>separate account for each person</B> rather than using role accounts.<BR><BR>".
   qq!<A HREF="${p}edit/access_user.html"><I>Add an internal user</I></A><BR><BR>!;
 
   "Internal users have access to the back-office interface.  Typically, this is your employees and contractors, but in a VISP setup, you can also add accounts for your reseller's employees.  It is <B>highly recommended</B> to add a <B>separate account for each person</B> rather than using role accounts.<BR><BR>".
   qq!<A HREF="${p}edit/access_user.html"><I>Add an internal user</I></A><BR><BR>!;
 
-#false laziness w/agent_type.cgi
+#false laziness w/access_group.html & agent_type.cgi
 my $groups_sub = sub {
   my $access_user = shift;
 
 my $groups_sub = sub {
   my $access_user = shift;
 
index 11b8df7..d7f7667 100644 (file)
@@ -5,6 +5,42 @@
                                'groupnum'   => 'Group number',
                                'groupname'  => 'Group name',
                              },
                                'groupnum'   => 'Group number',
                                'groupname'  => 'Group name',
                              },
+
                  'viewall_dir' => 'browse',
                  'viewall_dir' => 'browse',
+
+                 'html_bottom' =>
+                   sub {
+                     my $access_group = shift;
+
+                     "<BR>Group virtualized to customers of agents:<BR>".
+                     ntable("#cccccc",2).
+                     '<TR><TD>'.
+                     include( '/elements/checkboxes-table.html',
+                                'source_obj'   => $access_group,
+                                'link_table'   => 'access_groupagent',
+                                'target_table' => 'agent',
+                                'name_col'     => 'agent',
+                                'target_link'  => $p.'edit/agent.cgi?',
+                                'disable-able' => 1,
+                            ).
+                     '</TR></TD></TABLE>'.
+
+                     "<BR>Group rights:<BR>".
+                     ntable("#cccccc",2).
+                     '<TR><TD>'.
+                     include( '/elements/checkboxes-table-name.html',
+                                'source_obj'   => $access_group,
+                                'link_table'   => 'access_right',
+                                'link_static'  => { 'righttype' =>
+                                                      'FS::access_group',
+                                                  },
+                                'num_col'      => 'rightobjnum',
+                                'name_col'     => 'rightname',
+                                'names_list'   => [ FS::AccessRight->rights() ],
+                            ).
+                     '</TR></TD></TABLE>'
+
+                     ;
+                   },
            )
 %>
            )
 %>
index 120c03a..94bf6ee 100644 (file)
   #
   # 'menubar'     => '', #menubar arrayref
   #
   #
   # 'menubar'     => '', #menubar arrayref
   #
+  # #run when re-displaying with an error
+  # 'error_callback' => sub { my $cgi, $object = @_; },
+  #
+  # #run when editing
+  # 'edit_callback' => sub { my $cgi, $object = @_; },
+  #
+  # #run when adding
+  # 'new_callback' => sub { my $cgi, $object = @_; },
+  #
+  # #broken'html_table_bottom' => '', #string or listref of additinal HTML to
+  #                            #add before </TABLE>
+  #
   # 'viewall_dir' => '', #'search' or 'browse', defaults to 'search'
   #
   # 'html_bottom' => '', #string
   # 'viewall_dir' => '', #'search' or 'browse', defaults to 'search'
   #
   # 'html_bottom' => '', #string
       map { $_ => scalar($cgi->param($_)) } fields($table)
     });
 
       map { $_ => scalar($cgi->param($_)) } fields($table)
     });
 
+    &{$opt{'error_callback'}}($cgi, $object)
+      if $opt{'error_callback'};
+
   } elsif ( $cgi->keywords ) { #editing
 
     my( $query ) = $cgi->keywords;
     $query =~ /^(\d+)$/;
     $object = qsearchs( $table, { $pkey => $1 } );
 
   } elsif ( $cgi->keywords ) { #editing
 
     my( $query ) = $cgi->keywords;
     $query =~ /^(\d+)$/;
     $object = qsearchs( $table, { $pkey => $1 } );
 
+    &{$opt{'edit_callback'}}($cgi, $object)
+      if $opt{'edit_callback'};
+
   } else { #adding
 
     $object = $class->new( {} );
 
   } else { #adding
 
     $object = $class->new( {} );
 
+    &{$opt{'new_callback'}}($cgi, $object)
+      if $opt{'new_callback'};
+
   }
 
   my $action = $object->$pkey() ? 'Edit' : 'Add';
   }
 
   my $action = $object->$pkey() ? 'Edit' : 'Add';
index 462d516..b085d22 100755 (executable)
@@ -237,7 +237,7 @@ if ( dbdef->table('pkg_svc')->column('primary_svc') ) {
   push @form_radio, 'pkg_svc_primary';
 }
 
   push @form_radio, 'pkg_svc_primary';
 }
 
-tie my %freq, 'Tie::IxHash', %FS::part_pkg::freq;
+tie my %freq, 'Tie::IxHash', %{FS::part_pkg->freqs_href()};
 if ( $part_pkg->dbdef_table->column('freq')->type =~ /(int)/i ) {
   delete $freq{$_} foreach grep { ! /^\d+$/ } keys %freq;
 }
 if ( $part_pkg->dbdef_table->column('freq')->type =~ /(int)/i ) {
   delete $freq{$_} foreach grep { ! /^\d+$/ } keys %freq;
 }
index e8c6d07..9bb9d1d 100644 (file)
@@ -1,5 +1,15 @@
 <%= include( 'elements/process.html',
                'table'       => 'access_group',
                'viewall_dir' => 'browse',
 <%= include( 'elements/process.html',
                'table'       => 'access_group',
                'viewall_dir' => 'browse',
+               'process_m2m' => { 'link_table'   => 'access_groupagent',
+                                  'target_table' => 'agent',
+                                },
+               'process_m2name' => {
+                     'link_table'   => 'access_right',
+                     'link_static'  => { 'righttype' => 'FS::access_group', },
+                     'num_col'      => 'rightobjnum',
+                     'name_col'     => 'rightname',
+                     'names_list'   => [ FS::AccessRight->rights() ],
+               },
            )
 %>
            )
 %>
index 59ad35e..a6e3b50 100644 (file)
   # 'viewall_dir' => '', #'search' or 'browse', defaults to 'search'
   # 'process_m2m' => { 'link_table'   => 'link_table_name',
   #                    'target_table' => 'target_table_name',
   # 'viewall_dir' => '', #'search' or 'browse', defaults to 'search'
   # 'process_m2m' => { 'link_table'   => 'link_table_name',
   #                    'target_table' => 'target_table_name',
-  #                  }.
+  #                  },
+  # 'process_m2name' => { 'link_table'   => 'link_table_name',
+  #                       'link_static' => { 'column' => 'value' },
+  #                       'num_col' => 'column', #if column name is different in
+  #                                              #link_table than source_table 
+  #                       'name_col' => 'name_column',
+  #                       'names_list' => [ 'list', 'names' ],
+  #                     },
 
   my(%opt) = @_;
 
 
   my(%opt) = @_;
 
                               );
   }
 
                               );
   }
 
+  if ( !$error && $opt{'process_m2name'} ) {
+    $error = $new->process_m2name( %{ $opt{'process_m2name'} },
+                                   'params' => scalar($cgi->Vars),
+                                 );
+  }
+
   if ( $error ) {
     $cgi->param('error', $error);
     print $cgi->redirect(popurl(2). "$table.html?". $cgi->query_string );
   if ( $error ) {
     $cgi->param('error', $error);
     print $cgi->redirect(popurl(2). "$table.html?". $cgi->query_string );
diff --git a/httemplate/elements/checkboxes-table-name.html b/httemplate/elements/checkboxes-table-name.html
new file mode 100644 (file)
index 0000000..8e9dd29
--- /dev/null
@@ -0,0 +1,85 @@
+<%
+
+  ##
+  # required
+  ##
+  # 'link_table'      => 'table_name',
+  #
+  # 'name_col' => 'name_column',
+  # #or
+  # 'name_callback' => sub { },
+  #
+  # 'names_list' => [ 'value', 'other value' ],
+  #
+  ##
+  # recommended (required?)
+  ##
+  # 'source_obj'   => $obj,
+  # #or?
+  # #'source_table' => 'table_name',
+  # #'sourcenum'    => '4', #current value of primary key in source_table
+  # #                       # (none is okay, just pass it if you have it)
+  ##
+  # optional
+  ##
+  # 'num_col' => 'col_name' #if column name is different in link_table than
+  #                         #source_table
+  # 'link_static' => { 'column' => 'value' },
+
+  my( %opt ) = @_;
+
+  my( $source_pkey, $sourcenum, $source_obj );
+  if ( $opt{'source_obj'} ) {
+
+    $source_obj = $opt{'source_obj'};
+    #$source_table = $source_obj->dbdef_table->table;
+    $source_pkey = $source_obj->dbdef_table->primary_key;
+    $sourcenum = $source_obj->$source_pkey();
+
+  } else {
+
+    #$source_obj?
+    $source_pkey = $opt{'source_table'}
+                     ? dbdef->table($opt{'source_table'})->primary_key
+                     : '';
+    $sourcenum = $opt{'sourcenum'};
+  }
+
+  $source_pkey = $opt{'num_col'} || $source_pkey;
+
+  my $link_static = $opt{'link_static'} || {};
+
+%>
+
+<% foreach my $name ( @{ $opt{'names_list'} } ) {
+
+     my $checked;
+     if ( $cgi->param('error') ) {
+
+       $checked = $cgi->param($opt{'link_table'}. ".$name" )
+                    ? 'CHECKED'
+                    : '';
+
+     } else {
+
+       $checked =
+         qsearchs( $opt{'link_table'}, {
+                                         $source_pkey     => $sourcenum,
+                                         $opt{'name_col'} => $name,
+                                         %$link_static,
+                                       }                                 )
+                    ? 'CHECKED'
+                    : ''
+
+     }
+
+%>
+
+  <INPUT TYPE="checkbox" NAME="<%= $opt{'link_table'}. ".$name" %>" <%= $checked %> VALUE="ON">
+
+  <%= $name %>
+
+  <BR>
+
+<% } %>
+
index d26ebef..16376fa 100644 (file)
    ) {
 
      my $targetnum = $target_obj->$target_pkey();
    ) {
 
      my $targetnum = $target_obj->$target_pkey();
+
+     my $checked;
+     if ( $cgi->param('error') ) {
+
+       $checked = $cgi->param($target_pkey.$targetnum)
+                    ? 'CHECKED'
+                    : '';
+
+     } else {
+
+       $checked = qsearchs( $opt{'link_table'}, {
+                                                  $source_pkey => $sourcenum,
+                                                  $target_pkey => $targetnum,
+                                                }                             )
+                    ? 'CHECKED'
+                    : ''
+
+     }
+
 %>
 
 %>
 
-  <INPUT TYPE="checkbox" NAME="<%= $target_pkey. $targetnum %>" <%=
-        qsearchs( $opt{'link_table'}, {
-          $source_pkey => $sourcenum,
-          $target_pkey => $targetnum,
-        })
-          ? 'CHECKED '
-          : ''
-  %> VALUE="ON">
+  <INPUT TYPE="checkbox" NAME="<%= $target_pkey. $targetnum %>" <%= $checked %> VALUE="ON">
 
   <% if ( $opt{'target_link'} ) { %>
 
 
   <% if ( $opt{'target_link'} ) { %>