--- loncom/interface/lonhtmlcommon.pm 2007/03/12 18:09:36 1.157 +++ loncom/interface/lonhtmlcommon.pm 2009/10/30 05:10:03 1.236 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # a pile of common html routines # -# $Id: lonhtmlcommon.pm,v 1.157 2007/03/12 18:09:36 albertel Exp $ +# $Id: lonhtmlcommon.pm,v 1.236 2009/10/30 05:10:03 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -67,6 +67,87 @@ use LONCAPA; =pod +=item confirm_success + +Successful completion of an operation message + +=cut + +sub confirm_success { + my ($message,$failure)=@_; + if ($failure) { + return '<span class="LC_error">'."\n" + .'<img src="/adm/lonIcons/navmap.wrong.gif" alt="'.&mt('Error').'" /> '."\n" + .$message."\n" + .'</span>'."\n"; + } else { + return '<span class="LC_success">'."\n" + .'<img src="/adm/lonIcons/navmap.correct.gif" alt="'.&mt('OK').'" /> '."\n" + .$message."\n" + .'</span>'."\n"; + } +} + +############################################## +############################################## + +=pod + +=item dragmath_button + +Creates a button that launches a dragmath popup-window, in which an +expression can be edited and pasted as LaTeX into a specified textarea. + + textarea - Name of the textarea to edit. + helpicon - If true, show a help icon to the right of the button. + +=cut + +sub dragmath_button { + my ($textarea,$helpicon) = @_; + my $help_text; + if ($helpicon) { + $help_text = &Apache::loncommon::help_open_topic('Authoring_Math_Editor'); + } + my $buttontext=&mt('Edit Math'); + return <<ENDDRAGMATH; + <input type="button" value="$buttontext", onclick="javascript:mathedit('$textarea',document)" />$help_text +ENDDRAGMATH +} + +############################################## + +=pod + +=item dragmath_js + +Javascript used to open pop-up window containing dragmath applet which +can be used to paste LaTeX into a textarea. + +=cut + +sub dragmath_js { + my ($popup) = @_; + return <<ENDDRAGMATHJS; + <script type="text/javascript"> + // <![CDATA[ + function mathedit(textarea, doc) { + targetEntry = textarea; + targetDoc = doc; + newwin = window.open("/adm/dragmath/applet/$popup.html","","width=565,height=500,resizable"); + } + // ]]> + </script> + +ENDDRAGMATHJS +} + + +############################################## +############################################## + +=pod + =item authorbombs =cut @@ -79,10 +160,10 @@ sub authorbombs { $url=&Apache::lonnet::declutter($url); my ($udom,$uname)=($url=~m{^($LONCAPA::domain_re)/($LONCAPA::username_re)/}); my %bombs=&Apache::lonmsg::all_url_author_res_msg($uname,$udom); - foreach (keys %bombs) { - if ($_=~/^$udom\/$uname\//) { + foreach my $bomb (keys(%bombs)) { + if ($bomb =~ /^$udom\/$uname\//) { return '<a href="/adm/bombs/'.$url. - '"><img src="'.&Apache::loncommon::lonhttpdurl('/adm/lonMisc/bomb.gif').'" border="0" /></a>'. + '"><img src="'.&Apache::loncommon::lonhttpdurl('/adm/lonMisc/bomb.gif').'" alt="'.&mt('Bomb').'" border="0" /></a>'. &Apache::loncommon::help_open_topic('About_Bombs'); } } @@ -140,6 +221,7 @@ sub select_recent { foreach my $value (sort(keys(%recent))) { unless ($value =~/^error\:/) { my $escaped = &Apache::loncommon::escape_url($value); + &Apache::loncommon::inhibit_menu_check(\$escaped); $return.="\n<option value='$escaped'>". &unescape((split(/\&/,$recent{$value}))[1]). '</option>'; @@ -157,7 +239,7 @@ sub get_recent { # Begin filling return_hash with any 'always_include' option my %time_hash = (); my %return_hash = (); - foreach my $item (keys %recent) { + foreach my $item (keys(%recent)) { my ($thistime,$thisvalue)=(split(/\&/,$recent{$item})); if ($thistime eq 'always_include') { $return_hash{$item} = &unescape($thisvalue); @@ -230,7 +312,7 @@ sub checkbox { $Str .= 'value="'.$value.'"'; } if ($checked) { - $Str .= ' checked="1"'; + $Str .= ' checked="checked"'; } $Str .= ' />'; return $Str; @@ -252,7 +334,7 @@ sub radio { $Str .= 'value="'.$value.'"'; } if ($checked eq $value) { - $Str .= ' checked="1"'; + $Str .= ' checked="checked"'; } $Str .= ' />'; return $Str; @@ -310,6 +392,7 @@ The method used to restrict user input w sub date_setter { my ($formname,$dname,$currentvalue,$special,$includeempty,$state, $no_hh_mm_ss,$defhour,$defmin,$defsec,$nolink) = @_; + my $now = time; my $wasdefined=1; if (! defined($state) || $state ne 'disabled') { $state = ''; @@ -318,28 +401,25 @@ sub date_setter { $no_hh_mm_ss = 0; } if ($currentvalue eq 'now') { - $currentvalue=time; + $currentvalue = $now; } if ((!defined($currentvalue)) || ($currentvalue eq '')) { $wasdefined=0; if ($includeempty) { $currentvalue = 0; } else { - $currentvalue = time; + $currentvalue = $now; } } # other potentially useful values: wkday,yrday,is_daylight_savings + my $tzname; my ($sec,$min,$hour,$mday,$month,$year)=('','',undef,'','',''); if ($currentvalue) { - ($sec,$min,$hour,$mday,$month,$year,undef,undef,undef) = - localtime($currentvalue); - $year += 1900; + ($tzname,$sec,$min,$hour,$mday,$month,$year) = &get_timedates($currentvalue); } unless ($wasdefined) { + ($tzname,$sec,$min,$hour,$mday,$month,$year) = &get_timedates($now); if (($defhour) || ($defmin) || ($defsec)) { - ($sec,$min,$hour,$mday,$month,$year,undef,undef,undef) = - localtime(time); - $year += 1900; $sec=($defsec?$defsec:0); $min=($defmin?$defmin:0); $hour=($defhour?$defhour:0); @@ -352,6 +432,7 @@ sub date_setter { my $result = "\n<!-- $dname date setting form -->\n"; $result .= <<ENDJS; <script type="text/javascript"> +// <![CDATA[ function $dname\_checkday() { var day = document.$formname.$dname\_day.value; var month = document.$formname.$dname\_month.value; @@ -412,9 +493,10 @@ document.$formname.$dname\_year.value, } } +// ]]> </script> ENDJS - $result .= ' <span style="white-space: nowrap;">'; + $result .= ' <span class="LC_nobreak">'; my $monthselector = qq{<select name="$dname\_month" $special $state onchange="javascript:$dname\_checkday()" >}; # Month my @Months = qw/January February March April May June @@ -423,23 +505,23 @@ ENDJS unshift(@Months,'If you can read this an error occurred'); if ($includeempty) { $monthselector.="<option value=''></option>"; } for(my $m = 1;$m <=$#Months;$m++) { - $monthselector .= qq{ <option value="$m" }; - $monthselector .= "selected " if ($m-1 eq $month); - $monthselector .= '> '.&mt($Months[$m]).' </option>'; + $monthselector .= qq{ <option value="$m"}; + $monthselector .= ' selected="selected"' if ($m-1 eq $month); + $monthselector .= '> '.&mt($Months[$m]).' </option>'."\n"; } $monthselector.= ' </select>'; # Day my $dayselector = qq{<input type="text" name="$dname\_day" $state value="$mday" size="3" $special onchange="javascript:$dname\_checkday()" />}; # Year - my $yearselector = qq{<input type="year" name="$dname\_year" $state value="$year" size="5" $special onchange="javascript:$dname\_checkday()" />}; + my $yearselector = qq{<input type="text" name="$dname\_year" $state value="$year" size="5" $special onchange="javascript:$dname\_checkday()" />}; # my $hourselector = qq{<select name="$dname\_hour" $special $state >}; if ($includeempty) { $hourselector.=qq{<option value=''></option>}; } for (my $h = 0;$h<24;$h++) { - $hourselector .= qq{<option value="$h" }; - $hourselector .= "selected " if (defined($hour) && $hour == $h); + $hourselector .= qq{<option value="$h"}; + $hourselector .= ' selected="selected"' if (defined($hour) && $hour == $h); $hourselector .= ">"; my $timest=''; if ($h == 0) { @@ -462,16 +544,19 @@ ENDJS $cal_link = qq{<a href="javascript:$dname\_opencalendar()">}; } # + my $tzone = ' '.$tzname.' '; if ($no_hh_mm_ss) { $result .= &mt('[_1] [_2] [_3] ', - $monthselector,$dayselector,$yearselector); + $monthselector,$dayselector,$yearselector). + $tzone; if (!$nolink) { $result .= &mt('[_1]Select Date[_2]',$cal_link,'</a>'); } } else { $result .= &mt('[_1] [_2] [_3] [_4] [_5]m [_6]s ', $monthselector,$dayselector,$yearselector, - $hourselector,$minuteselector,$secondselector); + $hourselector,$minuteselector,$secondselector). + $tzone; if (!$nolink) { $result .= &mt('[_1]Select Date[_2]',$cal_link,'</a>'); } @@ -480,6 +565,35 @@ ENDJS return $result; } +sub get_timedates { + my ($epoch) = @_; + my $dt = DateTime->from_epoch(epoch => $epoch) + ->set_time_zone(&Apache::lonlocal::gettimezone()); + my $tzname = $dt->time_zone_short_name(); + my $sec = $dt->second; + my $min = $dt->minute; + my $hour = $dt->hour; + my $mday = $dt->day; + my $month = $dt->month; + if ($month) { + $month --; + } + my $year = $dt->year; + return ($tzname,$sec,$min,$hour,$mday,$month,$year); +} + +sub build_url { + my ($base, $fields)=@_; + my $url; + $url = $base.'?'; + foreach my $key (keys(%$fields)) { + $url.=&escape($key).'='.&escape($$fields{$key}).'&'; + } + $url =~ s/&$//; + return $url; +} + + ############################################## ############################################## @@ -495,7 +609,7 @@ Inputs: =item $dname -The name passed to &datesetter, which prefixes the form elements. +The name passed to &date_setter, which prefixes the form elements. =item $defaulttime @@ -548,20 +662,33 @@ sub get_date_from_form { if (defined($env{'form.'.$dname.'_month'})) { my $tmpmonth = $env{'form.'.$dname.'_month'}; if (($tmpmonth =~ /^\d+$/) && ($tmpmonth > 0) && ($tmpmonth < 13)) { - $month = $tmpmonth - 1; + $month = $tmpmonth; } } if (defined($env{'form.'.$dname.'_year'})) { my $tmpyear = $env{'form.'.$dname.'_year'}; - if (($tmpyear =~ /^\d+$/) && ($tmpyear > 1900)) { - $year = $tmpyear - 1900; + if (($tmpyear =~ /^\d+$/) && ($tmpyear >= 1970)) { + $year = $tmpyear; } } - if (($year<70) || ($year>137)) { return undef; } + if (($year<1970) || ($year>2037)) { return undef; } if (defined($sec) && defined($min) && defined($hour) && - defined($day) && defined($month) && defined($year) && - eval('&timelocal($sec,$min,$hour,$day,$month,$year)')) { - return &timelocal($sec,$min,$hour,$day,$month,$year); + defined($day) && defined($month) && defined($year)) { + my $timezone = &Apache::lonlocal::gettimezone(); + my $dt = DateTime->new( year => $year, + month => $month, + day => $day, + hour => $hour, + minute => $min, + second => $sec, + time_zone => $timezone, + ); + my $epoch_time = $dt->epoch; + if ($epoch_time ne '') { + return $epoch_time; + } else { + return undef; + } } else { return undef; } @@ -631,6 +758,8 @@ sub javascript_nothing { ############################################## ############################################## sub javascript_docopen { + my ($mimetype) = @_; + $mimetype ||= 'text/html'; # safari does not understand document.open() and loads "text/html" my $nothing = "''"; my $user_browser; @@ -644,7 +773,7 @@ sub javascript_docopen { if ($user_browser eq 'safari' && $user_os =~ 'mac') { $nothing = "document.clear()"; } else { - $nothing = "document.open('text/html','replace')"; + $nothing = "document.open('$mimetype','replace')"; } return $nothing; } @@ -681,7 +810,7 @@ Returns: a perl string as described. ############################################## ############################################## sub StatusOptions { - my ($status, $formName,$size,$onchange)=@_; + my ($status, $formName,$size,$onchange,$mult)=@_; $size = 1 if (!defined($size)); if (! defined($status)) { $status = 'Active'; @@ -690,6 +819,9 @@ sub StatusOptions { my $Str = ''; $Str .= '<select name="Status"'; + if (defined($mult)){ + $Str .= ' multiple="multiple" '; + } if(defined($formName) && $formName ne '' && ! defined($onchange)) { $Str .= ' onchange="document.'.$formName.'.submit()"'; } @@ -846,32 +978,32 @@ sub Create_PrgWin { #the whole function called through timeout is due to issues #in mozilla Read BUG #2665 if you want to know the whole story - &r_print($r,'<script type="text/javascript">'. + &r_print($r,&Apache::lonhtmlcommon::scripttag( "var popwin; function openpopwin () { popwin=open(\'\',\'popwin\',\'width=400,height=100\');". "popwin.document.writeln(\'".$start_page. - "<h4>$heading<\/h4>". - "<form name=\"popremain\" method=\"post\">". + "<h4>".&mt("$heading")."<\/h4>". + "<form action=\"\" name=\"popremain\" method=\"post\">". '<input type="text" size="'.$width.'" name="remaining" value="'. &mt('Starting').'" /><\\/form>'.$end_page. "\');". "popwin.document.close();}". - "\nwindow.setTimeout(openpopwin,0)</script>"); + "\nwindow.setTimeout(openpopwin,0)" + )); $prog_state{'formname'}='popremain'; $prog_state{'inputname'}="remaining"; } elsif ($type eq 'inline') { $prog_state{'window'}='window'; if (!$formname) { $prog_state{'formname'}=&get_uniq_name(); - &r_print($r,'<form name="'.$prog_state{'formname'}.'">'); + &r_print($r,'<form action="" name="'.$prog_state{'formname'}.'">'); } else { $prog_state{'formname'}=$formname; } if (!$inputname) { $prog_state{'inputname'}=&get_uniq_name(); - &r_print($r,$heading.' <input type="text" name="'.$prog_state{'inputname'}. - '" size="'.$width.'" />'); + &r_print($r,&mt("$heading [_1]",' <input type="text" name="'.$prog_state{'inputname'}.'" size="'.$width.'" />')); } else { $prog_state{'inputname'}=$inputname; @@ -891,10 +1023,12 @@ sub Create_PrgWin { # update progress sub Update_PrgWin { my ($r,$prog_state,$displayString)=@_; - &r_print($r,'<script>'.$$prog_state{'window'}.'.document.'. - $$prog_state{'formname'}.'.'. - $$prog_state{'inputname'}.'.value="'. - $displayString.'";</script>'); + &r_print($r,&Apache::lonhtmlcommon::scripttag( + $$prog_state{'window'}.'.document.'. + $$prog_state{'formname'}.'.'. + $$prog_state{'inputname'}.'.value="'. + $displayString.'";' + )); $$prog_state{'laststart'}=&Time::HiRes::time(); } @@ -947,11 +1081,13 @@ sub Increment_PrgWin { if ($user_browser eq 'explorer' && $user_os =~ 'mac') { $lasttime = ''; } - &r_print($r,'<script>'.$$prog_state{'window'}.'.document.'. - $$prog_state{'formname'}.'.'. - $$prog_state{'inputname'}.'.value="'. - $$prog_state{'done'}.'/'.$$prog_state{'max'}. - ': '.$time_est.' '.&mt('remaining').' '.$lasttime.'";'.'</script>'); + &r_print($r,&Apache::lonhtmlcommon::scripttag( + $$prog_state{'window'}.'.document.'. + $$prog_state{'formname'}.'.'. + $$prog_state{'inputname'}.'.value="'. + $$prog_state{'done'}.'/'.$$prog_state{'max'}. + ': '.$time_est.' '.&mt('remaining').' '.$lasttime.'";' + )); $$prog_state{'laststart'}=&Time::HiRes::time(); } @@ -959,7 +1095,9 @@ sub Increment_PrgWin { sub Close_PrgWin { my ($r,$prog_state)=@_; if ($$prog_state{'type'} eq 'popup') { - &r_print($r,'<script>popwin.close()</script>'."\n"); + &r_print($r,&Apache::lonhtmlcommon::scripttag( + 'popwin.close()' + )); } elsif ($$prog_state{'type'} eq 'inline') { &Update_PrgWin($r,$prog_state,&mt('Done')); } @@ -1003,13 +1141,14 @@ sub crumbs { } else { $path.='/'; } - my $linkpath = &Apache::loncommon::escape_single($path); - if ($form) { - $linkpath= - qq{javascript:$form.action='$linkpath';$form.submit();}; - } my $href_path = &HTML::Entities::encode($path,'<>&"'); - $output.=qq{<a href="$path" $target>$dir</a>/}; + &Apache::loncommon::inhibit_menu_check(\$href_path); + if ($form) { + my $href = 'javascript:'.$form.".action='".$href_path."';".$form.'.submit();'; + $output.=qq{<a href="$href" $target>$dir</a>/}; + } else { + $output.=qq{<a href="$href_path" $target>$dir</a>/}; + } } } else { foreach my $dir (split('/',$uri)) { @@ -1040,6 +1179,7 @@ sub spellheader { my $nothing=&javascript_nothing(); return (<<ENDCHECK); <script type="text/javascript"> +// <![CDATA[ //<!-- BEGIN LON-CAPA Internal var checkwin; @@ -1050,6 +1190,7 @@ function spellcheckerwindow(string) { checkwin.document.close(); } // END LON-CAPA Internal --> +// ]]> </script> ENDCHECK } @@ -1083,46 +1224,13 @@ ENDLINK } sub htmlareaheaders { - if (&htmlareablocked()) { return ''; } - unless (&htmlareabrowser()) { return ''; } - my $lang='en'; - if (&mt('htmlarea_lang') ne 'htmlarea_lang') { - $lang=&mt('htmlarea_lang'); - } + return if (&htmlareablocked()); + return if (!&htmlareabrowser()); return (<<ENDHEADERS); -<script type="text/javascript"> -_editor_url='/htmlarea/'; -_editor_lang='$lang'; -</script> -<script type="text/javascript" src="/htmlarea/htmlarea.js"></script> -<link rel="stylesheet" type="text/css" href="/htmlarea/htmlarea.css" /> +<script type="text/javascript" src="/fckeditor/fckeditor.js"></script> ENDHEADERS } -# ------------------------------------------------- Activate additional buttons - -sub htmlareaaddbuttons { - if (&htmlareablocked()) { return ''; } - unless (&htmlareabrowser()) { return ''; } - return (<<ENDADDBUTTON); - var config=new HTMLArea.Config(); - config.registerButton('ed_math','LaTeX Inline', - '/htmlarea/images/ed_math.gif',false, - function(editor,id) { - editor.surroundHTML(' <m>\$','\$</m> '); - } - ); - config.registerButton('ed_math_eqn','LaTeX Equation', - '/htmlarea/images/ed_math_eqn.gif',false, - function(editor,id) { - editor.surroundHTML( - ' \\n<center><m>\\\\[','\\\\]</m></center>\\n '); - } - ); - config.toolbar.push(['ed_math','ed_math_eqn']); -ENDADDBUTTON -} - # ----------------------------------------------------------------- Preferences sub disablelink { @@ -1141,18 +1249,38 @@ sub enablelink { return '<a href="'.&HTML::Entities::encode('/adm/preferences?action=set_wysiwyg&wysiwyg=on&returnurl=','<>&"').&escape($ENV{'REQUEST_URI'}).'">'.&mt('Enable WYSIWYG Editor').'</a>'; } +# ------------------------------------------------- lang to use in html editor +sub htmlarea_lang { + my $lang='en'; + if (&mt('htmlarea_lang') ne 'htmlarea_lang') { + $lang=&mt('htmlarea_lang'); + } + return $lang; +} + # ----------------------------------------- Script to activate only some fields sub htmlareaselectactive { my @fields=@_; unless (&htmlareabrowser()) { return ''; } if (&htmlareablocked()) { return '<br />'.&enablelink(@fields); } - my $output='<script type="text/javascript" defer="1">'. - &htmlareaaddbuttons(); - foreach(@fields) { - $output.="\nHTMLArea.replace('$_',config);"; - } - $output.="\nwindow.status='Activated Editfields';\n</script><br />". + my $output='<script type="text/javascript" defer="1">'."\n" + .'// <![CDATA['."\n"; + my $lang = &htmlarea_lang(); + foreach my $field (@fields) { + $output.=" +{ + var oFCKeditor = new FCKeditor('$field'); + oFCKeditor.Config['CustomConfigurationsPath'] = + '/fckeditor/loncapaconfig.js'; + oFCKeditor.ReplaceTextarea(); + oFCKeditor.Config['AutoDetectLanguage'] = false; + oFCKeditor.Config['DefaultLanguage'] = '$lang'; +}"; + } + $output.="\nwindow.status='Activated Editfields';\n" + .'// ]]>'."\n" + .'</script><br />'. &disablelink(@fields); return $output; } @@ -1178,19 +1306,19 @@ sub htmlareabrowser { =item breadcrumbs Compiles the previously registered breadcrumbs into an series of links. -FAQ and BUG links will be placed on the left side of the table if they -are defined for the last registered breadcrumb. Additionally supports a 'component', which will be displayed on the -right side of the table (without a link). +right side of the breadcrumbs enclosing div (without a link). A link to help for the component will be included if one is specified. All inputs can be undef without problems. -Inputs: $component (the large text on the right side of the table), +Inputs: $component (the text on the right side of the breadcrumbs trail), $component_help $menulink (boolean, controls whether to include a link to /adm/menu) $helplink (if 'nohelp' don't include the orange help link) $css_class (optional name for the class to apply to the table for CSS) + $no_mt (optional flag, 1 if &mt() is _not_ to be applied to $component + when including the text on the right. Returns a string containing breadcrumbs for the current page. =item clear_breadcrumbs @@ -1216,64 +1344,70 @@ returns: nothing my @Crumbs; sub breadcrumbs { - my ($component,$component_help,$menulink,$helplink,$css_class) = @_; - # - $css_class ||= 'LC_breadcrumbs'; - my $Str = "\n".'<table class="'.$css_class.'"><tr><td>'; + my ($component,$component_help,$menulink,$helplink,$css_class,$no_mt, $CourseBreadcrumbs) = @_; # + $css_class ||= 'LC_breadcrumbs'; + # Make the faq and bug data cascade - my $faq = ''; - my $bug = ''; - my $help=''; + my $faq = ''; + my $bug = ''; + my $help = ''; + # Crumb Symbol + my $crumbsymbol = '»'; # The last breadcrumb does not have a link, so handle it separately. my $last = pop(@Crumbs); # # The first one should be the course or a menu link - if (!defined($menulink)) { $menulink=1; } + if (!defined($menulink)) { $menulink=1; } if ($menulink) { my $description = 'Menu'; + my $no_mt_descr = 0; if (exists($env{'request.course.id'}) && $env{'request.course.id'} ne '') { $description = $env{'course.'.$env{'request.course.id'}.'.description'}; + $no_mt_descr = 1; + } + $menulink = { href =>'/adm/menu', + title =>'Go to main menu', + target =>'_top', + text =>$description, + no_mt =>$no_mt_descr, }; + if($last) { + #$last set, so we have some crumbs + unshift(@Crumbs,$menulink); + } else { + #only menulink crumb present + $last = $menulink; } - unshift(@Crumbs,{ - href =>'/adm/menu', - title =>'Go to main menu', - target =>'_top', - text =>$description, - }); } - my $links .= - join('->', + my $links = join "", map { - $faq = $_->{'faq'} if (exists($_->{'faq'})); - $bug = $_->{'bug'} if (exists($_->{'bug'})); + $faq = $_->{'faq'} if (exists($_->{'faq'})); + $bug = $_->{'bug'} if (exists($_->{'bug'})); $help = $_->{'help'} if (exists($_->{'help'})); - my $result = '<a href="'.$_->{'href'}.'" '; - if (defined($_->{'target'}) && $_->{'target'} ne '') { - $result .= 'target="'.$_->{'target'}.'" '; - } - if ($_->{'no_mt'}) { - $result .='title="'.$_->{'title'}.'">'. - $_->{'text'}.'</a>'; - } else { - $result .='title="'.&mt($_->{'title'}).'">'. - &mt($_->{'text'}).'</a>'; - } - $result; - } @Crumbs - ); - $links .= '->' if ($links ne ''); - if ($last->{'no_mt'}) { - $links .= '<b>'.$last->{'text'}.'</b>'; - } else { - $links .= '<b>'.&mt($last->{'text'}).'</b>'; - } - # + + my $result = htmltag( 'a', + $_->{no_mt} ? + $_->{text} : mt($_->{text}), + { + href => $_->{href}, + title => $_->{no_mt} ? + $_->{title} : mt($_->{title}), + target => $_->{target}, + }); + $result = htmltag( 'li', "$result $crumbsymbol"); + } @Crumbs; + + #should the last Element be translated? + $links .= htmltag( 'li', + htmltag( 'b', + $last->{'no_mt'} ? + $last->{'text'} : mt($last->{'text'}) )); + my $icons = ''; - $faq = $last->{'faq'} if (exists($last->{'faq'})); - $bug = $last->{'bug'} if (exists($last->{'bug'})); + $faq = $last->{'faq'} if (exists($last->{'faq'})); + $bug = $last->{'bug'} if (exists($last->{'bug'})); $help = $last->{'help'} if (exists($last->{'help'})); $component_help=($component_help?$component_help:$help); # if ($faq ne '') { @@ -1282,29 +1416,36 @@ returns: nothing # if ($bug ne '') { # $icons .= &Apache::loncommon::help_open_bug($bug); # } - if ($faq ne '' || $component_help ne '' || $bug ne '') { - $icons .= &Apache::loncommon::help_open_menu($component, - $component_help, - $faq,$bug); - } - # - $Str .= $links.'</td>'; - # - if (defined($component)) { - $Str .= '<td class="'.$css_class.'_component">'. - &mt($component); - if ($icons ne '') { - $Str .= ' '.$icons; - } - $Str .= '</td>'; + if ($faq ne '' || $component_help ne '' || $bug ne '') { + $icons .= &Apache::loncommon::help_open_menu($component, + $component_help, + $faq,$bug); } - $Str .= '</tr></table>'."\n"; # + + + unless ($CourseBreadcrumbs) { + $links = htmltag('ol', $links, { id => "LC_MenuBreadcrumbs" }); + } else { + $links = htmltag('ul', $links, { class => "LC_CourseBreadcrumbs" }); + } + + if ($component) { + $links = htmltag('span', + ( $no_mt ? $component : mt($component) ). + ( $icons ? $icons : '' ), + { class => 'LC_breadcrumbs_component' } ) + .$links; + } + + $links = htmltag('div', $links, + { id => "LC_breadcrumbs" }) unless ($CourseBreadcrumbs) ; + # Return the @Crumbs stack to what we started with push(@Crumbs,$last); shift(@Crumbs); - # - return $Str; + # Return the breadcrumb's line + return "$links"; } sub clear_breadcrumbs { @@ -1312,7 +1453,7 @@ returns: nothing } sub add_breadcrumb { - push (@Crumbs,@_); + push(@Crumbs,@_); } } # End of scope for @Crumbs @@ -1331,8 +1472,8 @@ returns: nothing # row1 # row2 # row3 ... etc. -# &submit_row(0 -# &end_pickbox() +# &submit_row() +# &end_pick_box() # # where row1, row 2 etc. are chosen from &role_select_row,&course_select_row, # &status_select_row and &email_default_row @@ -1373,6 +1514,9 @@ returns: nothing # routines, but can also be called directly to start and end rows which have # needs that are not accommodated by the *_select_row() routines. +{ # Start: row_count block for pick_box +my @row_count; + sub start_pick_box { my ($css_class) = @_; if (defined($css_class)) { @@ -1380,6 +1524,7 @@ sub start_pick_box { } else { $css_class= 'class="LC_pick_box"'; } + unshift(@row_count,0); my $output = <<"END"; <table $css_class> END @@ -1387,26 +1532,38 @@ END } sub end_pick_box { + shift(@row_count); my $output = <<"END"; </table> END return $output; } +sub row_headline { + my $output = <<"END"; + <tr><td colspan="2"> +END + return $output; +} + sub row_title { my ($title,$css_title_class,$css_value_class) = @_; + $row_count[0]++; + my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row'; $css_title_class ||= 'LC_pick_box_title'; $css_title_class = 'class="'.$css_title_class.'"'; $css_value_class ||= 'LC_pick_box_value'; - $css_value_class = 'class="'.$css_value_class.'"'; + if ($title ne '') { + $title .= ':'; + } my $output = <<"ENDONE"; <tr class="LC_pick_box_row"> <td $css_title_class> - $title: + $title </td> - <td $css_value_class> + <td class="$css_value_class $css_class"> ENDONE return $output; } @@ -1428,14 +1585,21 @@ ENDTWO return $output; } +} # End: row_count block for pick_box + + sub role_select_row { my ($roles,$title,$css_class,$show_separate_custom,$cdom,$cnum) = @_; + my $crstype = 'Course'; + if ($cdom ne '' && $cnum ne '') { + $crstype = &Apache::loncommon::course_type($cdom.'_'.$cnum); + } my $output; if (defined($title)) { $output = &row_title($title,$css_class); } $output .= qq| - <select name="roles" multiple >\n|; + <select name="roles" multiple="multiple">\n|; foreach my $role (@$roles) { my $plrole; if ($role eq 'ow') { @@ -1454,7 +1618,7 @@ sub role_select_row { $plrole = &mt('Custom Role'); } } else { - $plrole=&Apache::lonnet::plaintext($role); + $plrole=&Apache::lonnet::plaintext($role,$crstype); } if (($role ne 'cr') || (!$show_separate_custom)) { $output .= ' <option value="'.$role.'">'.$plrole.'</option>'; @@ -1471,8 +1635,16 @@ sub course_select_row { my ($title,$formname,$totcodes,$codetitles,$idlist,$idlist_titles, $css_class) = @_; my $output = &row_title($title,$css_class); - $output .= qq| + $output .= &course_selection($formname,$totcodes,$codetitles,$idlist,$idlist_titles); + $output .= &row_closure(); + return $output; +} + +sub course_selection { + my ($formname,$totcodes,$codetitles,$idlist,$idlist_titles) = @_; + my $output = qq| <script type="text/javascript"> +// <![CDATA[ function coursePick (formname) { for (var i=0; i<formname.coursepick.length; i++) { if (formname.coursepick[i].value == 'category') { @@ -1495,6 +1667,7 @@ sub course_select_row { formname.courselist = ''; } } +// ]]> </script> |; my $courseform='<b>'.&Apache::loncommon::selectcourse_link @@ -1544,8 +1717,7 @@ sub course_select_row { $output .= '</tr></table><br />'; } } - $output .= '<input type="radio" name="coursepick" value="specific" onclick="coursePick(this.form);opencrsbrowser('."'".$formname."'".','."'".'dccourse'."'".','."'".'dcdomain'."'".','."'".'coursedesc'."','','1'".')" />'.&mt('Pick specific course(s):').' '.$courseform.' <input type="text" value="0" size="4" name="coursetotal" /><input type="hidden" name="courselist" value="" />selected.<br />'."\n"; - $output .= &row_closure(); + $output .= '<input type="radio" name="coursepick" value="specific" onclick="coursePick(this.form);opencrsbrowser('."'".$formname."','dccourse','dcdomain','coursedesc','','1'".')" />'.&mt('Pick specific course(s):').' '.$courseform.' <input type="text" value="0" size="4" name="coursetotal" /><input type="hidden" name="courselist" value="" />selected.<br />'."\n"; return $output; } @@ -1556,7 +1728,7 @@ sub status_select_row { $output = &row_title($title,$css_class,'LC_pick_box_select'); } $output .= qq| - <select name="types" multiple>\n|; + <select name="types" multiple="multiple">\n|; foreach my $status_type (sort(keys(%{$types}))) { $output .= ' <option value="'.$status_type.'">'.$$types{$status_type}.'</option>'; } @@ -1570,7 +1742,6 @@ sub status_select_row { sub email_default_row { my ($authtypes,$title,$descrip,$css_class) = @_; my $output = &row_title($title,$css_class); - my @rowcols = ('#eeeeee','#dddddd'); $output .= $descrip. &Apache::loncommon::start_data_table(). &Apache::loncommon::start_data_table_header_row(). @@ -1631,6 +1802,23 @@ sub course_custom_roles { ############################################## ############################################## + +# topic_bar +# +# Generates a div containing a numbered (static image) followed by a title +# with a background color defined in the corresponding CSS: LC_topic_bar +# +sub topic_bar { + my ($imgnum,$title) = @_; + return '<div class="LC_topic_bar">' + .'<img src="/res/adm/pages/bl_step'.$imgnum.'.gif"' + .' alt="'.&mt('Step [_1]',$imgnum).'" />' + .' '.$title + .'</div>'; +} + +############################################## +############################################## # echo_form_input # @@ -1761,7 +1949,7 @@ sub set_form_elements { $values{$name}[$i] =~ s/([\r\n\f]+)/\\n/g; $values{$name}[$i] =~ s/"/\\"/g; } - if ($$elements{$name} eq 'text') { + if (($$elements{$name} eq 'text') || ($$elements{$name} eq 'hidden')) { my $numvalues = @{$values{$name}}; if ($numvalues > 1) { my $valuestring = join('","',@{$values{$name}}); @@ -1796,6 +1984,8 @@ sub set_form_elements { $output .= qq| if (courseForm.elements['$name'].value == "$value") { courseForm.elements['$name'].checked = true; + } else { + courseForm.elements['$name'].checked = false; }|; } } @@ -1837,10 +2027,317 @@ sub set_form_elements { } } $output .= " + return; }\n"; return $output; } +############################################## +############################################## + +# javascript_valid_email +# +# Generates javascript to validate an e-mail address. +# Returns a javascript function which accetps a form field as argumnent, and +# returns false if field.value does not satisfy two regular expression matches +# for a valid e-mail address. Backwards compatible with old browsers without +# support for javascript RegExp (just checks for @ in field.value in this case). + +sub javascript_valid_email { + my $scripttag .= <<'END'; +function validmail(field) { + var str = field.value; + if (window.RegExp) { + var reg1str = "(@.*@)|(\\.\\.)|(@\\.)|(\\.@)|(^\\.)"; + var reg2str = "^.+\\@(\\[?)[a-zA-Z0-9\\-\\.]+\\.([a-zA-Z]{2,3}|[0-9]{1,3})(\\]?)$"; //" + var reg1 = new RegExp(reg1str); + var reg2 = new RegExp(reg2str); + if (!reg1.test(str) && reg2.test(str)) { + return true; + } + return false; + } + else + { + if(str.indexOf("@") >= 0) { + return true; + } + return false; + } +} +END + return $scripttag; +} + + +# USAGE: htmltag(element, content, {attribute => value,...}); +# +# EXAMPLES: +# - htmltag('a', 'this is an anchor', {href => 'www.example.com', +# title => 'this is a title'}) +# +# - You might want to set up needed tags like: +# +# my $h3 = sub { return htmltag( "h3", @_ ) }; +# +# ... and use them: $h3->("This is a headline") +# +# - To set up a couple of tags, see sub inittags +# +# NOTES: +# - Empty elements, such as <br/> are correctly terminated, +# i.e. htmltag('br') returns <br/> +# - Empty attributes (title="") are filtered out. +# - The function will not check for deprecated attributes. +# +# OUTPUT: content enclosed in xhtml conform tags +sub htmltag{ + return + qq|<$_[0]| + . join( '', map { qq| $_="${$_[2]}{$_}"| if ${$_[2]}{$_} } keys %{ $_[2] } ) + . ($_[1] ? qq|>$_[1]</$_[0]>| : qq|/>|). "\n"; +}; + + +# USAGE: inittags(@tags); +# +# EXAMPLES: +# - my ($h1, $h2, $h3) = initTags( qw( h1 h2 h3 ) ) +# $h1->("This is a headline") #Returns: <h1>This is a headline</h1> +# +# NOTES: See sub htmltag for further information. +# +# OUTPUT: List of subroutines. +sub inittags { + my @tags = @_; + return map { my $tag = $_; + sub { return htmltag( $tag, @_ ) } + } @tags; +} + + +# USAGE: scripttag(scriptcode, [start|end|both]); +# +# EXAMPLES: +# - scripttag("alert('Hello World!')", 'both') +# returns: +# <script type="text/javascript"> +# // BEGIN LON-CAPA Internal +# alert(Hello World!') +# // END LON-CAPA Internal +# </script> +# +# NOTES: +# - works currently only for javascripts +# +# OUTPUT: +# Scriptcode properly enclosed in <script> and CDATA tags (and LC +# Internal markers if 2nd argument is given) +sub scripttag { + my ( $content, $marker ) = @_; + return unless defined $content; + + my $begin = "\n// BEGIN LON-CAPA Internal\n"; + my $end = "\n// END LON-CAPA Internal\n"; + + if ($marker) { + $content = $begin . $content if $marker eq 'start' or $marker eq 'both'; + $content .= $end if $marker eq 'end' or $marker eq 'both'; + } + + $content = "\n// <![CDATA[\n$content\n// ]]>\n"; + + return htmltag('script', $content, {type => 'text/javascript'}); +}; + + +############################################## +############################################## + +# generate_menu +# +# Generates html markup for a menu. +# +# Inputs: +# An array of following structure: +# ({ categorytitle => 'Categorytitle', +# items => [ +# { +# linktext => 'Text to be displayed', +# url => 'URL the link is pointing to, i.e. /adm/site?action=dosomething', +# permission => 'Contains permissions as returned from lonnet::allowed(), +# must evaluate to true in order to activate the link', +# icon => 'icon filename', +# alttext => 'alt text for the icon', +# help => 'Name of the corresponding helpfile', +# linktitle => 'Description of the link (used for title tag)' +# }, +# ... +# ] +# }, +# ... +# ) +# +# Outputs: A scalar containing the html markup for the menu. + +# ---- Remove when done ---- +# This routine is part of the redesign of LON-CAPA and it's +# subject to change during this project. +# Don't rely on its current functionality as it might be +# changed or removed. +# -------------------------- +sub generate_menu { + my @menu = @_; + # subs for specific html elements + my ($h3, $div, $ul, $li, $a, $img) = inittags( qw(h3 div ul li a img) ); + + my @categories; # each element represents the entire markup for a category + + foreach my $category (@menu) { + my @links; # contains the links for the current $category + foreach my $link (@{$$category{items}}) { + next unless $$link{permission}; + + # create the markup for the current $link and push it into @links. + # each entry consists of an image and a text optionally followed + # by a help link. + push(@links,$li->( + $a->( + $img->("", { + class => "LC_noBorder LC_middle", + src => "/res/adm/pages/$$link{icon}", + alt => mt(defined($$link{alttext}) ? + $$link{alttext} : $$link{linktext}) + }), { + href => $$link{url}, + title => mt($$link{linktitle}) + }). + $a->(mt($$link{linktext}), { + href => $$link{url}, + title => mt($$link{linktitle}), + class => "LC_menubuttons_link" + }). + (defined($$link{help}) ? + Apache::loncommon::help_open_topic($$link{help}) : ''), + {class => "LC_menubuttons_inline_text"})); + } + + # wrap categorytitle in <h3>, concatenate with + # joined and in <ul> tags wrapped @links + # and wrap everything in an enclosing <div> and push it into + # @categories + # such that each element looks like: + # <div><h3>title</h3><ul><li>...</li>...</ul></div> + # the category won't be added if there aren't any links + push(@categories, + $div->($h3->(mt($$category{categorytitle}), {class=>"LC_hcell"}). + $ul->(join('' ,@links), {class =>"LC_ListStyleNormal" }), + {class=>"LC_Box LC_400Box"})) if scalar(@links); + } + + # wrap the joined @categories in another <div> (column layout) + return $div->(join('', @categories), {class => "LC_columnSection"}); +} + +############################################## +############################################## + +=pod + +=item &start_funclist + +Start list of available functions + +Typically used to offer a simple list of available functions +at top or bottom of page. +All available functions/actions for the current page +should be included in this list. + +If the optional headline text is not provided, a default text will be used. + + +Related routines: +=over 4 +add_item_funclist +end_funclist +=back + + +Inputs: (optional) headline text + +Returns: HTML code with function list start + +=cut + +############################################## +############################################## + +sub start_funclist { + my($legendtext)=@_; + $legendtext=&mt('Functions') if !$legendtext; + return "<fieldset>\n<legend>$legendtext</legend>\n" + .'<ul class="LC_funclist">'."\n"; +} + + +############################################## +############################################## + +=pod + +=item &add_item_funclist + +Adds an item to the list of available functions + +Related routines: +=over 4 +start_funclist +end_funclist +=back + +Inputs: content item with text and link to function + +Returns: HTML code with list item for funclist + +=cut + +############################################## +############################################## + +sub add_item_funclist { + my($content) = @_; + return '<li>'.$content.'</li>'."\n"; +} + +############################################## +############################################## + +=pod + +=item &end_funclist + +End list of available functions + +Related routines: +=over 4 +start_funclist +add_item_funclist +=back + +Inputs: ./. + +Returns: HTML code with function list end + +=cut + +############################################## +############################################## + +sub end_funclist { + my($r)=@_; + return "</ul>\n</fieldset>\n"; +} + 1; __END__