--- loncom/interface/lonhtmlcommon.pm 2002/09/17 15:32:46 1.10 +++ loncom/interface/lonhtmlcommon.pm 2011/01/03 13:10:12 1.285 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # a pile of common html routines # -# $Id: lonhtmlcommon.pm,v 1.10 2002/09/17 15:32:46 matthew Exp $ +# $Id: lonhtmlcommon.pm,v 1.285 2011/01/03 13:10:12 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -55,8 +55,311 @@ html. package Apache::lonhtmlcommon; -use Time::Local; use strict; +use Time::Local; +use Time::HiRes; +use Apache::lonlocal; +use Apache::lonnet; +use LONCAPA; + +sub java_not_enabled { + return "\n".''. + &mt('The required Java applet could not be started. Please make sure to have Java installed and active in your browser.'). + "\n"; +} + +sub coursepreflink { + my ($text,$category)=@_; + if (&Apache::lonnet::allowed('opa',$env{'request.course.id'})) { + return '&"').'">'.$text.''; + } else { + return ''; + } +} + +sub raw_href_to_link { + my ($message)=@_; + $message=~s/(https?\:\/\/[^\s\'\"\<]+)([\s\<]|$)/$1<\/tt><\/a>$2/gi; + return $message; +} + +############################################## +############################################## + +=item confirm_success + +Successful completion of an operation message + +=cut + +sub confirm_success { + my ($message,$failure)=@_; + if ($failure) { + return ''."\n" + .''.&mt('Error').' '."\n" + .$message."\n" + .''."\n"; + } else { + return ''."\n" + .''.&mt('OK').' '."\n" + .$message."\n" + .''."\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',undef,undef,undef,undef,'mathhelpicon_'.$textarea); + } + my $buttontext=&mt('Edit Math'); + return <$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 +} + + +############################################## +############################################## + +=pod + +=item authorbombs + +=cut + +############################################## +############################################## + +sub authorbombs { + my $url=shift; + $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 my $bomb (keys(%bombs)) { + if ($bomb =~ /^$udom\/$uname\//) { + return ''.&mt('Bomb').''. + &Apache::loncommon::help_open_topic('About_Bombs'); + } + } + return ''; +} + +############################################## +############################################## + +sub recent_filename { + my $area=shift; + return 'nohist_recent_'.&escape($area); +} + +sub store_recent { + my ($area,$name,$value,$freeze)=@_; + my $file=&recent_filename($area); + my %recent=&Apache::lonnet::dump($file); + if (scalar(keys(%recent))>20) { +# remove oldest value + my $oldest=time(); + my $delkey=''; + foreach my $item (keys(%recent)) { + my $thistime=(split(/\&/,$recent{$item}))[0]; + if (($thistime ne "always_include") && ($thistime<$oldest)) { + $oldest=$thistime; + $delkey=$item; + } + } + &Apache::lonnet::del($file,[$delkey]); + } +# store new value + my $timestamp; + if ($freeze) { + $timestamp = "always_include"; + } else { + $timestamp = time(); + } + &Apache::lonnet::put($file,{ $name => + $timestamp.'&'.&escape($value) }); +} + +sub remove_recent { + my ($area,$names)=@_; + my $file=&recent_filename($area); + return &Apache::lonnet::del($file,$names); +} + +sub select_recent { + my ($area,$fieldname,$event)=@_; + my %recent=&Apache::lonnet::dump(&recent_filename($area)); + my $return="\n\n"; + return $return; +} + +sub get_recent { + my ($area, $n) = @_; + my %recent=&Apache::lonnet::dump(&recent_filename($area)); + +# Create hash with key as time and recent as value +# Begin filling return_hash with any 'always_include' option + my %time_hash = (); + my %return_hash = (); + foreach my $item (keys(%recent)) { + my ($thistime,$thisvalue)=(split(/\&/,$recent{$item})); + if ($thistime eq 'always_include') { + $return_hash{$item} = &unescape($thisvalue); + $n--; + } else { + $time_hash{$thistime} = $item; + } + } + +# Sort by decreasing time and return key value pairs + my $idx = 1; + foreach my $item (reverse(sort(keys(%time_hash)))) { + $return_hash{$time_hash{$item}} = + &unescape((split(/\&/,$recent{$time_hash{$item}}))[1]); + if ($n && ($idx++ >= $n)) {last;} + } + + return %return_hash; +} + +sub get_recent_frozen { + my ($area) = @_; + my %recent=&Apache::lonnet::dump(&recent_filename($area)); + +# Create hash with all 'frozen' items + my %return_hash = (); + foreach my $item (keys(%recent)) { + my ($thistime,$thisvalue)=(split(/\&/,$recent{$item})); + if ($thistime eq 'always_include') { + $return_hash{$item} = &unescape($thisvalue); + } + } + return %return_hash; +} + + + +=pod + +=item textbox + +=cut + +############################################## +############################################## +sub textbox { + my ($name,$value,$size,$special) = @_; + $size = 40 if (! defined($size)); + $value = &HTML::Entities::encode($value,'<>&"'); + my $Str = ''; + return $Str; +} + +############################################## +############################################## + +=pod + +=item checkbox + +=cut + +############################################## +############################################## +sub checkbox { + my ($name,$checked,$value) = @_; + my $Str = ' + ENDJS - $result .= " }; + # Month my @Months = qw/January February March April May June July August September October November December/; # Pad @Months with a bogus value to make indexing easier unshift(@Months,'If you can read this an error occurred'); + if ($includeempty) { $monthselector.=""; } for(my $m = 1;$m <=$#Months;$m++) { - $result .= " \n"; + } + $hourselector .= " \n"; + my $minuteselector = qq{}; + my $secondselector= qq{}; + my $cal_link; + if (!$nolink) { + $cal_link = qq{}; + } + # + my $tzone = ' '.$tzname.' '; + if ($no_hh_mm_ss) { + $result .= &mt('[_1] [_2] [_3] ', + $monthselector,$dayselector,$yearselector). + $tzone; + if (!$nolink) { + $result .= &mt('[_1]Select Date[_2]',$cal_link,''); + } + } else { + $result .= &mt('[_1] [_2] [_3] [_4] [_5]m [_6]s ', + $monthselector,$dayselector,$yearselector, + $hourselector,$minuteselector,$secondselector). + $tzone; + if (!$nolink) { + $result .= &mt('[_1]Select Date[_2]',$cal_link,''); + } + } + $result .= "\n\n"; 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; +} + + ############################################## ############################################## +=pod + =item &get_date_from_form +get_date_from_form retrieves the date specified in an &date_setter form. + Inputs: =over 4 =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 @@ -203,44 +647,68 @@ sub get_date_from_form { my ($dname) = @_; my ($sec,$min,$hour,$day,$month,$year); # - if (defined($ENV{'form.'.$dname.'_second'})) { - my $tmpsec = $ENV{'form.'.$dname.'_second'}; + if (defined($env{'form.'.$dname.'_second'})) { + my $tmpsec = $env{'form.'.$dname.'_second'}; if (($tmpsec =~ /^\d+$/) && ($tmpsec >= 0) && ($tmpsec < 60)) { $sec = $tmpsec; } + if (!defined($tmpsec) || $tmpsec eq '') { $sec = 0; } + } else { + $sec = 0; } - if (defined($ENV{'form.'.$dname.'_minute'})) { - my $tmpmin = $ENV{'form.'.$dname.'_minute'}; + if (defined($env{'form.'.$dname.'_minute'})) { + my $tmpmin = $env{'form.'.$dname.'_minute'}; if (($tmpmin =~ /^\d+$/) && ($tmpmin >= 0) && ($tmpmin < 60)) { $min = $tmpmin; } + if (!defined($tmpmin) || $tmpmin eq '') { $min = 0; } + } else { + $min = 0; } - if (defined($ENV{'form.'.$dname.'_hour'})) { - my $tmphour = $ENV{'form.'.$dname.'_hour'}; - if (($tmphour =~ /^\d+$/) && ($tmphour > 0) && ($tmphour < 32)) { + if (defined($env{'form.'.$dname.'_hour'})) { + my $tmphour = $env{'form.'.$dname.'_hour'}; + if (($tmphour =~ /^\d+$/) && ($tmphour >= 0) && ($tmphour < 24)) { $hour = $tmphour; } + } else { + $hour = 0; } - if (defined($ENV{'form.'.$dname.'_day'})) { - my $tmpday = $ENV{'form.'.$dname.'_day'}; + if (defined($env{'form.'.$dname.'_day'})) { + my $tmpday = $env{'form.'.$dname.'_day'}; if (($tmpday =~ /^\d+$/) && ($tmpday > 0) && ($tmpday < 32)) { $day = $tmpday; } } - if (defined($ENV{'form.'.$dname.'_month'})) { - my $tmpmonth = $ENV{'form.'.$dname.'_month'}; + 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 (defined($env{'form.'.$dname.'_year'})) { + my $tmpyear = $env{'form.'.$dname.'_year'}; + if (($tmpyear =~ /^\d+$/) && ($tmpyear >= 1970)) { + $year = $tmpyear; } } - if (eval(&timelocal($sec,$min,$hour,$day,$month,$year))) { - return &timelocal($sec,$min,$hour,$day,$month,$year); + if (($year<1970) || ($year>2037)) { return undef; } + if (defined($sec) && defined($min) && defined($hour) && + 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; } @@ -249,365 +717,1937 @@ sub get_date_from_form { ############################################## ############################################## -sub AscendOrderOptions { - my ($order, $page, $formName)=@_; +=pod + +=item &pjump_javascript_definition() - my $OpSel1 = ''; - my $OpSel2 = ''; +Returns javascript defining the 'pjump' function, which opens up a +parameter setting wizard. - if($order eq 'Ascending') { - $OpSel1 = ' selected'; +=cut + +############################################## +############################################## +sub pjump_javascript_definition { + my $Str = <Ascending'."\n". - 'Descending'."\n"; + foreach my $type (['Active', &mt('Currently Has Access')], + ['Future', &mt('Will Have Future Access')], + ['Expired', &mt('Previously Had Access')], + ['Any', &mt('Any Access Status')]) { + my ($name,$label) = @$type; + $Str .= ''."\n"; - } - $Str .= ' 0 ? $step : 1; + $$prog_state{'done'} += $step; + + # Catch (max modulo step) <> 0 + my $current = $$prog_state{'done'}; + my $last = ($$prog_state{'max'} - $current); + if ($last <= 0) { + $last = 1; + $current = $$prog_state{'max'}; + } + + my $time_est= (&Time::HiRes::time() - $$prog_state{'firststart'})/ + $current * $last; + $time_est = int($time_est); + # + my $min = int($time_est/60); + my $sec = $time_est % 60; + + my $lasttime = &Time::HiRes::time()-$$prog_state{'laststart'}; + if ($lasttime > 9) { + $lasttime = int($lasttime); + } elsif ($lasttime < 0.01) { + $lasttime = 0; + } else { + $lasttime = sprintf("%3.2f",$lasttime); } - $Str .= '>All Maps'."\n"; - $Str .= ''."\n"; + $sec = 0 if ($min >= 10); # Don't show seconds if remaining time >= 10 min. + $sec = 1 if ( ($min == 0) && ($sec == 0) ); # Little cheating: pretend to have 1 second remaining instead of 0 to have something to display - return $Str; + my $timeinfo = + &mt('[_1]/[_2]:' + .' [quant,_3,minute,minutes,] [quant,_4,second ,seconds ,]remaining' + .' ([quant,_5,second] for '.$extraInfo.')', + $current, + $$prog_state{'max'}, + $min, + $sec, + $lasttime); + + &r_print($r,&Apache::lonhtmlcommon::scripttag( + $$prog_state{'window'}.'.document.'. + $$prog_state{'formname'}.'.'. + $$prog_state{'inputname'}.'.value="'.$timeinfo.'";' + )); + $$prog_state{'laststart'}=&Time::HiRes::time(); } -sub ProblemOptions { - my ($data, $page, $map, $formName)=@_; - my $Str = ''; - $Str .= '<\\/form>$end_page'); + checkwin.document.close(); +} +// END LON-CAPA Internal --> +// ]]> + +ENDCHECK +} + +# ---------------------------------- Generate link to spell checker for a field + +sub spelllink { + my ($form,$field)=@_; + my $linktext=&mt('Check Spelling'); + return (<$linktext +ENDLINK +} + +# ------------------------------------------------- Output headers for CKEditor + +sub htmlareaheaders { + my $s=""; + if (&htmlareabrowser()) { + $s.=(< +ENDEDITOR } + $s.=(< + + +ENDJQUERY + return $s; +} + +# ----------------------------------------------------------------- Preferences + +# ------------------------------------------------- lang to use in html editor +sub htmlarea_lang { + my $lang='en'; + if (&mt('htmlarea_lang') ne 'htmlarea_lang') { + $lang=&mt('htmlarea_lang'); } - $Str .= '{'lang'})) { + if ($args->{'lang'} ne '') { + $lang = $args->{'lang'}; + } + } + if (exists($args->{'fullpage'})) { + if ($args->{'fullpage'} eq 'true') { + $fullpage = $args->{'fullpage'}; + } + } + if (exists($args->{'dragmath'})) { + if ($args->{'dragmath'} ne '') { + $dragmath_prefix = $args->{'dragmath'}; + $dragmath_helpicon=&Apache::loncommon::lonhttpdurl("/adm/help/help.png"); + $dragmath_whitespace=&Apache::loncommon::lonhttpdurl("/adm/lonIcons/transparent1x1.gif"); + } + } } - $Str .= '>All Problems'."\n"; + $output.=' + + function containsBlockHtml(id) { + var re = $("#"+id).html().search(/(?:\<\;|\<)(br|h1|h2|h3|h4|h5|h6|p|ol|ul|table|pre|address|blockquote|center|div)[\s]*((?:[\/]*[\s]*(?:\>\;|\>)|(?:\>\;|\>)[\s\S]*(?:\<\;|\<)\/[\s]*\1[\s]*\(?:\>\;|\>))/im); + return (re >= 0); + } + + function startRichEditor(id) { + CKEDITOR.replace(id, + { + customConfig: "/ckeditor/loncapaconfig.js", + language : "'.$lang.'", + fullPage : '.$fullpage.', + } + ); + } + + function destroyRichEditor(id) { + CKEDITOR.instances[id].destroy(); + } + + function editorHandler(event) { + var rawid = $(this).attr("id"); + var id = new RegExp("LC_rt_(.*)").exec(rawid)[1]; + event.preventDefault(); + var rt_enabled = $(this).hasClass("LC_enable_rt"); + if (rt_enabled) { + startRichEditor(id); + $("#LC_rt_"+id).html("« Plain text"); + $("#LC_rt_"+id).attr("title", "Disable rich text formatting and edit in plain text"); + $("#LC_rt_"+id).addClass("LC_disable_rt"); + $("#LC_rt_"+id).removeClass("LC_enable_rt"); + } else { + destroyRichEditor(id); + $("#LC_rt_"+id).html("Rich formatting »"); + $("#LC_rt_"+id).attr("title", "Enable rich text formatting (bold, italic, etc.)"); + $("#LC_rt_"+id).addClass("LC_enable_rt"); + $("#LC_rt_"+id).removeClass("LC_disable_rt"); + }'; + if ($dragmath_prefix ne '') { + $output .= "\n var visible = ''; + if (rt_enabled) { + visible = 'none'; + } + editmath_visibility(id,visible);\n"; + } + $output .= ' + } + $(document).ready(function(){ + $(".LC_richAlwaysOn").each(function() { + startRichEditor($(this).attr("id")); + }); + $(".LC_richDetectHtml").each(function() { + var id = $(this).attr("id"); + var rt_enabled = containsBlockHtml(id); + if(rt_enabled) { + $(this).before(""); + startRichEditor(id); + $("#LC_rt_"+id).click(editorHandler); + } + else { + $(this).before(""); + $("#LC_rt_"+id).click(editorHandler); + }'; + if ($dragmath_prefix ne '') { + $output .= "\n var visible = ''; + if (rt_enabled) { + visible = 'none'; + } + editmath_visibility(id,visible);\n"; + } + $output .= ' + }); + $(".LC_richDefaultOn").each(function() { + var id = $(this).attr("id"); + $(this).before(""); + startRichEditor(id); + $("#LC_rt_"+id).click(editorHandler); + }); + $(".LC_richDefaultOff").each(function() { + var id = $(this).attr("id"); + $(this).before(""); + $("#LC_rt_"+id).click(editorHandler); + }); + }); +'; + if ($dragmath_prefix ne '') { + $output .= ' + + function editmath_visibility(id,value) { + + if ((id == "") || (id == null)) { + return; + } + var mathid = "'.$dragmath_prefix.'_"+id; + mathele = document.getElementById(mathid); + if (mathele == null) { + return; + } + mathele.style.display = value; + var mathhelpicon = "'.$dragmath_prefix.'helpicon'.'_"+id; + mathhelpiconele = document.getElementById(mathhelpicon); + if (mathhelpiconele == null) { + return; + } + if (value == "none") { + mathhelpiconele.src = "'.$dragmath_whitespace.'"; + } else { + mathhelpiconele.src = "'.$dragmath_helpicon.'"; + } + } +'; + + } + $output.="\nwindow.status='Activated Editfields';\n" + .'// ]]>'."\n" + .''; + return $output; +} - $Str .= ''."\n"; +# --------------------------------------------------------------------- Blocked - return $Str; +sub htmlareablocked { + unless ($env{'environment.wysiwygeditor'} eq 'on') { return 1; } + return 0; } -sub PartOptions { - my ($data, $page, $parts, $formName)=@_; - my $Str = ''; +# ---------------------------------------- Browser capable of running HTMLArea? + +sub htmlareabrowser { + return 1; +} + +############################################################ +############################################################ + +=pod + +=item breadcrumbs + +Compiles the previously registered breadcrumbs into an series of links. +Additionally supports a 'component', which will be displayed on the +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 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. - if(!defined($parts)) { - return ''; +=item clear_breadcrumbs + +Clears the previously stored breadcrumbs. + +=item add_breadcrumb + +Pushes a breadcrumb on the stack of crumbs. + +input: $breadcrumb, a hash reference. The keys 'href','title', and 'text' +are required. If present the keys 'faq' and 'bug' will be used to provide +links to the FAQ and bug sites. If the key 'no_mt' is present the 'title' +and 'text' values won't be sent through &mt() + +returns: nothing + +=cut + +############################################################ +############################################################ +{ + my @Crumbs; + my %tools = (); + + sub breadcrumbs { + 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 = ''; + # 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 ($menulink) { + my $description = 'Menu'; + my $no_mt_descr = 0; + if ((exists($env{'request.course.id'})) && + ($env{'request.course.id'} ne '') && + ($env{'course.'.$env{'request.course.id'}.'.description'} 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; + } + } + my $links = join "", + map { + $faq = $_->{'faq'} if (exists($_->{'faq'})); + $bug = $_->{'bug'} if (exists($_->{'bug'})); + $help = $_->{'help'} if (exists($_->{'help'})); + + my $result = $_->{no_mt} ? $_->{text} : mt($_->{text}); + + if ($_->{href}){ + $result = htmltag( 'a', $result, + { href => $_->{href}, + title => $_->{no_mt} ? $_->{title} : mt($_->{title}), + target => $_->{target}, }); + } + + $result = htmltag( 'li', "$result $crumbsymbol"); + } @Crumbs; + + #should the last Element be translated? + + my $lasttext = $last->{'no_mt'} ? $last->{'text'} + : mt( $last->{'text'} ); + + # last breadcrumb is the first order heading of a page + # for course breadcrumbs it's just bold + $links .= htmltag( 'li', htmltag($CourseBreadcrumbs ? 'b' : 'h1', + $lasttext), {title => $lasttext}); + + my $icons = ''; + $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 '') { +# $icons .= &Apache::loncommon::help_open_faq($faq); +# } +# 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); + } + # + + + 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; + } + + render_tools(\$links); + $links = htmltag('div', $links, + { id => "LC_breadcrumbs" }) unless ($CourseBreadcrumbs) ; + render_advtools(\$links); + + # Return the @Crumbs stack to what we started with + push(@Crumbs,$last); + shift(@Crumbs); + # Return the breadcrumb's line + return "$links"; } - $Str .= ''."\n"; +=item clear_breadcrumb_tools() - return $Str; +Clears the breadcrumb toolbar container. + +returns: nothing + +=cut + + sub clear_breadcrumb_tools { + undef(%tools); + } + +=item render_tools(\$breadcrumbs) + +Creates html for breadcrumb tools (categories navigation and tools) and inserts +\$breadcrumbs at the correct position. + +input: \$breadcrumbs - a reference to the string containing prepared +breadcrumbs. + +returns: nothing +=cut + +#TODO might split this in separate functions for each category + sub render_tools { + my ($breadcrumbs) = @_; + return unless (keys(%tools)); + + my $navigation = list_from_array($tools{navigation}, + { listattr => { class=>"LC_breadcrumb_tools_navigation" } }); + my $tools = list_from_array($tools{tools}, + { listattr => { class=>"LC_breadcrumb_tools_tools" } }); + $$breadcrumbs = list_from_array([$navigation, $tools, $$breadcrumbs], + { listattr => { class=>'LC_breadcrumb_tools_outerlist' } }); + } + +=item render_advtools(\$breadcrumbs) + +Creates html for advanced tools (category advtools) and inserts \$breadcrumbs +at the correct position. + +input: \$breadcrumbs - a reference to the string containing prepared +breadcrumbs (after render_tools call). + +returns: nothing +=cut + + sub render_advtools { + my ($breadcrumbs) = @_; + return unless (defined $tools{'advtools'}) + and (scalar(@{$tools{'advtools'}}) > 0); + + $$breadcrumbs .= Apache::loncommon::head_subbox( + funclist_from_array($tools{'advtools'}) ); + } + +} # End of scope for @Crumbs + +############################################################ +############################################################ + +# Nested table routines. +# +# Routines to display form items in a multi-row table with 2 columns. +# Uses nested tables to divide form elements into segments. +# For examples of use see loncom/interface/lonnotify.pm +# +# Can be used in following order: ... +# &start_pick_box() +# row1 +# row2 +# row3 ... etc. +# &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 +# +# Can also be used in following order: +# +# &start_pick_box() +# &row_title() +# &row_closure() +# &row_title() +# &row_closure() ... etc. +# &submit_row() +# &end_pick_box() +# +# In general a &submit_row() call should proceed the call to &end_pick_box(), +# as this routine adds a button for form submission. +# &submit_row() does not require a &row_closure after it. +# +# &start_pick_box() creates a bounding table with 1-pixel wide black border. +# rows should be placed between calls to &start_pick_box() and &end_pick_box. +# +# &row_title() adds a title in the left column for each segment. +# &row_closure() closes a row with a 1-pixel wide black line. +# +# &role_select_row() provides a select box from which to choose 1 or more roles +# &course_select_row provides ways of picking groups of courses +# radio buttons: all, by category or by picking from a course picker pop-up +# note: by category option is only displayed if a domain has implemented +# selection by year, semester, department, number etc. +# +# &status_select_row() provides a select box from which to choose 1 or more +# access types (current access, prior access, and future access) +# +# &email_default_row() provides text boxes for default e-mail suffixes for +# different authentication types in a domain. +# +# &row_title() and &row_closure() are called internally by the &*_select_row +# 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)) { + $css_class = 'class="'.$css_class.'"'; + } else { + $css_class= 'class="LC_pick_box"'; + } + unshift(@row_count,0); + my $output = <<"END"; + +END + return $output; } -sub StudentOptions { - my ($cache, $students, $selectedName, $page, $formName)=@_; +sub end_pick_box { + shift(@row_count); + my $output = <<"END"; +
+END + return $output; +} - my $Str = ''; - $Str .= '\n|; + if (defined($title)) { + $output .= &row_closure(); + } + return $output; +} - $Str .= ' +// No Student Selected'."\n"; + function setPick (formname) { + for (var i=0; i + + |; - $Str .= ''; + $output .= ''.$allcrs.'
'; + if ($totcodes > 0) { + my $numtitles = @$codetitles; + if ($numtitles > 0) { + $output .= ''.&mt('Pick courses by category:').'
'; + $output .= ''; + for (my $i=1; $i<$numtitles; $i++) { + $output .= ''; + } + $output .= '
'.$$codetitles[0].'
'."\n". + '
'.$$codetitles[$i].'
'."\n". + ''."\n". + '

'; + } + } + $output .= ''.$pickspec.' '.$courseform.'  selected.
'."\n"; + return $output; +} - return $Str; +sub status_select_row { + my ($types,$title,$css_class) = @_; + my $output; + if (defined($title)) { + $output = &row_title($title,$css_class,'LC_pick_box_select'); + } + $output .= qq| + \n|; + if (defined($title)) { + $output .= &row_closure(); + } + return $output; } -sub StatusOptions { - my ($status, $formName)=@_; +sub email_default_row { + my ($authtypes,$title,$descrip,$css_class) = @_; + my $output = &row_title($title,$css_class); + $output .= $descrip. + &Apache::loncommon::start_data_table(). + &Apache::loncommon::start_data_table_header_row(). + ''.&mt('Authentication Method').''. + ''.&mt('Username -> e-mail conversion').''."\n". + &Apache::loncommon::end_data_table_header_row(); + my $rownum = 0; + foreach my $auth (sort(keys(%{$authtypes}))) { + my ($userentry,$size); + if ($auth =~ /^krb/) { + $userentry = ''; + $size = 25; + } else { + $userentry = 'username@'; + $size = 15; + } + $output .= &Apache::loncommon::start_data_table_row(). + ' '.$$authtypes{$auth}.''. + ''.$userentry. + ''. + &Apache::loncommon::end_data_table_row(); + } + $output .= &Apache::loncommon::end_data_table(); + $output .= &row_closure(); + return $output; +} - my $OpSel1 = ''; - my $OpSel2 = ''; - my $OpSel3 = ''; - - if($status eq 'Any') { $OpSel3 = ' selected'; } - elsif($status eq 'Expired' ) { $OpSel2 = ' selected'; } - else { $OpSel1 = ' selected'; } - my $Str = ''; - $Str .= ' +   +

+ \n|; + return $output; +} + +sub course_custom_roles { + my ($cdom,$cnum) = @_; + my %returnhash=(); + my %coursepersonnel=&Apache::lonnet::dump('nohist_userroles',$cdom,$cnum); + foreach my $person (sort(keys(%coursepersonnel))) { + my ($role) = ($person =~ /^([^:]+):/); + my ($end,$start) = split(/:/,$coursepersonnel{$person}); + if ($end == -1 && $start == -1) { + next; + } + if ($role =~ m|^cr/[^/]+/[^/]+/[^/]|) { + $returnhash{$role} ++; + } } - $Str .= '>'."\n"; - $Str .= 'Active'."\n"; - $Str .= 'Expired'."\n"; - $Str .= 'Any'."\n"; - $Str .= ''."\n"; + return %returnhash; } -sub MultipleSectionSelect { - my ($sections,$selectedSections)=@_; - my $Str = ''; - $Str .= ''."\n"; + } + } else { + my $value = &HTML::Entities::encode($env{$key},'<>&"'); + $output .= ''."\n"; } } - $Str .= '>'.$_.''."\n"; } - $Str .= ''."\n"; + return $output; +} - return $Str; +############################################## +############################################## +# set_form_elements +# +# Generates javascript to set form elements to values based on +# corresponding values for the same form elements when the page was +# previously submitted. +# +# Last submission values are read from hidden form elements in referring +# page which have the same name, i.e., generated by &echo_form_input(). +# +# Intended to be called by onload event. +# +# Inputs: +# (a) Reference to hash of echoed form elements to be set. +# +# In the hash, keys are the form element names, and the values are the +# element type (selectbox, radio, checkbox or text -for textbox, textarea or +# hidden). +# +# (b) Optional reference to hash of stored elements to be set. +# +# If the page being displayed is a page which permits modification of +# previously stored data, e.g., the first page in a multi-page submission, +# then if stored is supplied, form elements will be set to the last stored +# values. If user supplied values are also available for the same elements +# these will replace the stored values. +# +# Output: +# +# javascript function - set_form_elements() which sets form elements, +# expects an argument: formname - the name of the form according to +# the DOM, e.g., document.compose + +sub set_form_elements { + my ($elements,$stored) = @_; + my %values; + my $output .= 'function setFormElements(courseForm) { +'; + if (defined($stored)) { + foreach my $name (keys(%{$stored})) { + if (exists($$elements{$name})) { + if (ref($$stored{$name}) eq 'ARRAY') { + $values{$name} = $$stored{$name}; + } else { + @{$values{$name}} = ($$stored{$name}); + } + } + } + } + + foreach my $key (keys(%env)) { + if ($key =~ /^form\.(.+)$/) { + my $name = $1; + if (exists($$elements{$name})) { + @{$values{$name}} = &Apache::loncommon::get_env_multiple($key); + } + } + } + + foreach my $name (keys(%values)) { + for (my $i=0; $i<@{$values{$name}}; $i++) { + $values{$name}[$i] = &HTML::Entities::decode($values{$name}[$i],'<>&"'); + $values{$name}[$i] =~ s/([\r\n\f]+)/\\n/g; + $values{$name}[$i] =~ s/"/\\"/g; + } + if (($$elements{$name} eq 'text') || ($$elements{$name} eq 'hidden')) { + my $numvalues = @{$values{$name}}; + if ($numvalues > 1) { + my $valuestring = join('","',@{$values{$name}}); + $output .= qq| + var textvalues = new Array ("$valuestring"); + var total = courseForm.elements['$name'].length; + if (total > $numvalues) { + total = $numvalues; + } + for (var i=0; i= 0) { + return true; + } + return false; + } +} +END + return $scripttag; +} - $Str .= ''.$pageName.''."\n"; - $Str .= &Apache::loncommon::bodytag($pageName)."\n"; - $Str .= ''."\n"; - return $Str; +# 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
are correctly terminated, +# i.e. htmltag('br') returns
+# - 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]| : qq|/>|). "\n"; +}; + + +# USAGE: inittags(@tags); +# +# EXAMPLES: +# - my ($h1, $h2, $h3) = inittags( qw( h1 h2 h3 ) ) +# $h1->("This is a headline") #Returns:

This is a headline

+# +# NOTES: See sub htmltag for further information. +# +# OUTPUT: List of subroutines. +sub inittags { + my @tags = @_; + return map { my $tag = $_; + sub { return htmltag( $tag, @_ ) } + } @tags; } -=pod -=item &CreateTableHeadings() +# USAGE: scripttag(scriptcode, [start|end|both]); +# +# EXAMPLES: +# - scripttag("alert('Hello World!')", 'both') +# returns: +# +# +# NOTES: +# - works currently only for javascripts +# +# OUTPUT: +# Scriptcode properly enclosed in "); +Inputs: ./. - $r->rflush(); -} +Returns: HTML code with function list end +=cut -# update progress -sub Update_PrgWin { - my ($displayString,$r)=@_; - $r->print(''); - $r->rflush(); +sub end_funclist { + return "\n"; } -# close Progress Line -sub Close_PrgWin { - my ($r)=@_; - $r->print(''."\n"); - $r->rflush(); -} +=pod + +=item funclist_from_array( \@array, {legend => 'text for legend'} ) + +Constructs a XHTML list from \@array with the first item being visually +highlighted and set to the value of legend or 'Functions' if legend is +empty. + +=over + +=item \@array + +A reference to the array containing text that will be wrapped in
  • tags. + +=item { legend => 'text' } + +A string that's used as visually highlighted first item. 'Functions' is used if +it's value evaluates to false. + +=back + +returns: XHTML list as string. + +=back + +=cut + +sub funclist_from_array { + my ($items, $args) = @_; + return unless(ref($items) eq 'ARRAY'); + $args->{legend} ||= mt('Functions'); + return list_from_array( [$args->{legend}, @$items], + { listattr => {class => 'LC_funclist'} }); +} 1; + __END__