![]() ![]() | ![]() |
Fix cycle of "Edit Resource" and going back
1: # The LearningOnline Network with CAPA 2: # a pile of common html routines 3: # 4: # $Id: lonhtmlcommon.pm,v 1.289 2011/01/18 22:59:06 www Exp $ 5: # 6: # Copyright Michigan State University Board of Trustees 7: # 8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA). 9: # 10: # LON-CAPA is free software; you can redistribute it and/or modify 11: # it under the terms of the GNU General Public License as published by 12: # the Free Software Foundation; either version 2 of the License, or 13: # (at your option) any later version. 14: # 15: # LON-CAPA is distributed in the hope that it will be useful, 16: # but WITHOUT ANY WARRANTY; without even the implied warranty of 17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18: # GNU General Public License for more details. 19: # 20: # You should have received a copy of the GNU General Public License 21: # along with LON-CAPA; if not, write to the Free Software 22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23: # 24: # /home/httpd/html/adm/gpl.txt 25: # 26: # http://www.lon-capa.org/ 27: # 28: ###################################################################### 29: ###################################################################### 30: 31: =pod 32: 33: =head1 NAME 34: 35: Apache::lonhtmlcommon - routines to do common html things 36: 37: =head1 SYNOPSIS 38: 39: Referenced by other mod_perl Apache modules. 40: 41: =head1 INTRODUCTION 42: 43: lonhtmlcommon is a collection of subroutines used to present information 44: in a consistent html format, or provide other functionality related to 45: html. 46: 47: =head2 General Subroutines 48: 49: =over 4 50: 51: =cut 52: 53: ###################################################################### 54: ###################################################################### 55: 56: package Apache::lonhtmlcommon; 57: 58: use strict; 59: use Time::Local; 60: use Time::HiRes; 61: use Apache::lonlocal; 62: use Apache::lonnet; 63: use HTML::Entities(); 64: use LONCAPA; 65: 66: sub java_not_enabled { 67: return "\n".'<span class="LC_error">'. 68: &mt('The required Java applet could not be started. Please make sure to have Java installed and active in your browser.'). 69: "</span>\n"; 70: } 71: 72: sub coursepreflink { 73: my ($text,$category)=@_; 74: if (&Apache::lonnet::allowed('opa',$env{'request.course.id'})) { 75: return '<a href="'.&HTML::Entities::encode("/adm/courseprefs?phase=display&actions=$category",'<>&"').'">'.$text.'</a>'; 76: } else { 77: return ''; 78: } 79: } 80: 81: sub raw_href_to_link { 82: my ($message)=@_; 83: $message=~s/(https?\:\/\/[^\s\'\"\<]+)([\s\<]|$)/<a href="$1"><tt>$1<\/tt><\/a>$2/gi; 84: return $message; 85: } 86: 87: sub entity_encode { 88: my ($text)=@_; 89: return &HTML::Entities::encode($text, '<>&"'); 90: } 91: 92: sub direct_parm_link { 93: my ($linktext,$symb,$filter,$part,$target)=@_; 94: $symb=&entity_encode($symb); 95: $filter=&entity_encode($filter); 96: $part=&entity_encode($part); 97: if (($symb) && (&Apache::lonnet::allowed('opa')) && ($target ne 'tex')) { 98: return "<a href='/adm/parmset?symb=$symb&filter=$filter&part=$part'>$linktext</a>"; 99: } else { 100: return $linktext; 101: } 102: } 103: ############################################## 104: ############################################## 105: 106: =item confirm_success 107: 108: Successful completion of an operation message 109: 110: =cut 111: 112: sub confirm_success { 113: my ($message,$failure)=@_; 114: if ($failure) { 115: return '<span class="LC_error" style="font-size: inherit;">'."\n" 116: .'<img src="/adm/lonIcons/navmap.wrong.gif" alt="'.&mt('Error').'" /> '."\n" 117: .$message."\n" 118: .'</span>'."\n"; 119: } else { 120: return '<span class="LC_success">'."\n" 121: .'<img src="/adm/lonIcons/navmap.correct.gif" alt="'.&mt('OK').'" /> '."\n" 122: .$message."\n" 123: .'</span>'."\n"; 124: } 125: } 126: 127: ############################################## 128: ############################################## 129: 130: =pod 131: 132: =item dragmath_button 133: 134: Creates a button that launches a dragmath popup-window, in which an 135: expression can be edited and pasted as LaTeX into a specified textarea. 136: 137: textarea - Name of the textarea to edit. 138: helpicon - If true, show a help icon to the right of the button. 139: 140: =cut 141: 142: sub dragmath_button { 143: my ($textarea,$helpicon) = @_; 144: my $help_text; 145: if ($helpicon) { 146: $help_text = &Apache::loncommon::help_open_topic('Authoring_Math_Editor',undef,undef,undef,undef,'mathhelpicon_'.$textarea); 147: } 148: my $buttontext=&mt('Edit Math'); 149: return <<ENDDRAGMATH; 150: <input type="button" value="$buttontext" onclick="javascript:mathedit('$textarea',document)" />$help_text 151: ENDDRAGMATH 152: } 153: 154: ############################################## 155: 156: =pod 157: 158: =item dragmath_js 159: 160: Javascript used to open pop-up window containing dragmath applet which 161: can be used to paste LaTeX into a textarea. 162: =cut 163: 164: sub dragmath_js { 165: my ($popup) = @_; 166: return <<ENDDRAGMATHJS; 167: <script type="text/javascript"> 168: // <![CDATA[ 169: function mathedit(textarea, doc) { 170: targetEntry = textarea; 171: targetDoc = doc; 172: newwin = window.open("/adm/dragmath/applet/$popup.html","","width=565,height=500,resizable"); 173: } 174: // ]]> 175: </script> 176: 177: ENDDRAGMATHJS 178: } 179: 180: 181: ############################################## 182: ############################################## 183: 184: =pod 185: 186: =item authorbombs 187: 188: =cut 189: 190: ############################################## 191: ############################################## 192: 193: sub authorbombs { 194: my $url=shift; 195: $url=&Apache::lonnet::declutter($url); 196: my ($udom,$uname)=($url=~m{^($LONCAPA::domain_re)/($LONCAPA::username_re)/}); 197: my %bombs=&Apache::lonmsg::all_url_author_res_msg($uname,$udom); 198: foreach my $bomb (keys(%bombs)) { 199: if ($bomb =~ /^$udom\/$uname\//) { 200: return '<a href="/adm/bombs/'.$url. 201: '"><img src="'.&Apache::loncommon::lonhttpdurl('/adm/lonMisc/bomb.gif').'" alt="'.&mt('Bomb').'" border="0" /></a>'. 202: &Apache::loncommon::help_open_topic('About_Bombs'); 203: } 204: } 205: return ''; 206: } 207: 208: ############################################## 209: ############################################## 210: 211: sub recent_filename { 212: my $area=shift; 213: return 'nohist_recent_'.&escape($area); 214: } 215: 216: sub store_recent { 217: my ($area,$name,$value,$freeze)=@_; 218: my $file=&recent_filename($area); 219: my %recent=&Apache::lonnet::dump($file); 220: if (scalar(keys(%recent))>20) { 221: # remove oldest value 222: my $oldest=time(); 223: my $delkey=''; 224: foreach my $item (keys(%recent)) { 225: my $thistime=(split(/\&/,$recent{$item}))[0]; 226: if (($thistime ne "always_include") && ($thistime<$oldest)) { 227: $oldest=$thistime; 228: $delkey=$item; 229: } 230: } 231: &Apache::lonnet::del($file,[$delkey]); 232: } 233: # store new value 234: my $timestamp; 235: if ($freeze) { 236: $timestamp = "always_include"; 237: } else { 238: $timestamp = time(); 239: } 240: &Apache::lonnet::put($file,{ $name => 241: $timestamp.'&'.&escape($value) }); 242: } 243: 244: sub remove_recent { 245: my ($area,$names)=@_; 246: my $file=&recent_filename($area); 247: return &Apache::lonnet::del($file,$names); 248: } 249: 250: sub select_recent { 251: my ($area,$fieldname,$event)=@_; 252: my %recent=&Apache::lonnet::dump(&recent_filename($area)); 253: my $return="\n<select name='$fieldname'". 254: ($event?" onchange='$event'":''). 255: ">\n<option value=''>--- ".&mt('Recent')." ---</option>"; 256: foreach my $value (sort(keys(%recent))) { 257: unless ($value =~/^error\:/) { 258: my $escaped = &Apache::loncommon::escape_url($value); 259: &Apache::loncommon::inhibit_menu_check(\$escaped); 260: if ($area eq 'residx') { 261: next if ((!&Apache::lonnet::allowed('bre',$value)) && (!&Apache::lonnet::allowed('bro',$value))); 262: } 263: $return.="\n<option value='$escaped'>". 264: &unescape((split(/\&/,$recent{$value}))[1]). 265: '</option>'; 266: } 267: } 268: $return.="\n</select>\n"; 269: return $return; 270: } 271: 272: sub get_recent { 273: my ($area, $n) = @_; 274: my %recent=&Apache::lonnet::dump(&recent_filename($area)); 275: 276: # Create hash with key as time and recent as value 277: # Begin filling return_hash with any 'always_include' option 278: my %time_hash = (); 279: my %return_hash = (); 280: foreach my $item (keys(%recent)) { 281: my ($thistime,$thisvalue)=(split(/\&/,$recent{$item})); 282: if ($thistime eq 'always_include') { 283: $return_hash{$item} = &unescape($thisvalue); 284: $n--; 285: } else { 286: $time_hash{$thistime} = $item; 287: } 288: } 289: 290: # Sort by decreasing time and return key value pairs 291: my $idx = 1; 292: foreach my $item (reverse(sort(keys(%time_hash)))) { 293: $return_hash{$time_hash{$item}} = 294: &unescape((split(/\&/,$recent{$time_hash{$item}}))[1]); 295: if ($n && ($idx++ >= $n)) {last;} 296: } 297: 298: return %return_hash; 299: } 300: 301: sub get_recent_frozen { 302: my ($area) = @_; 303: my %recent=&Apache::lonnet::dump(&recent_filename($area)); 304: 305: # Create hash with all 'frozen' items 306: my %return_hash = (); 307: foreach my $item (keys(%recent)) { 308: my ($thistime,$thisvalue)=(split(/\&/,$recent{$item})); 309: if ($thistime eq 'always_include') { 310: $return_hash{$item} = &unescape($thisvalue); 311: } 312: } 313: return %return_hash; 314: } 315: 316: 317: 318: =pod 319: 320: =item textbox 321: 322: =cut 323: 324: ############################################## 325: ############################################## 326: sub textbox { 327: my ($name,$value,$size,$special) = @_; 328: $size = 40 if (! defined($size)); 329: $value = &HTML::Entities::encode($value,'<>&"'); 330: my $Str = '<input type="text" name="'.$name.'" size="'.$size.'" '. 331: 'value="'.$value.'" '.$special.' />'; 332: return $Str; 333: } 334: 335: ############################################## 336: ############################################## 337: 338: =pod 339: 340: =item checkbox 341: 342: =cut 343: 344: ############################################## 345: ############################################## 346: sub checkbox { 347: my ($name,$checked,$value) = @_; 348: my $Str = '<input type="checkbox" name="'.$name.'" '; 349: if (defined($value)) { 350: $Str .= 'value="'.$value.'"'; 351: } 352: if ($checked) { 353: $Str .= ' checked="checked"'; 354: } 355: $Str .= ' />'; 356: return $Str; 357: } 358: 359: 360: =pod 361: 362: =item radiobutton 363: 364: =cut 365: 366: ############################################## 367: ############################################## 368: sub radio { 369: my ($name,$checked,$value) = @_; 370: my $Str = '<input type="radio" name="'.$name.'" '; 371: if (defined($value)) { 372: $Str .= 'value="'.$value.'"'; 373: } 374: if ($checked eq $value) { 375: $Str .= ' checked="checked"'; 376: } 377: $Str .= ' />'; 378: return $Str; 379: } 380: 381: ############################################## 382: ############################################## 383: 384: =pod 385: 386: =item &date_setter 387: 388: &date_setter returns html and javascript for a compact date-setting form. 389: To retrieve values from it, use &get_date_from_form(). 390: 391: Inputs 392: 393: =over 4 394: 395: =item $dname 396: 397: The name to prepend to the form elements. 398: The form elements defined will be dname_year, dname_month, dname_day, 399: dname_hour, dname_min, and dname_sec. 400: 401: =item $currentvalue 402: 403: The current setting for this time parameter. A unix format time 404: (time in seconds since the beginning of Jan 1st, 1970, GMT. 405: An undefined value is taken to indicate the value is the current time 406: unless it is requested to leave it empty. See $includeempty. 407: Also, to be explicit, a value of 'now' also indicates the current time. 408: 409: =item $special 410: 411: Additional html/javascript to be associated with each element in 412: the date_setter. See lonparmset for example usage. 413: 414: =item $includeempty 415: 416: If it is set (true) and no date/time value is provided, 417: the date/time fields are left empty. 418: 419: =item $state 420: 421: Specifies the initial state of the form elements. Either 'disabled' or empty. 422: Defaults to empty, which indiciates the form elements are not disabled. 423: 424: =back 425: 426: Bugs 427: 428: The method used to restrict user input will fail in the year 2400. 429: 430: =cut 431: 432: ############################################## 433: ############################################## 434: sub date_setter { 435: my ($formname,$dname,$currentvalue,$special,$includeempty,$state, 436: $no_hh_mm_ss,$defhour,$defmin,$defsec,$nolink) = @_; 437: my $now = time; 438: 439: my $tzname; 440: my ($sec,$min,$hour,$mday,$month,$year) = ('', '', undef,''.''.''); 441: #other potentially useful values: wkday,yrday,is_daylight_savings 442: 443: if (! defined($state) || $state ne 'disabled') { 444: $state = ''; 445: } 446: if (! defined($no_hh_mm_ss)) { 447: $no_hh_mm_ss = 0; 448: } 449: if ($currentvalue eq 'now') { 450: $currentvalue = $now; 451: } 452: 453: # Default value: Set empty date field to current time 454: # unless empty inclusion is requested 455: if ((!$includeempty) && (!$currentvalue)) { 456: $currentvalue = $now; 457: } 458: # Do we have a date? Split it! 459: if ($currentvalue) { 460: ($tzname,$sec,$min,$hour,$mday,$month,$year) = &get_timedates($currentvalue); 461: 462: #No values provided for hour, min, sec? Use default 0 463: if (($defhour) || ($defmin) || ($defsec)) { 464: $sec = ($defsec ? $defsec : 0); 465: $min = ($defmin ? $defmin : 0); 466: $hour = ($defhour ? $defhour : 0); 467: } 468: } 469: my $result = "\n<!-- $dname date setting form -->\n"; 470: $result .= <<ENDJS; 471: <script type="text/javascript"> 472: // <![CDATA[ 473: function $dname\_checkday() { 474: var day = document.$formname.$dname\_day.value; 475: var month = document.$formname.$dname\_month.value; 476: var year = document.$formname.$dname\_year.value; 477: var valid = true; 478: if (day < 1) { 479: document.$formname.$dname\_day.value = 1; 480: } 481: if (day > 31) { 482: document.$formname.$dname\_day.value = 31; 483: } 484: if ((month == 1) || (month == 3) || (month == 5) || 485: (month == 7) || (month == 8) || (month == 10) || 486: (month == 12)) { 487: if (day > 31) { 488: document.$formname.$dname\_day.value = 31; 489: day = 31; 490: } 491: } else if (month == 2 ) { 492: if ((year % 4 == 0) && (year % 100 != 0)) { 493: if (day > 29) { 494: document.$formname.$dname\_day.value = 29; 495: } 496: } else if (day > 29) { 497: document.$formname.$dname\_day.value = 28; 498: } 499: } else if (day > 30) { 500: document.$formname.$dname\_day.value = 30; 501: } 502: } 503: 504: function $dname\_disable() { 505: document.$formname.$dname\_month.disabled=true; 506: document.$formname.$dname\_day.disabled=true; 507: document.$formname.$dname\_year.disabled=true; 508: document.$formname.$dname\_hour.disabled=true; 509: document.$formname.$dname\_minute.disabled=true; 510: document.$formname.$dname\_second.disabled=true; 511: } 512: 513: function $dname\_enable() { 514: document.$formname.$dname\_month.disabled=false; 515: document.$formname.$dname\_day.disabled=false; 516: document.$formname.$dname\_year.disabled=false; 517: document.$formname.$dname\_hour.disabled=false; 518: document.$formname.$dname\_minute.disabled=false; 519: document.$formname.$dname\_second.disabled=false; 520: } 521: 522: function $dname\_opencalendar() { 523: if (! document.$formname.$dname\_month.disabled) { 524: var calwin=window.open( 525: "/adm/announcements?pickdate=yes&formname=$formname&element=$dname&month="+ 526: document.$formname.$dname\_month.value+"&year="+ 527: document.$formname.$dname\_year.value, 528: "LONCAPAcal", 529: "height=350,width=350,scrollbars=yes,resizable=yes,menubar=no"); 530: } 531: 532: } 533: // ]]> 534: </script> 535: ENDJS 536: $result .= ' <span class="LC_nobreak">'; 537: my $monthselector = qq{<select name="$dname\_month" $special $state onchange="javascript:$dname\_checkday()" >}; 538: # Month 539: my @Months = qw/January February March April May June 540: July August September October November December/; 541: # Pad @Months with a bogus value to make indexing easier 542: unshift(@Months,'If you can read this an error occurred'); 543: if ($includeempty) { $monthselector.="<option value=''></option>"; } 544: for(my $m = 1;$m <=$#Months;$m++) { 545: $monthselector .= qq{ <option value="$m"}; 546: $monthselector .= ' selected="selected"' if ($m-1 eq $month); 547: $monthselector .= '> '.&mt($Months[$m]).' </option>'."\n"; 548: } 549: $monthselector.= ' </select>'; 550: # Day 551: my $dayselector = qq{<input type="text" name="$dname\_day" $state value="$mday" size="3" $special onchange="javascript:$dname\_checkday()" />}; 552: # Year 553: my $yearselector = qq{<input type="text" name="$dname\_year" $state value="$year" size="5" $special onchange="javascript:$dname\_checkday()" />}; 554: # 555: my $hourselector = qq{<select name="$dname\_hour" $special $state >}; 556: if ($includeempty) { 557: $hourselector.=qq{<option value=''></option>}; 558: } 559: for (my $h = 0;$h<24;$h++) { 560: $hourselector .= qq{<option value="$h"}; 561: $hourselector .= ' selected="selected"' if (defined($hour) && $hour == $h); 562: $hourselector .= ">"; 563: my $timest=''; 564: if ($h == 0) { 565: $timest .= "12 am"; 566: } elsif($h == 12) { 567: $timest .= "12 noon"; 568: } elsif($h < 12) { 569: $timest .= "$h am"; 570: } else { 571: $timest .= $h-12 ." pm"; 572: } 573: $timest=&mt($timest); 574: $hourselector .= $timest." </option>\n"; 575: } 576: $hourselector .= " </select>\n"; 577: my $minuteselector = qq{<input type="text" name="$dname\_minute" $special $state value="$min" size="3" />}; 578: my $secondselector= qq{<input type="text" name="$dname\_second" $special $state value="$sec" size="3" />}; 579: my $cal_link; 580: if (!$nolink) { 581: $cal_link = qq{<a href="javascript:$dname\_opencalendar()">}; 582: } 583: # 584: my $tzone = ' '.$tzname.' '; 585: if ($no_hh_mm_ss) { 586: $result .= &mt('[_1] [_2] [_3] ', 587: $monthselector,$dayselector,$yearselector). 588: $tzone; 589: if (!$nolink) { 590: $result .= &mt('[_1]Select Date[_2]',$cal_link,'</a>'); 591: } 592: } else { 593: $result .= &mt('[_1] [_2] [_3] [_4] [_5]m [_6]s ', 594: $monthselector,$dayselector,$yearselector, 595: $hourselector,$minuteselector,$secondselector). 596: $tzone; 597: if (!$nolink) { 598: $result .= &mt('[_1]Select Date[_2]',$cal_link,'</a>'); 599: } 600: } 601: $result .= "</span>\n<!-- end $dname date setting form -->\n"; 602: return $result; 603: } 604: 605: sub get_timedates { 606: my ($epoch) = @_; 607: my $dt = DateTime->from_epoch(epoch => $epoch) 608: ->set_time_zone(&Apache::lonlocal::gettimezone()); 609: my $tzname = $dt->time_zone_short_name(); 610: my $sec = $dt->second; 611: my $min = $dt->minute; 612: my $hour = $dt->hour; 613: my $mday = $dt->day; 614: my $month = $dt->month; 615: if ($month) { 616: $month --; 617: } 618: my $year = $dt->year; 619: return ($tzname,$sec,$min,$hour,$mday,$month,$year); 620: } 621: 622: sub build_url { 623: my ($base, $fields)=@_; 624: my $url; 625: $url = $base.'?'; 626: foreach my $key (keys(%$fields)) { 627: $url.=&escape($key).'='.&escape($$fields{$key}).'&'; 628: } 629: $url =~ s/&$//; 630: return $url; 631: } 632: 633: 634: ############################################## 635: ############################################## 636: 637: =pod 638: 639: =item &get_date_from_form 640: 641: get_date_from_form retrieves the date specified in an &date_setter form. 642: 643: Inputs: 644: 645: =over 4 646: 647: =item $dname 648: 649: The name passed to &date_setter, which prefixes the form elements. 650: 651: =item $defaulttime 652: 653: The unix time to use as the default in case of poor inputs. 654: 655: =back 656: 657: Returns: Unix time represented in the form. 658: 659: =cut 660: 661: ############################################## 662: ############################################## 663: sub get_date_from_form { 664: my ($dname) = @_; 665: my ($sec,$min,$hour,$day,$month,$year); 666: # 667: if (defined($env{'form.'.$dname.'_second'})) { 668: my $tmpsec = $env{'form.'.$dname.'_second'}; 669: if (($tmpsec =~ /^\d+$/) && ($tmpsec >= 0) && ($tmpsec < 60)) { 670: $sec = $tmpsec; 671: } 672: if (!defined($tmpsec) || $tmpsec eq '') { $sec = 0; } 673: } else { 674: $sec = 0; 675: } 676: if (defined($env{'form.'.$dname.'_minute'})) { 677: my $tmpmin = $env{'form.'.$dname.'_minute'}; 678: if (($tmpmin =~ /^\d+$/) && ($tmpmin >= 0) && ($tmpmin < 60)) { 679: $min = $tmpmin; 680: } 681: if (!defined($tmpmin) || $tmpmin eq '') { $min = 0; } 682: } else { 683: $min = 0; 684: } 685: if (defined($env{'form.'.$dname.'_hour'})) { 686: my $tmphour = $env{'form.'.$dname.'_hour'}; 687: if (($tmphour =~ /^\d+$/) && ($tmphour >= 0) && ($tmphour < 24)) { 688: $hour = $tmphour; 689: } 690: } else { 691: $hour = 0; 692: } 693: if (defined($env{'form.'.$dname.'_day'})) { 694: my $tmpday = $env{'form.'.$dname.'_day'}; 695: if (($tmpday =~ /^\d+$/) && ($tmpday > 0) && ($tmpday < 32)) { 696: $day = $tmpday; 697: } 698: } 699: if (defined($env{'form.'.$dname.'_month'})) { 700: my $tmpmonth = $env{'form.'.$dname.'_month'}; 701: if (($tmpmonth =~ /^\d+$/) && ($tmpmonth > 0) && ($tmpmonth < 13)) { 702: $month = $tmpmonth; 703: } 704: } 705: if (defined($env{'form.'.$dname.'_year'})) { 706: my $tmpyear = $env{'form.'.$dname.'_year'}; 707: if (($tmpyear =~ /^\d+$/) && ($tmpyear >= 1970)) { 708: $year = $tmpyear; 709: } 710: } 711: if (($year<1970) || ($year>2037)) { return undef; } 712: if (defined($sec) && defined($min) && defined($hour) && 713: defined($day) && defined($month) && defined($year)) { 714: my $timezone = &Apache::lonlocal::gettimezone(); 715: my $dt = DateTime->new( year => $year, 716: month => $month, 717: day => $day, 718: hour => $hour, 719: minute => $min, 720: second => $sec, 721: time_zone => $timezone, 722: ); 723: my $epoch_time = $dt->epoch; 724: if ($epoch_time ne '') { 725: return $epoch_time; 726: } else { 727: return undef; 728: } 729: } else { 730: return undef; 731: } 732: } 733: 734: ############################################## 735: ############################################## 736: 737: =pod 738: 739: =item &pjump_javascript_definition() 740: 741: Returns javascript defining the 'pjump' function, which opens up a 742: parameter setting wizard. 743: 744: =cut 745: 746: ############################################## 747: ############################################## 748: sub pjump_javascript_definition { 749: my $Str = <<END; 750: function pjump(type,dis,value,marker,ret,call,hour,min,sec) { 751: parmwin=window.open("/adm/rat/parameter.html?type="+escape(type) 752: +"&value="+escape(value)+"&marker="+escape(marker) 753: +"&return="+escape(ret) 754: +"&call="+escape(call)+"&name="+escape(dis) 755: +"&defhour="+escape(hour)+"&defmin="+escape(min) 756: +"&defsec="+escape(sec),"LONCAPAparms", 757: "height=350,width=350,scrollbars=no,menubar=no"); 758: } 759: END 760: return $Str; 761: } 762: 763: ############################################## 764: ############################################## 765: 766: =pod 767: 768: =item &javascript_nothing() 769: 770: Return an appropriate null for the users browser. This is used 771: as the first arguement for window.open calls when you want a blank 772: window that you can then write to. 773: 774: =cut 775: 776: ############################################## 777: ############################################## 778: sub javascript_nothing { 779: # mozilla and other browsers work with "''", but IE on mac does not. 780: my $nothing = "''"; 781: my $user_browser; 782: my $user_os; 783: $user_browser = $env{'browser.type'} if (exists($env{'browser.type'})); 784: $user_os = $env{'browser.os'} if (exists($env{'browser.os'})); 785: if (! defined($user_browser) || ! defined($user_os)) { 786: (undef,$user_browser,undef,undef,undef,$user_os) = 787: &Apache::loncommon::decode_user_agent(); 788: } 789: if ($user_browser eq 'explorer' && $user_os =~ 'mac') { 790: $nothing = "'javascript:void(0);'"; 791: } 792: return $nothing; 793: } 794: 795: ############################################## 796: ############################################## 797: sub javascript_docopen { 798: my ($mimetype) = @_; 799: $mimetype ||= 'text/html'; 800: # safari does not understand document.open() and loads "text/html" 801: my $nothing = "''"; 802: my $user_browser; 803: my $user_os; 804: $user_browser = $env{'browser.type'} if (exists($env{'browser.type'})); 805: $user_os = $env{'browser.os'} if (exists($env{'browser.os'})); 806: if (! defined($user_browser) || ! defined($user_os)) { 807: (undef,$user_browser,undef,undef,undef,$user_os) = 808: &Apache::loncommon::decode_user_agent(); 809: } 810: if ($user_browser eq 'safari' && $user_os =~ 'mac') { 811: $nothing = "document.clear()"; 812: } else { 813: $nothing = "document.open('$mimetype','replace')"; 814: } 815: return $nothing; 816: } 817: 818: 819: ############################################## 820: ############################################## 821: 822: =pod 823: 824: =item &StatusOptions() 825: 826: Returns html for a selection box which allows the user to choose the 827: enrollment status of students. The selection box name is 'Status'. 828: 829: Inputs: 830: 831: $status: the currently selected status. If undefined the value of 832: $env{'form.Status'} is taken. If that is undefined, a value of 'Active' 833: is used. 834: 835: $formname: The name of the form. If defined the onchange attribute of 836: the selection box is set to document.$formname.submit(). 837: 838: $size: the size (number of lines) of the selection box. 839: 840: $onchange: javascript to use when the value is changed. Enclosed in 841: double quotes, ""s, not single quotes. 842: 843: Returns: a perl string as described. 844: 845: =cut 846: 847: ############################################## 848: ############################################## 849: sub StatusOptions { 850: my ($status, $formName,$size,$onchange,$mult)=@_; 851: $size = 1 if (!defined($size)); 852: if (! defined($status)) { 853: $status = 'Active'; 854: $status = $env{'form.Status'} if (exists($env{'form.Status'})); 855: } 856: 857: my $Str = ''; 858: $Str .= '<select name="Status"'; 859: if (defined($mult)){ 860: $Str .= ' multiple="multiple" '; 861: } 862: if(defined($formName) && $formName ne '' && ! defined($onchange)) { 863: $Str .= ' onchange="document.'.$formName.'.submit()"'; 864: } 865: if (defined($onchange)) { 866: $Str .= ' onchange="'.$onchange.'"'; 867: } 868: $Str .= ' size="'.$size.'" '; 869: $Str .= '>'."\n"; 870: foreach my $type (['Active', &mt('Currently Has Access')], 871: ['Future', &mt('Will Have Future Access')], 872: ['Expired', &mt('Previously Had Access')], 873: ['Any', &mt('Any Access Status')]) { 874: my ($name,$label) = @$type; 875: $Str .= '<option value="'.$name.'" '; 876: if ($status eq $name) { 877: $Str .= 'selected="selected" '; 878: } 879: $Str .= '>'.$label.'</option>'."\n"; 880: } 881: 882: $Str .= '</select>'."\n"; 883: } 884: 885: ######################################################## 886: ######################################################## 887: 888: =pod 889: 890: =item Progess Window Handling Routines 891: 892: These routines handle the creation, update, increment, and closure of 893: progress windows. The progress window reports to the user the number 894: of items completed and an estimate of the time required to complete the rest. 895: 896: =over 4 897: 898: 899: =item &Create_PrgWin 900: 901: Writes javascript to the client to open a progress window and returns a 902: data structure used for bookkeeping. 903: 904: Inputs 905: 906: =over 4 907: 908: =item $r Apache request 909: 910: =item $title The title of the progress window 911: 912: =item $heading A description (usually 1 line) of the process being initiated. 913: 914: =item $number_to_do The total number of items being processed. 915: 916: =item $type Either 'popup' or 'inline' (popup is assumed if nothing is 917: specified) 918: 919: =item $width Specify the width in charaters of the input field. 920: 921: =item $formname Only useful in the inline case, if a form already exists, this needs to be used and specfiy the name of the form, otherwise the Progress line will be created in a new form of it's own 922: 923: =item $inputname Only useful in the inline case, if a form and an input of type text exists, use this to specify the name of the input field 924: 925: =back 926: 927: Returns a hash containing the progress state data structure. 928: 929: 930: =item &Update_PrgWin 931: 932: Updates the text in the progress indicator. Does not increment the count. 933: See &Increment_PrgWin. 934: 935: Inputs: 936: 937: =over 4 938: 939: =item $r Apache request 940: 941: =item $prog_state Pointer to the data structure returned by &Create_PrgWin 942: 943: =item $displaystring The string to write to the status indicator 944: 945: =back 946: 947: Returns: none 948: 949: 950: =item Increment_PrgWin 951: 952: Increment the count of items completed for the progress window by $step or 1 if no step is provided. 953: 954: Inputs: 955: 956: =over 4 957: 958: =item $r Apache request 959: 960: =item $prog_state Pointer to the data structure returned by Create_PrgWin 961: 962: =item $extraInfo A description of the items being iterated over. Typically 963: 'student'. 964: 965: =item $step (optional) counter step. Will be set to default 1 if ommited. step must be greater than 0 or empty. 966: 967: =back 968: 969: Returns: none 970: 971: 972: =item Close_PrgWin 973: 974: Closes the progress window. 975: 976: Inputs: 977: 978: =over 4 979: 980: =item $r Apache request 981: 982: =item $prog_state Pointer to the data structure returned by Create_PrgWin 983: 984: =back 985: 986: Returns: none 987: 988: =back 989: 990: =cut 991: 992: ######################################################## 993: ######################################################## 994: 995: my $uniq=0; 996: sub get_uniq_name { 997: $uniq++; 998: return 'uniquename'.$uniq; 999: } 1000: 1001: # Create progress 1002: sub Create_PrgWin { 1003: my ($r, $title, $heading, $number_to_do,$type,$width,$formname, 1004: $inputname)=@_; 1005: if (!defined($type)) { $type='popup'; } 1006: if (!defined($width)) { $width=55; } 1007: my %prog_state; 1008: $prog_state{'type'}=$type; 1009: if ($type eq 'popup') { 1010: $prog_state{'window'}='popwin'; 1011: my $start_page = 1012: &Apache::loncommon::start_page($title,undef, 1013: {'only_body' => 1, 1014: 'bgcolor' => '#88DDFF', 1015: 'js_ready' => 1}); 1016: my $end_page = &Apache::loncommon::end_page({'js_ready' => 1}); 1017: 1018: #the whole function called through timeout is due to issues 1019: #in mozilla Read BUG #2665 if you want to know the whole story 1020: &r_print($r,&Apache::lonhtmlcommon::scripttag( 1021: "var popwin; 1022: function openpopwin () { 1023: popwin=open(\'\',\'popwin\',\'width=400,height=100\');". 1024: "popwin.document.writeln(\'".$start_page. 1025: "<h4>".&mt("$heading")."<\/h4>". 1026: "<form action=\"\" name=\"popremain\" method=\"post\">". 1027: '<input type="text" size="'.$width.'" name="remaining" value="'. 1028: &mt('Starting').'" /><\\/form>'.$end_page. 1029: "\');". 1030: "popwin.document.close();}". 1031: "\nwindow.setTimeout(openpopwin,0)" 1032: )); 1033: $prog_state{'formname'}='popremain'; 1034: $prog_state{'inputname'}="remaining"; 1035: } elsif ($type eq 'inline') { 1036: $prog_state{'window'}='window'; 1037: if (!$formname) { 1038: $prog_state{'formname'}=&get_uniq_name(); 1039: &r_print($r,'<form action="" name="'.$prog_state{'formname'}.'">'); 1040: } else { 1041: $prog_state{'formname'}=$formname; 1042: } 1043: if (!$inputname) { 1044: $prog_state{'inputname'}=&get_uniq_name(); 1045: &r_print($r,&mt("$heading [_1]",' <input type="text" name="'.$prog_state{'inputname'}.'" size="'.$width.'" />')); 1046: } else { 1047: $prog_state{'inputname'}=$inputname; 1048: 1049: } 1050: if (!$formname) { &r_print($r,'</form>'); } 1051: &Update_PrgWin($r,\%prog_state,&mt('Starting')); 1052: } 1053: 1054: $prog_state{'done'}=0; 1055: $prog_state{'firststart'}=&Time::HiRes::time(); 1056: $prog_state{'laststart'}=&Time::HiRes::time(); 1057: $prog_state{'max'}=$number_to_do; 1058: 1059: return %prog_state; 1060: } 1061: 1062: # update progress 1063: sub Update_PrgWin { 1064: my ($r,$prog_state,$displayString)=@_; 1065: &r_print($r,&Apache::lonhtmlcommon::scripttag( 1066: $$prog_state{'window'}.'.document.'. 1067: $$prog_state{'formname'}.'.'. 1068: $$prog_state{'inputname'}.'.value="'. 1069: $displayString.'";' 1070: )); 1071: $$prog_state{'laststart'}=&Time::HiRes::time(); 1072: } 1073: 1074: # increment progress state 1075: sub Increment_PrgWin { 1076: my ($r,$prog_state,$extraInfo,$step)=@_; 1077: $step = $step > 0 ? $step : 1; 1078: $$prog_state{'done'} += $step; 1079: 1080: # Catch (max modulo step) <> 0 1081: my $current = $$prog_state{'done'}; 1082: my $last = ($$prog_state{'max'} - $current); 1083: if ($last <= 0) { 1084: $last = 1; 1085: $current = $$prog_state{'max'}; 1086: } 1087: 1088: my $time_est= (&Time::HiRes::time() - $$prog_state{'firststart'})/ 1089: $current * $last; 1090: $time_est = int($time_est); 1091: # 1092: my $min = int($time_est/60); 1093: my $sec = $time_est % 60; 1094: 1095: my $lasttime = &Time::HiRes::time()-$$prog_state{'laststart'}; 1096: if ($lasttime > 9) { 1097: $lasttime = int($lasttime); 1098: } elsif ($lasttime < 0.01) { 1099: $lasttime = 0; 1100: } else { 1101: $lasttime = sprintf("%3.2f",$lasttime); 1102: } 1103: 1104: $sec = 0 if ($min >= 10); # Don't show seconds if remaining time >= 10 min. 1105: $sec = 1 if ( ($min == 0) && ($sec == 0) ); # Little cheating: pretend to have 1 second remaining instead of 0 to have something to display 1106: 1107: my $timeinfo = 1108: &mt('[_1]/[_2]:' 1109: .' [quant,_3,minute,minutes,] [quant,_4,second ,seconds ,]remaining' 1110: .' ([quant,_5,second] for '.$extraInfo.')', 1111: $current, 1112: $$prog_state{'max'}, 1113: $min, 1114: $sec, 1115: $lasttime); 1116: 1117: &r_print($r,&Apache::lonhtmlcommon::scripttag( 1118: $$prog_state{'window'}.'.document.'. 1119: $$prog_state{'formname'}.'.'. 1120: $$prog_state{'inputname'}.'.value="'.$timeinfo.'";' 1121: )); 1122: $$prog_state{'laststart'}=&Time::HiRes::time(); 1123: } 1124: 1125: # close Progress Line 1126: sub Close_PrgWin { 1127: my ($r,$prog_state)=@_; 1128: if ($$prog_state{'type'} eq 'popup') { 1129: &r_print($r,&Apache::lonhtmlcommon::scripttag( 1130: 'popwin.close()' 1131: )); 1132: } elsif ($$prog_state{'type'} eq 'inline') { 1133: &Update_PrgWin($r,$prog_state,&mt('Done')); 1134: } 1135: undef(%$prog_state); 1136: } 1137: 1138: sub r_print { 1139: my ($r,$to_print)=@_; 1140: if ($r) { 1141: $r->print($to_print); 1142: $r->rflush(); 1143: } else { 1144: print($to_print); 1145: } 1146: } 1147: 1148: # ------------------------------------------------------- Puts directory header 1149: 1150: sub crumbs { 1151: my ($uri,$target,$prefix,$form,$skiplast)=@_; 1152: if ($target) { 1153: $target = ' target="'. 1154: &Apache::loncommon::escape_single($target).'"'; 1155: } 1156: my $output='<span class="LC_filename">'; 1157: $output.=$prefix.'/'; 1158: if (($env{'user.adv'}) || ($env{'user.author'})) { 1159: my $path=$prefix.'/'; 1160: foreach my $dir (split('/',$uri)) { 1161: if (! $dir) { next; } 1162: $path .= $dir; 1163: if ($path eq $uri) { 1164: if ($skiplast) { 1165: $output.=$dir; 1166: last; 1167: } 1168: } else { 1169: $path.='/'; 1170: } 1171: my $href_path = &HTML::Entities::encode($path,'<>&"'); 1172: &Apache::loncommon::inhibit_menu_check(\$href_path); 1173: if ($form) { 1174: my $href = 'javascript:'.$form.".action='".$href_path."';".$form.'.submit();'; 1175: $output.=qq{<a href="$href"$target>$dir</a>/}; 1176: } else { 1177: $output.=qq{<a href="$href_path"$target>$dir</a>/}; 1178: } 1179: } 1180: } else { 1181: foreach my $dir (split('/',$uri)) { 1182: if (! $dir) { next; } 1183: $output.=$dir.'/'; 1184: } 1185: } 1186: if ($uri !~ m|/$|) { $output=~s|/$||; } 1187: $output.='</span>'; 1188: 1189: return $output; 1190: } 1191: 1192: # --------------------- A function that generates a window for the spellchecker 1193: 1194: sub spellheader { 1195: my $start_page= 1196: &Apache::loncommon::start_page('Speller Suggestions',undef, 1197: {'only_body' => 1, 1198: 'js_ready' => 1, 1199: 'bgcolor' => '#DDDDDD', 1200: 'add_entries' => { 1201: 'onload' => 1202: 'document.forms.spellcheckform.submit()', 1203: } 1204: }); 1205: my $end_page= 1206: &Apache::loncommon::end_page({'js_ready' => 1}); 1207: 1208: my $nothing=&javascript_nothing(); 1209: return (<<ENDCHECK); 1210: <script type="text/javascript"> 1211: // <![CDATA[ 1212: //<!-- BEGIN LON-CAPA Internal 1213: var checkwin; 1214: 1215: function spellcheckerwindow(string) { 1216: var esc_string = string.replace(/\"/g,'"'); 1217: checkwin=window.open($nothing,'spellcheckwin','height=320,width=280,resizable=yes,scrollbars=yes,location=no,menubar=no,toolbar=no'); 1218: checkwin.document.writeln('$start_page<form name="spellcheckform" action="/adm/spellcheck" method="post"><input type="hidden" name="text" value="'+esc_string+'" /><\\/form>$end_page'); 1219: checkwin.document.close(); 1220: } 1221: // END LON-CAPA Internal --> 1222: // ]]> 1223: </script> 1224: ENDCHECK 1225: } 1226: 1227: # ---------------------------------- Generate link to spell checker for a field 1228: 1229: sub spelllink { 1230: my ($form,$field)=@_; 1231: my $linktext=&mt('Check Spelling'); 1232: return (<<ENDLINK); 1233: <a href="javascript:if (typeof(document.$form.onsubmit)!='undefined') { if (document.$form.onsubmit!=null) { document.$form.onsubmit();}};spellcheckerwindow(this.document.forms.$form.$field.value);">$linktext</a> 1234: ENDLINK 1235: } 1236: 1237: # ------------------------------------------------- Output headers for CKEditor 1238: 1239: sub htmlareaheaders { 1240: my $s=""; 1241: if (&htmlareabrowser()) { 1242: $s.=(<<ENDEDITOR); 1243: <script type="text/javascript" src="/ckeditor/ckeditor.js"></script> 1244: ENDEDITOR 1245: } 1246: $s.=(<<ENDJQUERY); 1247: <script type="text/javascript" src="/adm/jQuery/js/jquery-1.3.2.min.js"></script> 1248: <script type="text/javascript" src="/adm/jQuery/js/jquery-ui-1.7.2.custom.min.js"></script> 1249: <link rel="stylesheet" type="text/css" href="/adm/jQuery/css/smoothness/jquery-ui-1.7.2.custom.css" /> 1250: ENDJQUERY 1251: return $s; 1252: } 1253: 1254: # ----------------------------------------------------------------- Preferences 1255: 1256: # ------------------------------------------------- lang to use in html editor 1257: sub htmlarea_lang { 1258: my $lang='en'; 1259: if (&mt('htmlarea_lang') ne 'htmlarea_lang') { 1260: $lang=&mt('htmlarea_lang'); 1261: } 1262: return $lang; 1263: } 1264: 1265: # ----------------------------------------- Script to activate only some fields 1266: 1267: sub htmlareaselectactive { 1268: my ($args) = @_; 1269: unless (&htmlareabrowser()) { return ''; } 1270: my $output='<script type="text/javascript" defer="defer">'."\n" 1271: .'// <![CDATA['."\n"; 1272: my $lang = &htmlarea_lang(); 1273: my $fullpage = 'false'; 1274: my ($dragmath_prefix,$dragmath_helpicon,$dragmath_whitespace); 1275: if (ref($args) eq 'HASH') { 1276: if (exists($args->{'lang'})) { 1277: if ($args->{'lang'} ne '') { 1278: $lang = $args->{'lang'}; 1279: } 1280: } 1281: if (exists($args->{'fullpage'})) { 1282: if ($args->{'fullpage'} eq 'true') { 1283: $fullpage = $args->{'fullpage'}; 1284: } 1285: } 1286: if (exists($args->{'dragmath'})) { 1287: if ($args->{'dragmath'} ne '') { 1288: $dragmath_prefix = $args->{'dragmath'}; 1289: $dragmath_helpicon=&Apache::loncommon::lonhttpdurl("/adm/help/help.png"); 1290: $dragmath_whitespace=&Apache::loncommon::lonhttpdurl("/adm/lonIcons/transparent1x1.gif"); 1291: } 1292: } 1293: } 1294: $output.=' 1295: 1296: function containsBlockHtml(id) { 1297: 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); 1298: return (re >= 0); 1299: } 1300: 1301: function startRichEditor(id) { 1302: CKEDITOR.replace(id, 1303: { 1304: customConfig: "/ckeditor/loncapaconfig.js", 1305: language : "'.$lang.'", 1306: fullPage : '.$fullpage.', 1307: } 1308: ); 1309: } 1310: 1311: function destroyRichEditor(id) { 1312: CKEDITOR.instances[id].destroy(); 1313: } 1314: 1315: function editorHandler(event) { 1316: var rawid = $(this).attr("id"); 1317: var id = new RegExp("LC_rt_(.*)").exec(rawid)[1]; 1318: event.preventDefault(); 1319: var rt_enabled = $(this).hasClass("LC_enable_rt"); 1320: if (rt_enabled) { 1321: startRichEditor(id); 1322: $("#LC_rt_"+id).html("<b>« Plain text</b>"); 1323: $("#LC_rt_"+id).attr("title", "Disable rich text formatting and edit in plain text"); 1324: $("#LC_rt_"+id).addClass("LC_disable_rt"); 1325: $("#LC_rt_"+id).removeClass("LC_enable_rt"); 1326: } else { 1327: destroyRichEditor(id); 1328: $("#LC_rt_"+id).html("<b>Rich formatting »</b>"); 1329: $("#LC_rt_"+id).attr("title", "Enable rich text formatting (bold, italic, etc.)"); 1330: $("#LC_rt_"+id).addClass("LC_enable_rt"); 1331: $("#LC_rt_"+id).removeClass("LC_disable_rt"); 1332: }'; 1333: if ($dragmath_prefix ne '') { 1334: $output .= "\n var visible = ''; 1335: if (rt_enabled) { 1336: visible = 'none'; 1337: } 1338: editmath_visibility(id,visible);\n"; 1339: } 1340: $output .= ' 1341: } 1342: $(document).ready(function(){ 1343: $(".LC_richAlwaysOn").each(function() { 1344: startRichEditor($(this).attr("id")); 1345: }); 1346: $(".LC_richDetectHtml").each(function() { 1347: var id = $(this).attr("id"); 1348: var rt_enabled = containsBlockHtml(id); 1349: if(rt_enabled) { 1350: $(this).before("<div><a href=\"#\" id=\"LC_rt_"+id+"\" title=\"Disable rich text formatting and edit in plain text\" class=\"LC_disable_rt\"><b>« Plain text</b></a></div>"); 1351: startRichEditor(id); 1352: $("#LC_rt_"+id).click(editorHandler); 1353: } 1354: else { 1355: $(this).before("<div><a href=\"#\" id=\"LC_rt_"+id+"\" title=\"Enable rich text formatting (bold, italic, etc.)\" class=\"LC_enable_rt\"><b>Rich formatting »</b></a></div>"); 1356: $("#LC_rt_"+id).click(editorHandler); 1357: }'; 1358: if ($dragmath_prefix ne '') { 1359: $output .= "\n var visible = ''; 1360: if (rt_enabled) { 1361: visible = 'none'; 1362: } 1363: editmath_visibility(id,visible);\n"; 1364: } 1365: $output .= ' 1366: }); 1367: $(".LC_richDefaultOn").each(function() { 1368: var id = $(this).attr("id"); 1369: $(this).before("<div><a href=\"#\" id=\"LC_rt_"+id+"\" title=\"Disable rich text formatting and edit in plain text\" class=\"LC_disable_rt\"><b>« Plain text</b></a></div>"); 1370: startRichEditor(id); 1371: $("#LC_rt_"+id).click(editorHandler); 1372: }); 1373: $(".LC_richDefaultOff").each(function() { 1374: var id = $(this).attr("id"); 1375: $(this).before("<div><a href=\"#\" id=\"LC_rt_"+id+"\" title=\"Enable rich text formatting (bold, italic, etc.)\" class=\"LC_enable_rt\"><b>Rich formatting »</b></a></div>"); 1376: $("#LC_rt_"+id).click(editorHandler); 1377: }); 1378: }); 1379: '; 1380: if ($dragmath_prefix ne '') { 1381: $output .= ' 1382: 1383: function editmath_visibility(id,value) { 1384: 1385: if ((id == "") || (id == null)) { 1386: return; 1387: } 1388: var mathid = "'.$dragmath_prefix.'_"+id; 1389: mathele = document.getElementById(mathid); 1390: if (mathele == null) { 1391: return; 1392: } 1393: mathele.style.display = value; 1394: var mathhelpicon = "'.$dragmath_prefix.'helpicon'.'_"+id; 1395: mathhelpiconele = document.getElementById(mathhelpicon); 1396: if (mathhelpiconele == null) { 1397: return; 1398: } 1399: if (value == "none") { 1400: mathhelpiconele.src = "'.$dragmath_whitespace.'"; 1401: } else { 1402: mathhelpiconele.src = "'.$dragmath_helpicon.'"; 1403: } 1404: } 1405: '; 1406: 1407: } 1408: $output.="\nwindow.status='Activated Editfields';\n" 1409: .'// ]]>'."\n" 1410: .'</script>'; 1411: return $output; 1412: } 1413: 1414: # --------------------------------------------------------------------- Blocked 1415: 1416: sub htmlareablocked { 1417: unless ($env{'environment.wysiwygeditor'} eq 'on') { return 1; } 1418: return 0; 1419: } 1420: 1421: # ---------------------------------------- Browser capable of running HTMLArea? 1422: 1423: sub htmlareabrowser { 1424: return 1; 1425: } 1426: 1427: # 1428: # Should the "return to content" link be shown? 1429: # 1430: 1431: sub show_return_link { 1432: 1433: unless ($env{'request.course.id'}) { return 0; } 1434: if ($env{'request.noversionuri'}=~m{^/priv/} || 1435: $env{'request.uri'}=~m{^/~}) { return 1; } 1436: 1437: if (($env{'request.noversionuri'} =~ m{^/adm/(viewclasslist|navmaps)($|\?)}) 1438: || ($env{'request.noversionuri'} =~ m{^/adm/.*/aboutme($|\?)})) { 1439: 1440: return if ($env{'form.register'}); 1441: } 1442: return (($env{'request.noversionuri'}=~m{^/(res|public)/} && 1443: $env{'request.symb'} eq '') 1444: || 1445: ($env{'request.noversionuri'}=~ m{^/cgi-bin/printout.pl}) 1446: || 1447: (($env{'request.noversionuri'}=~/^\/adm\//) && 1448: ($env{'request.noversionuri'}!~/^\/adm\/wrapper\//) && 1449: ($env{'request.noversionuri'}!~ 1450: m{^/adm/.*/(smppg|bulletinboard)($|\?)}) 1451: )); 1452: } 1453: 1454: 1455: ############################################################ 1456: ############################################################ 1457: 1458: =pod 1459: 1460: =item breadcrumbs 1461: 1462: Compiles the previously registered breadcrumbs into an series of links. 1463: Additionally supports a 'component', which will be displayed on the 1464: right side of the breadcrumbs enclosing div (without a link). 1465: A link to help for the component will be included if one is specified. 1466: 1467: All inputs can be undef without problems. 1468: 1469: Inputs: $component (the text on the right side of the breadcrumbs trail), 1470: $component_help 1471: $menulink (boolean, controls whether to include a link to /adm/menu) 1472: $helplink (if 'nohelp' don't include the orange help link) 1473: $css_class (optional name for the class to apply to the table for CSS) 1474: $no_mt (optional flag, 1 if &mt() is _not_ to be applied to $component 1475: when including the text on the right. 1476: Returns a string containing breadcrumbs for the current page. 1477: 1478: =item clear_breadcrumbs 1479: 1480: Clears the previously stored breadcrumbs. 1481: 1482: =item add_breadcrumb 1483: 1484: Pushes a breadcrumb on the stack of crumbs. 1485: 1486: input: $breadcrumb, a hash reference. The keys 'href','title', and 'text' 1487: are required. If present the keys 'faq' and 'bug' will be used to provide 1488: links to the FAQ and bug sites. If the key 'no_mt' is present the 'title' 1489: and 'text' values won't be sent through &mt() 1490: 1491: returns: nothing 1492: 1493: =cut 1494: 1495: ############################################################ 1496: ############################################################ 1497: { 1498: my @Crumbs; 1499: my %tools = (); 1500: 1501: sub breadcrumbs { 1502: my ($component,$component_help,$menulink,$helplink,$css_class,$no_mt, $CourseBreadcrumbs) = @_; 1503: # 1504: $css_class ||= 'LC_breadcrumbs'; 1505: 1506: # Make the faq and bug data cascade 1507: my $faq = ''; 1508: my $bug = ''; 1509: my $help = ''; 1510: # Crumb Symbol 1511: my $crumbsymbol = '»'; 1512: # The last breadcrumb does not have a link, so handle it separately. 1513: my $last = pop(@Crumbs); 1514: # 1515: # The first one should be the course or a menu link 1516: if (!defined($menulink)) { $menulink=1; } 1517: if ($menulink) { 1518: my $description = 'Menu'; 1519: my $no_mt_descr = 0; 1520: if ((exists($env{'request.course.id'})) && 1521: ($env{'request.course.id'} ne '') && 1522: ($env{'course.'.$env{'request.course.id'}.'.description'} ne '')) { 1523: $description = 1524: $env{'course.'.$env{'request.course.id'}.'.description'}; 1525: $no_mt_descr = 1; 1526: } 1527: $menulink = { href =>'/adm/menu', 1528: title =>'Go to main menu', 1529: target =>'_top', 1530: text =>$description, 1531: no_mt =>$no_mt_descr, }; 1532: if($last) { 1533: #$last set, so we have some crumbs 1534: unshift(@Crumbs,$menulink); 1535: } else { 1536: #only menulink crumb present 1537: $last = $menulink; 1538: } 1539: } 1540: my $links; 1541: if ((&show_return_link) && (!$CourseBreadcrumbs)) { 1542: $links=&htmltag( 'a',"<img src='/res/adm/pages/reload.png' border='0' style='vertical-align:middle;' />", 1543: { href => '/adm/flip?postdata=return:', 1544: title => &mt("Back to most recent content resource") }); 1545: } 1546: $links.= join "", 1547: map { 1548: $faq = $_->{'faq'} if (exists($_->{'faq'})); 1549: $bug = $_->{'bug'} if (exists($_->{'bug'})); 1550: $help = $_->{'help'} if (exists($_->{'help'})); 1551: 1552: my $result = $_->{no_mt} ? $_->{text} : &mt($_->{text}); 1553: 1554: if ($_->{href}){ 1555: $result = &htmltag( 'a', $result, 1556: { href => $_->{href}, 1557: title => $_->{no_mt} ? $_->{title} : &mt($_->{title}), 1558: target => $_->{target}, }); 1559: } 1560: 1561: $result = &htmltag( 'li', "$result $crumbsymbol"); 1562: } @Crumbs; 1563: 1564: #should the last Element be translated? 1565: 1566: my $lasttext = $last->{'no_mt'} ? $last->{'text'} 1567: : mt( $last->{'text'} ); 1568: 1569: # last breadcrumb is the first order heading of a page 1570: # for course breadcrumbs it's just bold 1571: $links .= &htmltag( 'li', htmltag($CourseBreadcrumbs ? 'b' : 'h1', 1572: $lasttext), {title => $lasttext}); 1573: 1574: my $icons = ''; 1575: $faq = $last->{'faq'} if (exists($last->{'faq'})); 1576: $bug = $last->{'bug'} if (exists($last->{'bug'})); 1577: $help = $last->{'help'} if (exists($last->{'help'})); 1578: $component_help=($component_help?$component_help:$help); 1579: # if ($faq ne '') { 1580: # $icons .= &Apache::loncommon::help_open_faq($faq); 1581: # } 1582: # if ($bug ne '') { 1583: # $icons .= &Apache::loncommon::help_open_bug($bug); 1584: # } 1585: if ($faq ne '' || $component_help ne '' || $bug ne '') { 1586: $icons .= &Apache::loncommon::help_open_menu($component, 1587: $component_help, 1588: $faq,$bug); 1589: } 1590: # 1591: 1592: 1593: unless ($CourseBreadcrumbs) { 1594: $links = &htmltag('ol', $links, { id => "LC_MenuBreadcrumbs" }); 1595: } else { 1596: $links = &htmltag('ul', $links, { class => "LC_CourseBreadcrumbs" }); 1597: } 1598: 1599: if ($component) { 1600: $links = &htmltag('span', 1601: ( $no_mt ? $component : mt($component) ). 1602: ( $icons ? $icons : '' ), 1603: { class => 'LC_breadcrumbs_component' } ) 1604: .$links; 1605: } 1606: 1607: &render_tools(\$links); 1608: $links = &htmltag('div', $links, 1609: { id => "LC_breadcrumbs" }) unless ($CourseBreadcrumbs) ; 1610: &render_advtools(\$links); 1611: 1612: # Return the @Crumbs stack to what we started with 1613: push(@Crumbs,$last); 1614: shift(@Crumbs); 1615: # Return the breadcrumb's line 1616: return "$links"; 1617: } 1618: 1619: sub clear_breadcrumbs { 1620: undef(@Crumbs); 1621: undef(%tools); 1622: } 1623: 1624: sub add_breadcrumb { 1625: push(@Crumbs,@_); 1626: } 1627: 1628: =item add_breadcrumb_tool($category, $html) 1629: 1630: Adds $html to $category of the breadcrumb toolbar container. 1631: 1632: $html is usually a link to a page that invokes a function on the currently 1633: displayed data (e.g. print when viewing a problem) 1634: 1635: Currently there are 3 possible values for $category: 1636: 1637: =over 1638: 1639: =item navigation 1640: left of breadcrumbs line 1641: 1642: =item tools 1643: right of breadcrumbs line 1644: 1645: =item advtools 1646: advanced tools shown in a separate box below breadcrumbs line 1647: 1648: =back 1649: 1650: returns: nothing 1651: 1652: =cut 1653: 1654: sub add_breadcrumb_tool { 1655: my ($category, @html) = @_; 1656: return unless @html; 1657: if (!keys(%tools)) { 1658: %tools = ( navigation => [], tools => [], advtools => []); 1659: } 1660: 1661: #this cleans data received from lonmenu::innerregister 1662: @html = grep {defined $_ && $_ ne ''} @html; 1663: for (@html) { 1664: s/align="(right|left)"//; 1665: # s/<span.*?\/span>// if $category ne 'advtools'; 1666: } 1667: 1668: push @{$tools{$category}}, @html; 1669: } 1670: 1671: =item clear_breadcrumb_tools() 1672: 1673: Clears the breadcrumb toolbar container. 1674: 1675: returns: nothing 1676: 1677: =cut 1678: 1679: sub clear_breadcrumb_tools { 1680: undef(%tools); 1681: } 1682: 1683: =item render_tools(\$breadcrumbs) 1684: 1685: Creates html for breadcrumb tools (categories navigation and tools) and inserts 1686: \$breadcrumbs at the correct position. 1687: 1688: input: \$breadcrumbs - a reference to the string containing prepared 1689: breadcrumbs. 1690: 1691: returns: nothing 1692: =cut 1693: 1694: #TODO might split this in separate functions for each category 1695: sub render_tools { 1696: my ($breadcrumbs) = @_; 1697: return unless (keys(%tools)); 1698: 1699: my $navigation = list_from_array($tools{navigation}, 1700: { listattr => { class=>"LC_breadcrumb_tools_navigation" } }); 1701: my $tools = list_from_array($tools{tools}, 1702: { listattr => { class=>"LC_breadcrumb_tools_tools" } }); 1703: $$breadcrumbs = list_from_array([$navigation, $tools, $$breadcrumbs], 1704: { listattr => { class=>'LC_breadcrumb_tools_outerlist' } }); 1705: } 1706: 1707: =item render_advtools(\$breadcrumbs) 1708: 1709: Creates html for advanced tools (category advtools) and inserts \$breadcrumbs 1710: at the correct position. 1711: 1712: input: \$breadcrumbs - a reference to the string containing prepared 1713: breadcrumbs (after render_tools call). 1714: 1715: returns: nothing 1716: =cut 1717: 1718: sub render_advtools { 1719: my ($breadcrumbs) = @_; 1720: return unless (defined $tools{'advtools'}) 1721: and (scalar(@{$tools{'advtools'}}) > 0); 1722: 1723: $$breadcrumbs .= Apache::loncommon::head_subbox( 1724: funclist_from_array($tools{'advtools'}) ); 1725: } 1726: 1727: } # End of scope for @Crumbs 1728: 1729: ############################################################ 1730: ############################################################ 1731: 1732: # Nested table routines. 1733: # 1734: # Routines to display form items in a multi-row table with 2 columns. 1735: # Uses nested tables to divide form elements into segments. 1736: # For examples of use see loncom/interface/lonnotify.pm 1737: # 1738: # Can be used in following order: ... 1739: # &start_pick_box() 1740: # row1 1741: # row2 1742: # row3 ... etc. 1743: # &submit_row() 1744: # &end_pick_box() 1745: # 1746: # where row1, row 2 etc. are chosen from &role_select_row,&course_select_row, 1747: # &status_select_row and &email_default_row 1748: # 1749: # Can also be used in following order: 1750: # 1751: # &start_pick_box() 1752: # &row_title() 1753: # &row_closure() 1754: # &row_title() 1755: # &row_closure() ... etc. 1756: # &submit_row() 1757: # &end_pick_box() 1758: # 1759: # In general a &submit_row() call should proceed the call to &end_pick_box(), 1760: # as this routine adds a button for form submission. 1761: # &submit_row() does not require a &row_closure after it. 1762: # 1763: # &start_pick_box() creates a bounding table with 1-pixel wide black border. 1764: # rows should be placed between calls to &start_pick_box() and &end_pick_box. 1765: # 1766: # &row_title() adds a title in the left column for each segment. 1767: # &row_closure() closes a row with a 1-pixel wide black line. 1768: # 1769: # &role_select_row() provides a select box from which to choose 1 or more roles 1770: # &course_select_row provides ways of picking groups of courses 1771: # radio buttons: all, by category or by picking from a course picker pop-up 1772: # note: by category option is only displayed if a domain has implemented 1773: # selection by year, semester, department, number etc. 1774: # 1775: # &status_select_row() provides a select box from which to choose 1 or more 1776: # access types (current access, prior access, and future access) 1777: # 1778: # &email_default_row() provides text boxes for default e-mail suffixes for 1779: # different authentication types in a domain. 1780: # 1781: # &row_title() and &row_closure() are called internally by the &*_select_row 1782: # routines, but can also be called directly to start and end rows which have 1783: # needs that are not accommodated by the *_select_row() routines. 1784: 1785: { # Start: row_count block for pick_box 1786: my @row_count; 1787: 1788: sub start_pick_box { 1789: my ($css_class) = @_; 1790: if (defined($css_class)) { 1791: $css_class = 'class="'.$css_class.'"'; 1792: } else { 1793: $css_class= 'class="LC_pick_box"'; 1794: } 1795: unshift(@row_count,0); 1796: my $output = <<"END"; 1797: <table $css_class> 1798: END 1799: return $output; 1800: } 1801: 1802: sub end_pick_box { 1803: shift(@row_count); 1804: my $output = <<"END"; 1805: </table> 1806: END 1807: return $output; 1808: } 1809: 1810: sub row_headline { 1811: my $output = <<"END"; 1812: <tr><td colspan="2"> 1813: END 1814: return $output; 1815: } 1816: 1817: sub row_title { 1818: my ($title,$css_title_class,$css_value_class, $css_value_furtherAttributes) = @_; 1819: $row_count[0]++; 1820: my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row'; 1821: $css_title_class ||= 'LC_pick_box_title'; 1822: $css_title_class = 'class="'.$css_title_class.'"'; 1823: 1824: $css_value_class ||= 'LC_pick_box_value'; 1825: 1826: if ($title ne '') { 1827: $title .= ':'; 1828: } 1829: my $output = <<"ENDONE"; 1830: <tr class="LC_pick_box_row" $css_value_furtherAttributes> 1831: <td $css_title_class> 1832: $title 1833: </td> 1834: <td class="$css_value_class $css_class"> 1835: ENDONE 1836: return $output; 1837: } 1838: 1839: sub row_closure { 1840: my ($no_separator) =@_; 1841: my $output = <<"ENDTWO"; 1842: </td> 1843: </tr> 1844: ENDTWO 1845: if (!$no_separator) { 1846: $output .= <<"ENDTWO"; 1847: <tr> 1848: <td colspan="2" class="LC_pick_box_separator"> 1849: </td> 1850: </tr> 1851: ENDTWO 1852: } 1853: return $output; 1854: } 1855: 1856: } # End: row_count block for pick_box 1857: 1858: sub role_select_row { 1859: my ($roles,$title,$css_class,$show_separate_custom,$cdom,$cnum) = @_; 1860: my $crstype = 'Course'; 1861: if ($cdom ne '' && $cnum ne '') { 1862: $crstype = &Apache::loncommon::course_type($cdom.'_'.$cnum); 1863: } 1864: my $output; 1865: if (defined($title)) { 1866: $output = &row_title($title,$css_class); 1867: } 1868: $output .= qq| 1869: <select name="roles" multiple="multiple">\n|; 1870: foreach my $role (@$roles) { 1871: my $plrole; 1872: if ($role eq 'ow') { 1873: $plrole = &mt('Course Owner'); 1874: } elsif ($role eq 'cr') { 1875: if ($show_separate_custom) { 1876: if ($cdom ne '' && $cnum ne '') { 1877: my %course_customroles = &course_custom_roles($cdom,$cnum); 1878: foreach my $crrole (sort(keys(%course_customroles))) { 1879: my ($plcrrole) = ($crrole =~ m|^cr/[^/]+/[^/]+/(.+)$|); 1880: $output .= ' <option value="'.$crrole.'">'.$plcrrole. 1881: '</option>'; 1882: } 1883: } 1884: } else { 1885: $plrole = &mt('Custom Role'); 1886: } 1887: } else { 1888: $plrole=&Apache::lonnet::plaintext($role,$crstype); 1889: } 1890: if (($role ne 'cr') || (!$show_separate_custom)) { 1891: $output .= ' <option value="'.$role.'">'.$plrole.'</option>'; 1892: } 1893: } 1894: $output .= qq| </select>\n|; 1895: if (defined($title)) { 1896: $output .= &row_closure(); 1897: } 1898: return $output; 1899: } 1900: 1901: sub course_select_row { 1902: my ($title,$formname,$totcodes,$codetitles,$idlist,$idlist_titles, 1903: $css_class,$crstype,$standardnames) = @_; 1904: my $output = &row_title($title,$css_class); 1905: $output .= &course_selection($formname,$totcodes,$codetitles,$idlist,$idlist_titles,$crstype,$standardnames); 1906: $output .= &row_closure(); 1907: return $output; 1908: } 1909: 1910: sub course_selection { 1911: my ($formname,$totcodes,$codetitles,$idlist,$idlist_titles,$crstype,$standardnames) = @_; 1912: my $output = qq| 1913: <script type="text/javascript"> 1914: // <![CDATA[ 1915: function coursePick (formname) { 1916: for (var i=0; i<formname.coursepick.length; i++) { 1917: if (formname.coursepick[i].value == 'category') { 1918: courseSet(''); 1919: } 1920: if (!formname.coursepick[i].checked) { 1921: if (formname.coursepick[i].value == 'specific') { 1922: formname.coursetotal.value = 0; 1923: formname.courselist = ''; 1924: } 1925: } 1926: } 1927: } 1928: function setPick (formname) { 1929: for (var i=0; i<formname.coursepick.length; i++) { 1930: if (formname.coursepick[i].value == 'category') { 1931: formname.coursepick[i].checked = true; 1932: } 1933: formname.coursetotal.value = 0; 1934: formname.courselist = ''; 1935: } 1936: } 1937: // ]]> 1938: </script> 1939: |; 1940: 1941: my ($allcrs,$pickspec); 1942: if ($crstype eq 'Community') { 1943: $allcrs = &mt('All communities'); 1944: $pickspec = &mt('Pick specific communities:'); 1945: } else { 1946: $allcrs = &mt('All courses'); 1947: $pickspec = &mt('Pick specific course(s):'); 1948: } 1949: 1950: my $courseform='<b>'.&Apache::loncommon::selectcourse_link 1951: ($formname,'pickcourse','pickdomain','coursedesc','',1,$crstype).'</b>'; 1952: $output .= '<input type="radio" name="coursepick" value="all" onclick="coursePick(this.form)" />'.$allcrs.'<br />'; 1953: if ($totcodes > 0) { 1954: my $numtitles = @$codetitles; 1955: if ($numtitles > 0) { 1956: $output .= '<input type="radio" name="coursepick" value="category" onclick="coursePick(this.form);alert('."'".&mt('Choose categories, from left to right')."'".')" />'.&mt('Pick courses by category:').' <br />'; 1957: $output .= '<table><tr><td>'.$$codetitles[0].'<br />'."\n". 1958: '<select name="'.$standardnames->[0]. 1959: '" onChange="setPick(this.form);courseSet('."'$$codetitles[0]'".')">'."\n". 1960: ' <option value="-1" />Select'."\n"; 1961: my @items = (); 1962: my @longitems = (); 1963: if ($$idlist{$$codetitles[0]} =~ /","/) { 1964: @items = split(/","/,$$idlist{$$codetitles[0]}); 1965: } else { 1966: $items[0] = $$idlist{$$codetitles[0]}; 1967: } 1968: if (defined($$idlist_titles{$$codetitles[0]})) { 1969: if ($$idlist_titles{$$codetitles[0]} =~ /","/) { 1970: @longitems = split(/","/,$$idlist_titles{$$codetitles[0]}); 1971: } else { 1972: $longitems[0] = $$idlist_titles{$$codetitles[0]}; 1973: } 1974: for (my $i=0; $i<@longitems; $i++) { 1975: if ($longitems[$i] eq '') { 1976: $longitems[$i] = $items[$i]; 1977: } 1978: } 1979: } else { 1980: @longitems = @items; 1981: } 1982: for (my $i=0; $i<@items; $i++) { 1983: $output .= ' <option value="'.$items[$i].'">'.$longitems[$i].'</option>'; 1984: } 1985: $output .= '</select></td>'; 1986: for (my $i=1; $i<$numtitles; $i++) { 1987: $output .= '<td>'.$$codetitles[$i].'<br />'."\n". 1988: '<select name="'.$standardnames->[$i]. 1989: '" onChange="courseSet('."'$$codetitles[$i]'".')">'."\n". 1990: '<option value="-1"><-Pick '.$$codetitles[$i-1].'</option>'."\n". 1991: '</select>'."\n". 1992: '</td>'; 1993: } 1994: $output .= '</tr></table><br />'; 1995: } 1996: } 1997: $output .= '<input type="radio" name="coursepick" value="specific" onclick="coursePick(this.form);opencrsbrowser('."'".$formname."','dccourse','dcdomain','coursedesc','','1','$crstype'".')" />'.$pickspec.' '.$courseform.' <input type="text" value="0" size="4" name="coursetotal" /><input type="hidden" name="courselist" value="" />selected.<br />'."\n"; 1998: return $output; 1999: } 2000: 2001: sub status_select_row { 2002: my ($types,$title,$css_class) = @_; 2003: my $output; 2004: if (defined($title)) { 2005: $output = &row_title($title,$css_class,'LC_pick_box_select'); 2006: } 2007: $output .= qq| 2008: <select name="types" multiple="multiple">\n|; 2009: foreach my $status_type (sort(keys(%{$types}))) { 2010: $output .= ' <option value="'.$status_type.'">'.$$types{$status_type}.'</option>'; 2011: } 2012: $output .= qq| </select>\n|; 2013: if (defined($title)) { 2014: $output .= &row_closure(); 2015: } 2016: return $output; 2017: } 2018: 2019: sub email_default_row { 2020: my ($authtypes,$title,$descrip,$css_class) = @_; 2021: my $output = &row_title($title,$css_class); 2022: $output .= $descrip. 2023: &Apache::loncommon::start_data_table(). 2024: &Apache::loncommon::start_data_table_header_row(). 2025: '<th>'.&mt('Authentication Method').'</th>'. 2026: '<th align="right">'.&mt('Username -> e-mail conversion').'</th>'."\n". 2027: &Apache::loncommon::end_data_table_header_row(); 2028: my $rownum = 0; 2029: foreach my $auth (sort(keys(%{$authtypes}))) { 2030: my ($userentry,$size); 2031: if ($auth =~ /^krb/) { 2032: $userentry = ''; 2033: $size = 25; 2034: } else { 2035: $userentry = 'username@'; 2036: $size = 15; 2037: } 2038: $output .= &Apache::loncommon::start_data_table_row(). 2039: '<td> '.$$authtypes{$auth}.'</td>'. 2040: '<td align="right">'.$userentry. 2041: '<input type="text" name="'.$auth.'" size="'.$size.'" /></td>'. 2042: &Apache::loncommon::end_data_table_row(); 2043: } 2044: $output .= &Apache::loncommon::end_data_table(); 2045: $output .= &row_closure(); 2046: return $output; 2047: } 2048: 2049: 2050: sub submit_row { 2051: my ($title,$cmd,$submit_text,$css_class) = @_; 2052: my $output = &row_title($title,$css_class,'LC_pick_box_submit'); 2053: $output .= qq| 2054: <br /> 2055: <input type="hidden" name="command" value="$cmd" /> 2056: <input type="submit" value="$submit_text"/> 2057: <br /><br /> 2058: \n|; 2059: return $output; 2060: } 2061: 2062: sub course_custom_roles { 2063: my ($cdom,$cnum) = @_; 2064: my %returnhash=(); 2065: my %coursepersonnel=&Apache::lonnet::dump('nohist_userroles',$cdom,$cnum); 2066: foreach my $person (sort(keys(%coursepersonnel))) { 2067: my ($role) = ($person =~ /^([^:]+):/); 2068: my ($end,$start) = split(/:/,$coursepersonnel{$person}); 2069: if ($end == -1 && $start == -1) { 2070: next; 2071: } 2072: if ($role =~ m|^cr/[^/]+/[^/]+/[^/]|) { 2073: $returnhash{$role} ++; 2074: } 2075: } 2076: return %returnhash; 2077: } 2078: 2079: 2080: sub resource_info_box { 2081: my ($symb,$onlyfolderflag)=@_; 2082: my $return=''; 2083: if ($symb) { 2084: $return=&Apache::loncommon::start_data_table(); 2085: my ($map,$id,$resource)=&Apache::lonnet::decode_symb($symb); 2086: my $folder=&Apache::lonnet::gettitle($map); 2087: $return.=&Apache::loncommon::start_data_table_row(). 2088: '<th>'.&mt('Folder:').'</th><td>'.$folder.'</td>'. 2089: &Apache::loncommon::end_data_table_row(); 2090: unless ($onlyfolderflag) { 2091: $return.=&Apache::loncommon::start_data_table_row(). 2092: '<th>'.&mt('Resource:').'</th><td>'.&Apache::lonnet::gettitle($symb).'</td>'. 2093: &Apache::loncommon::end_data_table_row(); 2094: } 2095: $return.=&Apache::loncommon::end_data_table(); 2096: } else { 2097: $return='<p><span class="LC_error">'.&mt('No context provided.').'</span></p>'; 2098: } 2099: return $return; 2100: 2101: } 2102: 2103: ############################################## 2104: ############################################## 2105: 2106: # topic_bar 2107: # 2108: # Generates a div containing an (optional) number with a white background followed by a 2109: # title with a background color defined in the corresponding CSS: LC_topic_bar 2110: # Inputs: 2111: # 1. number to display. 2112: # If input for number is empty only the title will be displayed. 2113: # 2. title text to display. 2114: # Outputs - a scalar containing html mark-up for the div. 2115: 2116: sub topic_bar { 2117: my ($num,$title) = @_; 2118: my $number = ''; 2119: if ($num ne '') { 2120: $number = '<span>'.$num.'</span>'; 2121: } 2122: return '<div class="LC_topic_bar">'.$number.$title.'</div>'; 2123: } 2124: 2125: ############################################## 2126: ############################################## 2127: # echo_form_input 2128: # 2129: # Generates html markup to add form elements from the referrer page 2130: # as hidden form elements (values encoded) in the new page. 2131: # 2132: # Intended to support two types of use 2133: # (a) to allow backing up to earlier pages in a multi-page 2134: # form submission process using a breadcrumb trail. 2135: # 2136: # (b) to allow the current page to be reloaded with form elements 2137: # set on previous page to remain unchanged. An example would 2138: # be where the a page containing a dynamically-built table of data is 2139: # is to be redisplayed, with only the sort order of the data changed. 2140: # 2141: # Inputs: 2142: # 1. Reference to array of form elements in the submitted form on 2143: # the referrer page which are to be excluded from the echoed elements. 2144: # 2145: # 2. Reference to array of regular expressions, which if matched in the 2146: # name of the form element n the referrer page will be omitted from echo. 2147: # 2148: # Outputs: A scalar containing the html markup for the echoed form 2149: # elements (all as hidden elements, with values encoded). 2150: 2151: 2152: sub echo_form_input { 2153: my ($excluded,$regexps) = @_; 2154: my $output = ''; 2155: foreach my $key (keys(%env)) { 2156: if ($key =~ /^form\.(.+)$/) { 2157: my $name = $1; 2158: my $match = 0; 2159: if (ref($excluded) eq 'ARRAY') { 2160: next if (grep(/^\Q$name\E$/,@{$excluded})); 2161: } 2162: if (ref($regexps) eq 'ARRAY') { 2163: if (@{$regexps} > 0) { 2164: foreach my $regexp (@{$regexps}) { 2165: if ($name =~ /$regexp/) { 2166: $match = 1; 2167: last; 2168: } 2169: } 2170: } 2171: } 2172: next if ($match); 2173: if (ref($env{$key}) eq 'ARRAY') { 2174: foreach my $value (@{$env{$key}}) { 2175: $value = &HTML::Entities::encode($value,'<>&"'); 2176: $output .= '<input type="hidden" name="'.$name. 2177: '" value="'.$value.'" />'."\n"; 2178: } 2179: } else { 2180: my $value = &HTML::Entities::encode($env{$key},'<>&"'); 2181: $output .= '<input type="hidden" name="'.$name. 2182: '" value="'.$value.'" />'."\n"; 2183: } 2184: } 2185: } 2186: return $output; 2187: } 2188: 2189: ############################################## 2190: ############################################## 2191: # set_form_elements 2192: # 2193: # Generates javascript to set form elements to values based on 2194: # corresponding values for the same form elements when the page was 2195: # previously submitted. 2196: # 2197: # Last submission values are read from hidden form elements in referring 2198: # page which have the same name, i.e., generated by &echo_form_input(). 2199: # 2200: # Intended to be called by onload event. 2201: # 2202: # Inputs: 2203: # (a) Reference to hash of echoed form elements to be set. 2204: # 2205: # In the hash, keys are the form element names, and the values are the 2206: # element type (selectbox, radio, checkbox or text -for textbox, textarea or 2207: # hidden). 2208: # 2209: # (b) Optional reference to hash of stored elements to be set. 2210: # 2211: # If the page being displayed is a page which permits modification of 2212: # previously stored data, e.g., the first page in a multi-page submission, 2213: # then if stored is supplied, form elements will be set to the last stored 2214: # values. If user supplied values are also available for the same elements 2215: # these will replace the stored values. 2216: # 2217: # Output: 2218: # 2219: # javascript function - set_form_elements() which sets form elements, 2220: # expects an argument: formname - the name of the form according to 2221: # the DOM, e.g., document.compose 2222: 2223: sub set_form_elements { 2224: my ($elements,$stored) = @_; 2225: my %values; 2226: my $output .= 'function setFormElements(courseForm) { 2227: '; 2228: if (defined($stored)) { 2229: foreach my $name (keys(%{$stored})) { 2230: if (exists($$elements{$name})) { 2231: if (ref($$stored{$name}) eq 'ARRAY') { 2232: $values{$name} = $$stored{$name}; 2233: } else { 2234: @{$values{$name}} = ($$stored{$name}); 2235: } 2236: } 2237: } 2238: } 2239: 2240: foreach my $key (keys(%env)) { 2241: if ($key =~ /^form\.(.+)$/) { 2242: my $name = $1; 2243: if (exists($$elements{$name})) { 2244: @{$values{$name}} = &Apache::loncommon::get_env_multiple($key); 2245: } 2246: } 2247: } 2248: 2249: foreach my $name (keys(%values)) { 2250: for (my $i=0; $i<@{$values{$name}}; $i++) { 2251: $values{$name}[$i] = &HTML::Entities::decode($values{$name}[$i],'<>&"'); 2252: $values{$name}[$i] =~ s/([\r\n\f]+)/\\n/g; 2253: $values{$name}[$i] =~ s/"/\\"/g; 2254: } 2255: if (($$elements{$name} eq 'text') || ($$elements{$name} eq 'hidden')) { 2256: my $numvalues = @{$values{$name}}; 2257: if ($numvalues > 1) { 2258: my $valuestring = join('","',@{$values{$name}}); 2259: $output .= qq| 2260: var textvalues = new Array ("$valuestring"); 2261: var total = courseForm.elements['$name'].length; 2262: if (total > $numvalues) { 2263: total = $numvalues; 2264: } 2265: for (var i=0; i<total; i++) { 2266: courseForm.elements['$name']\[i].value = textvalues[i]; 2267: } 2268: |; 2269: } else { 2270: $output .= qq| 2271: courseForm.elements['$name'].value = "$values{$name}[0]"; 2272: |; 2273: } 2274: } else { 2275: $output .= qq| 2276: var elementLength = courseForm.elements['$name'].length; 2277: if (elementLength==undefined) { 2278: |; 2279: foreach my $value (@{$values{$name}}) { 2280: if ($$elements{$name} eq 'selectbox') { 2281: $output .= qq| 2282: if (courseForm.elements['$name'].options[0].value == "$value") { 2283: courseForm.elements['$name'].options[0].selected = true; 2284: }|; 2285: } elsif (($$elements{$name} eq 'radio') || 2286: ($$elements{$name} eq 'checkbox')) { 2287: $output .= qq| 2288: if (courseForm.elements['$name'].value == "$value") { 2289: courseForm.elements['$name'].checked = true; 2290: } else { 2291: courseForm.elements['$name'].checked = false; 2292: }|; 2293: } 2294: } 2295: $output .= qq| 2296: } 2297: else { 2298: for (var i=0; i<courseForm.elements['$name'].length; i++) { 2299: |; 2300: if ($$elements{$name} eq 'selectbox') { 2301: $output .= qq| 2302: courseForm.elements['$name'].options[i].selected = false;|; 2303: } elsif (($$elements{$name} eq 'radio') || 2304: ($$elements{$name} eq 'checkbox')) { 2305: $output .= qq| 2306: courseForm.elements['$name']\[i].checked = false;|; 2307: } 2308: $output .= qq| 2309: } 2310: for (var j=0; j<courseForm.elements['$name'].length; j++) { 2311: |; 2312: foreach my $value (@{$values{$name}}) { 2313: if ($$elements{$name} eq 'selectbox') { 2314: $output .= qq| 2315: if (courseForm.elements['$name'].options[j].value == "$value") { 2316: courseForm.elements['$name'].options[j].selected = true; 2317: }|; 2318: } elsif (($$elements{$name} eq 'radio') || 2319: ($$elements{$name} eq 'checkbox')) { 2320: $output .= qq| 2321: if (courseForm.elements['$name']\[j].value == "$value") { 2322: courseForm.elements['$name']\[j].checked = true; 2323: }|; 2324: } 2325: } 2326: $output .= qq| 2327: } 2328: } 2329: |; 2330: } 2331: } 2332: $output .= " 2333: return; 2334: }\n"; 2335: return $output; 2336: } 2337: 2338: ############################################## 2339: ############################################## 2340: 2341: # javascript_valid_email 2342: # 2343: # Generates javascript to validate an e-mail address. 2344: # Returns a javascript function which accetps a form field as argumnent, and 2345: # returns false if field.value does not satisfy two regular expression matches 2346: # for a valid e-mail address. Backwards compatible with old browsers without 2347: # support for javascript RegExp (just checks for @ in field.value in this case). 2348: 2349: sub javascript_valid_email { 2350: my $scripttag .= <<'END'; 2351: function validmail(field) { 2352: var str = field.value; 2353: if (window.RegExp) { 2354: var reg1str = "(@.*@)|(\\.\\.)|(@\\.)|(\\.@)|(^\\.)"; 2355: var reg2str = "^.+\\@(\\[?)[a-zA-Z0-9\\-\\.]+\\.([a-zA-Z]{2,3}|[0-9]{1,3})(\\]?)$"; //" 2356: var reg1 = new RegExp(reg1str); 2357: var reg2 = new RegExp(reg2str); 2358: if (!reg1.test(str) && reg2.test(str)) { 2359: return true; 2360: } 2361: return false; 2362: } 2363: else 2364: { 2365: if(str.indexOf("@") >= 0) { 2366: return true; 2367: } 2368: return false; 2369: } 2370: } 2371: END 2372: return $scripttag; 2373: } 2374: 2375: 2376: # USAGE: htmltag(element, content, {attribute => value,...}); 2377: # 2378: # EXAMPLES: 2379: # - htmltag('a', 'this is an anchor', {href => 'www.example.com', 2380: # title => 'this is a title'}) 2381: # 2382: # - You might want to set up needed tags like: 2383: # 2384: # my $h3 = sub { return htmltag( "h3", @_ ) }; 2385: # 2386: # ... and use them: $h3->("This is a headline") 2387: # 2388: # - To set up a couple of tags, see sub inittags 2389: # 2390: # NOTES: 2391: # - Empty elements, such as <br/> are correctly terminated, 2392: # i.e. htmltag('br') returns <br/> 2393: # - Empty attributes (title="") are filtered out. 2394: # - The function will not check for deprecated attributes. 2395: # 2396: # OUTPUT: content enclosed in xhtml conform tags 2397: sub htmltag{ 2398: return 2399: qq|<$_[0]| 2400: . join( '', map { qq| $_="${$_[2]}{$_}"| if ${$_[2]}{$_} } keys %{ $_[2] } ) 2401: . ($_[1] ? qq|>$_[1]</$_[0]>| : qq|/>|). "\n"; 2402: }; 2403: 2404: 2405: # USAGE: inittags(@tags); 2406: # 2407: # EXAMPLES: 2408: # - my ($h1, $h2, $h3) = inittags( qw( h1 h2 h3 ) ) 2409: # $h1->("This is a headline") #Returns: <h1>This is a headline</h1> 2410: # 2411: # NOTES: See sub htmltag for further information. 2412: # 2413: # OUTPUT: List of subroutines. 2414: sub inittags { 2415: my @tags = @_; 2416: return map { my $tag = $_; 2417: sub { return htmltag( $tag, @_ ) } 2418: } @tags; 2419: } 2420: 2421: 2422: # USAGE: scripttag(scriptcode, [start|end|both]); 2423: # 2424: # EXAMPLES: 2425: # - scripttag("alert('Hello World!')", 'both') 2426: # returns: 2427: # <script type="text/javascript"> 2428: # // BEGIN LON-CAPA Internal 2429: # alert(Hello World!') 2430: # // END LON-CAPA Internal 2431: # </script> 2432: # 2433: # NOTES: 2434: # - works currently only for javascripts 2435: # 2436: # OUTPUT: 2437: # Scriptcode properly enclosed in <script> and CDATA tags (and LC 2438: # Internal markers if 2nd argument is given) 2439: sub scripttag { 2440: my ( $content, $marker ) = @_; 2441: return unless defined $content; 2442: 2443: my $begin = "\n// BEGIN LON-CAPA Internal\n"; 2444: my $end = "\n// END LON-CAPA Internal\n"; 2445: 2446: if ($marker) { 2447: $content = $begin . $content if $marker eq 'start' or $marker eq 'both'; 2448: $content .= $end if $marker eq 'end' or $marker eq 'both'; 2449: } 2450: 2451: $content = "\n// <![CDATA[\n$content\n// ]]>\n"; 2452: 2453: return htmltag('script', $content, {type => 'text/javascript'}); 2454: }; 2455: 2456: 2457: =item list_from_array( \@array, { listattr =>{}, itemattr =>{} } ) 2458: 2459: Constructs a XHTML list from \@array. 2460: 2461: input: 2462: 2463: =over 2464: 2465: =item \@array 2466: 2467: A reference to the array containing text that will be wrapped in <li></li> tags. 2468: 2469: =item { listattr => {}, itemattr =>{} } 2470: 2471: Attributes for <ul> and <li> passed in as hash references. 2472: See htmltag() for more details. 2473: 2474: =back 2475: 2476: returns: XHTML list as String. 2477: 2478: =cut 2479: 2480: # \@items, {listattr => { class => 'abc', id => 'xyx' }, itemattr => {class => 'abc', id => 'xyx'}} 2481: sub list_from_array { 2482: my ($items, $args) = @_; 2483: return unless (ref($items) eq 'ARRAY'); 2484: return unless scalar @$items; 2485: my ($ul, $li) = inittags( qw(ul li) ); 2486: my $listitems = join '', map { $li->($_, $args->{itemattr}) } @$items; 2487: return $ul->( $listitems, $args->{listattr} ); 2488: } 2489: 2490: 2491: ############################################## 2492: ############################################## 2493: 2494: # generate_menu 2495: # 2496: # Generates html markup for a menu. 2497: # 2498: # Inputs: 2499: # An array of following structure: 2500: # ({ categorytitle => 'Categorytitle', 2501: # items => [ 2502: # { 2503: # linktext => 'Text to be displayed', 2504: # url => 'URL the link is pointing to, i.e. /adm/site?action=dosomething', 2505: # permission => 'Contains permissions as returned from lonnet::allowed(), 2506: # must evaluate to true in order to activate the link', 2507: # icon => 'icon filename', 2508: # alttext => 'alt text for the icon', 2509: # help => 'Name of the corresponding helpfile', 2510: # linktitle => 'Description of the link (used for title tag)' 2511: # }, 2512: # ... 2513: # ] 2514: # }, 2515: # ... 2516: # ) 2517: # 2518: # Outputs: A scalar containing the html markup for the menu. 2519: 2520: sub generate_menu { 2521: my @menu = @_; 2522: # subs for specific html elements 2523: my ($h3, $div, $ul, $li, $a, $img) = inittags( qw(h3 div ul li a img) ); 2524: 2525: my @categories; # each element represents the entire markup for a category 2526: 2527: foreach my $category (@menu) { 2528: my @links; # contains the links for the current $category 2529: foreach my $link (@{$$category{items}}) { 2530: next unless $$link{permission}; 2531: 2532: # create the markup for the current $link and push it into @links. 2533: # each entry consists of an image and a text optionally followed 2534: # by a help link. 2535: my $src; 2536: if ($$link{icon} ne '') { 2537: $src = '/res/adm/pages/'.$$link{icon}; 2538: } 2539: push(@links,$li->( 2540: $a->( 2541: $img->("", { 2542: class => "LC_noBorder LC_middle", 2543: src => $src, 2544: alt => mt(defined($$link{alttext}) ? 2545: $$link{alttext} : $$link{linktext}) 2546: }), { 2547: href => $$link{url}, 2548: title => mt($$link{linktitle}) 2549: }). 2550: $a->(mt($$link{linktext}), { 2551: href => $$link{url}, 2552: title => mt($$link{linktitle}), 2553: class => "LC_menubuttons_link" 2554: }). 2555: (defined($$link{help}) ? 2556: Apache::loncommon::help_open_topic($$link{help}) : ''), 2557: {class => "LC_menubuttons_inline_text"})); 2558: } 2559: 2560: # wrap categorytitle in <h3>, concatenate with 2561: # joined and in <ul> tags wrapped @links 2562: # and wrap everything in an enclosing <div> and push it into 2563: # @categories 2564: # such that each element looks like: 2565: # <div><h3>title</h3><ul><li>...</li>...</ul></div> 2566: # the category won't be added if there aren't any links 2567: push(@categories, 2568: $div->($h3->(mt($$category{categorytitle}), {class=>"LC_hcell"}). 2569: $ul->(join('' ,@links), {class =>"LC_ListStyleNormal" }), 2570: {class=>"LC_Box LC_400Box"})) if scalar(@links); 2571: } 2572: 2573: # wrap the joined @categories in another <div> (column layout) 2574: return $div->(join('', @categories), {class => "LC_columnSection"}); 2575: } 2576: 2577: ############################################## 2578: ############################################## 2579: 2580: =pod 2581: 2582: =item &start_funclist 2583: 2584: Start list of available functions 2585: 2586: Typically used to offer a simple list of available functions 2587: at top or bottom of page. 2588: All available functions/actions for the current page 2589: should be included in this list. 2590: 2591: If the optional headline text is not provided, a default text will be used. 2592: 2593: 2594: Related routines: 2595: =over 4 2596: add_item_funclist 2597: end_funclist 2598: =back 2599: 2600: 2601: Inputs: (optional) headline text 2602: 2603: Returns: HTML code with function list start 2604: 2605: =cut 2606: 2607: ############################################## 2608: ############################################## 2609: 2610: sub start_funclist { 2611: my($legendtext)=@_; 2612: $legendtext=&mt('Functions') if !$legendtext; 2613: return '<ul class="LC_funclist"><li style="font-weight:bold; margin-left:0.8em;">'.$legendtext.'</li>'."\n"; 2614: } 2615: 2616: 2617: ############################################## 2618: ############################################## 2619: 2620: =pod 2621: 2622: =item &add_item_funclist 2623: 2624: Adds an item to the list of available functions 2625: 2626: Related routines: 2627: =over 4 2628: start_funclist 2629: end_funclist 2630: =back 2631: 2632: Inputs: content item with text and link to function 2633: 2634: Returns: HTML code with list item for funclist 2635: 2636: =cut 2637: 2638: ############################################## 2639: ############################################## 2640: 2641: sub add_item_funclist { 2642: my($content) = @_; 2643: return '<li>'.$content.'</li>'."\n"; 2644: } 2645: 2646: =pod 2647: 2648: =item &end_funclist 2649: 2650: End list of available functions 2651: 2652: Related routines: 2653: =over 4 2654: start_funclist 2655: add_item_funclist 2656: =back 2657: 2658: Inputs: ./. 2659: 2660: Returns: HTML code with function list end 2661: =cut 2662: 2663: sub end_funclist { 2664: return "</ul>\n"; 2665: } 2666: 2667: =pod 2668: 2669: =item funclist_from_array( \@array, {legend => 'text for legend'} ) 2670: 2671: Constructs a XHTML list from \@array with the first item being visually 2672: highlighted and set to the value of legend or 'Functions' if legend is 2673: empty. 2674: 2675: =over 2676: 2677: =item \@array 2678: 2679: A reference to the array containing text that will be wrapped in <li></li> tags. 2680: 2681: =item { legend => 'text' } 2682: 2683: A string that's used as visually highlighted first item. 'Functions' is used if 2684: it's value evaluates to false. 2685: 2686: =back 2687: 2688: returns: XHTML list as string. 2689: 2690: =back 2691: 2692: =cut 2693: 2694: sub funclist_from_array { 2695: my ($items, $args) = @_; 2696: return unless(ref($items) eq 'ARRAY'); 2697: $args->{legend} ||= mt('Functions'); 2698: return list_from_array( [$args->{legend}, @$items], 2699: { listattr => {class => 'LC_funclist'} }); 2700: } 2701: 2702: 1; 2703: 2704: __END__