diff options
-rw-r--r-- | FS/FS/Record.pm | 18 | ||||
-rw-r--r-- | FS/FS/access_user.pm | 14 | ||||
-rw-r--r-- | FS/FS/m2m_Common.pm | 92 | ||||
-rw-r--r-- | FS/FS/option_Common.pm | 105 | ||||
-rw-r--r-- | httemplate/elements/header.html | 68 | ||||
-rw-r--r-- | httemplate/elements/menu.html | 15 | ||||
-rw-r--r-- | httemplate/elements/xmenu.css | 11 | ||||
-rw-r--r-- | httemplate/elements/xmenu.top.css | 211 | ||||
-rw-r--r-- | httemplate/elements/xmenu.top.js | 671 | ||||
-rw-r--r-- | httemplate/images/arrow.down.png | bin | 170 -> 155 bytes | |||
-rw-r--r-- | httemplate/images/menu-left-example.png | bin | 0 -> 24709 bytes | |||
-rw-r--r-- | httemplate/images/menu-top-example.png | bin | 0 -> 22816 bytes | |||
-rw-r--r-- | httemplate/pref/pref-process.html | 41 | ||||
-rw-r--r-- | httemplate/pref/pref.html | 28 |
14 files changed, 1173 insertions, 101 deletions
diff --git a/FS/FS/Record.pm b/FS/FS/Record.pm index 34b556eea..6b7e8d5fa 100644 --- a/FS/FS/Record.pm +++ b/FS/FS/Record.pm @@ -26,7 +26,7 @@ use Tie::IxHash; #export dbdef for now... everything else expects to find it here @EXPORT_OK = qw(dbh fields hfields qsearch qsearchs dbdef jsearch); -$DEBUG = 0; +$DEBUG = 3; $me = '[FS::Record]'; $nowarn_identical = 0; @@ -563,6 +563,17 @@ sub dbdef_table { dbdef->table($table); } +=item primary_key + +Returns the primary key for the table. + +=cut + +sub primary_key { + my $self = shift; + my $pkey = $self->dbdef_table->primary_key; +} + =item get, getfield COLUMN Returns the value of the column/field/key COLUMN. @@ -688,6 +699,8 @@ sub insert { my $self = shift; my $saved = {}; + warn "$self -> insert" if $DEBUG; + my $error = $self->check; return $error if $error; @@ -784,8 +797,7 @@ sub insert { dbh->rollback if $FS::UID::AutoCommit; return dbh->errstr; }; - #$i_sth->execute($oid) or do { - $i_sth->execute() or do { + $i_sth->execute() or do { #$i_sth->execute($oid) dbh->rollback if $FS::UID::AutoCommit; return $i_sth->errstr; }; diff --git a/FS/FS/access_user.pm b/FS/FS/access_user.pm index f45f17d60..9be9166f0 100644 --- a/FS/FS/access_user.pm +++ b/FS/FS/access_user.pm @@ -6,10 +6,12 @@ use FS::UID; use FS::Conf; use FS::Record qw( qsearch qsearchs dbh ); use FS::m2m_Common; +use FS::option_Common; use FS::access_usergroup; use FS::agent; -@ISA = qw( FS::m2m_Common FS::Record ); +@ISA = qw( FS::m2m_Common FS::option_Common FS::Record ); +#@ISA = qw( FS::m2m_Common FS::option_Common ); #kludge htpasswd for now (i hope this bootstraps okay) FS::UID->install_callback( sub { @@ -74,6 +76,10 @@ points to. You can ask the object for a copy with the I<hash> method. sub table { 'access_user'; } +sub _option_table { 'access_user_pref'; } +sub _option_namecol { 'prefname'; } +sub _option_valuecol { 'prefvalue'; } + =item insert Adds this record to the database. If there is an error, returns the error, @@ -177,7 +183,11 @@ returns the error, otherwise returns false. =cut sub replace { - my($new, $old) = ( shift, shift ); + my $new = shift; + + my $old = ( ref($_[0]) eq ref($new) ) + ? shift + : $new->replace_old; local $SIG{HUP} = 'IGNORE'; local $SIG{INT} = 'IGNORE'; diff --git a/FS/FS/m2m_Common.pm b/FS/FS/m2m_Common.pm index fd8700a2d..5dc2a8ec8 100644 --- a/FS/FS/m2m_Common.pm +++ b/FS/FS/m2m_Common.pm @@ -3,25 +3,26 @@ package FS::m2m_Common; use strict; use vars qw( @ISA $DEBUG ); use FS::Schema qw( dbdef ); -use FS::Record qw( qsearch qsearchs ); #dbh ); +use FS::Record qw( qsearch qsearchs dbh ); -@ISA = qw( FS::Record ); +#hmm. well. we seem to be used as a mixin. +#@ISA = qw( FS::Record ); $DEBUG = 0; =head1 NAME -FS::m2m_Common - Base class for classes in a many-to-many relationship +FS::m2m_Common - Mixin class for classes in a many-to-many relationship =head1 SYNOPSIS use FS::m2m_Common; -@ISA = qw( FS::m2m_Common ); +@ISA = qw( FS::m2m_Common FS::Record ); =head1 DESCRIPTION -FS::m2m_Common is intended as a base class for classes which have a +FS::m2m_Common is intended as a mixin class for classes which have a many-to-many relationship with another table (via a linking table). Note: It is currently assumed that the link table contains two fields @@ -31,7 +32,17 @@ named the same as the primary keys of ths base and target tables. =over 4 -=item process_m2m +=item process_m2m OPTION => VALUE, ... + +Available options: + +link_table (required) - + +target_table (required) - + +params (required) - hashref; keys are primary key values in target_table (values are boolean). For convenience, keys may optionally be prefixed with the name +of the primary key, as in agentnum54 instead of 54, or passed as an arrayref +of values. =cut @@ -39,41 +50,64 @@ sub process_m2m { my( $self, %opt ) = @_; my $self_pkey = $self->dbdef_table->primary_key; + my %hash = ( $self_pkey => $self->$self_pkey() ); my $link_table = $self->_load_table($opt{'link_table'}); my $target_table = $self->_load_table($opt{'target_table'}); my $target_pkey = dbdef->table($target_table)->primary_key; - foreach my $target_obj ( qsearch($target_table, {} ) ) { + if ( ref($opt{'params'}) eq 'ARRAY' ) { + $opt{'params'} = { map { $_=>1 } @{$opt{'params'}} }; + } - my $targetnum = $target_obj->$target_pkey(); + local $SIG{HUP} = 'IGNORE'; + local $SIG{INT} = 'IGNORE'; + local $SIG{QUIT} = 'IGNORE'; + local $SIG{TERM} = 'IGNORE'; + local $SIG{TSTP} = 'IGNORE'; + local $SIG{PIPE} = 'IGNORE'; + + my $oldAutoCommit = $FS::UID::AutoCommit; + local $FS::UID::AutoCommit = 0; + my $dbh = dbh; + + foreach my $del_obj ( + grep { + my $targetnum = $_->$target_pkey(); + ( ! $opt{'params'}->{$targetnum} + && ! $opt{'params'}->{"$target_pkey$targetnum"} + ); + } + qsearch( $link_table, \%hash ) + ) { + my $error = $del_obj->delete; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + } - my $link_obj = qsearchs( $link_table, { - $self_pkey => $self->$self_pkey(), - $target_pkey => $targetnum, + foreach my $add_targetnum ( + grep { ! qsearchs( $link_table, { %hash, $target_pkey => $_ } ) } + map { /^($target_pkey)?(\d+)$/; $2; } + grep { /^($target_pkey)?(\d+)$/ } + grep { $opt{'params'}->{$_} } + keys %{ $opt{'params'} } + ) { + + my $add_obj = "FS::$link_table"->new( { + %hash, + $target_pkey => $add_targetnum, }); - - if ( $link_obj && ! $opt{'params'}->{"$target_pkey$targetnum"} ) { - - my $d_link_obj = $link_obj; #need to save $link_obj for below. - my $error = $d_link_obj->delete; - die $error if $error; - - } elsif ( $opt{'params'}->{"$target_pkey$targetnum"} && ! $link_obj ) { - - #ok to clobber it now (but bad form nonetheless?) - #$link_obj = new "FS::$link_table" ( { - $link_obj = "FS::$link_table"->new( { - $self_pkey => $self->$self_pkey(), - $target_pkey => $targetnum, - }); - my $error = $link_obj->insert; - die $error if $error; + my $error = $add_obj->insert; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; } - } + $dbh->commit or die $dbh->errstr if $oldAutoCommit; ''; } diff --git a/FS/FS/option_Common.pm b/FS/FS/option_Common.pm index ad3c26958..0efacbd8a 100644 --- a/FS/FS/option_Common.pm +++ b/FS/FS/option_Common.pm @@ -6,7 +6,7 @@ use FS::Record qw( qsearch qsearchs dbh ); @ISA = qw( FS::Record ); -$DEBUG = 0; +$DEBUG = 3; =head1 NAME @@ -18,6 +18,11 @@ use FS::option_Common; @ISA = qw( FS::option_Common ); +#optional for non-standard names +sub _option_table { 'table_name'; } #defaults to ${table}_option +sub _option_namecol { 'column_name'; } #defaults to optionname +sub _option_valuecol { 'column_name'; } #defaults to optionvalue + =head1 DESCRIPTION FS::option_Common is intended as a base class for classes which have a @@ -66,14 +71,17 @@ sub insert { return $error; } - my $pkey = $self->pkey; + my $pkey = $self->primary_key; my $option_table = $self->option_table; + my $namecol = $self->_option_namecol; + my $valuecol = $self->_option_valuecol; + foreach my $optionname ( keys %{$options} ) { my $href = { - $pkey => $self->get($pkey), - 'optionname' => $optionname, - 'optionvalue' => $options->{$optionname}, + $pkey => $self->get($pkey), + $namecol => $optionname, + $valuecol => $options->{$optionname}, }; #my $option_record = eval "new FS::$option_table \$href"; @@ -123,7 +131,7 @@ sub delete { return $error; } - my $pkey = $self->pkey; + my $pkey = $self->primary_key; #my $option_table = $self->option_table; foreach my $obj ( $self->option_objects ) { @@ -140,7 +148,7 @@ sub delete { } -=item replace OLD_RECORD [ HASHREF | OPTION => VALUE ... ] +=item replace [ OLD_RECORD ] [ HASHREF | OPTION => VALUE ... ] Replaces the OLD_RECORD with this one in the database. If there is an error, returns the error, otherwise returns false. @@ -152,12 +160,16 @@ created or modified (see L<FS::part_export_option>). sub replace { my $self = shift; - my $old = shift; + + my $old = ( ref($_[0]) eq ref($self) ) + ? shift + : $self->replace_old; + my $options = ( ref($_[0]) eq 'HASH' ) ? shift : { @_ }; - warn "FS::option_Common::insert called on $self with options ". + warn "FS::option_Common::replace called on $self with options ". join(', ', map "$_ => ". $options->{$_}, keys %$options) if $DEBUG; @@ -178,30 +190,44 @@ sub replace { return $error; } - my $pkey = $self->pkey; + my $pkey = $self->primary_key; my $option_table = $self->option_table; + my $namecol = $self->_option_namecol; + my $valuecol = $self->_option_valuecol; + foreach my $optionname ( keys %{$options} ) { - my $old = qsearchs( $option_table, { - $pkey => $self->get($pkey), - 'optionname' => $optionname, + + warn "FS::option_Common::replace: inserting or replacing option: $optionname" + if $DEBUG > 1; + + my $oldopt = qsearchs( $option_table, { + $pkey => $self->get($pkey), + $namecol => $optionname, } ); my $href = { - $pkey => $self->get($pkey), - 'optionname' => $optionname, - 'optionvalue' => $options->{$optionname}, + $pkey => $self->get($pkey), + $namecol => $optionname, + $valuecol => $options->{$optionname}, }; - #my $new = eval "new FS::$option_table \$href"; + #my $newopt = eval "new FS::$option_table \$href"; #if ( $@ ) { # $dbh->rollback if $oldAutoCommit; # return $@; #} - my $new = "FS::$option_table"->new($href); + my $newopt = "FS::$option_table"->new($href); + + my $opt_pkey = $newopt->primary_key; - $new->optionnum($old->optionnum) if $old; - my $error = $old ? $new->replace($old) : $new->insert; + $newopt->$opt_pkey($oldopt->$opt_pkey) if $oldopt; + warn $oldopt; + warn "FS::option_Common::replace: ". + ( $oldopt ? "$newopt -> replace($oldopt)" : "$newopt -> insert" ) + if $DEBUG > 2; + my $error = $oldopt ? $newopt->replace($oldopt) : $newopt->insert; + warn $error; if ( $error ) { $dbh->rollback if $oldAutoCommit; return $error; @@ -210,7 +236,7 @@ sub replace { #remove extraneous old options foreach my $opt ( - grep { !exists $options->{$_->optionname} } $old->option_objects + grep { !exists $options->{$_->$namecol()} } $old->option_objects ) { my $error = $opt->delete; if ( $error ) { @@ -233,7 +259,7 @@ Returns all options as FS::I<tablename>_option objects. sub option_objects { my $self = shift; - my $pkey = $self->pkey; + my $pkey = $self->primary_key; my $option_table = $self->option_table; qsearch($option_table, { $pkey => $self->get($pkey) } ); } @@ -246,7 +272,9 @@ Returns a list of option names and values suitable for assigning to a hash. sub options { my $self = shift; - map { $_->optionname => $_->optionvalue } $self->option_objects; + my $namecol = $self->_option_namecol; + my $valuecol = $self->_option_valuecol; + map { $_->$namecol() => $_->$valuecol() } $self->option_objects; } =item option OPTIONNAME @@ -257,30 +285,35 @@ Returns the option value for the given name, or the empty string. sub option { my $self = shift; - my $pkey = $self->pkey; + my $pkey = $self->primary_key; my $option_table = $self->option_table; - my $obj = - qsearchs($option_table, { - $pkey => $self->get($pkey), - optionname => shift, - } ); - $obj ? $obj->optionvalue : ''; + my $namecol = $self->_option_namecol; + my $valuecol = $self->_option_valuecol; + my $hashref = { + $pkey => $self->get($pkey), + $namecol => shift, + }; + warn "$self -> option: searching for ". + join(' / ', map { "$_ => ". $hashref->{$_} } keys %$hashref ) + if $DEBUG; + my $obj = qsearchs($option_table, $hashref); + $obj ? $obj->$valuecol() : ''; } -sub pkey { - my $self = shift; - my $pkey = $self->dbdef_table->primary_key; -} - sub option_table { my $self = shift; - my $option_table = $self->table . '_option'; + my $option_table = $self->_option_table; eval "use FS::$option_table"; die $@ if $@; $option_table; } +#defaults +sub _option_table { shift->table .'_option'; } +sub _option_namecol { 'optionname'; } +sub _option_valuecol { 'optionvalue'; } + =back =head1 BUGS diff --git a/httemplate/elements/header.html b/httemplate/elements/header.html index 0e69b19ca..1f5674885 100644 --- a/httemplate/elements/header.html +++ b/httemplate/elements/header.html @@ -1,11 +1,3 @@ -% -% my($title, $menubar) = ( shift, shift ); -% my $etc = @_ ? shift : ''; #$etc is for things like onLoad= etc. -% my $head = @_ ? shift : ''; #$head is for things that go in the <HEAD> section -% my $conf = new FS::Conf; -% -% - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <HTML> <HEAD> @@ -16,7 +8,10 @@ <META HTTP-Equiv="Pragma" Content="no-cache"> <META HTTP-Equiv="Expires" Content="0"> - <% include('menu.html', 'freeside_baseurl' => $fsurl ) %> + <% include('menu.html', 'freeside_baseurl' => $fsurl, + 'position' => $menu_position, + ) + %> <SCRIPT TYPE="text/javascript"> function clearhint_search_cust (what) { @@ -43,7 +38,7 @@ <% $head %> </HEAD> - <BODY BACKGROUND="<%$fsurl%>images/background-cheat.png" <% $etc %> STYLE="margin-top:0; margin-bottom:0; margin-left:0; margin-right:0"> + <BODY <% $menu_position eq 'left' ? qq( BACKGROUND="${fsurl}images/background-cheat.png" ) : ' BGCOLOR="#e8e8e8" ' %> <% $etc %> STYLE="margin-top:0; margin-bottom:0; margin-left:0; margin-right:0"> <table width="100%" CELLPADDING=0 CELLSPACING=0 STYLE="padding-left:0; padding-right:4"> <tr> <td rowspan=2 BGCOLOR="#ffffff"><IMG BORDER=0 ALT="freeside" SRC="<%$fsurl%>images/small-logo.png"></td> @@ -121,6 +116,30 @@ input.fsblackbuttonselected { <TD COLSPAN=5 WIDTH="100%" STYLE="padding:0"><IMG BORDER=0 ALT="" SRC="<%$fsurl%>images/black-gradient.png" HEIGHT="13" WIDTH="100%"></TD> </TR> +% if ( $menu_position eq 'top' ) { + + <TR> + + <TD COLSPAN="5" WIDTH="100%" STYLE="padding:0"> + <SCRIPT TYPE="text/javascript"> + document.write(myBar); + </SCRIPT> + </TD> + + </TR> + + <TR> + <TD COLSPAN="5" WIDTH="100%" HEIGHT="2px" STYLE="padding:0" BGCOLOR="#000000"> + </TD> + </TR> + + <TR> + <TD COLSPAN="5" WIDTH="100%" HEIGHT="4px" STYLE="padding:0" BGCOLOR="#000000"> + </TD> + </TR> + +% } + <TR> <TD COLSPAN=1 BGCOLOR="#000000" ALIGN="right"> @@ -172,13 +191,26 @@ input.fsblackbuttonselected { </TR> </TABLE> + <TABLE WIDTH="100%" HEIGHT="100%" CELLSPACING=0 CELLPADDING=4> + <TR> + +% if ( $menu_position eq 'left' ) { + <TD BGCOLOR="#000000" STYLE="padding:0" WIDTH="154"></TD> <TD STYLE="padding:0" WIDTH="13"><IMG BORDER=0 ALT="" SRC="<%$fsurl%>images/black-gray-corner.png"></TD> - <TD STYLE="padding:0"><IMG BORDER=0 ALT="" SRC="<%$fsurl%>images/black-gray-top.png" HEIGHT="13" WIDTH="100%"></TD> + +% } + + <TD STYLE="padding:0" WIDTH="100%"><IMG BORDER=0 ALT="" SRC="<%$fsurl%>images/black-gray-top.png" HEIGHT="13" WIDTH="100%"></TD> + </TR> + <TR HEIGHT="100%"> + +% if ( $menu_position eq 'left' ) { + <TD BGCOLOR="#000000" ALIGN="left" HEIGHT="100%" WIDTH="154" VALIGN="top" ALIGN="right"> <SCRIPT TYPE="text/javascript"> document.write(myBar); @@ -188,6 +220,9 @@ input.fsblackbuttonselected { </TD> <TD STYLE="padding:0" HEIGHT="100%" WIDTH=13 VALIGN="top"><IMG WIDTH="13" HEIGHT="100%" BORDER=0 ALT="" SRC="<%$fsurl%>images/black-gray-side.png"></TD> + +% } + <TD BGCOLOR="#e8e8e8" HEIGHT="100%" VALIGN="top"> <!-- WIDTH="100%"> --> <FONT SIZE=6> @@ -196,3 +231,14 @@ input.fsblackbuttonselected { <BR><BR> <% $menubar !~ /^\s*$/ ? "$menubar<BR><BR>" : '' %> +<%init> + +my($title, $menubar) = ( shift, shift ); +my $etc = @_ ? shift : ''; #$etc is for things like onLoad= etc. +my $head = @_ ? shift : ''; #$head is for things that go in the <HEAD> section +my $conf = new FS::Conf; + +my $menu_position = $FS::CurrentUser::CurrentUser->option('menu_position') + || 'left'; + +</%init> diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html index 25dd619f6..94bb0e0ba 100644 --- a/httemplate/elements/menu.html +++ b/httemplate/elements/menu.html @@ -1,6 +1,17 @@ <script type="text/javascript" src="<%$fsurl%>elements/cssexpr.js"></script> -<script type="text/javascript" src="<%$fsurl%>elements/xmenu.js"></script> -<link href="<%$fsurl%>elements/xmenu.css" type="text/css" rel="stylesheet"> + +% if ( $opt{'position'} eq 'top' ) { + + <script type="text/javascript" src="<%$fsurl%>elements/xmenu.top.js"></script> + <link href="<%$fsurl%>elements/xmenu.top.css" type="text/css" rel="stylesheet"> + +% } else { # elsif ( $opt{'position'} eq 'left' ) { + + <script type="text/javascript" src="<%$fsurl%>elements/xmenu.js"></script> + <link href="<%$fsurl%>elements/xmenu.css" type="text/css" rel="stylesheet"> + +% } + <link href="<%$fsurl%>elements/freeside.css" type="text/css" rel="stylesheet"> <SCRIPT TYPE="text/javascript"> diff --git a/httemplate/elements/xmenu.css b/httemplate/elements/xmenu.css index 46b221bda..97c7da8bb 100644 --- a/httemplate/elements/xmenu.css +++ b/httemplate/elements/xmenu.css @@ -95,6 +95,9 @@ } .webfx-menu-bar { + /* i want a vertical bar */ + display: block; + /* background: rgb(120,172,255);/*rgb(255,128,0);*/ /* background: #a097ed; */ background: #000000; @@ -167,10 +170,10 @@ */ ie-dummy: expression(this.hideFocus=true); - border-left: 1px solid rgb(0,66,174); - border-right: 1px solid rgb(234,242,255); - border-top: 1px solid rgb(0,66,174); - border-bottom: 1px solid rgb(234,242,255); +/* border-left: 1px solid rgb(0,66,174); */ +/* border-right: 1px solid rgb(234,242,255); */ +/* border-top: 1px solid rgb(0,66,174); */ +/* border-bottom: 1px solid rgb(234,242,255); */ } .webfx-menu-title { diff --git a/httemplate/elements/xmenu.top.css b/httemplate/elements/xmenu.top.css new file mode 100644 index 000000000..75917031b --- /dev/null +++ b/httemplate/elements/xmenu.top.css @@ -0,0 +1,211 @@ + +.webfx-menu, .webfx-menu * { + /* + Set the box sizing to content box + in the future when IE6 supports box-sizing + there will be an issue to fix the sizes + + There is probably an issue with IE5 mac now + because IE5 uses content-box but the script + assumes all versions of IE uses border-box. + + At the time of this writing mozilla did not support + box-sizing for absolute positioned element. + + Opera only supports content-box + */ + box-sizing: content-box; + -moz-box-sizing: content-box; +} + +.webfx-menu { + position: absolute; + z-index: 100; + visibility: hidden; + border: 1px solid black; + padding: 1px; + background: white; + filter: progid:DXImageTransform.Microsoft.Shadow(color="#777777", Direction=135, Strength=4) + alpha(Opacity=95); + -moz-opacity: 0.95; + /* a drop shadow would be nice in moz/others too... */ +} + +.webfx-menu-empty { + display: block; + border: 1px solid white; + padding: 2px 5px 2px 5px; + font-size: 11px; + /* font-family: Tahoma, Verdan, Helvetica, Sans-Serif; */ + color: black; +} + +.webfx-menu a { + display: block; + /* width: expression(constExpression(ieBox ? "100%": "auto")); /* should be ignored by mz and op */ + width: expression(constExpression(ie ? "98%": "auto")); /* should be ignored by mz and op */ + overflow: visible; + /* padding: 2px 0px 2px 5px; */ + padding: 1px 0px 1px 5px; + font-size: 14px; +/* font-family: Verdana, Arial, Helvetica, sans-serif; */ + font-weight: bold; + text-decoration: none; + vertical-align: center; + color: black; + border: 1px solid white; +} + +.webfx-menu a:visited { + color: black; + border: 1px solid white; +} + +.webfx-menu a:hover { + color: black; + border: 1px solid #7e0079; +} + +.webfx-menu a:hover { + color: black; + /* background: #faf7fa; #f5ebf4; #efdfef; white; #BC79B8; */ + /* background: #ffe6fe; */ + /* background: #ffc2fe; */ + background: #fff2fe; + border: 1px solid #7e0079; /*rgb(120,172,255);#ff8800;*/ +} + +.webfx-menu a .arrow { + float: right; + border: 0; + width: 3px; + margin-right: 3px; + margin-top: 4px; +} + +/* separtor */ +.webfx-menu div { + height: 0; + height: expression(constExpression(ieBox ? "2px" : "0")); + border-top: 1px solid #7e0079; /* rgb(120,172,255); */ + border-bottom: 1px solid rgb(234,242,255); + overflow: hidden; + margin: 2px 0px 2px 0px; + font-size: 0mm; +} + +.webfx-menu-bar { + /* background: rgb(120,172,255);/*rgb(255,128,0);*/ + /* background: #a097ed; */ + background: #000000; + /* border: 1px solid #7E0079; */ + /* border: 1px solid #000000; */ + /* border: none */ + color: white; + + padding: 2px; + + /* IE5.0 has the wierdest box model for inline elements */ + padding: expression(constExpression(ie50 ? "0px" : "2px")); +} + +.webfx-menu-bar a, +.webfx-menu-bar a:visited { + /* i want a vertical bar */ + /* display: block; */ + + /* border: 1px solid black; /*rgb(0,0,0);/*rgb(255,128,0);*/ + /* border: 1px solid black; /* #ffffff; */ + /* border-bottom: 1px solid black; */ + /* border-bottom: 1px solid white; */ + /* border-bottom: 1px solid rgb(0,66,174); + /* border-bottom: 1px solid black; + border-bottom: 1px solid black; + border-bottom: 1px solid black; */ + + padding: 1px 5px 1px 5px; + + /* color: black; */ + color: white; + text-decoration: none; + + /* IE5.0 Does not paint borders and padding on inline elements without a height/width */ + height: expression(constExpression(ie50 ? "17px" : "auto")); + + background-color:#333333; + border:1px solid; + border-top-color:#cccccc; + border-left-color:#cccccc; + border-right-color:#aaaaaa; + border-bottom-color:#aaaaaa; + + margin-right: 4px + +} + +.webfx-menu-bar a:link { + color: white; +} + +.webfx-menu-bar a:hover { + /* color: black; */ + color: white; + /* background: rgb(120,172,255); */ + /* background: #BC79B8; */ + background: #7e0079; + /* border-left: 1px solid rgb(234,242,255); + border-right: 1px solid rgb(0,66,174); + border-top: 1px solid rgb(234,242,255); + border-bottom: 1px solid rgb(0,66,174); */ + + border:1px solid; + border-top-color:#cccccc; + border-left-color:#cccccc; + border-right-color:#aaaaaa; + border-bottom-color:#aaaaaa; + +} + +.webfx-menu-bar a .arrow { + /* float: right; */ + border: 0; +/* vertical-align: top; */ +/* width: 3px; */ +/* margin-right: 3px; */ + margin-bottom: 2px; + +} + +.webfx-menu-bar a:active, .webfx-menu-bar a:focus { + -moz-outline: none; + outline: none; + /* + ie does not support outline but ie55 can hide the outline using + a proprietary property on HTMLElement. Did I say that IE sucks at CSS? + */ + ie-dummy: expression(this.hideFocus=true); + +/* border-left: 1px solid rgb(0,66,174); */ +/* border-right: 1px solid rgb(234,242,255); */ +/* border-top: 1px solid rgb(0,66,174); */ +/* border-bottom: 1px solid rgb(234,242,255); */ +} + +.webfx-menu-title { + color: black; + /* background: #faf7fa; #f5ebf4; #efdfef; white; #BC79B8; */ + background: #7e0079; +/* border: 1px solid #7e0079; /*rgb(120,172,255);#ff8800;*/ + /* padding: 3px 1px 3px 6px; */ + padding: 3px 1px 3px 5px; + display: block; + font-size: 16px; +/* font-family: Verdana, Arial, Helvetica, sans-serif; */ + font-weight: bold; + text-decoration: none; + color: white; +/* border: 1px solid white; */ + border-bottom: 1px solid white; + width: expression(constExpression(ie ? "98%": "auto")); /* should be ignored by mz and op */ +} + diff --git a/httemplate/elements/xmenu.top.js b/httemplate/elements/xmenu.top.js new file mode 100644 index 000000000..8d81035a2 --- /dev/null +++ b/httemplate/elements/xmenu.top.js @@ -0,0 +1,671 @@ +//<script> +/* + * This script was created by Erik Arvidsson (erik@eae.net) + * for WebFX (http://webfx.eae.net) + * Copyright 2001 + * + * For usage see license at http://webfx.eae.net/license.html + * + * Created: 2001-01-12 + * Updates: 2001-11-20 Added hover mode support and removed Opera focus hacks + * 2001-12-20 Added auto positioning and some properties to support this + * 2002-08-13 toString used ' for attributes. Changed to " to allow in args + */ + +// check browsers +var ua = navigator.userAgent; +var opera = /opera [56789]|opera\/[56789]/i.test(ua); +var ie = !opera && /MSIE/.test(ua); +var ie50 = ie && /MSIE 5\.[01234]/.test(ua); +var ie6 = ie && /MSIE [6789]/.test(ua); +var ieBox = ie && (document.compatMode == null || document.compatMode != "CSS1Compat"); +var moz = !opera && /gecko/i.test(ua); +var nn6 = !opera && /netscape.*6\./i.test(ua); +var khtml = /KHTML/i.test(ua); + +// define the default values + +webfxMenuDefaultWidth = 154; + +webfxMenuDefaultBorderLeft = 1; +webfxMenuDefaultBorderRight = 1; +webfxMenuDefaultBorderTop = 1; +webfxMenuDefaultBorderBottom = 1; + +webfxMenuDefaultPaddingLeft = 1; +webfxMenuDefaultPaddingRight = 1; +webfxMenuDefaultPaddingTop = 1; +webfxMenuDefaultPaddingBottom = 1; + +webfxMenuDefaultShadowLeft = 0; +webfxMenuDefaultShadowRight = ie && !ie50 && /win32/i.test(navigator.platform) ? 4 :0; +webfxMenuDefaultShadowTop = 0; +webfxMenuDefaultShadowBottom = ie && !ie50 && /win32/i.test(navigator.platform) ? 4 : 0; + + +webfxMenuItemDefaultHeight = 18; +webfxMenuItemDefaultText = "Untitled"; +webfxMenuItemDefaultHref = "javascript:void(0)"; + +webfxMenuSeparatorDefaultHeight = 6; + +webfxMenuDefaultEmptyText = "Empty"; + +webfxMenuDefaultUseAutoPosition = nn6 ? false : true; + + + +// other global constants + +webfxMenuImagePath = ""; + +webfxMenuUseHover = opera ? true : false; +webfxMenuHideTime = 500; +webfxMenuShowTime = 200; + + + +var webFXMenuHandler = { + idCounter : 0, + idPrefix : "webfx-menu-object-", + all : {}, + getId : function () { return this.idPrefix + this.idCounter++; }, + overMenuItem : function (oItem) { + if (this.showTimeout != null) + window.clearTimeout(this.showTimeout); + if (this.hideTimeout != null) + window.clearTimeout(this.hideTimeout); + var jsItem = this.all[oItem.id]; + if (webfxMenuShowTime <= 0) + this._over(jsItem); + else if ( jsItem ) + //this.showTimeout = window.setTimeout(function () { webFXMenuHandler._over(jsItem) ; }, webfxMenuShowTime); + // I hate IE5.0 because the piece of shit crashes when using setTimeout with a function object + this.showTimeout = window.setTimeout("webFXMenuHandler._over(webFXMenuHandler.all['" + jsItem.id + "'])", webfxMenuShowTime); + }, + outMenuItem : function (oItem) { + if (this.showTimeout != null) + window.clearTimeout(this.showTimeout); + if (this.hideTimeout != null) + window.clearTimeout(this.hideTimeout); + var jsItem = this.all[oItem.id]; + if (webfxMenuHideTime <= 0) + this._out(jsItem); + else if ( jsItem ) + //this.hideTimeout = window.setTimeout(function () { webFXMenuHandler._out(jsItem) ; }, webfxMenuHideTime); + this.hideTimeout = window.setTimeout("webFXMenuHandler._out(webFXMenuHandler.all['" + jsItem.id + "'])", webfxMenuHideTime); + }, + blurMenu : function (oMenuItem) { + window.setTimeout("webFXMenuHandler.all[\"" + oMenuItem.id + "\"].subMenu.hide();", webfxMenuHideTime); + }, + _over : function (jsItem) { + if (jsItem.subMenu) { + jsItem.parentMenu.hideAllSubs(); + jsItem.subMenu.show(); + } + else + jsItem.parentMenu.hideAllSubs(); + }, + _out : function (jsItem) { + // find top most menu + var root = jsItem; + var m; + if (root instanceof WebFXMenuButton) + m = root.subMenu; + else { + m = jsItem.parentMenu; + while (m.parentMenu != null && !(m.parentMenu instanceof WebFXMenuBar)) + m = m.parentMenu; + } + if (m != null) + m.hide(); + }, + hideMenu : function (menu) { + if (this.showTimeout != null) + window.clearTimeout(this.showTimeout); + if (this.hideTimeout != null) + window.clearTimeout(this.hideTimeout); + + this.hideTimeout = window.setTimeout("webFXMenuHandler.all['" + menu.id + "'].hide()", webfxMenuHideTime); + }, + showMenu : function (menu, src, dir) { + if (this.showTimeout != null) + window.clearTimeout(this.showTimeout); + if (this.hideTimeout != null) + window.clearTimeout(this.hideTimeout); + + if (arguments.length < 3) + dir = "vertical"; + + menu.show(src, dir); + } +}; + +function WebFXMenu() { + this._menuItems = []; + this._subMenus = []; + this.id = webFXMenuHandler.getId(); + this.top = 0; + this.left = 0; + this.shown = false; + this.parentMenu = null; + webFXMenuHandler.all[this.id] = this; +} + +WebFXMenu.prototype.width = webfxMenuDefaultWidth; +WebFXMenu.prototype.emptyText = webfxMenuDefaultEmptyText; +WebFXMenu.prototype.useAutoPosition = webfxMenuDefaultUseAutoPosition; + +WebFXMenu.prototype.borderLeft = webfxMenuDefaultBorderLeft; +WebFXMenu.prototype.borderRight = webfxMenuDefaultBorderRight; +WebFXMenu.prototype.borderTop = webfxMenuDefaultBorderTop; +WebFXMenu.prototype.borderBottom = webfxMenuDefaultBorderBottom; + +WebFXMenu.prototype.paddingLeft = webfxMenuDefaultPaddingLeft; +WebFXMenu.prototype.paddingRight = webfxMenuDefaultPaddingRight; +WebFXMenu.prototype.paddingTop = webfxMenuDefaultPaddingTop; +WebFXMenu.prototype.paddingBottom = webfxMenuDefaultPaddingBottom; + +WebFXMenu.prototype.shadowLeft = webfxMenuDefaultShadowLeft; +WebFXMenu.prototype.shadowRight = webfxMenuDefaultShadowRight; +WebFXMenu.prototype.shadowTop = webfxMenuDefaultShadowTop; +WebFXMenu.prototype.shadowBottom = webfxMenuDefaultShadowBottom; + + + +WebFXMenu.prototype.add = function (menuItem) { + this._menuItems[this._menuItems.length] = menuItem; + if (menuItem.subMenu) { + this._subMenus[this._subMenus.length] = menuItem.subMenu; + menuItem.subMenu.parentMenu = this; + } + + menuItem.parentMenu = this; +}; + +WebFXMenu.prototype.show = function (relObj, sDir) { + if (this.useAutoPosition) + this.position(relObj, sDir); + + var divElement = document.getElementById(this.id); + if ( divElement ) { + + divElement.style.left = opera ? this.left : this.left + "px"; + divElement.style.top = opera ? this.top : this.top + "px"; + divElement.style.visibility = "visible"; + + if ( ie ) { + var shimElement = document.getElementById(this.id + "Shim"); + if ( shimElement ) { + shimElement.style.width = divElement.offsetWidth; + shimElement.style.height = divElement.offsetHeight; + shimElement.style.top = divElement.style.top; + shimElement.style.left = divElement.style.left; + /*shimElement.style.zIndex = divElement.style.zIndex - 1; */ + shimElement.style.display = "block"; + shimElement.style.filter='progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)'; + } + } + + } + + this.shown = true; + + if (this.parentMenu) + this.parentMenu.show(); +}; + +WebFXMenu.prototype.hide = function () { + this.hideAllSubs(); + var divElement = document.getElementById(this.id); + if ( divElement ) { + divElement.style.visibility = "hidden"; + if ( ie ) { + var shimElement = document.getElementById(this.id + "Shim"); + if ( shimElement ) { + shimElement.style.display = "none"; + } + } + } + + this.shown = false; +}; + +WebFXMenu.prototype.hideAllSubs = function () { + for (var i = 0; i < this._subMenus.length; i++) { + if (this._subMenus[i].shown) + this._subMenus[i].hide(); + } +}; + +WebFXMenu.prototype.toString = function () { + var top = this.top + this.borderTop + this.paddingTop; + var str = "<div id='" + this.id + "' class='webfx-menu' style='" + + "width:" + (!ieBox ? + this.width - this.borderLeft - this.paddingLeft - this.borderRight - this.paddingRight : + this.width) + "px;" + + (this.useAutoPosition ? + "left:" + this.left + "px;" + "top:" + this.top + "px;" : + "") + + (ie50 ? "filter: none;" : "") + + "'>"; + + if (this._menuItems.length == 0) { + str += "<span class='webfx-menu-empty'>" + this.emptyText + "</span>"; + } + else { + str += '<span class="webfx-menu-title" onmouseover="webFXMenuHandler.overMenuItem(this)"' + + (webfxMenuUseHover ? " onmouseout='webFXMenuHandler.outMenuItem(this)'" : "") + + '>' + this.emptyText + '</span>'; + // str += '<div id="' + this.id + '-title">' + this.emptyText + '</div>'; + // loop through all menuItems + for (var i = 0; i < this._menuItems.length; i++) { + var mi = this._menuItems[i]; + str += mi; + if (!this.useAutoPosition) { + if (mi.subMenu && !mi.subMenu.useAutoPosition) + mi.subMenu.top = top - mi.subMenu.borderTop - mi.subMenu.paddingTop; + top += mi.height; + } + } + + } + + str += "</div>"; + + if ( ie ) { + str += "<iframe id='" + this.id + "Shim' src='javascript:false;' scrolling='no' frameBorder='0' style='position:absolute; top:0px; left: 0px; display:none;'></iframe>"; + } + + for (var i = 0; i < this._subMenus.length; i++) { + this._subMenus[i].left = this.left + this.width - this._subMenus[i].borderLeft; + str += this._subMenus[i]; + } + + return str; +}; +// WebFXMenu.prototype.position defined later + +function WebFXMenuItem(sText, sHref, sToolTip, oSubMenu) { + this.text = sText || webfxMenuItemDefaultText; + this.href = (sHref == null || sHref == "") ? webfxMenuItemDefaultHref : sHref; + this.subMenu = oSubMenu; + if (oSubMenu) + oSubMenu.parentMenuItem = this; + this.toolTip = sToolTip; + this.id = webFXMenuHandler.getId(); + webFXMenuHandler.all[this.id] = this; +}; +WebFXMenuItem.prototype.height = webfxMenuItemDefaultHeight; +WebFXMenuItem.prototype.toString = function () { + return "<a" + + " id='" + this.id + "'" + + " href=\"" + this.href + "\"" + + (this.toolTip ? " title=\"" + this.toolTip + "\"" : "") + + " onmouseover='webFXMenuHandler.overMenuItem(this)'" + + (webfxMenuUseHover ? " onmouseout='webFXMenuHandler.outMenuItem(this)'" : "") + + (this.subMenu ? " unselectable='on' tabindex='-1'" : "") + + ">" + + (this.subMenu ? "<img class='arrow' src=\"" + webfxMenuImagePath + "arrow.right.black.png\">" : "") + + this.text + + "</a>"; +}; + + +function WebFXMenuSeparator() { + this.id = webFXMenuHandler.getId(); + webFXMenuHandler.all[this.id] = this; +}; +WebFXMenuSeparator.prototype.height = webfxMenuSeparatorDefaultHeight; +WebFXMenuSeparator.prototype.toString = function () { + return "<div" + + " id='" + this.id + "'" + + (webfxMenuUseHover ? + " onmouseover='webFXMenuHandler.overMenuItem(this)'" + + " onmouseout='webFXMenuHandler.outMenuItem(this)'" + : + "") + + "></div>" +}; + +function WebFXMenuBar() { + this._parentConstructor = WebFXMenu; + this._parentConstructor(); +} +WebFXMenuBar.prototype = new WebFXMenu; +WebFXMenuBar.prototype.toString = function () { + var str = "<div id='" + this.id + "' class='webfx-menu-bar'>"; + + // loop through all menuButtons + for (var i = 0; i < this._menuItems.length; i++) + str += this._menuItems[i]; + + str += "</div>"; + + for (var i = 0; i < this._subMenus.length; i++) + str += this._subMenus[i]; + + return str; +}; + +function WebFXMenuButton(sText, sHref, sToolTip, oSubMenu) { + this._parentConstructor = WebFXMenuItem; + this._parentConstructor(sText, sHref, sToolTip, oSubMenu); +} +WebFXMenuButton.prototype = new WebFXMenuItem; +WebFXMenuButton.prototype.toString = function () { + return "<a" + + " id='" + this.id + "'" + + " href='" + this.href + "'" + + (this.toolTip ? " title='" + this.toolTip + "'" : "") + + (webfxMenuUseHover ? + (" onmouseover='webFXMenuHandler.overMenuItem(this)'" + + " onmouseout='webFXMenuHandler.outMenuItem(this)'") : + ( + " onfocus='webFXMenuHandler.overMenuItem(this)'" + + (this.subMenu ? + " onblur='webFXMenuHandler.blurMenu(this)'" : + "" + ) + )) + + ">" + + this.text + + (this.subMenu ? "<img class='arrow' src='" + webfxMenuImagePath + "arrow.down.png'>" : "") + + "</a>"; +}; + + + + + +/* Position functions */ + + +function getInnerLeft(el, debug) { + + if (el == null) return 0; + + if (ieBox && el == document.body || !ieBox && el == document.documentElement) return 0; + + //if ( debug ) + // alert ( 'getInnerLeft: ' + getLeft(el) + ' - ' + getBorderLeft(el) ); + + return parseInt( getLeft(el) + parseInt(getBorderLeft(el)) ); + +} + + + +function getLeft(el, debug) { + + if (el == null) return 0; + + //if ( debug ) + // alert ( el + ': ' + el.offsetLeft + ' - ' + getInnerLeft(el.offsetParent) ); + + return parseInt( el.offsetLeft + parseInt(getInnerLeft(el.offsetParent)) ); + +} + + + +function getInnerTop(el) { + + if (el == null) return 0; + + if (ieBox && el == document.body || !ieBox && el == document.documentElement) return 0; + + return parseInt( getTop(el) + parseInt(getBorderTop(el)) ); + +} + + + +function getTop(el) { + + if (el == null) return 0; + + return parseInt( el.offsetTop + parseInt(getInnerTop(el.offsetParent)) ); + +} + + + +function getBorderLeft(el) { + + return ie ? + + el.clientLeft : + + ( khtml + ? parseInt(document.defaultView.getComputedStyle(el, null).getPropertyValue("border-left-width")) + : parseInt(window.getComputedStyle(el, null).getPropertyValue("border-left-width")) + ); + +} + + + +function getBorderTop(el) { + + return ie ? + + el.clientTop : + + ( khtml + ? parseInt(document.defaultView.getComputedStyle(el, null).getPropertyValue("border-left-width")) + : parseInt(window.getComputedStyle(el, null).getPropertyValue("border-top-width")) + ); + +} + + + +function opera_getLeft(el) { + + if (el == null) return 0; + + return el.offsetLeft + opera_getLeft(el.offsetParent); + +} + + + +function opera_getTop(el) { + + if (el == null) return 0; + + return el.offsetTop + opera_getTop(el.offsetParent); + +} + + + +function getOuterRect(el, debug) { + + return { + + left: (opera ? opera_getLeft(el) : getLeft(el, debug)), + + top: (opera ? opera_getTop(el) : getTop(el)), + + width: el.offsetWidth, + + height: el.offsetHeight + + }; + +} + + + +// mozilla bug! scrollbars not included in innerWidth/height + +function getDocumentRect(el) { + + return { + + left: 0, + + top: 0, + + width: (ie ? + + (ieBox ? document.body.clientWidth : document.documentElement.clientWidth) : + + window.innerWidth + + ), + + height: (ie ? + + (ieBox ? document.body.clientHeight : document.documentElement.clientHeight) : + + window.innerHeight + + ) + + }; + +} + + + +function getScrollPos(el) { + + return { + + left: (ie ? + + (ieBox ? document.body.scrollLeft : document.documentElement.scrollLeft) : + + window.pageXOffset + + ), + + top: (ie ? + + (ieBox ? document.body.scrollTop : document.documentElement.scrollTop) : + + window.pageYOffset + + ) + + }; + +} + + +/* end position functions */ + +WebFXMenu.prototype.position = function (relEl, sDir) { + var dir = sDir; + // find parent item rectangle, piRect + var piRect; + if (!relEl) { + var pi = this.parentMenuItem; + if (!this.parentMenuItem) + return; + + relEl = document.getElementById(pi.id); + if (dir == null) + dir = pi instanceof WebFXMenuButton ? "vertical" : "horizontal"; + //alert('created RelEl from parent: ' + pi.id); + piRect = getOuterRect(relEl, 1); + } + else if (relEl.left != null && relEl.top != null && relEl.width != null && relEl.height != null) { // got a rect + //alert('passed a Rect as RelEl: ' + typeof(relEl)); + + piRect = relEl; + } + else { + //alert('passed an element as RelEl: ' + typeof(relEl)); + piRect = getOuterRect(relEl); + } + + var menuEl = document.getElementById(this.id); + var menuRect = getOuterRect(menuEl); + var docRect = getDocumentRect(); + var scrollPos = getScrollPos(); + var pMenu = this.parentMenu; + + if (dir == "vertical") { + if (piRect.left + menuRect.width - scrollPos.left <= docRect.width) { + //alert('piRect.left: ' + piRect.left); + this.left = piRect.left; +// if ( ! ie ) +// this.left = this.left + 138; + } else if (docRect.width >= menuRect.width) { + //konq (not safari though) winds up here by accident and positions the menus all weird + //alert('docRect.width + scrollPos.left - menuRect.width'); + + this.left = docRect.width + scrollPos.left - menuRect.width; + } else { + //alert('scrollPos.left: ' + scrollPos.left); + this.left = scrollPos.left; + } + + if (piRect.top + piRect.height + menuRect.height <= docRect.height + scrollPos.top) + + this.top = piRect.top + piRect.height; + + else if (piRect.top - menuRect.height >= scrollPos.top) + + this.top = piRect.top - menuRect.height; + + else if (docRect.height >= menuRect.height) + + this.top = docRect.height + scrollPos.top - menuRect.height; + + else + + this.top = scrollPos.top; + } + else { + if (piRect.top + menuRect.height - this.borderTop - this.paddingTop <= docRect.height + scrollPos.top) + + this.top = piRect.top - this.borderTop - this.paddingTop; + + else if (piRect.top + piRect.height - menuRect.height + this.borderTop + this.paddingTop >= 0) + + this.top = piRect.top + piRect.height - menuRect.height + this.borderBottom + this.paddingBottom + this.shadowBottom; + + else if (docRect.height >= menuRect.height) + + this.top = docRect.height + scrollPos.top - menuRect.height; + + else + + this.top = scrollPos.top; + + + + var pMenuPaddingLeft = pMenu ? pMenu.paddingLeft : 0; + + var pMenuBorderLeft = pMenu ? pMenu.borderLeft : 0; + + var pMenuPaddingRight = pMenu ? pMenu.paddingRight : 0; + + var pMenuBorderRight = pMenu ? pMenu.borderRight : 0; + + + + if (piRect.left + piRect.width + menuRect.width + pMenuPaddingRight + + + pMenuBorderRight - this.borderLeft + this.shadowRight <= docRect.width + scrollPos.left) + + this.left = piRect.left + piRect.width + pMenuPaddingRight + pMenuBorderRight - this.borderLeft; + + else if (piRect.left - menuRect.width - pMenuPaddingLeft - pMenuBorderLeft + this.borderRight + this.shadowRight >= 0) + + this.left = piRect.left - menuRect.width - pMenuPaddingLeft - pMenuBorderLeft + this.borderRight + this.shadowRight; + + else if (docRect.width >= menuRect.width) + + this.left = docRect.width + scrollPos.left - menuRect.width; + + else + + this.left = scrollPos.left; + } +}; diff --git a/httemplate/images/arrow.down.png b/httemplate/images/arrow.down.png Binary files differindex 675d84bde..34cb0286a 100644 --- a/httemplate/images/arrow.down.png +++ b/httemplate/images/arrow.down.png diff --git a/httemplate/images/menu-left-example.png b/httemplate/images/menu-left-example.png Binary files differnew file mode 100644 index 000000000..375725cb9 --- /dev/null +++ b/httemplate/images/menu-left-example.png diff --git a/httemplate/images/menu-top-example.png b/httemplate/images/menu-top-example.png Binary files differnew file mode 100644 index 000000000..bd9bea883 --- /dev/null +++ b/httemplate/images/menu-top-example.png diff --git a/httemplate/pref/pref-process.html b/httemplate/pref/pref-process.html index a342a51ec..221edc682 100644 --- a/httemplate/pref/pref-process.html +++ b/httemplate/pref/pref-process.html @@ -1,26 +1,41 @@ % my $error = ''; % -% my $access_user = qsearchs( 'access_user', { -% 'username' => getotaker, -% '_password' => $cgi->param('_password'), -% } ); +% my $access_user; +% if ( grep { $cgi->param($_) !~ /^\s*$/ } +% qw(_password new_password new_password2) +% ) { % -% $error = 'Current password incorrect; password not changed' -% unless $access_user; +% my $access_user = qsearchs( 'access_user', { +% 'username' => getotaker, +% '_password' => $cgi->param('_password'), +% } ); % -% $error ||= "New passwords don't match" -% unless $cgi->param('new_password') eq $cgi->param('new_password2'); +% $error = 'Current password incorrect; password not changed' +% unless $access_user; % -% $error ||= "No new password entered" -% unless length($cgi->param('new_password')); +% $error ||= "New passwords don't match" +% unless $cgi->param('new_password') eq $cgi->param('new_password2'); % -% $access_user->_password($cgi->param('new_password')) unless $error; -% $error ||= $access_user->replace; +% $error ||= "No new password entered" +% unless length($cgi->param('new_password')); +% +% $access_user->_password($cgi->param('new_password')) unless $error; +% +% } else { +% +% $access_user = $FS::CurrentUser::CurrentUser; +% +% } +% +% $error ||= $access_user->replace( { map { $_ => scalar($cgi->param($_)) } +% qw( menu_position ) #XXX autogen +% } +% ); % % if ( $error ) { % $cgi->param('error', $error); % print $cgi->redirect(popurl(1). "pref.html?". $cgi->query_string ); % } else { -<% include('/elements/header.html', 'Password changed') %> +<% include('/elements/header.html', 'Preferences updated') %> <% include('/elements/footer.html') %> % } diff --git a/httemplate/pref/pref.html b/httemplate/pref/pref.html index 259523941..2dca3b84d 100644 --- a/httemplate/pref/pref.html +++ b/httemplate/pref/pref.html @@ -4,6 +4,8 @@ <% include('/elements/error.html') %> + +Change password (leave blank for no change) <% ntable("#cccccc",2) %> <TR> @@ -22,7 +24,31 @@ </TR> </TABLE> +<BR> + +Interface +<% ntable("#cccccc",2) %> + +<TR> + <TD>Menu location: </TD> + <TD> + <INPUT TYPE="radio" NAME="menu_position" VALUE="left" onClick="document.images['menu_example'].src='../images/menu-left-example.png';" <% $menu_position eq 'left' ? ' CHECKED' : ''%>> Left<BR> + <INPUT TYPE="radio" NAME="menu_position" VALUE="top"onClick="document.images['menu_example'].src='../images/menu-top-example.png';" <% $menu_position eq 'top' ? ' CHECKED' : ''%>> Top <BR> + </TD> + <TD><IMG NAME="menu_example" SRC="../images/menu-<% $menu_position %>-example.png"></TD> +</TR> + +</TABLE> +<BR> -<INPUT TYPE="submit" VALUE="Change password"> +<INPUT TYPE="submit" VALUE="Update preferences"> <% include('/elements/footer.html') %> +<%init> + +# XSS via your own preferences? seems unlikely, but nice try anyway... +( $FS::CurrentUser::CurrentUser->option('menu_position') || 'left' ) + =~ /^(\w+)$/ or die "illegal menu_position"; +my $menu_position = $1; + +</%init> |