--- loncom/interface/lonhtmlcommon.pm 2016/08/08 00:57:36 1.358.2.5 +++ loncom/interface/lonhtmlcommon.pm 2023/09/11 14:13:31 1.358.2.20 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # a pile of common html routines # -# $Id: lonhtmlcommon.pm,v 1.358.2.5 2016/08/08 00:57:36 raeburn Exp $ +# $Id: lonhtmlcommon.pm,v 1.358.2.20 2023/09/11 14:13:31 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -92,7 +92,7 @@ sub raw_href_to_link { sub entity_encode { my ($text)=@_; - return &HTML::Entities::encode($text, '<>&"'); + return &HTML::Entities::encode($text, '\'<>&"'); } sub direct_parm_link { @@ -407,7 +407,7 @@ sub textbox { ############################################## ############################################## sub checkbox { - my ($name,$checked,$value) = @_; + my ($name,$checked,$value,$special) = @_; my $Str = ''; return $Str; } @@ -534,6 +534,8 @@ sub date_setter { if (! defined($state) || $state ne 'disabled') { $state = ''; + } else { + $state = 'disabled="disabled"'; } if (! defined($no_hh_mm_ss)) { $no_hh_mm_ss = 0; @@ -669,7 +671,7 @@ ENDJS my $minuteselector = qq{}; my $secondselector= qq{}; my $cal_link; - if (!$nolink) { + unless (($nolink) || ($state eq 'disabled')) { $cal_link = qq{}; } # @@ -694,7 +696,7 @@ ENDJS $hourselector,$minuteselector,$secondselector). $tzone; } - if (!$nolink) { + unless (($nolink) || ($state eq 'disabled')) { $result .= &mt('[_1]Select Date[_2]',$cal_link,''); } $result .= "\n\n"; @@ -1007,10 +1009,13 @@ Inputs =item $number_to_do The total number of items being processed. +=item $preamble Optional HTML to display before the progress bar. + =back Returns a hash containing the progress state data structure. - +If $number_to_do is zero or null, an indeterminate progress bar will +be used. =item &Update_PrgWin() @@ -1080,20 +1085,20 @@ Returns: none # Create progress sub Create_PrgWin { - my ($r,$number_to_do)=@_; + my ($r,$number_to_do,$preamble)=@_; my %prog_state; $prog_state{'done'}=0; $prog_state{'firststart'}=&Time::HiRes::time(); $prog_state{'laststart'}=&Time::HiRes::time(); $prog_state{'max'}=$number_to_do; - &Apache::loncommon::LCprogressbar($r); + &Apache::loncommon::LCprogressbar($r,$prog_state{'max'},$preamble); return %prog_state; } # update progress sub Update_PrgWin { my ($r,$prog_state,$displayString)=@_; - &Apache::loncommon::LCprogressbarUpdate($r,undef,$displayString); + &Apache::loncommon::LCprogressbarUpdate($r,undef,$displayString,$$prog_state{'max'}); $$prog_state{'laststart'}=&Time::HiRes::time(); } @@ -1143,7 +1148,7 @@ sub Increment_PrgWin { if ($$prog_state{'max'}) { $percent=int(100.*$current/$$prog_state{'max'}); } - &Apache::loncommon::LCprogressbarUpdate($r,$percent,$timeinfo); + &Apache::loncommon::LCprogressbarUpdate($r,$percent,$timeinfo,$$prog_state{'max'}); $$prog_state{'laststart'}=&Time::HiRes::time(); } @@ -1160,7 +1165,7 @@ sub Close_PrgWin { sub crumbs { my ($uri,$target,$prefix,$form,$skiplast,$onclick)=@_; # You cannot crumbnify uploaded or adm resources - if ($uri=~/^\/*(uploaded|adm)\//) { return &mt('(Internal Course/Group Content)'); } + if ($uri=~/^\/*(uploaded|adm)\//) { return &mt('(Internal Course/Community Content)'); } if ($target) { $target = ' target="'. &Apache::loncommon::escape_single($target).'"'; @@ -1180,6 +1185,12 @@ sub crumbs { } else { $path.='/'; } + if ($path eq '/res/') { + unless (&Apache::lonnet::allowed('bre',$path)) { + $output.="$dir/"; + next; + } + } my $href_path = &HTML::Entities::encode($path,'<>&"'); &Apache::loncommon::inhibit_menu_check(\$href_path); if ($form) { @@ -1257,9 +1268,9 @@ sub htmlareaheaders { ENDEDITOR } $s.=(< - - + + + @@ -1342,6 +1353,41 @@ sub htmlareaselectactive { } function startRichEditor(id) { + // fix character entities inside + // NOTE: this is not fixing characters inside + // NOTE: < and > inside should fix automatically because there should not be a letter after <. + var ta = document.getElementById(id); + var value = ta.value; + var in_m = false; // in the m element + var in_text = false; // in the text inside the m element + var im = -1; // position of + var it = -1; // position of the text inside + for (var i=0; i if found twice + in_m = true; + in_text = false; + im = i; + it = -1; + } else if (in_m) { + if (!in_text) { + if (value.charAt(i) == ">") { + in_text = true; + it = i+1; + } + } else if (value.substr(i, 4) == "") { + in_m = false; + var text = value.substr(it, i-it); + var l1 = text.length; + text = text.replace(//g, ">"); + var l2 = text.length; + value = value.substr(0, it) + text + "" + value.substr(i+4); + i = i + (l2-l1); + } + } + } + ta.value = value; CKEDITOR.replace(id, { customConfig: "/ckeditor/loncapaconfig.js", @@ -1353,6 +1399,68 @@ sub htmlareaselectactive { function destroyRichEditor(id) { CKEDITOR.instances[id].destroy(); + // replace character entities < and > in and + // and "&fctname(" by "&fctname(" + // and the quotes inside functions: "&fct(1, "a")" -> "&fct(1, "a")" + var ta = document.getElementById(id); + var value = ta.value; + var in_element = false; // in the m or chem element + var tagname = ""; // m or chem + var in_text = false; // in the text inside the element + var im = -1; // position of start tag + var it = -1; // position of the text inside + for (var i=0; i") { + in_text = true; + it = i+1; + } + } else if (value.substr(i, 3+tagname.length) == "") { + in_element = false; + var text = value.substr(it, i-it); + var l1 = text.length; + text = text.replace(/</g, "<"); + text = text.replace(/>/g, ">"); + var l2 = text.length; + value = value.substr(0, it) + text + value.substr(i); + i = i + (l2-l1); + } + } + } + // fix function names + value = value.replace(/&([a-zA-Z_]+)\(/g, "&$1("); + // fix quotes in functions + var pos_next_fct = value.search(/&[a-zA-Z_]+\(/); + var depth = 0; + for (var i=0; i 0) { + if (value.charAt(i) == ")") + depth--; + else if (value.substr(i, 6) == """) + value = value.substr(0, i) + "\"" + value.substr(i+6); + } + } + // replace the text value + ta.value = value; } function editorHandler(event) { @@ -1645,6 +1753,48 @@ clientTime = (new Date()).getTime(); END } +## +# Client-side javascript to convert any dashes in text pasted +# into textbox(es) for numericalresponse item(s) to a standard +# minus, i.e., - . Calls to dash_to_minus_js() in end_problem() +# and in loncommon::endbodytag() for a .page (arg: dashjs => 1) +# +# Will apply to any input tag with class: LC_numresponse_text. +# Currently set in start_textline for numericalresponse items. +# + +sub dash_to_minus_js { + return <<'ENDJS'; + + + +ENDJS +} + ############################################################ ############################################################ @@ -1725,7 +1875,7 @@ returns: nothing $env{'course.'.$env{'request.course.id'}.'.description'}; $no_mt_descr = 1; if ($env{'request.noversionuri'} =~ - m{^/public/($match_domain)/($match_courseid)/syllabus$}) { + m{^/?public/($match_domain)/($match_courseid)/syllabus$}) { unless (($env{'course.'.$env{'request.course.id'}.'.domain'} eq $1) && ($env{'course.'.$env{'request.course.id'}.'.num'} eq $2)) { $description = 'Menu'; @@ -1926,6 +2076,16 @@ returns: nothing undef(%tools); } +=item ¤t_breadcrumb_tools() + +returns: a hash containing the current breadcrumb tools. + +=cut + + sub current_breadcrumb_tools { + return %tools; + } + =item &render_tools(\$breadcrumbs) Creates html for breadcrumb tools (categories navigation and tools) and inserts @@ -1992,11 +2152,11 @@ sub docs_breadcrumbs { my $foldername=shift(@folders); if ($folderpath) {$folderpath.='&';} $folderpath.=$folder.'&'.$foldername; - my $url; + my $url = $env{'request.use_absolute'}; if ($allowed) { - $url = '/adm/coursedocs?folderpath='; + $url .= '/adm/coursedocs?folderpath='; } else { - $url = '/adm/supplemental?folderpath='; + $url .= '/adm/supplemental?folderpath='; } $url .= &escape($folderpath); my $name=&unescape($foldername); @@ -2421,9 +2581,9 @@ sub course_custom_roles { sub resource_info_box { - my ($symb,$onlyfolderflag,$stuvcurrent,$stuvdisp)=@_; + my ($symb,$onlyfolderflag,$stuvcurrent,$stuvdisp,$divforres)=@_; my $return=''; - if ($stuvcurrent ne '') { + if (($stuvcurrent ne '') || ($divforres)) { $return = '
'; } if ($symb) { @@ -2452,7 +2612,7 @@ sub resource_info_box { } else { $return='

'.&mt('No context provided.').'

'; } - if ($stuvcurrent ne '') { + if (($stuvcurrent ne '') || ($divforres)) { $return .= '
'; } return $return; @@ -3183,12 +3343,18 @@ PARAMSONE if (itemid != null) { itemh = itemid.offsetHeight; } - var primaryheight = document.getElementById('LC_nav_bar').offsetHeight; - var secondaryheight; + var primaryheight = 0; + if (document.getElementById('LC_nav_bar') != null) { + primaryheight = document.getElementById('LC_nav_bar').offsetHeight; + } + var secondaryheight = 0; if (document.getElementById('LC_secondary_menu') != null) { secondaryheight = document.getElementById('LC_secondary_menu').offsetHeight; } - var crumbsheight = document.getElementById('LC_breadcrumbs').offsetHeight; + var crumbsheight = 0; + if (document.getElementById('LC_breadcrumbs') != null) { + crumbsheight = document.getElementById('LC_breadcrumbs').offsetHeight; + } var dccidheight = 0; if (document.getElementById('dccid') != null) { dccidheight = document.getElementById('dccid').offsetHeight; @@ -3268,7 +3434,17 @@ function go(url) { if (url!='' && url!= null) { currentURL = null; currentSymb= null; - window.location.href=url; + var lcHostname = setLCHost(); + if (lcHostname!='' && lcHostname!= null) { + var RegExp = /^https?\:/; + if (RegExp.test(url)) { + window.location.href=url; + } else { + window.location.href=lcHostname+url; + } + } else { + window.location.href=url; + } } } @@ -3287,8 +3463,8 @@ ENDUTILITY sub jump_to_editres { my ($cfile,$home,$switchserver,$forceedit,$forcereg,$symb,$folderpath, - $title,$idx,$suppurl,$todocs) = @_; - my $jscall; + $title,$hostname,$idx,$suppurl,$todocs,$suppanchor) = @_; + my ($jscall,$anchor,$usehttp,$usehttps,$is_ext); if ($switchserver) { if ($home) { $cfile = '/adm/switchserver?otherserver='.$home.'&role='. @@ -3308,7 +3484,44 @@ sub jump_to_editres { } } else { unless ($cfile =~ m{^/priv/}) { + if ($cfile =~ m{^(/adm/wrapper/ext/([^#]+))(?:|#([^#]+))$}) { + $cfile = $1; + my $extlink = $2; + $anchor = $3; + $is_ext = 1; + if (($extlink !~ /^https:/) && ($ENV{'SERVER_PORT'} == 443)) { + unless ((&Apache::lonnet::uses_sts()) || (&Apache::lonnet::waf_allssl($hostname))) { + $usehttp = 1; + } + } elsif ($env{'request.use_absolute'}) { + if ($env{'request.use_absolute'} =~ m{^https://}) { + $usehttps = 1; + } + } + } elsif ($cfile =~ m{^/?public/($match_domain)/($match_courseid)/syllabus}) { + if ($ENV{'SERVER_PORT'} == 443) { + my ($cdom,$cnum) = ($1,$2); + if (($env{'request.course.id'}) && + ($env{'course.'.$env{'request.course.id'}.'.num'} eq $cnum) && + ($env{'course.'.$env{'request.course.id'}.'.domain'} eq $cdom)) { + if ($env{'course.'.$env{'request.course.id'}.'.externalsyllabus'} =~ m{^http://}) { + unless ((&Apache::lonnet::uses_sts()) || (&Apache::lonnet::waf_allssl($hostname))) { + $usehttp = 1; + } + } + } + } elsif ($env{'request.use_absolute'}) { + if ($env{'request.use_absolute'} =~ m{^https://}) { + $usehttps = 1; + } + } + } if ($symb) { + if ($anchor ne '') { + if ($symb =~ m{^([^#]+)\Q#$anchor\E$}) { + $symb = $1.&escape(&escape('#')).$anchor; + } + } $cfile .= (($cfile=~/\?/)?'&':'?')."symb=$symb"; } elsif ($folderpath) { $cfile .= (($cfile=~/\?/)?'&':'?'). @@ -3327,13 +3540,30 @@ sub jump_to_editres { } if ($forceedit) { $cfile .= (($cfile=~/\?/)?'&':'?').'forceedit=1'; + if ($usehttps) { + $cfile = $env{'request.use_absolute'}.(($cfile =~ /^\//)? '':'/').$cfile; + } + } elsif ($usehttp) { + if ($hostname ne '') { + $cfile = 'http://'.$hostname.(($cfile =~ /^\//)? '':'/').$cfile; + } + $cfile .= (($cfile=~/\?/)?'&':'?').'usehttp=1'; + } elsif ($usehttps) { + $cfile = $env{'request.use_absolute'}.(($cfile =~ /^\//)? '':'/').$cfile; } if ($forcereg) { $cfile .= (($cfile=~/\?/)?'&':'?').'register=1'; } if ($todocs) { - $cfile .= (($cfile=~/\?/)?'&':'?').'todocs=1'; + $cfile .= (($cfile=~/\?/)?'&':'?').'todocs=1'; } + if ($suppanchor ne '') { + $cfile .= (($cfile=~/\?/)?'&':'?').'anchor='. + &HTML::Entities::encode($suppanchor,'"<>&'); + } + } + if ($anchor ne '') { + $cfile .= '#'.$anchor; } $jscall = "go('".&Apache::loncommon::escape_single($cfile)."')"; } @@ -3346,15 +3576,18 @@ sub jump_to_editres { # javascript_valid_email # # Generates javascript to validate an e-mail address. -# Returns a javascript function which accetps a form field as argumnent, and +# Returns a javascript function which accepts a form field as argument, 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) { +function validmail(field,suffix) { var str = field.value; + if (suffix != '' && suffix != undefined) { + str += suffix; + } if (window.RegExp) { var reg1str = "(@.*@)|(\\.\\.)|(@\\.)|(\\.@)|(^\\.)"; var reg2str = "^.+\\@(\\[?)[a-zA-Z0-9\\-\\.]+\\.([a-zA-Z]{2,3}|[0-9]{1,3})(\\]?)$"; //"