diff options
author | Ivan Kohler <ivan@freeside.biz> | 2013-12-10 20:59:42 -0800 |
---|---|---|
committer | Ivan Kohler <ivan@freeside.biz> | 2013-12-10 20:59:42 -0800 |
commit | 430b2c784d2ee9ea5be00b821d2dbd27279ef132 (patch) | |
tree | c8a224219c1425bd3fa03b1a54487bf1f3de7449 | |
parent | 6ced51db0e73603461591e54b7f606467e7c7af0 (diff) | |
parent | e1157f37c618a7b9cdb2793d61999458076dc51b (diff) |
Merge branch 'master' of git.freeside.biz:/home/git/freeside
Conflicts:
FS/FS/Mason.pm
FS/MANIFEST
-rw-r--r-- | FS/FS/ClientAPI/MyAccount.pm | 5 | ||||
-rw-r--r-- | FS/FS/Conf.pm | 6 | ||||
-rw-r--r-- | FS/FS/IP_Mixin.pm | 11 | ||||
-rw-r--r-- | FS/FS/Mason.pm | 1 | ||||
-rw-r--r-- | FS/FS/Schema.pm | 12 | ||||
-rwxr-xr-x | FS/FS/addr_block.pm | 12 | ||||
-rw-r--r-- | FS/FS/addr_range.pm | 259 | ||||
-rw-r--r-- | FS/FS/part_export/bulkvs_e911.pm | 2 | ||||
-rw-r--r-- | FS/FS/svc_Common.pm | 1 | ||||
-rw-r--r-- | FS/MANIFEST | 2 | ||||
-rwxr-xr-x | FS/bin/freeside-setup | 43 | ||||
-rw-r--r-- | FS/t/addr_range.t | 5 | ||||
-rwxr-xr-x | bin/cust_pkg-restore_setup | 27 | ||||
-rw-r--r-- | fs_selfservice/php/freeside_signup_example.php | 2 | ||||
-rw-r--r-- | httemplate/browse/addr_range.html | 66 | ||||
-rw-r--r-- | httemplate/edit/addr_range.html | 27 | ||||
-rw-r--r-- | httemplate/edit/cust_main/billing.html | 4 | ||||
-rw-r--r-- | httemplate/edit/elements/edit.html | 21 | ||||
-rw-r--r-- | httemplate/edit/process/addr_range.html | 22 | ||||
-rw-r--r-- | httemplate/elements/menu.html | 4 | ||||
-rw-r--r-- | httemplate/misc/delete-addr_range.html | 21 |
21 files changed, 487 insertions, 66 deletions
diff --git a/FS/FS/ClientAPI/MyAccount.pm b/FS/FS/ClientAPI/MyAccount.pm index a58099184..aa21ac076 100644 --- a/FS/FS/ClientAPI/MyAccount.pm +++ b/FS/FS/ClientAPI/MyAccount.pm @@ -7,6 +7,7 @@ use subs qw( _cache _provision ); use IO::Scalar; use Data::Dumper; use Digest::MD5 qw(md5_hex); +use Digest::SHA qw(sha512_hex); use Date::Format; use Time::Duration; use Time::Local qw(timelocal_nocheck); @@ -278,7 +279,7 @@ sub login { my $session_id; do { - $session_id = md5_hex(md5_hex(time(). {}. rand(). $$)) + $session_id = sha512_hex(time(). {}. rand(). $$) } until ( ! defined _cache->get($session_id) ); #just in case my $timeout = $conf->config('selfservice-session_timeout') || '1 hour'; @@ -2896,7 +2897,7 @@ sub reset_passwd { my $reset_session_id; do { - $reset_session_id = md5_hex(md5_hex(time(). {}. rand(). $$)) + $reset_session_id = sha512_hex(time(). {}. rand(). $$) } until ( ! defined _cache->get("reset_passwd_$reset_session_id") ); #just in case _cache->set( "reset_passwd_$reset_session_id", $reset_session, $timeout ); diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm index 0eed8ee5d..301d972b3 100644 --- a/FS/FS/Conf.pm +++ b/FS/FS/Conf.pm @@ -2150,7 +2150,7 @@ and customer address. Include units.', 'section' => 'self-service', 'description' => 'Acceptable payment types for the signup server', 'type' => 'selectmultiple', - 'select_enum' => [ qw(CARD DCRD CHEK DCHK LECB PREPAY PPAL BILL COMP) ], + 'select_enum' => [ qw(CARD DCRD CHEK DCHK PREPAY PPAL BILL COMP) ], }, { @@ -2538,7 +2538,7 @@ and customer address. Include units.', 'section' => 'billing', 'description' => 'Available payment types.', 'type' => 'selectmultiple', - 'select_enum' => [ qw(CARD DCRD CHEK DCHK LECB BILL CASH WEST MCRD PPAL COMP) ], + 'select_enum' => [ qw(CARD DCRD CHEK DCHK BILL CASH WEST MCRD PPAL COMP) ], }, { @@ -2546,7 +2546,7 @@ and customer address. Include units.', 'section' => 'UI', 'description' => 'Default payment type. HIDE disables display of billing information and sets customers to BILL.', 'type' => 'select', - 'select_enum' => [ '', qw(CARD DCRD CHEK DCHK LECB BILL CASH WEST MCRD PPAL COMP HIDE) ], + 'select_enum' => [ '', qw(CARD DCRD CHEK DCHK BILL CASH WEST MCRD PPAL COMP HIDE) ], }, { diff --git a/FS/FS/IP_Mixin.pm b/FS/FS/IP_Mixin.pm index fdeb51da7..b3c10528c 100644 --- a/FS/FS/IP_Mixin.pm +++ b/FS/FS/IP_Mixin.pm @@ -200,12 +200,21 @@ sub check_ip_addr { return '' if $addr eq ''; my $na = $self->NetAddr or return "Can't parse address '$addr'"; + # if there's a chosen address block, check that the address is in it if ( my $block = $self->addr_block ) { if ( !$block->NetAddr->contains($na) ) { return "Address $addr not in block ".$block->cidr; } } - # this returns '' if the address is in use by $self. + # if the address is in any designated ranges, check that they don't + # disallow use + foreach my $range (FS::addr_range->any_contains($addr)) { + if ( !$range->allow_use ) { + return "Address $addr is in ".$range->desc." range ".$range->as_string; + } + } + # check that nobody else is sitting on the address + # (this returns '' if the address is in use by $self) if ( my $dup = $self->is_used($self->ip_addr) ) { return "Address $addr in use by $dup"; } diff --git a/FS/FS/Mason.pm b/FS/FS/Mason.pm index d228bb173..4e9c4f19b 100644 --- a/FS/FS/Mason.pm +++ b/FS/FS/Mason.pm @@ -361,6 +361,7 @@ if ( -e $addl_handler_use_file ) { use FS::alarm_system; use FS::alarm_type; use FS::alarm_station; + use FS::addr_range; # Sammath Naur if ( $FS::Mason::addl_handler_use ) { diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index 6403782f8..647e2b106 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -4278,6 +4278,18 @@ sub tables_hashref { ], }, + 'addr_range' => { + 'columns' => [ + 'rangenum', 'serial', '', '', '', '', + 'start', 'varchar', '', 15, '', '', + 'length', 'int', '', '', '', '', + 'status', 'varchar', 'NULL', 32, '', '', + ], + 'primary_key' => 'rangenum', + 'unique' => [], + 'index' => [], + }, + 'svc_broadband' => { 'columns' => [ 'svcnum', 'int', '', '', '', '', diff --git a/FS/FS/addr_block.pm b/FS/FS/addr_block.pm index 6a62777be..1f4000b75 100755 --- a/FS/FS/addr_block.pm +++ b/FS/FS/addr_block.pm @@ -256,7 +256,17 @@ sub next_free_addr { # just do a linear search of the block my $freeaddr = $selfaddr->network + 1; while ( $freeaddr < $selfaddr->broadcast ) { - return $freeaddr unless $used{ $freeaddr->addr }; + # also make sure it's not blocked from assignment by an address range + if ( !$used{$freeaddr->addr } ) { + my ($range) = grep { !$_->allow_use } + FS::addr_range->any_contains($freeaddr); + if ( !$range ) { + # then we've found a free address + return $freeaddr; + } + # otherwise, skip to the end of the range + $freeaddr = NetAddr::IP->new($range->end, $self->ip_netmask); + } $freeaddr++; } return; diff --git a/FS/FS/addr_range.pm b/FS/FS/addr_range.pm new file mode 100644 index 000000000..18ae1e2ea --- /dev/null +++ b/FS/FS/addr_range.pm @@ -0,0 +1,259 @@ +package FS::addr_range; + +use strict; +use base qw( FS::Record ); +use vars qw( %status_desc + %status_allow_auto + %status_allow_use + ); +use FS::Record qw( qsearch qsearchs ); +use NetAddr::IP; + +# metadata about status strings: +# how to describe them +%status_desc = ( + '' => '', + 'unavailable' => 'unavailable', +); + +# whether addresses in this range are available for use +%status_allow_use = ( + '' => 1, + 'unavailable' => 0, +); + +=head1 NAME + +FS::addr_range - Object methods for addr_range records + +=head1 SYNOPSIS + + use FS::addr_range; + + $record = new FS::addr_range \%hash; + $record = new FS::addr_range { 'column' => 'value' }; + + $error = $record->insert; + + $error = $new_record->replace($old_record); + + $error = $record->delete; + + $error = $record->check; + +=head1 DESCRIPTION + +An FS::addr_range object represents a contiguous range of IP +addresses assigned to a certain purpose. Unlike L<FS::addr_block>, +this isn't a routing block; the range doesn't have to be aligned on +a subnet boundary, and doesn't have a gateway or broadcast address. +It's just a range. + +=over 4 + +=item rangenum - primary key + +=item start - starting address of the range, as a dotted quad + +=item length - number of addresses in the range, including start + +=item status - what to do with the addresses in this range; currently can +only be "unavailable", which makes the addresses unavailable for assignment +to any kind of service. + +=back + +=head1 METHODS + +=over 4 + +=item new HASHREF + +Creates a new range. To add the example 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. + +=cut + +sub table { 'addr_range'; } + +=item insert + +Adds this record to the database. If there is an error, returns the error, +otherwise returns false. + +=cut + +# the insert method can be inherited from FS::Record + +=item delete + +Delete this record from the database. + +=cut + +# the delete method can be inherited from FS::Record + +=item replace OLD_RECORD + +Replaces the OLD_RECORD with this one in the database. If there is an error, +returns the error, otherwise returns false. + +=cut + +# the replace method can be inherited from FS::Record + +=item check + +Checks all fields to make sure this is a valid example. If there is +an error, returns the error, otherwise returns false. Called by the insert +and replace methods. + +=cut + +# the check method should currently be supplied - FS::Record contains some +# data checking routines + +sub check { + my $self = shift; + + my $error = + $self->ut_numbern('rangenum') + || $self->ut_ip('start') + || $self->ut_number('length') + || $self->ut_textn('status') + ; + return $error if $error; + + $self->SUPER::check; +} + +=item end [ IPADDR ] + +Get/set the end IP address in the range. This isn't actually part of the +record but it's convenient. + +=cut + +sub end { + my $self = shift; + # if there's no start address, just return nothing + my $start = NetAddr::IP->new($self->start, 0) or return ''; + + my $new = shift; + if ( $new ) { + my $end = NetAddr::IP->new($new, 0) + or die "bad end address $new"; + if ( $end < $start ) { + $self->set('start', $end); + ($end, $start) = ($start, $end); + } + $self->set('length', $end - $start + 1); + return $end->addr; + } + my $end = $start + $self->get('length') - 1; + $end->addr; +} + +=item contains IPADDR + +Checks whether IPADDR (a dotted-quad IPv4 address) is within the range. + +=cut + +sub contains { + my $self = shift; + my $addr = shift; + $addr = NetAddr::IP->new($addr, 0) + unless ref($addr) and UNIVERSAL::isa($addr, 'NetAddr::IP'); + return 0 unless $addr; + + my $start = NetAddr::IP->new($self->start, 0); + + return ($addr >= $start and $addr - $start < $self->length) ? 1 : 0; +} + +=item as_string + +Returns a readable string showing the address range. + +=cut + +sub as_string { + my $self = shift; + my $start = NetAddr::IP->new($self->start, 0); + my $end = $start + $self->length; + + if ( $self->length == 1 ) { + # then just the address + return $self->start; + } else { # we have to get tricksy + my @end_octets = split('\.', $end->addr); + $start = ($start->numeric)[0] + 0; + $end = ($end->numeric)[0] + 0; + # which octets are different between start and end? + my $delta = $end ^ $start; + foreach (0xffffff, 0xffff, 0xff) { + if ( $delta <= $_ ) { + # then they are identical in the first 8/16/24 bits + shift @end_octets; + } + } + return $self->start . '-' . join('.', @end_octets); + } +} + +=item desc + +Returns a semi-friendly description of the block status. + +=item allow_use + +Returns true if addresses in this range can be used by services, etc. + +=cut + +sub desc { + my $self = shift; + $status_desc{ $self->status }; +} + +sub allow_use { + my $self = shift; + $status_allow_use{ $self->status }; +} + +=back + +=head1 CLASS METHODS + +=sub any_contains IPADDR + +Returns all address ranges that contain IPADDR. + +=cut + +sub any_contains { + my $self = shift; + my $addr = shift; + return grep { $_->contains($addr) } qsearch('addr_range', {}); +} + +=head1 DEVELOPER NOTE + +L<NetAddr::IP> objects have netmasks. When using them to represent +range endpoints, be sure to set the netmask to I<zero> so that math on +the address doesn't stop at the subnet boundary. (The default is /32, +which doesn't work very well. Address ranges ignore subnet boundaries.) + +=head1 BUGS + +=head1 SEE ALSO + +L<FS::svc_IP_Mixin>, L<FS::Record>, schema.html from the base documentation. + +=cut + +1; + diff --git a/FS/FS/part_export/bulkvs_e911.pm b/FS/FS/part_export/bulkvs_e911.pm index c66305bc4..a8af3a055 100644 --- a/FS/FS/part_export/bulkvs_e911.pm +++ b/FS/FS/part_export/bulkvs_e911.pm @@ -57,7 +57,7 @@ sub _export_insert { my ($self, $svc_phone) = @_; my @login = $self->login; - my $location = $svc_phone->cust_location + my $location = $svc_phone->cust_location_or_main or return 'no e911 location defined for this phone service'; warn "$me validating address for svcnum ".$svc_phone->svcnum."\n" diff --git a/FS/FS/svc_Common.pm b/FS/FS/svc_Common.pm index 56567e8ce..659255eaa 100644 --- a/FS/FS/svc_Common.pm +++ b/FS/FS/svc_Common.pm @@ -336,6 +336,7 @@ sub _check_duplcate { ''; } sub preinsert_hook { ''; } sub table_dupcheck_fields { (); } sub prereplace_hook { ''; } +sub prereplace_hook_first { ''; } sub predelete_hook { ''; } sub predelete_hook_first { ''; } diff --git a/FS/MANIFEST b/FS/MANIFEST index ba933996d..1f2dfccc4 100644 --- a/FS/MANIFEST +++ b/FS/MANIFEST @@ -735,3 +735,5 @@ FS/alarm_type.pm t/alarm_type.t FS/alarm_station.pm t/alarm_station.t +FS/addr_range.pm +t/addr_range.t diff --git a/FS/bin/freeside-setup b/FS/bin/freeside-setup index 07da88dea..a6908e172 100755 --- a/FS/bin/freeside-setup +++ b/FS/bin/freeside-setup @@ -37,51 +37,8 @@ getsecrets(); #needs to match FS::Record my($dbdef_file) = "%%%FREESIDE_CONF%%%/dbdef.". datasrc; -### - my $username_len = 32; -#print "\n\n", <<END, ":"; -#Freeside tracks the RADIUS User-Name, check attribute Password and -#reply attribute Framed-IP-Address for each user. You can specify additional -#check and reply attributes (or you can add them later with the -#fs-radius-add-check and fs-radius-add-reply programs). -# -#First enter any additional RADIUS check attributes you need to track for each -#user, separated by whitespace. -#END -#my @check_attributes = map { $attrib2db{lc($_)} or die "unknown attribute $_"; } -# split(" ",&getvalue); -# -#print "\n\n", <<END, ":"; -#Now enter any additional reply attributes you need to track for each user, -#separated by whitespace. -#END -#my @attributes = map { $attrib2db{lc($_)} or die "unknown attribute $_"; } -# split(" ",&getvalue); -# -#print "\n\n", <<END, ":"; -#Do you wish to enable the tracking of a second, separate shipping/service -#address? -#END -#my $ship = &_yesno; -# -#sub getvalue { -# my($x)=scalar(<STDIN>); -# chop $x; -# $x; -#} -# -#sub _yesno { -# print " [y/N]:"; -# my $x = scalar(<STDIN>); -# $x =~ /^y/i; -#} - -#my @check_attributes = (); #add later -#my @attributes = (); #add later -#my $ship = $opt_s; - ### # create a dbdef object from the old data structure ### diff --git a/FS/t/addr_range.t b/FS/t/addr_range.t new file mode 100644 index 000000000..6747d6768 --- /dev/null +++ b/FS/t/addr_range.t @@ -0,0 +1,5 @@ +BEGIN { $| = 1; print "1..1\n" } +END {print "not ok 1\n" unless $loaded;} +use FS::addr_range; +$loaded=1; +print "ok 1\n"; diff --git a/bin/cust_pkg-restore_setup b/bin/cust_pkg-restore_setup index 5467eade9..a3761d1c6 100755 --- a/bin/cust_pkg-restore_setup +++ b/bin/cust_pkg-restore_setup @@ -23,7 +23,7 @@ my $oldAutoCommit = $FS::UID::AutoCommit; local $FS::UID::AutoCommit = 0; my $dbh = dbh; -my $fuzz = 2; +#my $fuzz = 2; my $changed = 0; @@ -35,14 +35,10 @@ foreach my $cust_pkg ( }) ) { - #XXX only canceled packages? - #XXX only suspended packages? - my $h_cust_pkg = qsearchs({ table => 'h_cust_pkg', hashref => { pkgnum => $cust_pkg->pkgnum, - history_action => 'replace_old', setup => { op=>'!=', value=>'' }, ($opt_u ? ('susp' => { op=>'!=', value=>'' }) : () @@ -51,7 +47,8 @@ foreach my $cust_pkg ( : () ), }, - extra_sql => ' AND history_date >= ? AND history_date <= ? ', + extra_sql => " AND history_action IN ('insert','replace_old')". + ' AND history_date >= ? AND history_date <= ? ', extra_param => [ [$sdate,'int'], [$edate,'int'] ], order_by => 'ORDER BY history_date DESC LIMIT 1', }) @@ -60,14 +57,20 @@ foreach my $cust_pkg ( $changed++; #if ( $opt_r ) { - print "restoring setup for pkgnum ". $cust_pkg->pkgnum. - " (custnum ". $cust_pkg->custnum. - ") to ". time2str('%D', $h_cust_pkg->setup). "\n"; + #print "restoring setup for pkgnum ". $cust_pkg->pkgnum. + # " (custnum ". $cust_pkg->custnum. + # ") to ". time2str('%D', $h_cust_pkg->setup). "\n"; + print $cust_pkg->pkgnum. ','. + time2str('%D', $h_cust_pkg->setup). ','. + $cust_pkg->custnum. ','. + '"'. $cust_pkg->cust_main->name. '"'. "\n"; #} - $cust_pkg->set('setup', $h_cust_pkg->setup); - my $error = $cust_pkg->replace; - die $error if $error; + #don't actually do it yet ... + #$cust_pkg->set('setup', $h_cust_pkg->setup); + #my $error = $cust_pkg->replace; + ##die $error if $error; + #warn "error changing pkgnum ". $cust_pkg->pkgnum.': '. $error."\n"; } diff --git a/fs_selfservice/php/freeside_signup_example.php b/fs_selfservice/php/freeside_signup_example.php index 8b1dc193c..480587732 100644 --- a/fs_selfservice/php/freeside_signup_example.php +++ b/fs_selfservice/php/freeside_signup_example.php @@ -34,7 +34,7 @@ if ( ! $error ) { $custnum = $response['custnum']; - error_log("[new_customer] signup up with custnum $custnum"); + error_log("[new_customer] new signup with custnum $custnum"); } else { diff --git a/httemplate/browse/addr_range.html b/httemplate/browse/addr_range.html new file mode 100644 index 000000000..d657f32ec --- /dev/null +++ b/httemplate/browse/addr_range.html @@ -0,0 +1,66 @@ +<& elements/browse.html, + 'title' => 'Address Ranges', + 'name_singular' => 'address range', + 'html_init' => $html_init, + 'html_foot' => $html_foot, + 'query' => { 'table' => 'addr_range', + 'order_by' => $order_by, + }, + 'count_query' => "SELECT count(*) from addr_range", + 'header' => [ 'From', + '', # the dash + 'To', + 'Status', + # would be nice to show whether any addresses in the + # range are assigned, but that's ugly + ], + 'fields' => [ 'start', + sub { '–' }, + 'end', + 'desc', + ], + 'links' => [ + [ '#' ], + '', + [ '#' ], + ], + 'link_onclicks' => [ $edit_link, + '', + $edit_link, + '', + ], +&> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Broadband global configuration'); +# addr_ranges are ALWAYS global, else there will be chaos + +my $order_by = "ORDER BY inet(start)"; # Pg-ism +# though we could also make the field itself inet-type... +# this would simplify a lot of things. + +my $html_init = include('/elements/error.html'); + +my $edit_link = sub { + my $addr_range = shift; + include('/elements/popup_link_onclick.html', + action => $p.'edit/addr_range.html?rangenum='. + $addr_range->rangenum, + actionlabel => 'Edit address range', + width => 650, + height => 420, + ); +}; + +my $add_link = include('/elements/popup_link_onclick.html', + action => $p.'edit/addr_range.html', + actionlabel => 'Edit address range', + width => 650, + height => 420, +); + +my $html_foot = qq!<A HREF="#" onclick="$add_link"> +<I>Add a new address range</I></A>!; + +</%init> diff --git a/httemplate/edit/addr_range.html b/httemplate/edit/addr_range.html new file mode 100644 index 000000000..68efa5d79 --- /dev/null +++ b/httemplate/edit/addr_range.html @@ -0,0 +1,27 @@ +<& elements/edit.html, + 'name_singular' => 'address range', + 'popup' => 1, + 'table' => 'addr_range', + 'labels' => { 'start' => 'From', + 'end' => 'To', + 'status' => 'Status', + 'rangenum'=> 'Range', + }, + 'fields' => [ 'start', + 'end', + { field => 'status', + type => 'select', + labels => \%FS::addr_range::status_desc, + options => [ sort { $a cmp $b } + keys(%FS::addr_range::status_desc) ], + disable_empty => 1, + }, + ], + 'delete_url' => $p.'misc/delete-addr_range.html', +&> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Broadband global configuration'); + +</%init> diff --git a/httemplate/edit/cust_main/billing.html b/httemplate/edit/cust_main/billing.html index da5f0f27f..1088cf5fd 100644 --- a/httemplate/edit/cust_main/billing.html +++ b/httemplate/edit/cust_main/billing.html @@ -679,8 +679,8 @@ my $conf = new FS::Conf; my $payby_default = $conf->config('payby-default'); my @payby = grep /\w/, $conf->config('payby'); -#@payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH WEST COMP )) -@payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH COMP )) +#@payby = (qw( CARD DCRD CHEK DCHK BILL CASH WEST COMP )) +@payby = (qw( CARD DCRD CHEK DCHK BILL CASH COMP )) unless @payby; my $show_term = ''; diff --git a/httemplate/edit/elements/edit.html b/httemplate/edit/elements/edit.html index 9e27f2a4c..16d0817eb 100644 --- a/httemplate/edit/elements/edit.html +++ b/httemplate/edit/elements/edit.html @@ -108,6 +108,9 @@ Example: # overrides default popurl(1)."process/$table.html" 'post_url' => popurl(1).'process/something', + # optional link to delete this object; primary key will be appended + 'delete_url' => $p.'misc/delete-something.html?', + #we're in a popup (no title/menu/searchboxes) 'popup' => 1, @@ -211,6 +214,7 @@ Example: % ); % } + <% include('/elements/header'. ( $opt{popup} ? '-popup' : '' ). '.html', $title, include( '/elements/menubar.html', @menubar ), @@ -773,6 +777,23 @@ Example: : "Add ". ($opt{'name'} || $opt{'name_singular'}) %>" > +% if ( $opt{'delete_url'} and $object->get($pkey) ) { +% my $delete_msg = 'Delete this '. +% ($opt{'name_singular'} || $opt{'name'}); +% my $delete_url = $opt{'delete_url'}; +% $delete_url .= '?' unless $delete_url =~ /\?/; +% $delete_url .= $object->get($pkey); + <SCRIPT TYPE="text/javascript"> + function confirm_delete() { + if(confirm(<% $delete_msg . '?' |js_string %>)) { + window.location.href = <% $delete_url |js_string %>; + } + } + </SCRIPT> + <INPUT TYPE = "button" + VALUE = "<% $delete_msg |h %>" + onclick = "confirm_delete()"> +% } % } </FORM> diff --git a/httemplate/edit/process/addr_range.html b/httemplate/edit/process/addr_range.html new file mode 100644 index 000000000..6b05d23a5 --- /dev/null +++ b/httemplate/edit/process/addr_range.html @@ -0,0 +1,22 @@ +<& elements/process.html, + 'table' => 'addr_range', + 'popup_reload' => 'Address range changed', + 'precheck_callback' => sub { + my ($cgi) = @_; + my $start = NetAddr::IP->new($cgi->param('start'), 0) + or return 'Illegal or empty (IP address) start: '.$cgi->param('start'); + if ( length($cgi->param('end')) ) { + my $end = NetAddr::IP->new($cgi->param('end'), 0) + or return 'Illegal or empty (IP address) end: '.$cgi->param('end'); + if ( $end < $start ) { + ($start, $end) = ($end, $start); + $cgi->param('end', $end->addr); + $cgi->param('start', $start->addr); + } + $cgi->param('length', $end - $start + 1); + } else { + $cgi->param('length', 1); + } + ''; + }, +&> diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html index b4fff2285..2ae216c5c 100644 --- a/httemplate/elements/menu.html +++ b/httemplate/elements/menu.html @@ -508,6 +508,10 @@ tie my %config_broadband, 'Tie::IxHash', 'Routers' => [ $fsurl.'browse/router.cgi', 'Broadband access routers' ], 'Address blocks' => [ $fsurl.'browse/addr_block.cgi', 'Manage address blocks and block assignments to broadband routers' ], ; +if ( $curuser->access_right('Broadband global configuration') ) { + $config_broadband{'Address ranges'} = + [ $fsurl.'browse/addr_range.html', 'Designate special address ranges' ]; +} tie my %config_phone, 'Tie::IxHash', 'View/Edit phone device types' => [ $fsurl.'browse/part_device.html', 'Phone device types' ], diff --git a/httemplate/misc/delete-addr_range.html b/httemplate/misc/delete-addr_range.html new file mode 100644 index 000000000..c6310e9b1 --- /dev/null +++ b/httemplate/misc/delete-addr_range.html @@ -0,0 +1,21 @@ +% if ( $error ) { +<& /elements/errorpage-popup.html, $error &> +% } else { +<& /elements/header-popup.html, "Address range deleted" &> + <SCRIPT TYPE="text/javascript"> + window.top.location.reload(); + </SCRIPT> +</BODY> +</HTML> +% } +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Broadband global configuration'); + +my ($rangenum) = $cgi->keywords; +$rangenum =~ /^\d+$/ or die "bad rangenum '$rangenum'"; +my $addr_range = FS::addr_range->by_key($rangenum); +die "unknown rangenum $rangenum" unless $addr_range; +my $error = $addr_range->delete; +</%init> |