5 if ( $cgi->param('magic') eq 'process' ) {
7 my $svcpart = $cgi->param('svcpart');
8 my $old = qsearchs('part_svc', { 'svcpart' => $svcpart }) if $svcpart;
10 $cgi->param( 'svc_acct__usergroup',
11 join(',', $cgi->param('svc_acct__usergroup') ) );
13 my $new = new FS::part_svc ( {
15 $_, scalar($cgi->param($_));
16 # } qw(svcpart svc svcdb)
17 } ( fields('part_svc'),
19 my @fields = fields($svcdb);
20 push @fields, 'usergroup' if $svcdb eq 'svc_acct'; #kludge
21 map { ( $svcdb.'__'.$_, $svcdb.'__'.$_.'_flag' ) } @fields;
22 } grep defined( $FS::Record::dbdef->table($_) ),
23 qw( svc_acct svc_domain svc_forward svc_www svc_broadband )
28 map { $_->exportnum => ( $cgi->param('exportnum'.$_->exportnum) || '') }
29 qsearch('part_export', {} );
32 $error = $new->replace($old, '1.3-COMPAT', [ 'usergroup' ], \%exportnums );
34 $error = $new->insert( [ 'usergroup' ], \%exportnums );
35 $svcpart = $new->getfield('svcpart');
38 unless ( $error ) { #no error, redirect
39 #print $cgi->redirect(popurl(3)."browse/part_svc.cgi");
40 print $cgi->redirect("${p}browse/part_svc.cgi");
45 #$part_svc = new FS::part_svc ( {
46 # map { $_, scalar($cgi->param($_)) } fields('part_svc')
49 } elsif ( $cgi->param('clone') && $cgi->param('clone') =~ /^(\d+)$/ ) {#clone
50 #$cgi->param('clone') =~ /^(\d+)$/ or die "malformed query: $query";
51 $part_svc = qsearchs('part_svc', { 'svcpart'=>$1 } )
52 or die "unknown svcpart: $1";
53 $clone = $part_svc->svcpart;
54 $part_svc->svcpart('');
55 } elsif ( $cgi->keywords ) { #edit
56 my($query) = $cgi->keywords;
57 $query =~ /^(\d+)$/ or die "malformed query: $query";
58 $part_svc=qsearchs('part_svc', { 'svcpart'=>$1 } )
59 or die "unknown svcpart: $1";
61 $part_svc = new FS::part_svc {};
64 my $action = $part_svc->svcpart ? 'Edit' : 'Add';
65 my $hashref = $part_svc->hashref;
66 # my $p_svcdb = $part_svc->svcdb || 'svc_acct';
69 #" onLoad=\"visualize()\""
72 <%= header("$action Service Definition",
73 menubar( 'Main Menu' => $p,
74 'View all service definitions' => "${p}browse/part_svc.cgi"
80 <FONT SIZE="+1" COLOR="#ff0000">Error: <%= $error %></FONT>
85 Service Part #<%= $part_svc->svcpart ? $part_svc->svcpart : "(NEW)" %>
87 Service <INPUT TYPE="text" NAME="svc" VALUE="<%= $hashref->{svc} %>"><BR>
88 Disable new orders <INPUT TYPE="checkbox" NAME="disabled" VALUE="Y"<%= $hashref->{disabled} eq 'Y' ? ' CHECKED' : '' %>><BR>
89 <INPUT TYPE="hidden" NAME="magic" VALUE="process">
90 <INPUT TYPE="hidden" NAME="svcpart" VALUE="<%= $hashref->{svcpart} %>">
92 Services are items you offer to your customers.
93 <UL><LI>svc_acct - Shell accounts, POP mailboxes, SLIP/PPP and ISDN accounts
94 <LI>svc_domain - Domains
95 <LI>svc_forward - mail forwarding
96 <LI>svc_www - Virtual domain website
97 <LI>svc_broadband - Broadband/High-speed Internet service
98 <LI>svc_external - Externally-tracked service
99 <!-- <LI>svc_charge - One-time charges (Partially unimplemented)
100 <LI>svc_wo - Work orders (Partially unimplemented)
103 For the selected table, you can give fields default or fixed (unchangable)
104 values. For example, a SLIP/PPP account may have a default (or perhaps fixed)
105 <B>slipip</B> of <B>0.0.0.0</B>, while a POP mailbox will probably have a fixed
106 blank <B>slipip</B> as well as a fixed shell something like <B>/bin/true</B> or
107 <B>/usr/bin/passwd</B>.
114 #these might belong somewhere else for other user interfaces
115 #pry need to eventually create stuff that's shared amount UIs
116 my $conf = new FS::Conf;
119 'dir' => 'Home directory',
120 'uid' => 'UID (set to fixed and blank for dial-only)',
121 'slipip' => 'IP address (Set to fixed and blank to disable dialin, or, set a value to be exported to RADIUS Framed-IP-Address. Use the special value <code>0e0</code> [zero e zero] to enable export to RADIUS without a Framed-IP-Address.)',
122 # 'popnum' => qq!<A HREF="$p/browse/svc_acct_pop.cgi/">POP number</A>!,
124 desc => 'Access number',
126 select_table => 'svc_acct_pop',
127 select_key => 'popnum',
128 select_label => 'city',
135 '_password' => 'Password',
136 'gid' => 'GID (when blank, defaults to UID)',
138 desc =>'Shell (all service definitions should have a default or fixed shell that is present in the <b>shells</b> configuration file)',
140 select_list => [ $conf->config('shells') ],
144 desc =>'svcnum from svc_domain',
146 select_table => 'svc_domain',
147 select_key => 'svcnum',
148 select_label => 'domain',
151 desc =>'ICRADIUS/FreeRADIUS groups',
152 type =>'radius_usergroup_selector',
156 'domain' => 'Domain',
159 'srcsvc' => 'service from which mail is to be forwarded',
160 'dstsvc' => 'service to which mail is to be forwarded',
161 'dst' => 'someone@another.domain.com to use when dstsvc is 0',
164 # 'amount' => 'amount',
167 # 'worker' => 'Worker',
175 'speed_down' => 'Maximum download speed for this service in Kbps. 0 denotes unlimited.',
176 'speed_up' => 'Maximum upload speed for this service in Kbps. 0 denotes unlimited.',
177 'ip_addr' => 'IP address. Leave blank for automatic assignment.',
178 'blocknum' => 'Address block.',
186 foreach my $svcdb (grep dbdef->table($_), keys %defs ) {
187 my $self = "FS::$svcdb"->new;
188 $vfields{$svcdb} = {};
189 foreach my $field ($self->virtual_fields) { # svc_Common::virtual_fields with a null svcpart returns all of them
190 my $pvf = $self->pvf($field);
191 my @list = $pvf->list;
193 $defs{$svcdb}->{$field} = { desc => $pvf->label,
195 select_list => \@list };
197 $defs{$svcdb}->{$field} = $pvf->label;
199 $vfields{$svcdb}->{$field} = $pvf;
200 warn "\$vfields{$svcdb}->{$field} = $pvf";
204 my @dbs = $hashref->{svcdb}
205 ? ( $hashref->{svcdb} )
206 : qw( svc_acct svc_domain svc_forward svc_www svc_broadband svc_external );
208 tie my %svcdb, 'Tie::IxHash', map { $_=>$_ } grep dbdef->table($_), @dbs;
209 my $widget = new HTML::Widgets::SelectLayers(
210 #'selected_layer' => $p_svcdb,
211 'selected_layer' => $hashref->{svcdb} || 'svc_acct',
212 'options' => \%svcdb,
213 'form_name' => 'dummy',
214 #'form_action' => 'process/part_svc.cgi',
215 'form_action' => 'part_svc.cgi', #self
216 'form_text' => [ qw( magic svc svcpart ) ],
217 'form_checkbox' => [ 'disabled' ],
218 'layer_callback' => sub {
220 my $html = qq!<INPUT TYPE="hidden" NAME="svcdb" VALUE="$layer">!;
225 map { qsearch( 'part_export', {exporttype => $_ } ) }
226 keys %{FS::part_export::export_info($layer)};
227 $html .= '<BR><BR>'. table().
228 table(). "<TR><TH COLSPAN=$columns>Exports</TH></TR><TR>";
229 foreach my $part_export ( @part_export ) {
230 $html .= '<TD><INPUT TYPE="checkbox"'.
231 ' NAME="exportnum'. $part_export->exportnum. '" VALUE="1" ';
233 if ( $clone || $part_svc->svcpart ) #null svcpart search causing error
234 && qsearchs( 'export_svc', {
235 exportnum => $part_export->exportnum,
236 svcpart => $clone || $part_svc->svcpart });
237 $html .= '>'. $part_export->exportnum. ': '. $part_export->exporttype.
238 ' to '. $part_export->machine. '</TD>';
240 $html .= '</TR><TR>' unless $count % $columns;
242 $html .= '</TR></TABLE><BR><BR>';
244 $html .= table(). "<TH>Field</TH><TH COLSPAN=2>Modifier</TH>";
246 my @fields = defined( $FS::Record::dbdef->table($layer) )
247 ? grep { $_ ne 'svcnum' } fields($layer)
249 push @fields, 'usergroup' if $layer eq 'svc_acct'; #kludge
250 $part_svc->svcpart($clone) if $clone; #haha, undone below
251 foreach my $field (@fields) {
252 my $part_svc_column = $part_svc->part_svc_column($field);
254 ? $cgi->param("${layer}__${field}")
255 : $part_svc_column->columnvalue;
257 ? $cgi->param("${layer}__${field}_flag")
258 : $part_svc_column->columnflag;
259 my $def = $defs{$layer}{$field};
260 my $desc = ref($def) ? $def->{desc} : $def;
262 $html .= "<TR><TD>$field";
263 $html .= "- <FONT SIZE=-1>$desc</FONT>" if $desc;
265 $flag = '' if ref($def) && $def->{type} eq 'disabled';
267 qq!<TD><INPUT TYPE="radio" NAME="${layer}__${field}_flag" VALUE=""!.
268 ' CHECKED'x($flag eq ''). ">Off</TD>".
270 unless ( ref($def) && $def->{type} eq 'disabled' ) {
272 qq!<INPUT TYPE="radio" NAME="${layer}__${field}_flag" VALUE="D"!.
273 ' CHECKED'x($flag eq 'D'). ">Default ".
274 qq!<INPUT TYPE="radio" NAME="${layer}__${field}_flag" VALUE="F"!.
275 ' CHECKED'x($flag eq 'F'). ">Fixed ";
279 if ( $def->{type} eq 'select' ) {
280 $html .= qq!<SELECT NAME="${layer}__${field}">!;
281 $html .= '<OPTION> </OPTION>' unless $value;
282 if ( $def->{select_table} ) {
283 foreach my $record ( qsearch( $def->{select_table}, {} ) ) {
284 my $rvalue = $record->getfield($def->{select_key});
285 $html .= qq!<OPTION VALUE="$rvalue"!.
286 ( $rvalue==$value ? ' SELECTED>' : '>' ).
287 $record->getfield($def->{select_label}). '</OPTION>';
289 } else { # select_list
290 foreach my $item ( @{$def->{select_list}} ) {
291 $html .= qq!<OPTION VALUE="$item"!.
292 ( $item eq $value ? ' SELECTED>' : '>' ).
296 $html .= '</SELECT>';
297 } elsif ( $def->{type} eq 'radius_usergroup_selector' ) {
298 $html .= FS::svc_acct::radius_usergroup_selector(
299 [ split(',', $value) ], "${layer}__${field}" );
300 } elsif ( $def->{type} eq 'disabled' ) {
302 qq!<INPUT TYPE="hidden" NAME="${layer}__${field}" VALUE="">!;
304 $html .= '<font color="#ff0000">unknown type'. $def->{type};
308 qq!<INPUT TYPE="text" NAME="${layer}__${field}" VALUE="$value">!;
311 if($vfields{$layer}->{$field}) {
312 $html .= qq!<BR><INPUT TYPE="radio" NAME="${layer}__${field}_flag" VALUE="X"!.
313 ' CHECKED'x($flag eq 'X'). ">Excluded ";
315 $html .= "</TD></TR>\n";
317 $part_svc->svcpart('') if $clone; #undone
320 $html .= '<BR><INPUT TYPE="submit" VALUE="'.
321 ($hashref->{svcpart} ? 'Apply changes' : 'Add service'). '">';
329 Table <%= $widget->html %>