4 if ( $cgi->param('clone') && $cgi->param('clone') =~ /^(\d+)$/ ) {#clone
5 #$cgi->param('clone') =~ /^(\d+)$/ or die "malformed query: $query";
6 $part_svc = qsearchs('part_svc', { 'svcpart'=>$1 } )
7 or die "unknown svcpart: $1";
8 $clone = $part_svc->svcpart;
9 $part_svc->svcpart('');
10 } elsif ( $cgi->keywords ) { #edit
11 my($query) = $cgi->keywords;
12 $query =~ /^(\d+)$/ or die "malformed query: $query";
13 $part_svc=qsearchs('part_svc', { 'svcpart'=>$1 } )
14 or die "unknown svcpart: $1";
16 $part_svc = new FS::part_svc {};
19 my $action = $part_svc->svcpart ? 'Edit' : 'Add';
20 my $hashref = $part_svc->hashref;
21 # my $p_svcdb = $part_svc->svcdb || 'svc_acct';
24 #" onLoad=\"visualize()\""
26 <%= include("/elements/header.html","$action Service Definition",
27 menubar( 'Main Menu' => $p,
28 'View all service definitions' => "${p}browse/part_svc.cgi"
35 Service Part #<%= $part_svc->svcpart ? $part_svc->svcpart : "(NEW)" %>
37 Service <INPUT TYPE="text" NAME="svc" VALUE="<%= $hashref->{svc} %>"><BR>
38 Disable new orders <INPUT TYPE="checkbox" NAME="disabled" VALUE="Y"<%= $hashref->{disabled} eq 'Y' ? ' CHECKED' : '' %>><BR>
39 <INPUT TYPE="hidden" NAME="svcpart" VALUE="<%= $hashref->{svcpart} %>">
41 Service definitions are the templates for items you offer to your customers.
42 <UL><LI>svc_acct - Accounts - anything with a username (Mailboxes, PPP accounts, shell accounts, etc.)
43 <LI>svc_domain - Domains
44 <LI>svc_forward - mail forwarding
45 <LI>svc_www - Virtual domain website
46 <LI>svc_broadband - Broadband/High-speed Internet service (always-on)
47 <LI>svc_external - Externally-tracked service
48 <!-- <LI>svc_charge - One-time charges (Partially unimplemented)
49 <LI>svc_wo - Work orders (Partially unimplemented)
52 For the selected table, you can give fields default or fixed (unchangable)
53 values, or select an inventory class to manually or automatically fill in
59 #these might belong somewhere else for other user interfaces
60 #pry need to eventually create stuff that's shared amount UIs
61 my $conf = new FS::Conf;
64 'dir' => 'Home directory',
65 'uid' => 'UID (set to fixed and blank for no UIDs)',
66 'slipip' => 'IP address',
67 # 'popnum' => qq!<A HREF="$p/browse/svc_acct_pop.cgi/">POP number</A>!,
69 desc => 'Access number',
71 select_table => 'svc_acct_pop',
72 select_key => 'popnum',
73 select_label => 'city',
84 disable_inventory => 1,
86 '_password' => 'Password',
87 'gid' => 'GID (when blank, defaults to UID)',
89 #desc =>'Shell (all service definitions should have a default or fixed shell that is present in the <b>shells</b> configuration file, set to blank for no shell tracking)',
90 desc =>'Shell ( set to blank for no shell tracking)',
92 select_list => [ $conf->config('shells') ],
93 disable_inventory => 1,
95 'finger' => 'Real name (GECOS)',
97 desc =>'svcnum from svc_domain',
99 select_table => 'svc_domain',
100 select_key => 'svcnum',
101 select_label => 'domain',
102 disable_inventory => 1,
105 desc =>'RADIUS groups',
106 type =>'radius_usergroup_selector',
107 disable_inventory => 1,
109 'seconds' => { desc => '',
111 disable_inventory => 1,
115 'domain' => 'Domain',
118 'srcsvc' => 'service from which mail is to be forwarded',
119 'dstsvc' => 'service to which mail is to be forwarded',
120 'dst' => 'someone@another.domain.com to use when dstsvc is 0',
123 # 'amount' => 'amount',
126 # 'worker' => 'Worker',
134 'speed_down' => 'Maximum download speed for this service in Kbps. 0 denotes unlimited.',
135 'speed_up' => 'Maximum upload speed for this service in Kbps. 0 denotes unlimited.',
136 'ip_addr' => 'IP address. Leave blank for automatic assignment.',
137 'blocknum' => 'Address block.',
146 foreach my $svcdb (grep dbdef->table($_), keys %defs ) {
147 my $self = "FS::$svcdb"->new;
148 $vfields{$svcdb} = {};
149 foreach my $field ($self->virtual_fields) { # svc_Common::virtual_fields with a null svcpart returns all of them
150 my $pvf = $self->pvf($field);
151 my @list = $pvf->list;
153 $defs{$svcdb}->{$field} = { desc => $pvf->label,
155 select_list => \@list };
157 $defs{$svcdb}->{$field} = $pvf->label;
159 $vfields{$svcdb}->{$field} = $pvf;
160 warn "\$vfields{$svcdb}->{$field} = $pvf";
164 #code duplication w/ edit/part_svc.cgi, should move this hash to part_svc.pm
165 # and generalize the subs
166 # condition sub is tested to see whether to disable display of this choice
167 # params: ( $def, $layer, $field ) (see SUB below)
169 ref($_[0]) && ( $_[0]->{disable_inventory}
170 || $_[0]->{'type'} ne 'text' )
172 tie my %flag, 'Tie::IxHash',
173 '' => { 'desc' => 'No default', },
174 'D' => { 'desc' => 'Default',
176 sub { ref($_[0]) && $_[0]->{disable_default} },
178 'F' => { 'desc' => 'Fixed (unchangeable)',
180 sub { ref($_[0]) && $_[0]->{disable_fixed} },
182 # need to template-ize httemplate/edit/svc_* first
183 # 'M' => { 'desc' => 'Manual selection from inventory',
184 # 'condition' => $inv_sub,
186 'A' => { 'desc' => 'Automatically fill in from inventory',
187 'condition' => $inv_sub,
189 'X' => { 'desc' => 'Excluded',
191 sub { ! $vfields{$_[1]}->{$_[2]} },
196 my @dbs = $hashref->{svcdb}
197 ? ( $hashref->{svcdb} )
198 : qw( svc_acct svc_domain svc_forward svc_www svc_broadband svc_external );
200 tie my %svcdb, 'Tie::IxHash', map { $_=>$_ } grep dbdef->table($_), @dbs;
201 my $widget = new HTML::Widgets::SelectLayers(
202 #'selected_layer' => $p_svcdb,
203 'selected_layer' => $hashref->{svcdb} || 'svc_acct',
204 'options' => \%svcdb,
205 'form_name' => 'dummy',
206 #'form_action' => 'process/part_svc.cgi',
207 'form_action' => 'part_svc.cgi', #self
208 'form_text' => [ qw( svc svcpart ) ],
209 'form_checkbox' => [ 'disabled' ],
210 'layer_callback' => sub {
213 my $html = qq!<INPUT TYPE="hidden" NAME="svcdb" VALUE="$layer">!;
218 map { qsearch( 'part_export', {exporttype => $_ } ) }
219 keys %{FS::part_export::export_info($layer)};
220 $html .= '<BR><BR>'. table().
221 "<TR><TH COLSPAN=$columns>Exports</TH></TR><TR>";
222 foreach my $part_export ( @part_export ) {
223 $html .= '<TD><INPUT TYPE="checkbox"'.
224 ' NAME="exportnum'. $part_export->exportnum. '" VALUE="1" ';
226 if ( $clone || $part_svc->svcpart ) #null svcpart search causing error
227 && qsearchs( 'export_svc', {
228 exportnum => $part_export->exportnum,
229 svcpart => $clone || $part_svc->svcpart });
230 $html .= '>'. $part_export->exportnum. ': '. $part_export->exporttype.
231 ' to '. $part_export->machine. '</TD>';
233 $html .= '</TR><TR>' unless $count % $columns;
235 $html .= '</TR></TABLE><BR><BR>';
237 $html .= include('/elements/table-grid.html', 'cellpadding' => 4 ).
239 '<TH CLASS="grid" BGCOLOR="#cccccc">Field</TH>'.
240 '<TH CLASS="grid" BGCOLOR="#cccccc" COLSPAN=2>Modifier</TH>'.
243 my $bgcolor1 = '#eeeeee';
244 my $bgcolor2 = '#ffffff';
248 my @fields = defined( dbdef->table($layer) )
249 ? grep { $_ ne 'svcnum' } fields($layer)
251 push @fields, 'usergroup' if $layer eq 'svc_acct'; #kludge
252 $part_svc->svcpart($clone) if $clone; #haha, undone below
255 foreach my $field (@fields) {
257 my $part_svc_column = $part_svc->part_svc_column($field);
258 my $value = $part_svc_column->columnvalue;
259 my $flag = $part_svc_column->columnflag;
260 my $def = $defs{$layer}{$field};
261 my $desc = ref($def) ? $def->{desc} : $def;
263 if ( $bgcolor eq $bgcolor1 ) {
264 $bgcolor = $bgcolor2;
266 $bgcolor = $bgcolor1;
269 $html .= qq!<TR><TD CLASS="grid" BGCOLOR="$bgcolor" ALIGN="right">!.
271 $html .= "- <FONT SIZE=-1>$desc</FONT>" if $desc;
273 $flag = '' if ref($def) && $def->{type} eq 'disabled';
275 $html .= qq!<TD CLASS="grid" BGCOLOR="$bgcolor">!;
277 if ( ref($def) && $def->{type} eq 'disabled' ) {
279 $html .= 'No default';
283 $html .= qq!<SELECT NAME="${layer}__${field}_flag"!.
284 qq! onChange="${layer}__${field}_flag_changed(this)">!;
286 foreach my $f ( keys %flag ) {
288 #here is where the SUB from above is called, to skip some choices
289 next if $flag{$f}->{condition}
290 && &{ $flag{$f}->{condition} }( $def, $layer, $field );
292 $html .= qq!<OPTION VALUE="$f"!.
293 ' SELECTED'x($flag eq $f ).
294 '>'. $flag{$f}->{desc};
298 $html .= '</SELECT>';
302 " function ${layer}__${field}_flag_changed(what) {",
303 ' var f = what.options[what.selectedIndex].value;',
304 ' if ( f == "" || f == "X" ) { //disable',
305 " what.form.${layer}__${field}.disabled = true;".
306 " what.form.${layer}__${field}.style.backgroundColor = '#dddddd';".
307 " if ( what.form.${layer}__${field}_classnum ) {".
308 " what.form.${layer}__${field}_classnum.disabled = true;".
309 " what.form.${layer}__${field}_classnum.style.backgroundColor = '#dddddd';".
311 ' } else if ( f == "D" || f == "F" ) { //enable, text box',
312 " what.form.${layer}__${field}.disabled = false;".
313 " what.form.${layer}__${field}.style.backgroundColor = '#ffffff';".
314 " what.form.${layer}__${field}.style.display = '';".
315 " if ( what.form.${layer}__${field}_classnum ) {".
316 " what.form.${layer}__${field}_classnum.disabled = false;".
317 " what.form.${layer}__${field}_classnum.style.backgroundColor = '#ffffff';".
318 " what.form.${layer}__${field}_classnum.style.display = 'none';".
320 ' } else if ( f == "M" || f == "A" ) { //enable, inventory',
321 " what.form.${layer}__${field}.disabled = false;".
322 " what.form.${layer}__${field}.style.backgroundColor = '#ffffff';".
323 " what.form.${layer}__${field}.style.display = 'none';".
324 " if ( what.form.${layer}__${field}_classnum ) {".
325 " what.form.${layer}__${field}_classnum.disabled = false;".
326 " what.form.${layer}__${field}_classnum.style.backgroundColor = '#ffffff';".
327 " what.form.${layer}__${field}_classnum.style.display = '';".
336 $html .= qq!</TD><TD CLASS="grid" BGCOLOR="$bgcolor">!;
338 my $disabled = $flag ? ''
339 : 'DISABLED STYLE="background-color: #dddddd"';
341 if ( ! ref($def) || $def->{type} eq 'text' ) {
343 my $nodisplay = ' STYLE="display:none"';
344 my $is_inv = ( $flag =~ /^[MA]$/ );
347 qq!<INPUT TYPE="text" NAME="${layer}__${field}" VALUE="$value" !.
349 ( $is_inv ? $nodisplay : $disabled ).
352 $html .= include('/elements/select-table.html',
353 'element_name' => "${layer}__${field}_classnum",
354 'element_etc' => ( $is_inv
358 'table' => 'inventory_class',
359 'name_col' => 'classname',
361 'empty_label' => 'Select inventory class',
364 } elsif ( $def->{type} eq 'select' ) {
366 $html .= qq!<SELECT NAME="${layer}__${field}" $disabled>!;
367 $html .= '<OPTION> </OPTION>' unless $value;
368 if ( $def->{select_table} ) {
369 foreach my $record ( qsearch( $def->{select_table}, {} ) ) {
370 my $rvalue = $record->getfield($def->{select_key});
371 $html .= qq!<OPTION VALUE="$rvalue"!.
372 ( $rvalue==$value ? ' SELECTED>' : '>' ).
373 $record->getfield($def->{select_label}). '</OPTION>';
375 } else { # select_list
376 foreach my $item ( @{$def->{select_list}} ) {
377 $html .= qq!<OPTION VALUE="$item"!.
378 ( $item eq $value ? ' SELECTED>' : '>' ).
382 $html .= '</SELECT>';
384 } elsif ( $def->{type} eq 'radius_usergroup_selector' ) {
386 #XXX disable the RADIUS usergroup selector? ugh it sure does need
387 #an overhaul, people have dum group problems because of it
389 $html .= FS::svc_acct::radius_usergroup_selector(
390 [ split(',', $value) ], "${layer}__${field}" );
392 } elsif ( $def->{type} eq 'disabled' ) {
395 qq!<INPUT TYPE="hidden" NAME="${layer}__${field}" VALUE="">!;
399 $html .= '<font color="#ff0000">unknown type'. $def->{type};
403 $html .= "</TD></TR>\n";
405 } #foreach my $field (@fields) {
407 $part_svc->svcpart('') if $clone; #undone
410 $html .= include('/elements/progress-init.html',
412 [ qw(svc svcpart disabled exportnum), @fields ],
413 'process/part_svc.cgi',
414 $p.'browse/part_svc.cgi',
417 $html .= '<BR><INPUT NAME="submit" TYPE="button" VALUE="'.
418 ($hashref->{svcpart} ? 'Apply changes' : 'Add service'). '" '.
419 ' onClick="document.'. "$layer.submit.disabled=true; ".
420 "fixup(document.$layer); $layer". 'process();">';
422 #$html .= '<BR><INPUT TYPE="submit" VALUE="'.
423 # ($hashref->{svcpart} ? 'Apply changes' : 'Add service'). '">';
431 Table <%= $widget->html %>