![]() ![]() | ![]() |
- For 2.11 Backport 1.363 and 1.362 (part).
1: # The LearningOnline Network with CAPA 2: # a pile of common html routines 3: # 4: # $Id: lonhtmlcommon.pm,v 1.358.2.1 2015/05/11 17:18:36 raeburn 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 qw(:DEFAULT :match); 65: 66: sub java_not_enabled { 67: if (($env{'browser.mobile'}) && ($env{'browser.mobile'} =~ /^ipad|ipod|iphone$/i)) { 68: return "\n".'<span class="LC_error">'. 69: &mt('The required Java applet could not be started, because Java is not supported by your mobile device.'). 70: "</span>\n"; 71: } else { 72: return "\n".'<span class="LC_error">'. 73: &mt('The required Java applet could not be started. Please make sure to have Java installed and active in your browser.'). 74: "</span>\n"; 75: } 76: } 77: 78: sub coursepreflink { 79: my ($text,$category)=@_; 80: if (&Apache::lonnet::allowed('opa',$env{'request.course.id'})) { 81: return '<a target="_top" href="'.&HTML::Entities::encode("/adm/courseprefs?phase=display&actions=$category",'<>&"').'"><span class="LC_setting">'.$text.'</span></a>'; 82: } else { 83: return ''; 84: } 85: } 86: 87: sub raw_href_to_link { 88: my ($message)=@_; 89: $message=~s/(https?\:\/\/[^\s\'\"\<]+)([\s\<]|$)/<a href="$1"><tt>$1<\/tt><\/a>$2/gi; 90: return $message; 91: } 92: 93: sub entity_encode { 94: my ($text)=@_; 95: return &HTML::Entities::encode($text, '<>&"'); 96: } 97: 98: sub direct_parm_link { 99: my ($linktext,$symb,$filter,$part,$target)=@_; 100: $symb=&entity_encode($symb); 101: $filter=&entity_encode($filter); 102: $part=&entity_encode($part); 103: if (($symb) && (&Apache::lonnet::allowed('opa')) && ($target ne 'tex')) { 104: return "<a target='_top' href='/adm/parmset?symb=$symb&filter=$filter&part=$part'><span class='LC_setting'>$linktext</span></a>"; 105: } else { 106: return $linktext; 107: } 108: } 109: ############################################## 110: ############################################## 111: 112: =item &confirm_success() 113: 114: Successful completion of an operation message 115: 116: =cut 117: 118: sub confirm_success { 119: my ($message,$failure)=@_; 120: if ($failure) { 121: return '<span class="LC_error" style="font-size: inherit;">'."\n" 122: .'<img src="/adm/lonIcons/navmap.wrong.gif" alt="'.&mt('Error').'" /> '."\n" 123: .$message."\n" 124: .'</span>'."\n"; 125: } else { 126: return '<span class="LC_success">'."\n" 127: .'<img src="/adm/lonIcons/navmap.correct.gif" alt="'.&mt('OK').'" /> '."\n" 128: .$message."\n" 129: .'</span>'."\n"; 130: } 131: } 132: 133: ############################################## 134: ############################################## 135: 136: =pod 137: 138: =item &dragmath_button() 139: 140: Creates a button that launches a dragmath popup-window, in which an 141: expression can be edited and pasted as LaTeX into a specified textarea. 142: 143: textarea - Name of the textarea to edit. 144: helpicon - If true, show a help icon to the right of the button. 145: 146: =cut 147: 148: sub dragmath_button { 149: my ($textarea,$helpicon) = @_; 150: my $help_text; 151: if ($helpicon) { 152: $help_text = &Apache::loncommon::help_open_topic('Authoring_Math_Editor',undef,undef,undef,undef,'mathhelpicon_'.$textarea); 153: } 154: my $buttontext=&mt('Edit Math'); 155: return <<ENDDRAGMATH; 156: <input type="button" value="$buttontext" onclick="javascript:mathedit('$textarea',document)" />$help_text 157: ENDDRAGMATH 158: } 159: 160: ############################################## 161: 162: =pod 163: 164: =item &dragmath_js() 165: 166: Javascript used to open pop-up window containing dragmath applet which 167: can be used to paste LaTeX into a textarea. 168: 169: =cut 170: 171: sub dragmath_js { 172: my ($popup) = @_; 173: return <<ENDDRAGMATHJS; 174: <script type="text/javascript"> 175: // <![CDATA[ 176: function mathedit(textarea, doc) { 177: targetEntry = textarea; 178: targetDoc = doc; 179: newwin = window.open("/adm/dragmath/$popup.html","","width=565,height=500,resizable"); 180: } 181: // ]]> 182: </script> 183: 184: ENDDRAGMATHJS 185: } 186: 187: ############################################## 188: ############################################## 189: 190: =pod 191: 192: =item &dependencies_button() 193: 194: Creates a button that launches a popup-window, in which dependencies 195: for the web page in the main window can be added to, replaced or deleted. 196: 197: =cut 198: 199: sub dependencies_button { 200: my $buttontext=&mt('Manage Dependencies'); 201: return <<"END"; 202: <input type="button" value="$buttontext" onclick="javascript:dependencycheck();" /> 203: END 204: } 205: 206: ############################################## 207: 208: =pod 209: 210: =item &dependencycheck_js() 211: 212: Javascript used to open pop-up window containing interface to manage 213: dependencies for a web page uploaded diretcly to a course. 214: 215: =cut 216: 217: sub dependencycheck_js { 218: my ($symb,$title,$url,$folderpath,$uri) = @_; 219: my $link; 220: if ($symb) { 221: $link = '/adm/dependencies?symb='.&HTML::Entities::encode($symb,'<>&"'); 222: } elsif ($folderpath) { 223: $link = '/adm/dependencies?folderpath='.&HTML::Entities::encode($folderpath,'<>&"'); 224: $url = $uri; 225: } elsif ($uri =~ m{^/public/$match_domain/$match_courseid/syllabus$}) { 226: $link = '/adm/dependencies'; 227: } 228: $link .= (($link=~/\?/)?'&':'?').'title='. 229: &HTML::Entities::encode($title,'<>&"'); 230: if ($url) { 231: $link .= '&url='.&HTML::Entities::encode($url,'<>&"'); 232: } 233: return <<ENDJS; 234: <script type="text/javascript"> 235: // <![CDATA[ 236: function dependencycheck() { 237: depwin = window.open("$link","","width=750,height=500,resizable,scrollbars=yes"); 238: } 239: // ]]> 240: </script> 241: ENDJS 242: } 243: 244: ############################################## 245: ############################################## 246: 247: =pod 248: 249: =item &authorbombs() 250: 251: =cut 252: 253: ############################################## 254: ############################################## 255: 256: sub authorbombs { 257: my $url=shift; 258: $url=&Apache::lonnet::declutter($url); 259: my ($udom,$uname)=($url=~m{^($LONCAPA::domain_re)/($LONCAPA::username_re)/}); 260: my %bombs=&Apache::lonmsg::all_url_author_res_msg($uname,$udom); 261: foreach my $bomb (keys(%bombs)) { 262: if ($bomb =~ /^$udom\/$uname\//) { 263: return '<a href="/adm/bombs/'.$url. 264: '"><img src="'.&Apache::loncommon::lonhttpdurl('/adm/lonMisc/bomb.gif').'" alt="'.&mt('Bomb').'" border="0" /></a>'. 265: &Apache::loncommon::help_open_topic('About_Bombs'); 266: } 267: } 268: return ''; 269: } 270: 271: ############################################## 272: ############################################## 273: 274: sub recent_filename { 275: my $area=shift; 276: return 'nohist_recent_'.&escape($area); 277: } 278: 279: sub store_recent { 280: my ($area,$name,$value,$freeze)=@_; 281: my $file=&recent_filename($area); 282: my %recent=&Apache::lonnet::dump($file); 283: if (scalar(keys(%recent))>20) { 284: # remove oldest value 285: my $oldest=time(); 286: my $delkey=''; 287: foreach my $item (keys(%recent)) { 288: my $thistime=(split(/\&/,$recent{$item}))[0]; 289: if (($thistime ne "always_include") && ($thistime<$oldest)) { 290: $oldest=$thistime; 291: $delkey=$item; 292: } 293: } 294: &Apache::lonnet::del($file,[$delkey]); 295: } 296: # store new value 297: my $timestamp; 298: if ($freeze) { 299: $timestamp = "always_include"; 300: } else { 301: $timestamp = time(); 302: } 303: &Apache::lonnet::put($file,{ $name => 304: $timestamp.'&'.&escape($value) }); 305: } 306: 307: sub remove_recent { 308: my ($area,$names)=@_; 309: my $file=&recent_filename($area); 310: return &Apache::lonnet::del($file,$names); 311: } 312: 313: sub select_recent { 314: my ($area,$fieldname,$event)=@_; 315: my %recent=&Apache::lonnet::dump(&recent_filename($area)); 316: my $return="\n<select name='$fieldname'". 317: ($event?" onchange='$event'":''). 318: ">\n<option value=''>--- ".&mt('Recent')." ---</option>"; 319: foreach my $value (sort(keys(%recent))) { 320: unless ($value =~/^error\:/) { 321: my $escaped = &Apache::loncommon::escape_url($value); 322: &Apache::loncommon::inhibit_menu_check(\$escaped); 323: if ($area eq 'residx') { 324: next if ((!&Apache::lonnet::allowed('bre',$value)) && (!&Apache::lonnet::allowed('bro',$value))); 325: } 326: $return.="\n<option value='$escaped'>". 327: &unescape((split(/\&/,$recent{$value}))[1]). 328: '</option>'; 329: } 330: } 331: $return.="\n</select>\n"; 332: return $return; 333: } 334: 335: sub get_recent { 336: my ($area, $n) = @_; 337: my %recent=&Apache::lonnet::dump(&recent_filename($area)); 338: 339: # Create hash with key as time and recent as value 340: # Begin filling return_hash with any 'always_include' option 341: my %time_hash = (); 342: my %return_hash = (); 343: foreach my $item (keys(%recent)) { 344: my ($thistime,$thisvalue)=(split(/\&/,$recent{$item})); 345: if ($thistime eq 'always_include') { 346: $return_hash{$item} = &unescape($thisvalue); 347: $n--; 348: } else { 349: $time_hash{$thistime} = $item; 350: } 351: } 352: 353: # Sort by decreasing time and return key value pairs 354: my $idx = 1; 355: foreach my $item (reverse(sort(keys(%time_hash)))) { 356: $return_hash{$time_hash{$item}} = 357: &unescape((split(/\&/,$recent{$time_hash{$item}}))[1]); 358: if ($n && ($idx++ >= $n)) {last;} 359: } 360: 361: return %return_hash; 362: } 363: 364: sub get_recent_frozen { 365: my ($area) = @_; 366: my %recent=&Apache::lonnet::dump(&recent_filename($area)); 367: 368: # Create hash with all 'frozen' items 369: my %return_hash = (); 370: foreach my $item (keys(%recent)) { 371: my ($thistime,$thisvalue)=(split(/\&/,$recent{$item})); 372: if ($thistime eq 'always_include') { 373: $return_hash{$item} = &unescape($thisvalue); 374: } 375: } 376: return %return_hash; 377: } 378: 379: 380: 381: =pod 382: 383: =item &textbox() 384: 385: =cut 386: 387: ############################################## 388: ############################################## 389: sub textbox { 390: my ($name,$value,$size,$special) = @_; 391: $size = 40 if (! defined($size)); 392: $value = &HTML::Entities::encode($value,'<>&"'); 393: my $Str = '<input type="text" name="'.$name.'" size="'.$size.'" '. 394: 'value="'.$value.'" '.$special.' />'; 395: return $Str; 396: } 397: 398: ############################################## 399: ############################################## 400: 401: =pod 402: 403: =item &checkbox() 404: 405: =cut 406: 407: ############################################## 408: ############################################## 409: sub checkbox { 410: my ($name,$checked,$value) = @_; 411: my $Str = '<input type="checkbox" name="'.$name.'" '; 412: if (defined($value)) { 413: $Str .= 'value="'.$value.'"'; 414: } 415: if ($checked) { 416: $Str .= ' checked="checked"'; 417: } 418: $Str .= ' />'; 419: return $Str; 420: } 421: 422: 423: =pod 424: 425: =item &radiobutton() 426: 427: =cut 428: 429: ############################################## 430: ############################################## 431: sub radio { 432: my ($name,$checked,$value) = @_; 433: my $Str = '<input type="radio" name="'.$name.'" '; 434: if (defined($value)) { 435: $Str .= 'value="'.$value.'"'; 436: } 437: if ($checked eq $value) { 438: $Str .= ' checked="checked"'; 439: } 440: $Str .= ' />'; 441: return $Str; 442: } 443: 444: ############################################## 445: ############################################## 446: 447: =pod 448: 449: =item &date_setter() 450: 451: &date_setter returns html and javascript for a compact date-setting form. 452: To retrieve values from it, use &get_date_from_form. 453: 454: Inputs 455: 456: =over 4 457: 458: =item $dname 459: 460: The name to prepend to the form elements. 461: The form elements defined will be dname_year, dname_month, dname_day, 462: dname_hour, dname_min, and dname_sec. 463: 464: =item $currentvalue 465: 466: The current setting for this time parameter. A unix format time 467: (time in seconds since the beginning of Jan 1st, 1970, GMT. 468: An undefined value is taken to indicate the value is the current time 469: unless it is requested to leave it empty. See $includeempty. 470: Also, to be explicit, a value of 'now' also indicates the current time. 471: 472: =item $special 473: 474: Additional html/javascript to be associated with each element in 475: the date_setter. See lonparmset for example usage. 476: 477: =item $includeempty 478: 479: If it is set (true) and no date/time value is provided, 480: the date/time fields are left empty. 481: 482: =item $state 483: 484: Specifies the initial state of the form elements. Either 'disabled' or empty. 485: Defaults to empty, which indicates the form elements are not disabled. 486: 487: =item $no_hh_mm_ss 488: 489: If true, text boxes for hours, minutes and seconds are omitted. 490: 491: =item $defhour 492: 493: Default value for hours (a default of 0 is used otherwise). 494: 495: =item $defmin 496: 497: Default value for minutes (a default of 0 is used otherwise). 498: 499: =item defsec 500: 501: Default value for seconds (a default of 0 is used otherwise). 502: 503: =item $nolink 504: 505: If true, a "Select calendar" link (to pop-up a calendar) is not displayed 506: to the right of the items. 507: 508: =item $no_mm_ss 509: 510: If true, text boxes for minutes and seconds are omitted. 511: 512: =item $no_ss 513: 514: If true, text boxes for seconds are omitted. 515: 516: =back 517: 518: Bugs 519: 520: The method used to restrict user input will fail in the year 2400. 521: 522: =cut 523: 524: ############################################## 525: ############################################## 526: sub date_setter { 527: my ($formname,$dname,$currentvalue,$special,$includeempty,$state, 528: $no_hh_mm_ss,$defhour,$defmin,$defsec,$nolink,$no_mm_ss,$no_ss) = @_; 529: my $now = time; 530: 531: my $tzname; 532: my ($sec,$min,$hour,$mday,$month,$year) = ('', '', undef,''.''.''); 533: #other potentially useful values: wkday,yrday,is_daylight_savings 534: 535: if (! defined($state) || $state ne 'disabled') { 536: $state = ''; 537: } 538: if (! defined($no_hh_mm_ss)) { 539: $no_hh_mm_ss = 0; 540: } 541: if ($currentvalue eq 'now') { 542: $currentvalue = $now; 543: } 544: 545: # Default value: Set empty date field to current time 546: # unless empty inclusion is requested 547: if ((!$includeempty) && (!$currentvalue)) { 548: $currentvalue = $now; 549: } 550: # Do we have a date? Split it! 551: if ($currentvalue) { 552: ($tzname,$sec,$min,$hour,$mday,$month,$year) = &get_timedates($currentvalue); 553: 554: #No values provided for hour, min, sec? Use default 0 555: if (($defhour) || ($defmin) || ($defsec)) { 556: $sec = ($defsec ? $defsec : 0); 557: $min = ($defmin ? $defmin : 0); 558: $hour = ($defhour ? $defhour : 0); 559: } 560: } 561: my $result = "\n<!-- $dname date setting form -->\n"; 562: $result .= <<ENDJS; 563: <script type="text/javascript"> 564: // <![CDATA[ 565: function $dname\_checkday() { 566: var day = document.$formname.$dname\_day.value; 567: var month = document.$formname.$dname\_month.value; 568: var year = document.$formname.$dname\_year.value; 569: var valid = true; 570: if (day < 1) { 571: document.$formname.$dname\_day.value = 1; 572: } 573: if (day > 31) { 574: document.$formname.$dname\_day.value = 31; 575: } 576: if ((month == 1) || (month == 3) || (month == 5) || 577: (month == 7) || (month == 8) || (month == 10) || 578: (month == 12)) { 579: if (day > 31) { 580: document.$formname.$dname\_day.value = 31; 581: day = 31; 582: } 583: } else if (month == 2 ) { 584: if ((year % 4 == 0) && (year % 100 != 0)) { 585: if (day > 29) { 586: document.$formname.$dname\_day.value = 29; 587: } 588: } else if (day > 29) { 589: document.$formname.$dname\_day.value = 28; 590: } 591: } else if (day > 30) { 592: document.$formname.$dname\_day.value = 30; 593: } 594: } 595: 596: function $dname\_disable() { 597: document.$formname.$dname\_month.disabled=true; 598: document.$formname.$dname\_day.disabled=true; 599: document.$formname.$dname\_year.disabled=true; 600: document.$formname.$dname\_hour.disabled=true; 601: document.$formname.$dname\_minute.disabled=true; 602: document.$formname.$dname\_second.disabled=true; 603: } 604: 605: function $dname\_enable() { 606: document.$formname.$dname\_month.disabled=false; 607: document.$formname.$dname\_day.disabled=false; 608: document.$formname.$dname\_year.disabled=false; 609: document.$formname.$dname\_hour.disabled=false; 610: document.$formname.$dname\_minute.disabled=false; 611: document.$formname.$dname\_second.disabled=false; 612: } 613: 614: function $dname\_opencalendar() { 615: if (! document.$formname.$dname\_month.disabled) { 616: var calwin=window.open( 617: "/adm/announcements?pickdate=yes&formname=$formname&element=$dname&month="+ 618: document.$formname.$dname\_month.value+"&year="+ 619: document.$formname.$dname\_year.value, 620: "LONCAPAcal", 621: "height=350,width=350,scrollbars=yes,resizable=yes,menubar=no"); 622: } 623: 624: } 625: // ]]> 626: </script> 627: ENDJS 628: $result .= ' <span class="LC_nobreak">'; 629: my $monthselector = qq{<select name="$dname\_month" $special $state onchange="javascript:$dname\_checkday()" >}; 630: # Month 631: my @Months = qw/January February March April May June 632: July August September October November December/; 633: # Pad @Months with a bogus value to make indexing easier 634: unshift(@Months,'If you can read this an error occurred'); 635: if ($includeempty) { $monthselector.="<option value=''></option>"; } 636: for(my $m = 1;$m <=$#Months;$m++) { 637: $monthselector .= qq{ <option value="$m"}; 638: $monthselector .= ' selected="selected"' if ($m-1 eq $month); 639: $monthselector .= '> '.&mt($Months[$m]).' </option>'."\n"; 640: } 641: $monthselector.= ' </select>'; 642: # Day 643: my $dayselector = qq{<input type="text" name="$dname\_day" $state value="$mday" size="3" $special onchange="javascript:$dname\_checkday()" />}; 644: # Year 645: my $yearselector = qq{<input type="text" name="$dname\_year" $state value="$year" size="5" $special onchange="javascript:$dname\_checkday()" />}; 646: # 647: my $hourselector = qq{<select name="$dname\_hour" $special $state >}; 648: if ($includeempty) { 649: $hourselector.=qq{<option value=''></option>}; 650: } 651: for (my $h = 0;$h<24;$h++) { 652: $hourselector .= qq{<option value="$h"}; 653: $hourselector .= ' selected="selected"' if (defined($hour) && $hour == $h); 654: $hourselector .= ">"; 655: my $timest=''; 656: if ($h == 0) { 657: $timest .= "12 am"; 658: } elsif($h == 12) { 659: $timest .= "12 noon"; 660: } elsif($h < 12) { 661: $timest .= "$h am"; 662: } else { 663: $timest .= $h-12 ." pm"; 664: } 665: $timest=&mt($timest); 666: $hourselector .= $timest." </option>\n"; 667: } 668: $hourselector .= " </select>\n"; 669: my $minuteselector = qq{<input type="text" name="$dname\_minute" $special $state value="$min" size="3" />}; 670: my $secondselector= qq{<input type="text" name="$dname\_second" $special $state value="$sec" size="3" />}; 671: my $cal_link; 672: if (!$nolink) { 673: $cal_link = qq{<a href="javascript:$dname\_opencalendar()">}; 674: } 675: # 676: my $tzone = ' '.$tzname.' '; 677: if ($no_hh_mm_ss) { 678: $result .= &mt('[_1] [_2] [_3] ', 679: $monthselector,$dayselector,$yearselector). 680: $tzone; 681: } elsif ($no_mm_ss) { 682: $result .= &mt('[_1] [_2] [_3] [_4]', 683: $monthselector,$dayselector,$yearselector, 684: $hourselector). 685: $tzone; 686: } elsif ($no_ss) { 687: $result .= &mt('[_1] [_2] [_3] [_4] [_5]m', 688: $monthselector,$dayselector,$yearselector, 689: $hourselector,$minuteselector). 690: $tzone; 691: } else { 692: $result .= &mt('[_1] [_2] [_3] [_4] [_5]m [_6]s ', 693: $monthselector,$dayselector,$yearselector, 694: $hourselector,$minuteselector,$secondselector). 695: $tzone; 696: } 697: if (!$nolink) { 698: $result .= &mt('[_1]Select Date[_2]',$cal_link,'</a>'); 699: } 700: $result .= "</span>\n<!-- end $dname date setting form -->\n"; 701: return $result; 702: } 703: 704: sub get_timedates { 705: my ($epoch) = @_; 706: my $dt = DateTime->from_epoch(epoch => $epoch) 707: ->set_time_zone(&Apache::lonlocal::gettimezone()); 708: my $tzname = $dt->time_zone_short_name(); 709: my $sec = $dt->second; 710: my $min = $dt->minute; 711: my $hour = $dt->hour; 712: my $mday = $dt->day; 713: my $month = $dt->month; 714: if ($month) { 715: $month --; 716: } 717: my $year = $dt->year; 718: return ($tzname,$sec,$min,$hour,$mday,$month,$year); 719: } 720: 721: sub build_url { 722: my ($base, $fields)=@_; 723: my $url; 724: $url = $base.'?'; 725: foreach my $key (keys(%$fields)) { 726: $url.=&escape($key).'='.&escape($$fields{$key}).'&'; 727: } 728: $url =~ s/&$//; 729: return $url; 730: } 731: 732: 733: ############################################## 734: ############################################## 735: 736: =pod 737: 738: =item &get_date_from_form() 739: 740: get_date_from_form retrieves the date specified in an &date_setter form. 741: 742: Inputs: 743: 744: =over 4 745: 746: =item $dname 747: 748: The name passed to &date_setter, which prefixes the form elements. 749: 750: =item $defaulttime 751: 752: The unix time to use as the default in case of poor inputs. 753: 754: =back 755: 756: Returns: Unix time represented in the form. 757: 758: =cut 759: 760: ############################################## 761: ############################################## 762: sub get_date_from_form { 763: my ($dname) = @_; 764: my ($sec,$min,$hour,$day,$month,$year); 765: # 766: if (defined($env{'form.'.$dname.'_second'})) { 767: my $tmpsec = $env{'form.'.$dname.'_second'}; 768: if (($tmpsec =~ /^\d+$/) && ($tmpsec >= 0) && ($tmpsec < 60)) { 769: $sec = $tmpsec; 770: } 771: if (!defined($tmpsec) || $tmpsec eq '') { $sec = 0; } 772: } else { 773: $sec = 0; 774: } 775: if (defined($env{'form.'.$dname.'_minute'})) { 776: my $tmpmin = $env{'form.'.$dname.'_minute'}; 777: if (($tmpmin =~ /^\d+$/) && ($tmpmin >= 0) && ($tmpmin < 60)) { 778: $min = $tmpmin; 779: } 780: if (!defined($tmpmin) || $tmpmin eq '') { $min = 0; } 781: } else { 782: $min = 0; 783: } 784: if (defined($env{'form.'.$dname.'_hour'})) { 785: my $tmphour = $env{'form.'.$dname.'_hour'}; 786: if (($tmphour =~ /^\d+$/) && ($tmphour >= 0) && ($tmphour < 24)) { 787: $hour = $tmphour; 788: } 789: } else { 790: $hour = 0; 791: } 792: if (defined($env{'form.'.$dname.'_day'})) { 793: my $tmpday = $env{'form.'.$dname.'_day'}; 794: if (($tmpday =~ /^\d+$/) && ($tmpday > 0) && ($tmpday < 32)) { 795: $day = $tmpday; 796: } 797: } 798: if (defined($env{'form.'.$dname.'_month'})) { 799: my $tmpmonth = $env{'form.'.$dname.'_month'}; 800: if (($tmpmonth =~ /^\d+$/) && ($tmpmonth > 0) && ($tmpmonth < 13)) { 801: $month = $tmpmonth; 802: } 803: } 804: if (defined($env{'form.'.$dname.'_year'})) { 805: my $tmpyear = $env{'form.'.$dname.'_year'}; 806: if (($tmpyear =~ /^\d+$/) && ($tmpyear >= 1970)) { 807: $year = $tmpyear; 808: } 809: } 810: if (($year<1970) || ($year>2037)) { return undef; } 811: if (defined($sec) && defined($min) && defined($hour) && 812: defined($day) && defined($month) && defined($year)) { 813: my $timezone = &Apache::lonlocal::gettimezone(); 814: my $dt = DateTime->new( year => $year, 815: month => $month, 816: day => $day, 817: hour => $hour, 818: minute => $min, 819: second => $sec, 820: time_zone => $timezone, 821: ); 822: my $epoch_time = $dt->epoch; 823: if ($epoch_time ne '') { 824: return $epoch_time; 825: } else { 826: return undef; 827: } 828: } else { 829: return undef; 830: } 831: } 832: 833: ############################################## 834: ############################################## 835: 836: =pod 837: 838: =item &pjump_javascript_definition() 839: 840: Returns javascript defining the 'pjump' function, which opens up a 841: parameter setting wizard. 842: 843: =cut 844: 845: ############################################## 846: ############################################## 847: sub pjump_javascript_definition { 848: my $Str = <<END; 849: function pjump(type,dis,value,marker,ret,call,hour,min,sec) { 850: openMyModal("/adm/rat/parameter.html?type="+escape(type) 851: +"&value="+escape(value)+"&marker="+escape(marker) 852: +"&return="+escape(ret) 853: +"&call="+escape(call)+"&name="+escape(dis) 854: +"&defhour="+escape(hour)+"&defmin="+escape(min) 855: +"&defsec="+escape(sec)+"&modal=1",350,350,'no'); 856: } 857: END 858: return $Str; 859: } 860: 861: ############################################## 862: ############################################## 863: 864: =pod 865: 866: =item &javascript_nothing() 867: 868: Return an appropriate null for the users browser. This is used 869: as the first arguement for window.open calls when you want a blank 870: window that you can then write to. 871: 872: =cut 873: 874: ############################################## 875: ############################################## 876: sub javascript_nothing { 877: # mozilla and other browsers work with "''", but IE on mac does not. 878: my $nothing = "''"; 879: my $user_browser; 880: my $user_os; 881: $user_browser = $env{'browser.type'} if (exists($env{'browser.type'})); 882: $user_os = $env{'browser.os'} if (exists($env{'browser.os'})); 883: if (! defined($user_browser) || ! defined($user_os)) { 884: (undef,$user_browser,undef,undef,undef,$user_os) = 885: &Apache::loncommon::decode_user_agent(); 886: } 887: if ($user_browser eq 'explorer' && $user_os =~ 'mac') { 888: $nothing = "'javascript:void(0);'"; 889: } 890: return $nothing; 891: } 892: 893: ############################################## 894: ############################################## 895: sub javascript_docopen { 896: my ($mimetype) = @_; 897: $mimetype ||= 'text/html'; 898: # safari does not understand document.open() and loads "text/html" 899: my $nothing = "''"; 900: my $user_browser; 901: my $user_os; 902: $user_browser = $env{'browser.type'} if (exists($env{'browser.type'})); 903: $user_os = $env{'browser.os'} if (exists($env{'browser.os'})); 904: if (! defined($user_browser) || ! defined($user_os)) { 905: (undef,$user_browser,undef,undef,undef,$user_os) = 906: &Apache::loncommon::decode_user_agent(); 907: } 908: if ($user_browser eq 'safari' && $user_os =~ 'mac') { 909: $nothing = "document.clear()"; 910: } else { 911: $nothing = "document.open('$mimetype','replace')"; 912: } 913: return $nothing; 914: } 915: 916: 917: ############################################## 918: ############################################## 919: 920: =pod 921: 922: =item &StatusOptions() 923: 924: Returns html for a selection box which allows the user to choose the 925: enrollment status of students. The selection box name is 'Status'. 926: 927: Inputs: 928: 929: $status: the currently selected status. If undefined the value of 930: $env{'form.Status'} is taken. If that is undefined, a value of 'Active' 931: is used. 932: 933: $formname: The name of the form. If defined the onchange attribute of 934: the selection box is set to document.$formname.submit(). 935: 936: $size: the size (number of lines) of the selection box. 937: 938: $onchange: javascript to use when the value is changed. Enclosed in 939: double quotes, ""s, not single quotes. 940: 941: Returns: a perl string as described. 942: 943: =cut 944: 945: ############################################## 946: ############################################## 947: sub StatusOptions { 948: my ($status, $formName,$size,$onchange,$mult)=@_; 949: $size = 1 if (!defined($size)); 950: if (! defined($status)) { 951: $status = 'Active'; 952: $status = $env{'form.Status'} if (exists($env{'form.Status'})); 953: } 954: 955: my $Str = ''; 956: $Str .= '<select name="Status"'; 957: if (defined($mult)){ 958: $Str .= ' multiple="multiple" '; 959: } 960: if(defined($formName) && $formName ne '' && ! defined($onchange)) { 961: $Str .= ' onchange="document.'.$formName.'.submit()"'; 962: } 963: if (defined($onchange)) { 964: $Str .= ' onchange="'.$onchange.'"'; 965: } 966: $Str .= ' size="'.$size.'" '; 967: $Str .= '>'."\n"; 968: foreach my $type (['Active', &mt('Currently Has Access')], 969: ['Future', &mt('Will Have Future Access')], 970: ['Expired', &mt('Previously Had Access')], 971: ['Any', &mt('Any Access Status')]) { 972: my ($name,$label) = @$type; 973: $Str .= '<option value="'.$name.'" '; 974: if ($status eq $name) { 975: $Str .= 'selected="selected" '; 976: } 977: $Str .= '>'.$label.'</option>'."\n"; 978: } 979: 980: $Str .= '</select>'."\n"; 981: } 982: 983: ######################################################## 984: ######################################################## 985: 986: =pod 987: 988: =item Progess Window Handling Routines 989: 990: These routines handle the creation, update, increment, and closure of 991: progress windows. The progress window reports to the user the number 992: of items completed and an estimate of the time required to complete the rest. 993: 994: =over 4 995: 996: 997: =item &Create_PrgWin() 998: 999: Writes javascript to the client to open a progress window and returns a 1000: data structure used for bookkeeping. 1001: 1002: Inputs 1003: 1004: =over 4 1005: 1006: =item $r Apache request 1007: 1008: =item $number_to_do The total number of items being processed. 1009: 1010: =back 1011: 1012: Returns a hash containing the progress state data structure. 1013: 1014: 1015: =item &Update_PrgWin() 1016: 1017: Updates the text in the progress indicator. Does not increment the count. 1018: See &Increment_PrgWin. 1019: 1020: Inputs: 1021: 1022: =over 4 1023: 1024: =item $r Apache request 1025: 1026: =item $prog_state Pointer to the data structure returned by &Create_PrgWin 1027: 1028: =item $displaystring The string to write to the status indicator 1029: 1030: =back 1031: 1032: Returns: none 1033: 1034: 1035: =item Increment_PrgWin() 1036: 1037: Increment the count of items completed for the progress window by $step or 1 if no step is provided. 1038: 1039: Inputs: 1040: 1041: =over 4 1042: 1043: =item $r Apache request 1044: 1045: =item $prog_state Pointer to the data structure returned by Create_PrgWin 1046: 1047: =item $extraInfo A description of the items being iterated over. Typically 1048: 'student'. 1049: 1050: =item $step (optional) counter step. Will be set to default 1 if ommited. step must be greater than 0 or empty. 1051: 1052: =back 1053: 1054: Returns: none 1055: 1056: 1057: =item &Close_PrgWin() 1058: 1059: Closes the progress window. 1060: 1061: Inputs: 1062: 1063: =over 4 1064: 1065: =item $r Apache request 1066: 1067: =item $prog_state Pointer to the data structure returned by Create_PrgWin 1068: 1069: =back 1070: 1071: Returns: none 1072: 1073: =back 1074: 1075: =cut 1076: 1077: ######################################################## 1078: ######################################################## 1079: 1080: 1081: # Create progress 1082: sub Create_PrgWin { 1083: my ($r,$number_to_do)=@_; 1084: my %prog_state; 1085: $prog_state{'done'}=0; 1086: $prog_state{'firststart'}=&Time::HiRes::time(); 1087: $prog_state{'laststart'}=&Time::HiRes::time(); 1088: $prog_state{'max'}=$number_to_do; 1089: &Apache::loncommon::LCprogressbar($r); 1090: return %prog_state; 1091: } 1092: 1093: # update progress 1094: sub Update_PrgWin { 1095: my ($r,$prog_state,$displayString)=@_; 1096: &Apache::loncommon::LCprogressbarUpdate($r,undef,$displayString); 1097: $$prog_state{'laststart'}=&Time::HiRes::time(); 1098: } 1099: 1100: # increment progress state 1101: sub Increment_PrgWin { 1102: my ($r,$prog_state,$extraInfo,$step)=@_; 1103: $step = $step > 0 ? $step : 1; 1104: $$prog_state{'done'} += $step; 1105: 1106: # Catch (max modulo step) <> 0 1107: my $current = $$prog_state{'done'}; 1108: my $last = ($$prog_state{'max'} - $current); 1109: if ($last <= 0) { 1110: $last = 1; 1111: $current = $$prog_state{'max'}; 1112: } 1113: 1114: my $time_est= (&Time::HiRes::time() - $$prog_state{'firststart'})/ 1115: $current * $last; 1116: $time_est = int($time_est); 1117: # 1118: my $min = int($time_est/60); 1119: my $sec = $time_est % 60; 1120: 1121: my $lasttime = &Time::HiRes::time()-$$prog_state{'laststart'}; 1122: if ($lasttime > 9) { 1123: $lasttime = int($lasttime); 1124: } elsif ($lasttime < 0.01) { 1125: $lasttime = 0; 1126: } else { 1127: $lasttime = sprintf("%3.2f",$lasttime); 1128: } 1129: 1130: $sec = 0 if ($min >= 10); # Don't show seconds if remaining time >= 10 min. 1131: $sec = 1 if ( ($min == 0) && ($sec == 0) ); # Little cheating: pretend to have 1 second remaining instead of 0 to have something to display 1132: 1133: my $timeinfo = 1134: &mt('[_1]/[_2]:' 1135: .' [quant,_3,minute,minutes,] [quant,_4,second ,seconds ,]remaining' 1136: .' ([quant,_5,second] for '.$extraInfo.')', 1137: $current, 1138: $$prog_state{'max'}, 1139: $min, 1140: $sec, 1141: $lasttime); 1142: my $percent=0; 1143: if ($$prog_state{'max'}) { 1144: $percent=int(100.*$current/$$prog_state{'max'}); 1145: } 1146: &Apache::loncommon::LCprogressbarUpdate($r,$percent,$timeinfo); 1147: $$prog_state{'laststart'}=&Time::HiRes::time(); 1148: } 1149: 1150: # close Progress Line 1151: sub Close_PrgWin { 1152: my ($r,$prog_state)=@_; 1153: &Apache::loncommon::LCprogressbarClose($r); 1154: undef(%$prog_state); 1155: } 1156: 1157: 1158: # ------------------------------------------------------- Puts directory header 1159: 1160: sub crumbs { 1161: my ($uri,$target,$prefix,$form,$skiplast)=@_; 1162: # You cannot crumbnify uploaded or adm resources 1163: if ($uri=~/^\/*(uploaded|adm)\//) { return &mt('(Internal Course/Group Content)'); } 1164: if ($target) { 1165: $target = ' target="'. 1166: &Apache::loncommon::escape_single($target).'"'; 1167: } 1168: my $output='<span class="LC_filename">'; 1169: $output.=$prefix.'/'; 1170: if (($env{'user.adv'}) || ($env{'user.author'})) { 1171: my $path=$prefix.'/'; 1172: foreach my $dir (split('/',$uri)) { 1173: if (! $dir) { next; } 1174: $path .= $dir; 1175: if ($path eq $uri) { 1176: if ($skiplast) { 1177: $output.=$dir; 1178: last; 1179: } 1180: } else { 1181: $path.='/'; 1182: } 1183: my $href_path = &HTML::Entities::encode($path,'<>&"'); 1184: &Apache::loncommon::inhibit_menu_check(\$href_path); 1185: if ($form) { 1186: my $href = 'javascript:'.$form.".action='".$href_path."';".$form.'.submit();'; 1187: $output.=qq{<a href="$href"$target>$dir</a>/}; 1188: } else { 1189: $output.=qq{<a href="$href_path"$target>$dir</a>/}; 1190: } 1191: } 1192: } else { 1193: foreach my $dir (split('/',$uri)) { 1194: if (! $dir) { next; } 1195: $output.=$dir.'/'; 1196: } 1197: } 1198: if ($uri !~ m|/$|) { $output=~s|/$||; } 1199: $output.='</span>'; 1200: 1201: 1202: return $output; 1203: } 1204: 1205: # --------------------- A function that generates a window for the spellchecker 1206: 1207: sub spellheader { 1208: my $start_page= 1209: &Apache::loncommon::start_page('Speller Suggestions',undef, 1210: {'only_body' => 1, 1211: 'js_ready' => 1, 1212: 'bgcolor' => '#DDDDDD', 1213: 'add_entries' => { 1214: 'onload' => 1215: 'document.forms.spellcheckform.submit()', 1216: } 1217: }); 1218: my $end_page= 1219: &Apache::loncommon::end_page({'js_ready' => 1}); 1220: 1221: my $nothing=&javascript_nothing(); 1222: return (<<ENDCHECK); 1223: <script type="text/javascript"> 1224: // <![CDATA[ 1225: //<!-- BEGIN LON-CAPA Internal 1226: var checkwin; 1227: 1228: function spellcheckerwindow(string) { 1229: var esc_string = string.replace(/\"/g,'"'); 1230: checkwin=window.open($nothing,'spellcheckwin','height=320,width=280,resizable=yes,scrollbars=yes,location=no,menubar=no,toolbar=no'); 1231: checkwin.document.writeln('$start_page<form name="spellcheckform" action="/adm/spellcheck" method="post"><input type="hidden" name="text" value="'+esc_string+'" /><\\/form>$end_page'); 1232: checkwin.document.close(); 1233: } 1234: // END LON-CAPA Internal --> 1235: // ]]> 1236: </script> 1237: ENDCHECK 1238: } 1239: 1240: # ---------------------------------- Generate link to spell checker for a field 1241: 1242: sub spelllink { 1243: my ($form,$field)=@_; 1244: my $linktext=&mt('Check Spelling'); 1245: return (<<ENDLINK); 1246: <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> 1247: ENDLINK 1248: } 1249: 1250: # ------------------------------------------------- Output headers for CKEditor 1251: 1252: sub htmlareaheaders { 1253: my $s=""; 1254: if (&htmlareabrowser()) { 1255: $s.=(<<ENDEDITOR); 1256: <script type="text/javascript" src="/ckeditor/ckeditor.js"></script> 1257: ENDEDITOR 1258: } 1259: $s.=(<<ENDJQUERY); 1260: <script type="text/javascript" src="/adm/jQuery/js/jquery-1.6.2.min.js"></script> 1261: <script type="text/javascript" src="/adm/jQuery/js/jquery-ui-1.8.16.custom.min.js"></script> 1262: <link rel="stylesheet" type="text/css" href="/adm/jQuery/css/smoothness/jquery-ui-1.8.16.custom.css" /> 1263: <script type="text/javascript" src="/adm/jpicker/js/jpicker-1.1.6.min.js" > 1264: </script> 1265: <link rel="stylesheet" type="text/css" href="/adm/jpicker/css/jPicker-1.1.6.min.css" /> 1266: <script type="text/javascript" src="/adm/countdown/js/jquery.countdown.min.js"></script> 1267: <link rel="stylesheet" type="text/css" href="/adm/countdown/css/jquery.countdown.css" /> 1268: 1269: <script type="text/javascript" src="/adm/spellchecker/js/jquery.spellchecker.min.js"></script> 1270: <link rel="stylesheet" type="text/css" href="/adm/spellchecker/css/spellchecker.css" /> 1271: <script type="text/javascript" src="/adm/nicescroll/jquery.nicescroll.min.js"></script> 1272: 1273: ENDJQUERY 1274: return $s; 1275: } 1276: 1277: # ----------------------------------------------------------------- Preferences 1278: 1279: # ------------------------------------------------- lang to use in html editor 1280: sub htmlarea_lang { 1281: my $lang='en'; 1282: if (&mt('htmlarea_lang') ne 'htmlarea_lang') { 1283: $lang=&mt('htmlarea_lang'); 1284: } 1285: return $lang; 1286: } 1287: 1288: # return javacsript to activate elements of .colorchooser with jpicker: 1289: # Caller is responsible for enclosing this in <script> tags: 1290: # 1291: sub color_picker { 1292: return ' 1293: $(document).ready(function(){ 1294: $.fn.jPicker.defaults.images.clientPath="/adm/jpicker/images/"; 1295: $(".colorchooser").jPicker({window: { position: {x: "screenCenter", y: "bottom"}}}); 1296: });'; 1297: } 1298: 1299: # ----------------------------------------- Script to activate only some fields 1300: 1301: sub htmlareaselectactive { 1302: my ($args) = @_; 1303: unless (&htmlareabrowser()) { return ''; } 1304: my $output='<script type="text/javascript" defer="defer">'."\n" 1305: .'// <![CDATA['."\n" 1306: .'//<!-- BEGIN LON-CAPA Internal'."\n"; 1307: my $lang = &htmlarea_lang(); 1308: my $fullpage = 'false'; 1309: my ($dragmath_prefix,$dragmath_helpicon,$dragmath_whitespace); 1310: if (ref($args) eq 'HASH') { 1311: if (exists($args->{'lang'})) { 1312: if ($args->{'lang'} ne '') { 1313: $lang = $args->{'lang'}; 1314: } 1315: } 1316: if (exists($args->{'fullpage'})) { 1317: if ($args->{'fullpage'} eq 'true') { 1318: $fullpage = $args->{'fullpage'}; 1319: } 1320: } 1321: if (exists($args->{'dragmath'})) { 1322: if ($args->{'dragmath'} ne '') { 1323: $dragmath_prefix = $args->{'dragmath'}; 1324: $dragmath_helpicon=&Apache::loncommon::lonhttpdurl("/adm/help/help.png"); 1325: $dragmath_whitespace=&Apache::loncommon::lonhttpdurl("/adm/lonIcons/transparent1x1.gif"); 1326: } 1327: } 1328: } 1329: 1330: my %lt = &Apache::lonlocal::texthash( 1331: 'plain' => 'Plain text', 1332: 'rich' => 'Rich formatting', 1333: 'plain_title' => 'Disable rich text formatting and edit in plain text', 1334: 'rich_title' => 'Enable rich text formatting (bold, italic, etc.)', 1335: ); 1336: 1337: $output.=' 1338: 1339: function containsBlockHtml(id) { 1340: 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); 1341: return (re >= 0); 1342: } 1343: 1344: function startRichEditor(id) { 1345: CKEDITOR.replace(id, 1346: { 1347: customConfig: "/ckeditor/loncapaconfig.js", 1348: language : "'.$lang.'", 1349: fullPage : '.$fullpage.', 1350: } 1351: ); 1352: } 1353: 1354: function destroyRichEditor(id) { 1355: CKEDITOR.instances[id].destroy(); 1356: } 1357: 1358: function editorHandler(event) { 1359: var rawid = $(this).attr("id"); 1360: var id = new RegExp("LC_rt_(.*)").exec(rawid)[1]; 1361: event.preventDefault(); 1362: var rt_enabled = $(this).hasClass("LC_enable_rt"); 1363: if (rt_enabled) { 1364: startRichEditor(id); 1365: $("#LC_rt_"+id).html("<b>« '.$lt{'plain'}.'</b>"); 1366: $("#LC_rt_"+id).attr("title", "'.$lt{'plain_title'}.'"); 1367: $("#LC_rt_"+id).addClass("LC_disable_rt"); 1368: $("#LC_rt_"+id).removeClass("LC_enable_rt"); 1369: } else { 1370: destroyRichEditor(id); 1371: $("#LC_rt_"+id).html("<b>'.$lt{'rich'}.' »</b>"); 1372: $("#LC_rt_"+id).attr("title", "'.$lt{'rich_title'}.'"); 1373: $("#LC_rt_"+id).addClass("LC_enable_rt"); 1374: $("#LC_rt_"+id).removeClass("LC_disable_rt"); 1375: }'; 1376: if ($dragmath_prefix ne '') { 1377: $output .= "\n var visible = ''; 1378: if (rt_enabled) { 1379: visible = 'none'; 1380: } 1381: editmath_visibility(id,visible);\n"; 1382: } 1383: $output .= ' 1384: } 1385: $(document).ready(function(){ 1386: $(".LC_richAlwaysOn").each(function() { 1387: startRichEditor($(this).attr("id")); 1388: }); 1389: $(".LC_richDetectHtml").each(function() { 1390: var id = $(this).attr("id"); 1391: var rt_enabled = containsBlockHtml(id); 1392: if(rt_enabled) { 1393: $(this).before("<div><a href=\"#\" id=\"LC_rt_"+id+"\" title=\"'.$lt{'plain_title'}.'\" class=\"LC_disable_rt\"><b>« '.$lt{'plain'}.'</b></a></div>"); 1394: startRichEditor(id); 1395: $("#LC_rt_"+id).click(editorHandler); 1396: } 1397: else { 1398: $(this).before("<div><a href=\"#\" id=\"LC_rt_"+id+"\" title=\"'.$lt{'rich_title'}.'\" class=\"LC_enable_rt\"><b>'.$lt{'rich'}.' »</b></a></div>"); 1399: $("#LC_rt_"+id).click(editorHandler); 1400: }'; 1401: if ($dragmath_prefix ne '') { 1402: $output .= "\n var visible = ''; 1403: if (rt_enabled) { 1404: visible = 'none'; 1405: } 1406: editmath_visibility(id,visible);\n"; 1407: } 1408: $output .= ' 1409: }); 1410: $(".LC_richDefaultOn").each(function() { 1411: var id = $(this).attr("id"); 1412: $(this).before("<div><a href=\"#\" id=\"LC_rt_"+id+"\" title=\"'.$lt{'plain_title'}.'\" class=\"LC_disable_rt\"><b>« '.$lt{'plain'}.'</b></a></div>"); 1413: startRichEditor(id); 1414: $("#LC_rt_"+id).click(editorHandler); 1415: }); 1416: $(".LC_richDefaultOff").each(function() { 1417: var id = $(this).attr("id"); 1418: $(this).before("<div><a href=\"#\" id=\"LC_rt_"+id+"\" title=\"'.$lt{'rich_title'}.'\" class=\"LC_enable_rt\"><b>'.$lt{'rich'}.' »</b></a></div>"); 1419: $("#LC_rt_"+id).click(editorHandler); 1420: }); 1421: 1422: 1423: }); 1424: '; 1425: $output .= &color_picker; 1426: 1427: # Code to put a due date countdown in 'duedatecountdown' span. 1428: # This is currently located in the breadcrumb headers. 1429: # note that the dueDateLayout is internatinoalized below. 1430: # Here document is used to support the substitution into the javascript below. 1431: # ..which unforunately necessitates escaping the $'s in the javascript. 1432: # There are several times of importance 1433: # 1434: # serverDueDate - The absolute time at which the problem expires. 1435: # serverTime - The server's time when the problem finished computing. 1436: # clientTime - The client's time...as close to serverTime as possible. 1437: # The clientTime will be slightly later due to 1438: # 1. The latency between problem computation and 1439: # the first network action. 1440: # 2. The time required between the page load-start and the actual 1441: # initial javascript execution that got clientTime. 1442: # These are used as follows: 1443: # The difference between clientTime and serverTime are used to 1444: # correct for differences in clock settings between the browser's system and the 1445: # server's. 1446: # 1447: # The difference between clientTime and the time at which the ready() method 1448: # starts executing is used to estimate latencies for page load and submission. 1449: # Since this is an estimate, it is doubled. The latency estimate + one minute 1450: # is used to determine when the countdown timer turns red to warn the user 1451: # to think about submitting. 1452: 1453: my $dueDateLayout = &mt('Due in: {dn} {dl} {hnn}{sep}{mnn}{sep}{snn} [_1]', 1454: "<span id='submitearly'></span>"); 1455: my $early = '- <b>'.&mt('Submit Early').'</b>'; 1456: my $pastdue = '- <b>'.&mt('Past Due').'</b>'; 1457: $output .= <<JAVASCRIPT; 1458: 1459: var documentReadyTime; 1460: 1461: \$(document).ready(function() { 1462: if (typeof(dueDate) != "undefined") { 1463: documentReadyTime = (new Date()).getTime(); 1464: \$("#duedatecountdown").countdown({until: dueDate, compact: true, 1465: layout: "$dueDateLayout", 1466: onTick: function (periods) { 1467: var latencyEstimate = (documentReadyTime - clientTime) * 2; 1468: if(\$.countdown.periodsToSeconds(periods) < (300 + latencyEstimate)) { 1469: \$("#submitearly").html("$early"); 1470: if (\$.countdown.periodsToSeconds(periods) < 1) { 1471: \$("#submitearly").html("$pastdue"); 1472: } 1473: } 1474: if(\$.countdown.periodsToSeconds(periods) < (60 + latencyEstimate)) { 1475: \$(this).css("color", "red"); //Highlight last minute. 1476: } 1477: } 1478: }); 1479: } 1480: }); 1481: 1482: /* This code describes the spellcheck options that will be used for 1483: items with class 'spellchecked'. It is necessary for those objects' 1484: to explicitly request checking (e.g. onblur is a nice event for that). 1485: */ 1486: \$(document).ready(function() { 1487: \$(".spellchecked").spellchecker({ 1488: url: "/ajax/spellcheck", 1489: lang: "en", 1490: engine: "pspell", 1491: suggestionBoxPosition: "below", 1492: innerDocument: true 1493: }); 1494: \$("textarea.spellchecked").spellchecker({ 1495: url: "/ajax/spellcheck", 1496: lang: "en", 1497: engine: "pspell", 1498: suggestionBoxPosition: "below", 1499: innerDocument: true 1500: }); 1501: 1502: }); 1503: 1504: /* the muli colored editor can generate spellcheck with language 'none' 1505: to disable spellcheck as well 1506: */ 1507: function doSpellcheck(element, lang) { 1508: if (lang != 'none') { 1509: \$(element).spellchecker('option', {lang: lang}); 1510: \$(element).spellchecker('check'); 1511: } 1512: } 1513: 1514: 1515: JAVASCRIPT 1516: if ($dragmath_prefix ne '') { 1517: $output .= ' 1518: 1519: function editmath_visibility(id,value) { 1520: 1521: if ((id == "") || (id == null)) { 1522: return; 1523: } 1524: var mathid = "'.$dragmath_prefix.'_"+id; 1525: mathele = document.getElementById(mathid); 1526: if (mathele == null) { 1527: return; 1528: } 1529: mathele.style.display = value; 1530: var mathhelpicon = "'.$dragmath_prefix.'helpicon'.'_"+id; 1531: mathhelpiconele = document.getElementById(mathhelpicon); 1532: if (mathhelpiconele == null) { 1533: return; 1534: } 1535: if (value == "none") { 1536: mathhelpiconele.src = "'.$dragmath_whitespace.'"; 1537: } else { 1538: mathhelpiconele.src = "'.$dragmath_helpicon.'"; 1539: } 1540: } 1541: '; 1542: 1543: } 1544: $output.="\nwindow.status='Activated Editfields';\n" 1545: .'// END LON-CAPA Internal -->'."\n" 1546: .'// ]]>'."\n" 1547: .'</script>'; 1548: return $output; 1549: } 1550: 1551: # --------------------------------------------------------------------- Blocked 1552: 1553: sub htmlareablocked { 1554: unless ($env{'environment.wysiwygeditor'} eq 'on') { return 1; } 1555: return 0; 1556: } 1557: 1558: # ---------------------------------------- Browser capable of running HTMLArea? 1559: 1560: sub htmlareabrowser { 1561: return 1; 1562: } 1563: 1564: # 1565: # Should the "return to content" link be shown? 1566: # 1567: 1568: sub show_return_link { 1569: 1570: unless ($env{'request.course.id'}) { return 0; } 1571: if ($env{'request.noversionuri'}=~m{^/priv/} || 1572: $env{'request.uri'}=~m{^/priv/}) { return 1; } 1573: return if ($env{'request.noversionuri'} eq '/adm/supplemental'); 1574: 1575: if (($env{'request.noversionuri'} =~ m{^/adm/(viewclasslist|navmaps)($|\?)}) 1576: || ($env{'request.noversionuri'} =~ m{^/adm/.*/aboutme($|\?)})) { 1577: 1578: return if ($env{'form.register'}); 1579: } 1580: return (($env{'request.noversionuri'}=~m{^/(res|public)/} && 1581: $env{'request.symb'} eq '') 1582: || 1583: ($env{'request.noversionuri'}=~ m{^/cgi-bin/printout.pl}) 1584: || 1585: (($env{'request.noversionuri'}=~/^\/adm\//) && 1586: ($env{'request.noversionuri'}!~/^\/adm\/wrapper\//) && 1587: ($env{'request.noversionuri'}!~ 1588: m{^/adm/.*/(smppg|bulletinboard)($|\?)}) 1589: )); 1590: } 1591: 1592: 1593: ## 1594: # Set the dueDate variable...note this is done in the timezone 1595: # of the browser. 1596: # 1597: # @param epoch relative time at which the problem is due. 1598: # 1599: # @return the javascript fragment to set the date: 1600: # 1601: sub set_due_date { 1602: my $dueStamp = shift; 1603: my $duems = $dueStamp * 1000; # Javascript Date object needs ms not seconds. 1604: 1605: my $now = time()*1000; 1606: 1607: # This slightly obscure bit of javascript sets the dueDate variable 1608: # to the time in the browser at which the problem was due. 1609: # The code should correct for gross differences between the server 1610: # and client's time setting 1611: 1612: return <<"END"; 1613: 1614: <script type="text/javascript"> 1615: //<![CDATA[ 1616: var serverDueDate = $duems; 1617: var serverTime = $now; 1618: var clientTime = (new Date()).getTime(); 1619: var dueDate = new Date(serverDueDate + (clientTime - serverTime)); 1620: 1621: //]]> 1622: </script> 1623: 1624: END 1625: } 1626: ## 1627: # Sets the time at which the problem finished computing. 1628: # This just updates the serverTime and clientTime variables above. 1629: # Calling this in e.g. end_problem provides a better estimate of the 1630: # difference beetween the server and client time setting as 1631: # the difference contains less of the latency/problem compute time. 1632: # 1633: sub set_compute_end_time { 1634: 1635: my $now = time()*1000; # Javascript times are in ms. 1636: return <<"END"; 1637: 1638: <script type="text/javascript"> 1639: //<![CDATA[ 1640: serverTime = $now; 1641: clientTime = (new Date()).getTime(); 1642: //]]> 1643: </script> 1644: 1645: END 1646: } 1647: 1648: ############################################################ 1649: ############################################################ 1650: 1651: =pod 1652: 1653: =item &breadcrumbs() 1654: 1655: Compiles the previously registered breadcrumbs into an series of links. 1656: Additionally supports a 'component', which will be displayed on the 1657: right side of the breadcrumbs enclosing div (without a link). 1658: A link to help for the component will be included if one is specified. 1659: 1660: All inputs can be undef without problems. 1661: 1662: Inputs: $component (the text on the right side of the breadcrumbs trail), 1663: $component_help 1664: $menulink (boolean, controls whether to include a link to /adm/menu) 1665: $helplink (if 'nohelp' don't include the orange help link) 1666: $css_class (optional name for the class to apply to the table for CSS) 1667: $no_mt (optional flag, 1 if &mt() is _not_ to be applied to $component 1668: when including the text on the right. 1669: Returns a string containing breadcrumbs for the current page. 1670: 1671: =item &clear_breadcrumbs() 1672: 1673: Clears the previously stored breadcrumbs. 1674: 1675: =item &add_breadcrumb() 1676: 1677: Pushes a breadcrumb on the stack of crumbs. 1678: 1679: input: $breadcrumb, a hash reference. The keys 'href','title', and 'text' 1680: are required. If present the keys 'faq' and 'bug' will be used to provide 1681: links to the FAQ and bug sites. If the key 'no_mt' is present the 'title' 1682: and 'text' values won't be sent through &mt() 1683: 1684: returns: nothing 1685: 1686: =cut 1687: 1688: ############################################################ 1689: ############################################################ 1690: { 1691: my @Crumbs; 1692: my %tools = (); 1693: 1694: sub breadcrumbs { 1695: my ($component,$component_help,$menulink,$helplink,$css_class,$no_mt, 1696: $CourseBreadcrumbs) = @_; 1697: # 1698: $css_class ||= 'LC_breadcrumbs'; 1699: 1700: # Make the faq and bug data cascade 1701: my $faq = ''; 1702: my $bug = ''; 1703: my $help = ''; 1704: # Crumb Symbol 1705: my $crumbsymbol = '»'; 1706: # The last breadcrumb does not have a link, so handle it separately. 1707: my $last = pop(@Crumbs); 1708: # 1709: # The first one should be the course or a menu link 1710: if (!defined($menulink)) { $menulink=1; } 1711: if ($menulink) { 1712: my $description = 'Menu'; 1713: my $no_mt_descr = 0; 1714: if ((exists($env{'request.course.id'})) && 1715: ($env{'request.course.id'} ne '') && 1716: ($env{'course.'.$env{'request.course.id'}.'.description'} ne '')) { 1717: $description = 1718: $env{'course.'.$env{'request.course.id'}.'.description'}; 1719: $no_mt_descr = 1; 1720: if ($env{'request.noversionuri'} =~ 1721: m{^/public/($match_domain)/($match_courseid)/syllabus$}) { 1722: unless (($env{'course.'.$env{'request.course.id'}.'.domain'} eq $1) && 1723: ($env{'course.'.$env{'request.course.id'}.'.num'} eq $2)) { 1724: $description = 'Menu'; 1725: $no_mt_descr = 0; 1726: } 1727: } 1728: } 1729: $menulink = { href =>'/adm/menu', 1730: title =>'Go to main menu', 1731: target =>'_top', 1732: text =>$description, 1733: no_mt =>$no_mt_descr, }; 1734: if($last) { 1735: #$last set, so we have some crumbs 1736: unshift(@Crumbs,$menulink); 1737: } else { 1738: #only menulink crumb present 1739: $last = $menulink; 1740: } 1741: } 1742: my $links; 1743: if ((&show_return_link) && (!$CourseBreadcrumbs) && (ref($last) eq 'HASH')) { 1744: my $alttext = &mt('Go Back'); 1745: my $hashref = { href => '/adm/flip?postdata=return:', 1746: title => &mt('Back to most recent content resource'), 1747: class => 'LC_menubuttons_link', 1748: }; 1749: if ($env{'request.noversionuri'} eq '/adm/searchcat') { 1750: $hashref->{'target'} = '_top'; 1751: } 1752: $links=&htmltag( 'a','<img src="/res/adm/pages/tolastloc.png" alt="'.$alttext.'" class="LC_icon" />', 1753: $hashref); 1754: $links=&htmltag('li',$links); 1755: } 1756: $links.= join "", 1757: map { 1758: $faq = $_->{'faq'} if (exists($_->{'faq'})); 1759: $bug = $_->{'bug'} if (exists($_->{'bug'})); 1760: $help = $_->{'help'} if (exists($_->{'help'})); 1761: 1762: my $result = $_->{no_mt} ? $_->{text} : &mt($_->{text}); 1763: 1764: if ($_->{href}){ 1765: $result = &htmltag( 'a', $result, 1766: { href => $_->{href}, 1767: title => $_->{no_mt} ? $_->{title} : &mt($_->{title}), 1768: target => $_->{target}, }); 1769: } 1770: 1771: $result = &htmltag( 'li', "$result $crumbsymbol"); 1772: } @Crumbs; 1773: 1774: #should the last Element be translated? 1775: 1776: my $lasttext = $last->{'no_mt'} ? $last->{'text'} 1777: : mt( $last->{'text'} ); 1778: 1779: # last breadcrumb is the first order heading of a page 1780: # for course breadcrumbs it's just bold 1781: 1782: if ($lasttext ne '') { 1783: $links .= &htmltag( 'li', htmltag($CourseBreadcrumbs ? 'b' : 'h1', 1784: $lasttext), {title => $lasttext}); 1785: } 1786: 1787: my $icons = ''; 1788: $faq = $last->{'faq'} if (exists($last->{'faq'})); 1789: $bug = $last->{'bug'} if (exists($last->{'bug'})); 1790: $help = $last->{'help'} if (exists($last->{'help'})); 1791: $component_help=($component_help?$component_help:$help); 1792: # if ($faq ne '') { 1793: # $icons .= &Apache::loncommon::help_open_faq($faq); 1794: # } 1795: # if ($bug ne '') { 1796: # $icons .= &Apache::loncommon::help_open_bug($bug); 1797: # } 1798: if ($faq ne '' || $component_help ne '' || $bug ne '') { 1799: $icons .= &Apache::loncommon::help_open_menu($component, 1800: $component_help, 1801: $faq,$bug); 1802: } 1803: # 1804: 1805: 1806: if ($links ne '') { 1807: unless ($CourseBreadcrumbs) { 1808: $links = &htmltag('ol', $links, { id => "LC_MenuBreadcrumbs" }); 1809: } else { 1810: $links = &htmltag('ul', $links, { class => "LC_CourseBreadcrumbs" }); 1811: } 1812: } 1813: 1814: 1815: if ($component) { 1816: $links = &htmltag('span', 1817: ( $no_mt ? $component : mt($component) ). 1818: ( $icons ? $icons : '' ), 1819: { class => 'LC_breadcrumbs_component' } ) 1820: .$links 1821: ; 1822: } 1823: my $nav_and_tools = 0; 1824: foreach my $item ('navigation','tools') { 1825: if (ref($tools{$item}) eq 'ARRAY') { 1826: $nav_and_tools += scalar(@{$tools{$item}}) 1827: } 1828: } 1829: if (($links ne '') || ($nav_and_tools)) { 1830: &render_tools(\$links); 1831: $links = &htmltag('div', $links, 1832: { id => "LC_breadcrumbs" }) unless ($CourseBreadcrumbs) ; 1833: } 1834: my $adv_tools = 0; 1835: if (ref($tools{'advtools'}) eq 'ARRAY') { 1836: $adv_tools = scalar(@{$tools{'advtools'}}); 1837: } 1838: if (($links ne '') || ($adv_tools)) { 1839: &render_advtools(\$links); 1840: } 1841: 1842: # Return the @Crumbs stack to what we started with 1843: push(@Crumbs,$last); 1844: shift(@Crumbs); 1845: 1846: 1847: # Return the breadcrumb's line 1848: 1849: 1850: 1851: return "$links"; 1852: } 1853: 1854: sub clear_breadcrumbs { 1855: undef(@Crumbs); 1856: undef(%tools); 1857: } 1858: 1859: sub add_breadcrumb { 1860: push(@Crumbs,@_); 1861: } 1862: 1863: =item &add_breadcrumb_tool($category, $html) 1864: 1865: Adds $html to $category of the breadcrumb toolbar container. 1866: 1867: $html is usually a link to a page that invokes a function on the currently 1868: displayed data (e.g. print when viewing a problem) 1869: 1870: Currently there are 3 possible values for $category: 1871: 1872: =over 1873: 1874: =item navigation 1875: left of breadcrumbs line 1876: 1877: =item tools 1878: remaining items in right of breadcrumbs line 1879: 1880: =item advtools 1881: advanced tools shown in a separate box below breadcrumbs line 1882: 1883: =back 1884: 1885: returns: nothing 1886: 1887: =cut 1888: 1889: sub add_breadcrumb_tool { 1890: my ($category, @html) = @_; 1891: return unless @html; 1892: if (!keys(%tools)) { 1893: %tools = ( navigation => [], tools => [], advtools => []); 1894: } 1895: 1896: #this cleans data received from lonmenu::innerregister 1897: @html = grep {defined $_ && $_ ne ''} @html; 1898: for (@html) { 1899: s/align="(right|left)"//; 1900: # s/<span.*?\/span>// if $category ne 'advtools'; 1901: } 1902: 1903: push @{$tools{$category}}, @html; 1904: } 1905: 1906: =item &clear_breadcrumb_tools() 1907: 1908: Clears the breadcrumb toolbar container. 1909: 1910: returns: nothing 1911: 1912: =cut 1913: 1914: sub clear_breadcrumb_tools { 1915: undef(%tools); 1916: } 1917: 1918: =item &render_tools(\$breadcrumbs) 1919: 1920: Creates html for breadcrumb tools (categories navigation and tools) and inserts 1921: \$breadcrumbs at the correct position. 1922: 1923: input: \$breadcrumbs - a reference to the string containing prepared 1924: breadcrumbs. 1925: 1926: returns: nothing 1927: 1928: =cut 1929: 1930: #TODO might split this in separate functions for each category 1931: sub render_tools { 1932: my ($breadcrumbs) = @_; 1933: return unless (keys(%tools)); 1934: 1935: my $navigation = list_from_array($tools{navigation}, 1936: { listattr => { class=>"LC_breadcrumb_tools_navigation" } }); 1937: my $tools = list_from_array($tools{tools}, 1938: { listattr => { class=>"LC_breadcrumb_tools_tools" } }); 1939: $$breadcrumbs = list_from_array([$navigation, $tools, $$breadcrumbs], 1940: { listattr => { class=>'LC_breadcrumb_tools_outerlist' } }); 1941: } 1942: 1943: =pod 1944: 1945: =item &render_advtools(\$breadcrumbs) 1946: 1947: Creates html for advanced tools (category advtools) and inserts \$breadcrumbs 1948: at the correct position. 1949: 1950: input: \$breadcrumbs - a reference to the string containing prepared 1951: breadcrumbs (after render_tools call). 1952: 1953: returns: nothing 1954: 1955: =cut 1956: 1957: sub render_advtools { 1958: my ($breadcrumbs) = @_; 1959: return unless (defined $tools{'advtools'}) 1960: and (scalar(@{$tools{'advtools'}}) > 0); 1961: 1962: $$breadcrumbs .= Apache::loncommon::head_subbox( 1963: funclist_from_array($tools{'advtools'}) ); 1964: } 1965: 1966: } # End of scope for @Crumbs 1967: 1968: sub docs_breadcrumbs { 1969: my ($allowed,$crstype,$contenteditor,$title,$precleared)=@_; 1970: my ($folderpath,@folders,$supplementalflag); 1971: @folders = split('&',$env{'form.folderpath'}); 1972: if ($env{'form.folderpath'} =~ /^supplemental/) { 1973: $supplementalflag = 1; 1974: } 1975: my $plain=''; 1976: my $container = 'sequence'; 1977: my ($randompick,$isencrypted,$ishidden,$is_random_order) = (-1,0,0,0); 1978: my @docs_crumbs; 1979: while (@folders) { 1980: my $folder=shift(@folders); 1981: my $foldername=shift(@folders); 1982: if ($folderpath) {$folderpath.='&';} 1983: $folderpath.=$folder.'&'.$foldername; 1984: my $url; 1985: if ($allowed) { 1986: $url = '/adm/coursedocs?folderpath='; 1987: } else { 1988: $url = '/adm/supplemental?folderpath='; 1989: } 1990: $url .= &escape($folderpath); 1991: my $name=&unescape($foldername); 1992: # each of randompick number, hidden, encrypted, random order, is_page 1993: # are appended with ":"s to the foldername 1994: $name=~s/\:(\d*)\:(\w*)\:(\w*):(\d*)\:?(\d*)$//; 1995: unless ($supplementalflag) { 1996: if ($contenteditor) { 1997: if ($1 ne '') { 1998: $randompick=$1; 1999: } else { 2000: $randompick=-1; 2001: } 2002: if ($2) { $ishidden=1; } 2003: if ($3) { $isencrypted=1; } 2004: if ($4 ne '') { $is_random_order = 1; } 2005: if ($5 == 1) {$container = 'page'; } 2006: } 2007: } 2008: if ($folder eq 'supplemental') { 2009: $name = &mt('Supplemental Content'); 2010: } 2011: if ($contenteditor) { 2012: $plain.=$name.' > '; 2013: } 2014: push(@docs_crumbs, 2015: {'href' => $url, 2016: 'title' => $name, 2017: 'text' => $name, 2018: 'no_mt' => 1, 2019: }); 2020: } 2021: if ($title) { 2022: push(@docs_crumbs, 2023: {'title' => $title, 2024: 'text' => $title, 2025: 'no_mt' => 1,} 2026: ); 2027: } 2028: if (wantarray) { 2029: unless ($precleared) { 2030: &clear_breadcrumbs(); 2031: } 2032: &add_breadcrumb(@docs_crumbs); 2033: if ($contenteditor) { 2034: $plain=~s/\>\;\s*$//; 2035: } 2036: my $menulink = 0; 2037: if (!$allowed && !$contenteditor) { 2038: $menulink = 1; 2039: } 2040: return (&breadcrumbs(undef,undef,$menulink,'nohelp',undef,undef, 2041: $contenteditor), 2042: $randompick,$ishidden,$isencrypted,$plain, 2043: $is_random_order,$container); 2044: } else { 2045: return \@docs_crumbs; 2046: } 2047: } 2048: 2049: ############################################################ 2050: ############################################################ 2051: 2052: # Nested table routines. 2053: # 2054: # Routines to display form items in a multi-row table with 2 columns. 2055: # Uses nested tables to divide form elements into segments. 2056: # For examples of use see loncom/interface/lonnotify.pm 2057: # 2058: # Can be used in following order: ... 2059: # &start_pick_box() 2060: # row1 2061: # row2 2062: # row3 ... etc. 2063: # &submit_row() 2064: # &end_pick_box() 2065: # 2066: # where row1, row 2 etc. are chosen from &role_select_row,&course_select_row, 2067: # &status_select_row and &email_default_row 2068: # 2069: # Can also be used in following order: 2070: # 2071: # &start_pick_box() 2072: # &row_title() 2073: # &row_closure() 2074: # &row_title() 2075: # &row_closure() ... etc. 2076: # &submit_row() 2077: # &end_pick_box() 2078: # 2079: # In general a &submit_row() call should proceed the call to &end_pick_box(), 2080: # as this routine adds a button for form submission. 2081: # &submit_row() does not require a &row_closure after it. 2082: # 2083: # &start_pick_box() creates a bounding table with 1-pixel wide black border. 2084: # rows should be placed between calls to &start_pick_box() and &end_pick_box. 2085: # 2086: # &row_title() adds a title in the left column for each segment. 2087: # &row_closure() closes a row with a 1-pixel wide black line. 2088: # 2089: # &role_select_row() provides a select box from which to choose 1 or more roles 2090: # &course_select_row provides ways of picking groups of courses 2091: # radio buttons: all, by category or by picking from a course picker pop-up 2092: # note: by category option is only displayed if a domain has implemented 2093: # selection by year, semester, department, number etc. 2094: # 2095: # &status_select_row() provides a select box from which to choose 1 or more 2096: # access types (current access, prior access, and future access) 2097: # 2098: # &email_default_row() provides text boxes for default e-mail suffixes for 2099: # different authentication types in a domain. 2100: # 2101: # &row_title() and &row_closure() are called internally by the &*_select_row 2102: # routines, but can also be called directly to start and end rows which have 2103: # needs that are not accommodated by the *_select_row() routines. 2104: 2105: { # Start: row_count block for pick_box 2106: my @row_count; 2107: 2108: sub start_pick_box { 2109: my ($css_class,$id) = @_; 2110: if (defined($css_class)) { 2111: $css_class = 'class="'.$css_class.'"'; 2112: } else { 2113: $css_class= 'class="LC_pick_box"'; 2114: } 2115: my $table_id; 2116: if (defined($id)) { 2117: $table_id = ' id="'.$id.'"'; 2118: } 2119: unshift(@row_count,0); 2120: my $output = <<"END"; 2121: <table $css_class $table_id> 2122: END 2123: return $output; 2124: } 2125: 2126: sub end_pick_box { 2127: shift(@row_count); 2128: my $output = <<"END"; 2129: </table> 2130: END 2131: return $output; 2132: } 2133: 2134: sub row_headline { 2135: my $output = <<"END"; 2136: <tr><td colspan="2"> 2137: END 2138: return $output; 2139: } 2140: 2141: sub row_title { 2142: my ($title,$css_title_class,$css_value_class, $css_value_furtherAttributes) = @_; 2143: $row_count[0]++; 2144: my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row'; 2145: $css_title_class ||= 'LC_pick_box_title'; 2146: $css_title_class = 'class="'.$css_title_class.'"'; 2147: 2148: $css_value_class ||= 'LC_pick_box_value'; 2149: 2150: if ($title ne '') { 2151: $title .= ':'; 2152: } 2153: my $output = <<"ENDONE"; 2154: <tr class="LC_pick_box_row" $css_value_furtherAttributes> 2155: <td $css_title_class> 2156: $title 2157: </td> 2158: <td class="$css_value_class $css_class"> 2159: ENDONE 2160: return $output; 2161: } 2162: 2163: sub row_closure { 2164: my ($no_separator) =@_; 2165: my $output = <<"ENDTWO"; 2166: </td> 2167: </tr> 2168: ENDTWO 2169: if (!$no_separator) { 2170: $output .= <<"ENDTWO"; 2171: <tr> 2172: <td colspan="2" class="LC_pick_box_separator"> 2173: </td> 2174: </tr> 2175: ENDTWO 2176: } 2177: return $output; 2178: } 2179: 2180: } # End: row_count block for pick_box 2181: 2182: sub role_select_row { 2183: my ($roles,$title,$css_class,$show_separate_custom,$cdom,$cnum) = @_; 2184: my $crstype = 'Course'; 2185: if ($cdom ne '' && $cnum ne '') { 2186: $crstype = &Apache::loncommon::course_type($cdom.'_'.$cnum); 2187: } 2188: my $output; 2189: if (defined($title)) { 2190: $output = &row_title($title,$css_class); 2191: } 2192: $output .= qq| 2193: <select name="roles" multiple="multiple">\n|; 2194: foreach my $role (@$roles) { 2195: my $plrole; 2196: if ($role eq 'ow') { 2197: $plrole = &mt('Course Owner'); 2198: } elsif ($role eq 'cr') { 2199: if ($show_separate_custom) { 2200: if ($cdom ne '' && $cnum ne '') { 2201: my %course_customroles = &course_custom_roles($cdom,$cnum); 2202: foreach my $crrole (sort(keys(%course_customroles))) { 2203: my ($plcrrole) = ($crrole =~ m|^cr/[^/]+/[^/]+/(.+)$|); 2204: $output .= ' <option value="'.$crrole.'">'.$plcrrole. 2205: '</option>'; 2206: } 2207: } 2208: } else { 2209: $plrole = &mt('Custom Role'); 2210: } 2211: } else { 2212: $plrole=&Apache::lonnet::plaintext($role,$crstype); 2213: } 2214: if (($role ne 'cr') || (!$show_separate_custom)) { 2215: $output .= ' <option value="'.$role.'">'.$plrole.'</option>'; 2216: } 2217: } 2218: $output .= qq| </select>\n|; 2219: if (defined($title)) { 2220: $output .= &row_closure(); 2221: } 2222: return $output; 2223: } 2224: 2225: sub course_select_row { 2226: my ($title,$formname,$totcodes,$codetitles,$idlist,$idlist_titles, 2227: $css_class,$crstype,$standardnames) = @_; 2228: my $output = &row_title($title,$css_class); 2229: $output .= &course_selection($formname,$totcodes,$codetitles,$idlist,$idlist_titles,$crstype,$standardnames); 2230: $output .= &row_closure(); 2231: return $output; 2232: } 2233: 2234: sub course_selection { 2235: my ($formname,$totcodes,$codetitles,$idlist,$idlist_titles,$crstype,$standardnames) = @_; 2236: my $output = qq| 2237: <script type="text/javascript"> 2238: // <![CDATA[ 2239: function coursePick (formname) { 2240: for (var i=0; i<formname.coursepick.length; i++) { 2241: if (formname.coursepick[i].value == 'category') { 2242: courseSet(''); 2243: } 2244: if (!formname.coursepick[i].checked) { 2245: if (formname.coursepick[i].value == 'specific') { 2246: formname.coursetotal.value = 0; 2247: formname.courselist = ''; 2248: } 2249: } 2250: } 2251: } 2252: function setPick (formname) { 2253: for (var i=0; i<formname.coursepick.length; i++) { 2254: if (formname.coursepick[i].value == 'category') { 2255: formname.coursepick[i].checked = true; 2256: } 2257: formname.coursetotal.value = 0; 2258: formname.courselist = ''; 2259: } 2260: } 2261: // ]]> 2262: </script> 2263: |; 2264: 2265: my ($allcrs,$pickspec); 2266: if ($crstype eq 'Community') { 2267: $allcrs = &mt('All communities'); 2268: $pickspec = &mt('Pick specific communities:'); 2269: } else { 2270: $allcrs = &mt('All courses'); 2271: $pickspec = &mt('Pick specific course(s):'); 2272: } 2273: 2274: my $courseform='<b>'.&Apache::loncommon::selectcourse_link 2275: ($formname,'pickcourse','pickdomain','coursedesc','',1,$crstype).'</b>'; 2276: $output .= '<label><input type="radio" name="coursepick" value="all" onclick="coursePick(this.form)" />'.$allcrs.'</label><br />'; 2277: if ($totcodes > 0) { 2278: my $numtitles = @$codetitles; 2279: if ($numtitles > 0) { 2280: $output .= '<label><input type="radio" name="coursepick" value="category" onclick="coursePick(this.form);alert('."'".&mt('Choose categories, from left to right')."'".')" />'.&mt('Pick courses by category:').'</label><br />'; 2281: $output .= '<table><tr><td>'.$$codetitles[0].'<br />'."\n". 2282: '<select name="'.$standardnames->[0]. 2283: '" onchange="setPick(this.form);courseSet('."'$$codetitles[0]'".')">'."\n". 2284: ' <option value="-1" />Select'."\n"; 2285: my @items = (); 2286: my @longitems = (); 2287: if ($$idlist{$$codetitles[0]} =~ /","/) { 2288: @items = split(/","/,$$idlist{$$codetitles[0]}); 2289: } else { 2290: $items[0] = $$idlist{$$codetitles[0]}; 2291: } 2292: if (defined($$idlist_titles{$$codetitles[0]})) { 2293: if ($$idlist_titles{$$codetitles[0]} =~ /","/) { 2294: @longitems = split(/","/,$$idlist_titles{$$codetitles[0]}); 2295: } else { 2296: $longitems[0] = $$idlist_titles{$$codetitles[0]}; 2297: } 2298: for (my $i=0; $i<@longitems; $i++) { 2299: if ($longitems[$i] eq '') { 2300: $longitems[$i] = $items[$i]; 2301: } 2302: } 2303: } else { 2304: @longitems = @items; 2305: } 2306: for (my $i=0; $i<@items; $i++) { 2307: $output .= ' <option value="'.$items[$i].'">'.$longitems[$i].'</option>'; 2308: } 2309: $output .= '</select></td>'; 2310: for (my $i=1; $i<$numtitles; $i++) { 2311: $output .= '<td>'.$$codetitles[$i].'<br />'."\n". 2312: '<select name="'.$standardnames->[$i]. 2313: '" onchange="courseSet('."'$$codetitles[$i]'".')">'."\n". 2314: '<option value="-1"><-Pick '.$$codetitles[$i-1].'</option>'."\n". 2315: '</select>'."\n". 2316: '</td>'; 2317: } 2318: $output .= '</tr></table><br />'; 2319: } 2320: } 2321: $output .= 2322: '<label><input type="radio" name="coursepick" value="specific"' 2323: .' onclick="coursePick(this.form);opencrsbrowser('."'".$formname."','dccourse','dcdomain','coursedesc','','1','$crstype'".')" />' 2324: .$pickspec.'</label>' 2325: .' '.$courseform.' ' 2326: .&mt('[_1] selected.', 2327: '<input type="text" value="0" size="4" name="coursetotal" readonly="readonly" />' 2328: .'<input type="hidden" name="courselist" value="" />') 2329: .'<br />'."\n"; 2330: return $output; 2331: } 2332: 2333: sub status_select_row { 2334: my ($types,$title,$css_class) = @_; 2335: my $output; 2336: if (defined($title)) { 2337: $output = &row_title($title,$css_class,'LC_pick_box_select'); 2338: } 2339: $output .= qq| 2340: <select name="types" multiple="multiple">\n|; 2341: foreach my $status_type (sort(keys(%{$types}))) { 2342: $output .= ' <option value="'.$status_type.'">'.$$types{$status_type}.'</option>'; 2343: } 2344: $output .= qq| </select>\n|; 2345: if (defined($title)) { 2346: $output .= &row_closure(); 2347: } 2348: return $output; 2349: } 2350: 2351: sub email_default_row { 2352: my ($authtypes,$title,$descrip,$css_class) = @_; 2353: my $output = &row_title($title,$css_class); 2354: $output .= $descrip. 2355: &Apache::loncommon::start_data_table(). 2356: &Apache::loncommon::start_data_table_header_row(). 2357: '<th>'.&mt('Authentication Method').'</th>'. 2358: '<th align="right">'.&mt('Username -> e-mail conversion').'</th>'."\n". 2359: &Apache::loncommon::end_data_table_header_row(); 2360: my $rownum = 0; 2361: foreach my $auth (sort(keys(%{$authtypes}))) { 2362: my ($userentry,$size); 2363: if ($auth =~ /^krb/) { 2364: $userentry = ''; 2365: $size = 25; 2366: } else { 2367: $userentry = 'username@'; 2368: $size = 15; 2369: } 2370: $output .= &Apache::loncommon::start_data_table_row(). 2371: '<td> '.$$authtypes{$auth}.'</td>'. 2372: '<td align="right">'.$userentry. 2373: '<input type="text" name="'.$auth.'" size="'.$size.'" /></td>'. 2374: &Apache::loncommon::end_data_table_row(); 2375: } 2376: $output .= &Apache::loncommon::end_data_table(); 2377: $output .= &row_closure(); 2378: return $output; 2379: } 2380: 2381: 2382: sub submit_row { 2383: my ($title,$cmd,$submit_text,$css_class) = @_; 2384: my $output = &row_title($title,$css_class,'LC_pick_box_submit'); 2385: $output .= qq| 2386: <br /> 2387: <input type="hidden" name="command" value="$cmd" /> 2388: <input type="submit" value="$submit_text"/> 2389: <br /><br /> 2390: \n|; 2391: return $output; 2392: } 2393: 2394: sub course_custom_roles { 2395: my ($cdom,$cnum) = @_; 2396: my %returnhash=(); 2397: my %coursepersonnel=&Apache::lonnet::dump('nohist_userroles',$cdom,$cnum); 2398: foreach my $person (sort(keys(%coursepersonnel))) { 2399: my ($role) = ($person =~ /^([^:]+):/); 2400: my ($end,$start) = split(/:/,$coursepersonnel{$person}); 2401: if ($end == -1 && $start == -1) { 2402: next; 2403: } 2404: if ($role =~ m|^cr/[^/]+/[^/]+/[^/]|) { 2405: $returnhash{$role} ++; 2406: } 2407: } 2408: return %returnhash; 2409: } 2410: 2411: 2412: sub resource_info_box { 2413: my ($symb,$onlyfolderflag,$stuvcurrent,$stuvdisp)=@_; 2414: my $return=''; 2415: if ($stuvcurrent ne '') { 2416: $return = '<div class="LC_left_float">'; 2417: } 2418: if ($symb) { 2419: $return.=&Apache::loncommon::start_data_table(); 2420: my ($map,$id,$resource)=&Apache::lonnet::decode_symb($symb); 2421: my $folder=&Apache::lonnet::gettitle($map); 2422: $return.=&Apache::loncommon::start_data_table_row(). 2423: '<th align="left">'.&mt('Folder:').'</th><td>'.$folder.'</td>'. 2424: &Apache::loncommon::end_data_table_row(); 2425: unless ($onlyfolderflag) { 2426: $return.=&Apache::loncommon::start_data_table_row(). 2427: '<th align="left">'.&mt('Resource:').'</th><td>'.&Apache::lonnet::gettitle($symb).'</td>'. 2428: &Apache::loncommon::end_data_table_row(); 2429: } 2430: if ($stuvcurrent ne '') { 2431: $return .= &Apache::loncommon::start_data_table_row(). 2432: '<th align="left">'.&mt("Student's current version:").'</th><td>'.$stuvcurrent.'</td>'. 2433: &Apache::loncommon::end_data_table_row(); 2434: } 2435: if ($stuvdisp ne '') { 2436: $return .= &Apache::loncommon::start_data_table_row(). 2437: '<th align="left">'.&mt("Student's version displayed:").'</th><td>'.$stuvdisp.'</td>'. 2438: &Apache::loncommon::end_data_table_row(); 2439: } 2440: $return.=&Apache::loncommon::end_data_table(); 2441: } else { 2442: $return='<p><span class="LC_error">'.&mt('No context provided.').'</span></p>'; 2443: } 2444: if ($stuvcurrent ne '') { 2445: $return .= '</div>'; 2446: } 2447: return $return; 2448: } 2449: 2450: # display_usage 2451: # 2452: # Generates a div containing a block, filled to show percentage of current quota used 2453: # 2454: # Quotas available for user portfolios, group portfolios, authoring spaces, and course 2455: # content stored directly within a course (i.e., excluding published content). 2456: # 2457: 2458: sub display_usage { 2459: my ($current_disk_usage,$disk_quota) = @_; 2460: my $usage = $current_disk_usage/1000; 2461: my $quota = $disk_quota/1000; 2462: my $percent; 2463: if ($disk_quota == 0) { 2464: $percent = 100.0; 2465: } else { 2466: $percent = 100*($current_disk_usage/$disk_quota); 2467: } 2468: $usage = sprintf("%.2f",$usage); 2469: $quota = sprintf("%.2f",$quota); 2470: $percent = sprintf("%.0f",$percent); 2471: my ($color,$cssclass); 2472: if ($percent <= 60) { 2473: $color = '#00A000'; 2474: } elsif ($percent > 60 && $percent < 90) { 2475: $color = '#FFD300'; 2476: $cssclass = 'class="LC_warning"'; 2477: } elsif( $percent >= 90) { 2478: $color = '#FF0000'; 2479: $cssclass = 'class="LC_error"'; 2480: } 2481: my $prog_width = $percent; 2482: if ($prog_width > 100) { 2483: $prog_width = 100; 2484: } 2485: return ' 2486: <div id="meter1" align="left" '.$cssclass.'>'.&mt('Currently using [_1] of the [_2] available.',$usage.' MB <span style="font-weight:bold;">('.$percent.'%)</span>',$quota.' MB')."\n". 2487: ' <div id="meter2" style="display:block; margin-top:5px; margin-bottom:5px; margin-left:0px; margin-right:0px; width:400px; border:1px solid #000000; height:10px;">'."\n". 2488: ' <div id="meter3" style="display:block; background-color:'.$color.'; width:'.$prog_width.'%; height:10px; color:#000000; margin:0px;"></div>'."\n". 2489: ' </div>'."\n". 2490: ' </div>'; 2491: } 2492: 2493: ############################################## 2494: ############################################## 2495: 2496: # topic_bar 2497: # 2498: # Generates a div containing an (optional) number with a white background followed by a 2499: # title with a background color defined in the corresponding CSS: LC_topic_bar 2500: # Inputs: 2501: # 1. number to display. 2502: # If input for number is empty only the title will be displayed. 2503: # 2. title text to display. 2504: # 3. optional id for the <div> 2505: # Outputs - a scalar containing html mark-up for the div. 2506: 2507: sub topic_bar { 2508: my ($num,$title,$id) = @_; 2509: my $number = ''; 2510: if ($num ne '') { 2511: $number = '<span>'.$num.'</span>'; 2512: } 2513: if ($id ne '') { 2514: $id = 'id="'.$id.'"'; 2515: } 2516: return '<div class="LC_topic_bar" '.$id.'>'.$number.$title.'</div>'; 2517: } 2518: 2519: ############################################## 2520: ############################################## 2521: # echo_form_input 2522: # 2523: # Generates html markup to add form elements from the referrer page 2524: # as hidden form elements (values encoded) in the new page. 2525: # 2526: # Intended to support two types of use 2527: # (a) to allow backing up to earlier pages in a multi-page 2528: # form submission process using a breadcrumb trail. 2529: # 2530: # (b) to allow the current page to be reloaded with form elements 2531: # set on previous page to remain unchanged. An example would 2532: # be where the a page containing a dynamically-built table of data is 2533: # is to be redisplayed, with only the sort order of the data changed. 2534: # 2535: # Inputs: 2536: # 1. Reference to array of form elements in the submitted form on 2537: # the referrer page which are to be excluded from the echoed elements. 2538: # 2539: # 2. Reference to array of regular expressions, which if matched in the 2540: # name of the form element n the referrer page will be omitted from echo. 2541: # 2542: # Outputs: A scalar containing the html markup for the echoed form 2543: # elements (all as hidden elements, with values encoded). 2544: 2545: 2546: sub echo_form_input { 2547: my ($excluded,$regexps) = @_; 2548: my $output = ''; 2549: foreach my $key (keys(%env)) { 2550: if ($key =~ /^form\.(.+)$/) { 2551: my $name = $1; 2552: my $match = 0; 2553: if (ref($excluded) eq 'ARRAY') { 2554: next if (grep(/^\Q$name\E$/,@{$excluded})); 2555: } 2556: if (ref($regexps) eq 'ARRAY') { 2557: if (@{$regexps} > 0) { 2558: foreach my $regexp (@{$regexps}) { 2559: if ($name =~ /$regexp/) { 2560: $match = 1; 2561: last; 2562: } 2563: } 2564: } 2565: } 2566: next if ($match); 2567: if (ref($env{$key}) eq 'ARRAY') { 2568: foreach my $value (@{$env{$key}}) { 2569: $value = &HTML::Entities::encode($value,'<>&"'); 2570: $output .= '<input type="hidden" name="'.$name. 2571: '" value="'.$value.'" />'."\n"; 2572: } 2573: } else { 2574: my $value = &HTML::Entities::encode($env{$key},'<>&"'); 2575: $output .= '<input type="hidden" name="'.$name. 2576: '" value="'.$value.'" />'."\n"; 2577: } 2578: } 2579: } 2580: return $output; 2581: } 2582: 2583: ############################################## 2584: ############################################## 2585: # set_form_elements 2586: # 2587: # Generates javascript to set form elements to values based on 2588: # corresponding values for the same form elements when the page was 2589: # previously submitted. 2590: # 2591: # Last submission values are read from hidden form elements in referring 2592: # page which have the same name, i.e., generated by &echo_form_input(). 2593: # 2594: # Intended to be called by onload event. 2595: # 2596: # Inputs: 2597: # (a) Reference to hash of echoed form elements to be set. 2598: # 2599: # In the hash, keys are the form element names, and the values are the 2600: # element type (selectbox, radio, checkbox or text -for textbox, textarea or 2601: # hidden). 2602: # 2603: # (b) Optional reference to hash of stored elements to be set. 2604: # 2605: # If the page being displayed is a page which permits modification of 2606: # previously stored data, e.g., the first page in a multi-page submission, 2607: # then if stored is supplied, form elements will be set to the last stored 2608: # values. If user supplied values are also available for the same elements 2609: # these will replace the stored values. 2610: # 2611: # Output: 2612: # 2613: # javascript function - set_form_elements() which sets form elements, 2614: # expects an argument: formname - the name of the form according to 2615: # the DOM, e.g., document.compose 2616: 2617: sub set_form_elements { 2618: my ($elements,$stored) = @_; 2619: my %values; 2620: my $output .= 'function setFormElements(courseForm) { 2621: '; 2622: if (defined($stored)) { 2623: foreach my $name (keys(%{$stored})) { 2624: if (exists($$elements{$name})) { 2625: if (ref($$stored{$name}) eq 'ARRAY') { 2626: $values{$name} = $$stored{$name}; 2627: } else { 2628: @{$values{$name}} = ($$stored{$name}); 2629: } 2630: } 2631: } 2632: } 2633: 2634: foreach my $key (keys(%env)) { 2635: if ($key =~ /^form\.(.+)$/) { 2636: my $name = $1; 2637: if (exists($$elements{$name})) { 2638: @{$values{$name}} = &Apache::loncommon::get_env_multiple($key); 2639: } 2640: } 2641: } 2642: 2643: foreach my $name (keys(%values)) { 2644: for (my $i=0; $i<@{$values{$name}}; $i++) { 2645: $values{$name}[$i] = &HTML::Entities::decode($values{$name}[$i],'<>&"'); 2646: $values{$name}[$i] =~ s/([\r\n\f]+)/\\n/g; 2647: $values{$name}[$i] =~ s/"/\\"/g; 2648: } 2649: if (($$elements{$name} eq 'text') || ($$elements{$name} eq 'hidden')) { 2650: my $numvalues = @{$values{$name}}; 2651: if ($numvalues > 1) { 2652: my $valuestring = join('","',@{$values{$name}}); 2653: $output .= qq| 2654: var textvalues = new Array ("$valuestring"); 2655: var total = courseForm.elements['$name'].length; 2656: if (total > $numvalues) { 2657: total = $numvalues; 2658: } 2659: for (var i=0; i<total; i++) { 2660: courseForm.elements['$name']\[i].value = textvalues[i]; 2661: } 2662: |; 2663: } else { 2664: $output .= qq| 2665: courseForm.elements['$name'].value = "$values{$name}[0]"; 2666: |; 2667: } 2668: } else { 2669: $output .= qq| 2670: var elementLength = courseForm.elements['$name'].length; 2671: if (elementLength==undefined) { 2672: |; 2673: foreach my $value (@{$values{$name}}) { 2674: if ($$elements{$name} eq 'selectbox') { 2675: $output .= qq| 2676: if (courseForm.elements['$name'].options[0].value == "$value") { 2677: courseForm.elements['$name'].options[0].selected = true; 2678: }|; 2679: } elsif (($$elements{$name} eq 'radio') || 2680: ($$elements{$name} eq 'checkbox')) { 2681: $output .= qq| 2682: if (courseForm.elements['$name'].value == "$value") { 2683: courseForm.elements['$name'].checked = true; 2684: } else { 2685: courseForm.elements['$name'].checked = false; 2686: }|; 2687: } 2688: } 2689: $output .= qq| 2690: } 2691: else { 2692: for (var i=0; i<courseForm.elements['$name'].length; i++) { 2693: |; 2694: if ($$elements{$name} eq 'selectbox') { 2695: $output .= qq| 2696: courseForm.elements['$name'].options[i].selected = false;|; 2697: } elsif (($$elements{$name} eq 'radio') || 2698: ($$elements{$name} eq 'checkbox')) { 2699: $output .= qq| 2700: courseForm.elements['$name']\[i].checked = false;|; 2701: } 2702: $output .= qq| 2703: } 2704: for (var j=0; j<courseForm.elements['$name'].length; j++) { 2705: |; 2706: foreach my $value (@{$values{$name}}) { 2707: if ($$elements{$name} eq 'selectbox') { 2708: $output .= qq| 2709: if (courseForm.elements['$name'].options[j].value == "$value") { 2710: courseForm.elements['$name'].options[j].selected = true; 2711: }|; 2712: } elsif (($$elements{$name} eq 'radio') || 2713: ($$elements{$name} eq 'checkbox')) { 2714: $output .= qq| 2715: if (courseForm.elements['$name']\[j].value == "$value") { 2716: courseForm.elements['$name']\[j].checked = true; 2717: }|; 2718: } 2719: } 2720: $output .= qq| 2721: } 2722: } 2723: |; 2724: } 2725: } 2726: $output .= " 2727: return; 2728: }\n"; 2729: return $output; 2730: } 2731: 2732: ############################################## 2733: ############################################## 2734: 2735: sub file_submissionchk_js { 2736: my ($turninpaths,$multiples) = @_; 2737: my $overwritewarn = &mt('File(s) you uploaded for your submission will overwrite existing file(s) submitted for this item').'\\n'. 2738: &mt('Continue submission and overwrite the file(s)?'); 2739: my $delfilewarn = &mt('You have indicated you wish to remove some files previously included in your submission.').'\\n'. 2740: &mt('Continue submission with these files removed?'); 2741: my ($turninpathtext,$multtext,$arrayindexofjs); 2742: if (ref($turninpaths) eq 'HASH') { 2743: foreach my $key (sort(keys(%{$turninpaths}))) { 2744: $turninpathtext .= " if (prefix == '$key') {\n". 2745: " return '$turninpaths->{$key}';\n". 2746: " }\n"; 2747: } 2748: } 2749: $turninpathtext .= " return '';\n"; 2750: if (ref($multiples) eq 'HASH') { 2751: foreach my $key (sort(keys(%{$multiples}))) { 2752: $multtext .= " if (prefix == '$key') {\n". 2753: " return '$multiples->{$key}';\n". 2754: " }\n"; 2755: } 2756: } 2757: $multtext .= " return '';\n"; 2758: 2759: $arrayindexofjs = &Apache::loncommon::javascript_array_indexof(); 2760: return <<"ENDSCRIPT"; 2761: <script type="text/javascript"> 2762: // <![CDATA[ 2763: 2764: function file_submission_check(formname,path,multiresp) { 2765: var elemnum = formname.elements.length; 2766: if (elemnum == 0) { 2767: return true; 2768: } 2769: var alloverwrites = []; 2770: var alldelconfirm = []; 2771: var result = []; 2772: var submitter; 2773: var subprefix; 2774: var allsub = getIndexByName(formname,'all_submit'); 2775: if (allsub == -1) { 2776: var idx = getIndexByName(formname,'submitted'); 2777: if (idx != -1) { 2778: var subval = String(formname.elements[idx].value); 2779: submitter = subval.replace(/^part_/,''); 2780: result = overwritten_check(formname,path,multiresp,submitter); 2781: alloverwrites.push.apply(alloverwrites,result['overwrite']); 2782: alldelconfirm.push.apply(alldelconfirm,result['delete']); 2783: } 2784: } else { 2785: if (formname.elements[allsub].type == 'submit') { 2786: var partsub = /^\\d+\\.\\d+_submit_.+\$/; 2787: var allprefixes = []; 2788: var allparts = []; 2789: for (var i=0; i<formname.elements.length; i++) { 2790: if (formname.elements[i].type == 'submit') { 2791: var elemname = formname.elements[i].name; 2792: var subname = String(elemname); 2793: var savesub = String(elemname); 2794: if (partsub.test(subname)) { 2795: var prefix = subname.replace(/_submit_.+\$/,''); 2796: if (allprefixes.indexOf(prefix) == -1) { 2797: allprefixes.push(prefix); 2798: allparts[prefix] = []; 2799: } 2800: var part = savesub.replace(/^\\d+\\.\\d+_submit_/,''); 2801: allparts[prefix].push(part); 2802: } 2803: } 2804: } 2805: for (var k=0; k<allprefixes.length; k++) { 2806: var idx = getIndexByName(formname,allprefixes[k]+'_submitted'); 2807: if (idx > -1) { 2808: if (formname.elements[idx].value != 'yes') { 2809: submitterval = formname.elements[idx].value; 2810: submitter = submitterval.replace(/^part_/,''); 2811: subprefix = allprefixes[k]; 2812: result = overwritten_check(formname,path,multiresp,submitter,subprefix); 2813: alloverwrites.push.apply(alloverwrites,result['overwrite']); 2814: alldelconfirm.push.apply(alldelconfirm,result['delete']); 2815: break; 2816: } 2817: } 2818: } 2819: if (submitter == '' || submitter == undefined) { 2820: for (var m=0; m<allprefixes.length; m++) { 2821: for (var n=0; n<allparts[allprefixes[m]].length; n++) { 2822: var result = overwritten_check(formname,path,multiresp,allparts[allprefixes[m]][n],allprefixes[m]); 2823: alloverwrites.push.apply(alloverwrites,result['overwrite']); 2824: alldelconfirm.push.apply(alldelconfirm,result['delete']); 2825: } 2826: } 2827: } 2828: } 2829: } 2830: if (alloverwrites.length > 0) { 2831: if (!confirm("$overwritewarn")) { 2832: for (var n=0; n<alloverwrites.length; n++) { 2833: formname.elements[alloverwrites[n]].value = ""; 2834: } 2835: return false; 2836: } 2837: } 2838: if (alldelconfirm.length > 0) { 2839: if (!confirm("$delfilewarn")) { 2840: for (var p=0; p<alldelconfirm.length; p++) { 2841: formname.elements[alldelconfirm[p]].checked = false; 2842: } 2843: return false; 2844: } 2845: } 2846: return true; 2847: } 2848: 2849: function getIndexByName(formname,item) { 2850: for (var i=0;i<formname.elements.length;i++) { 2851: if (formname.elements[i].name == item) { 2852: return i; 2853: } 2854: } 2855: return -1; 2856: } 2857: 2858: function overwritten_check(formname,path,multiresp,part,prefix) { 2859: var result = []; 2860: result['overwrite'] = []; 2861: result['delete'] = []; 2862: var elemnum = formname.elements.length; 2863: if (elemnum == 0) { 2864: return result; 2865: } 2866: var uploadstr; 2867: var deletestr; 2868: if ((prefix != undefined) && (prefix != '')) { 2869: var prepend = prefix+'_'; 2870: uploadstr = new RegExp("^"+prepend+"HWFILE"+part+".+\$"); 2871: deletestr = new RegExp("^"+prepend+"HWFILE"+part+".+_\\\\d+_delete\$"); 2872: multiresp = check_for_multiples(prepend); 2873: path = check_for_turninpath(prepend); 2874: } else { 2875: uploadstr = new RegExp("^HWFILE"+part+".+\$"); 2876: deletestr = new RegExp("^HWFILE"+part+".+_\\\\d+_delete\$"); 2877: } 2878: var alluploads = []; 2879: var allchecked = []; 2880: var allskipdel = []; 2881: var fnametrim = /[^\\/\\\\]+\$/; 2882: for (var i=0; i<formname.elements.length; i++) { 2883: var id = formname.elements[i].id; 2884: if (id != '') { 2885: if (uploadstr.test(id)) { 2886: if (formname.elements[i].type == 'file') { 2887: alluploads.push(id); 2888: } else { 2889: if (deletestr.test(id)) { 2890: if (formname.elements[i].type == 'checkbox') { 2891: if (formname.elements[i].checked) { 2892: allchecked.push(id); 2893: } 2894: } 2895: } 2896: } 2897: } 2898: } 2899: } 2900: for (var j=0; j<alluploads.length; j++) { 2901: var delstr = new RegExp("^"+alluploads[j]+"_\\\\d+_delete\$"); 2902: var delboxes = []; 2903: for (var k=0; k<formname.elements.length; k++) { 2904: var id = formname.elements[k].id; 2905: if ((id != '') && (id != undefined)) { 2906: if (delstr.test(id)) { 2907: if (formname.elements[k].type == 'checkbox') { 2908: delboxes.push(id); 2909: } 2910: } 2911: } 2912: } 2913: if (delboxes.length > 0) { 2914: if ((formname.elements[alluploads[j]].value != undefined) && 2915: (formname.elements[alluploads[j]].value != '')) { 2916: var filepath = formname.elements[alluploads[j]].value; 2917: var newfilename = fnametrim.exec(filepath); 2918: if (newfilename != null) { 2919: var filename = String(newfilename); 2920: var nospaces = filename.replace(/\\s+/g,'_'); 2921: var nospecials = nospaces.replace(/[^\\/\\w\\.\\-]/g,''); 2922: var cleanfilename = nospecials.replace(/\\.(\\d+\\.)/g,"_\$1"); 2923: if (cleanfilename != '') { 2924: var fullpath = path+"/"+cleanfilename; 2925: if (multiresp == 1) { 2926: var partid = String(alluploads[i]); 2927: var subdir = partid.replace(/^\\d*.?\\d*_?HWFILE/,''); 2928: if (subdir != "" && subdir != undefined) { 2929: fullpath = path+"/"+subdir+"/"+cleanfilename; 2930: } 2931: } 2932: for (var m=0; m<delboxes.length; m++) { 2933: if (fullpath == formname.elements[delboxes[m]].value) { 2934: if (formname.elements[delboxes[m]].checked) { 2935: allskipdel.push(delboxes[m]); 2936: } else { 2937: result['overwrite'].push(alluploads[j]); 2938: } 2939: break; 2940: } 2941: } 2942: } 2943: } 2944: } 2945: } 2946: } 2947: if (allchecked.length > 0) { 2948: if (allskipdel.length > 0) { 2949: for (var n=0; n<allchecked.length; n++) { 2950: if (allskipdel.indexOf(allchecked[n]) == -1) { 2951: result['delete'].push(allchecked[n]); 2952: } 2953: } 2954: } else { 2955: result['delete'].push.apply(result['delete'],allchecked); 2956: } 2957: } 2958: return result; 2959: } 2960: 2961: function check_for_multiples(prefix) { 2962: $multtext 2963: } 2964: 2965: function check_for_turninpath(prefix) { 2966: $turninpathtext 2967: } 2968: 2969: // ]]> 2970: </script> 2971: 2972: $arrayindexofjs 2973: 2974: ENDSCRIPT 2975: } 2976: 2977: ############################################## 2978: ############################################## 2979: 2980: sub resize_scrollbox_js { 2981: my ($context,$tabidstr,$tid) = @_; 2982: my (%names,$paddingwfrac,$offsetwfrac,$offsetv,$minw,$minv); 2983: if ($context eq 'docs') { 2984: %names = ( 2985: boxw => 'contenteditor', 2986: item => 'contentlist', 2987: header => 'uploadfileresult', 2988: scroll => 'contentscroll', 2989: boxh => 'contenteditor', 2990: ); 2991: $paddingwfrac = 0.09; 2992: $offsetwfrac = 0.015; 2993: $offsetv = 20; 2994: $minw = 250; 2995: $minv = 200; 2996: } elsif ($context eq 'params') { 2997: %names = ( 2998: boxw => 'parameditor', 2999: item => 'mapmenuinner', 3000: header => 'parmstep1', 3001: scroll => 'mapmenuscroll', 3002: boxh => 'parmlevel', 3003: ); 3004: $paddingwfrac = 0.2; 3005: $offsetwfrac = 0.015; 3006: $offsetv = 80; 3007: $minw = 100; 3008: $minv = 100; 3009: } 3010: my $viewport_js = &Apache::loncommon::viewport_geometry_js(); 3011: my $output = ' 3012: 3013: window.onresize=callResize; 3014: 3015: '; 3016: if ($context eq 'docs') { 3017: if ($env{'form.active'}) { 3018: $output .= "\nvar activeTab = '$env{'form.active'}$tid';\n"; 3019: } else { 3020: $output .= "\nvar activeTab = '';\n"; 3021: } 3022: } 3023: $output .= <<"FIRST"; 3024: 3025: $viewport_js 3026: 3027: function resize_scrollbox(scrollboxname,chkw,chkh) { 3028: var scrollboxid = 'div_'+scrollboxname; 3029: var scrolltableid = 'table_'+scrollboxname; 3030: var scrollbox; 3031: var scrolltable; 3032: var ismobile = '$env{'browser.mobile'}'; 3033: 3034: if (document.getElementById("$names{'boxw'}") == null) { 3035: return; 3036: } 3037: 3038: if (document.getElementById(scrollboxid) == null) { 3039: return; 3040: } else { 3041: scrollbox = document.getElementById(scrollboxid); 3042: } 3043: 3044: 3045: if (document.getElementById(scrolltableid) == null) { 3046: return; 3047: } else { 3048: scrolltable = document.getElementById(scrolltableid); 3049: } 3050: 3051: init_geometry(); 3052: var vph = Geometry.getViewportHeight(); 3053: var vpw = Geometry.getViewportWidth(); 3054: 3055: FIRST 3056: if ($context eq 'docs') { 3057: $output .= " 3058: var alltabs = ['$tabidstr']; 3059: "; 3060: } elsif ($context eq 'params') { 3061: $output .= " 3062: if (document.getElementById('$names{'boxh'}') == null) { 3063: return; 3064: } 3065: "; 3066: } 3067: $output .= <<"SECOND"; 3068: var listwchange; 3069: var scrollchange; 3070: if (chkw == 1) { 3071: var boxw = document.getElementById("$names{'boxw'}").offsetWidth; 3072: var itemw; 3073: var itemid = document.getElementById("$names{'item'}"); 3074: if (itemid != null) { 3075: itemw = itemid.offsetWidth; 3076: } 3077: var itemwstart = itemw; 3078: 3079: var scrollboxw = scrollbox.offsetWidth; 3080: var scrollboxscrollw = scrollbox.scrollWidth; 3081: var scrollstart = scrollboxw; 3082: 3083: var offsetw = parseInt(vpw * $offsetwfrac); 3084: var paddingw = parseInt(vpw * $paddingwfrac); 3085: 3086: var minscrollboxw = $minw; 3087: var maxcolw = 0; 3088: SECOND 3089: if ($context eq 'docs') { 3090: $output .= <<"DOCSONE"; 3091: var actabw = 0; 3092: for (var i=0; i<alltabs.length; i++) { 3093: if (activeTab == alltabs[i]) { 3094: actabw = document.getElementById(alltabs[i]).offsetWidth; 3095: if (actabw > maxcolw) { 3096: maxcolw = actabw; 3097: } 3098: } else { 3099: if (document.getElementById(alltabs[i]) != null) { 3100: var thistab = document.getElementById(alltabs[i]); 3101: thistab.style.visibility = 'hidden'; 3102: thistab.style.display = 'block'; 3103: var tabw = document.getElementById(alltabs[i]).offsetWidth; 3104: thistab.style.display = 'none'; 3105: thistab.style.visibility = ''; 3106: if (tabw > maxcolw) { 3107: maxcolw = tabw; 3108: } 3109: } 3110: } 3111: } 3112: DOCSONE 3113: } elsif ($context eq 'params') { 3114: $output .= <<"PARAMSONE"; 3115: var parmlevelrows = new Array(); 3116: var mapmenucells = new Array(); 3117: parmlevelrows = document.getElementById("$names{'boxh'}").rows; 3118: var numrows = parmlevelrows.length; 3119: if (numrows > 1) { 3120: mapmenucells = parmlevelrows[2].getElementsByTagName('td'); 3121: } 3122: maxcolw = mapmenucells[0].offsetWidth; 3123: PARAMSONE 3124: } 3125: $output .= <<"THIRD"; 3126: if (maxcolw > 0) { 3127: var newscrollboxw; 3128: if (maxcolw+paddingw+scrollboxscrollw<boxw) { 3129: newscrollboxw = boxw-paddingw-maxcolw; 3130: if (newscrollboxw < minscrollboxw) { 3131: newscrollboxw = minscrollboxw; 3132: } 3133: scrollbox.style.width = newscrollboxw+"px"; 3134: if (newscrollboxw != scrollboxw) { 3135: var newitemw = newscrollboxw-offsetw; 3136: itemid.style.width = newitemw+"px"; 3137: } 3138: } else { 3139: newscrollboxw = boxw-paddingw-maxcolw; 3140: if (newscrollboxw < minscrollboxw) { 3141: newscrollboxw = minscrollboxw; 3142: } 3143: scrollbox.style.width = newscrollboxw+"px"; 3144: if (newscrollboxw != scrollboxw) { 3145: var newitemw = newscrollboxw-offsetw; 3146: itemid.style.width = newitemw+"px"; 3147: } 3148: } 3149: 3150: if (newscrollboxw != scrollboxw) { 3151: var newscrolltablew = newscrollboxw+offsetw; 3152: scrolltable.style.width = newscrolltablew+"px"; 3153: } 3154: } 3155: 3156: if (newscrollboxw != scrollboxw) { 3157: scrollchange = 1; 3158: } 3159: 3160: if (itemid.offsetWidth != itemwstart) { 3161: listwchange = 1; 3162: } 3163: } 3164: if ((chkh == 1) || (listwchange)) { 3165: var itemid = document.getElementById("$names{'item'}"); 3166: if (itemid != null) { 3167: itemh = itemid.offsetHeight; 3168: } 3169: var primaryheight = document.getElementById('LC_nav_bar').offsetHeight; 3170: var secondaryheight; 3171: if (document.getElementById('LC_secondary_menu') != null) { 3172: secondaryheight = document.getElementById('LC_secondary_menu').offsetHeight; 3173: } 3174: var crumbsheight = document.getElementById('LC_breadcrumbs').offsetHeight; 3175: var dccidheight = 0; 3176: if (document.getElementById('dccid') != null) { 3177: dccidheight = document.getElementById('dccid').offsetHeight; 3178: } 3179: var headerheight = 0; 3180: if (document.getElementById("$names{'header'}") != null) { 3181: headerheight = document.getElementById("$names{'header'}").offsetHeight; 3182: } 3183: var tabbedheight = document.getElementById("tabbededitor").offsetHeight; 3184: var boxheight = document.getElementById("$names{'boxh'}").offsetHeight; 3185: var freevspace = vph-(primaryheight+secondaryheight+crumbsheight+dccidheight+headerheight+tabbedheight+boxheight); 3186: 3187: var scrollboxheight = scrollbox.offsetHeight; 3188: var scrollboxscrollheight = scrollbox.scrollHeight; 3189: var scrollboxh = scrollboxheight; 3190: 3191: var minvscrollbox = $minv; 3192: var offsetv = $offsetv; 3193: var newscrollboxheight; 3194: if (freevspace < 0) { 3195: newscrollboxheight = scrollboxheight+freevspace-offsetv; 3196: if (newscrollboxheight < minvscrollbox) { 3197: newscrollboxheight = minvscrollbox; 3198: } 3199: scrollbox.style.height = newscrollboxheight + "px"; 3200: } else { 3201: if (scrollboxscrollheight > scrollboxheight) { 3202: if (freevspace > offsetv) { 3203: newscrollboxheight = scrollboxheight+freevspace-offsetv; 3204: if (newscrollboxheight < minvscrollbox) { 3205: newscrollboxheight = minvscrollbox; 3206: } 3207: scrollbox.style.height = newscrollboxheight+"px"; 3208: } 3209: } 3210: } 3211: scrollboxheight = scrollbox.offsetHeight; 3212: var itemh = document.getElementById("$names{'item'}").offsetHeight; 3213: 3214: if (scrollboxscrollheight <= scrollboxheight) { 3215: if ((itemh+offsetv)<scrollboxheight) { 3216: newscrollheight = itemh+offsetv; 3217: scrollbox.style.height = newscrollheight+"px"; 3218: } 3219: } 3220: var newscrollboxh = scrollbox.offsetHeight; 3221: if (scrollboxh != newscrollboxh) { 3222: scrollchange = 1; 3223: } 3224: } 3225: if (ismobile && scrollchange) { 3226: \$("#div_$names{'scroll'}").getNiceScroll().onResize(); 3227: } 3228: return; 3229: } 3230: 3231: function callResize() { 3232: var timer; 3233: clearTimeout(timer); 3234: timer=setTimeout('resize_scrollbox("$names{'scroll'}","1","1")',500); 3235: } 3236: 3237: THIRD 3238: return $output; 3239: } 3240: 3241: ############################################## 3242: ############################################## 3243: 3244: sub javascript_jumpto_resource { 3245: my $confirm_switch = &mt("Editing requires switching to the resource's home server.").'\n'. 3246: &mt('Switch server?'); 3247: return (<<ENDUTILITY) 3248: 3249: function go(url) { 3250: if (url!='' && url!= null) { 3251: currentURL = null; 3252: currentSymb= null; 3253: window.location.href=url; 3254: } 3255: } 3256: 3257: function need_switchserver(url) { 3258: if (url!='' && url!= null) { 3259: if (confirm("$confirm_switch")) { 3260: go(url); 3261: } 3262: } 3263: return; 3264: } 3265: 3266: ENDUTILITY 3267: 3268: } 3269: 3270: sub jump_to_editres { 3271: my ($cfile,$home,$switchserver,$forceedit,$forcereg,$symb,$folderpath, 3272: $title,$idx,$suppurl,$todocs) = @_; 3273: my $jscall; 3274: if ($switchserver) { 3275: if ($home) { 3276: $cfile = '/adm/switchserver?otherserver='.$home.'&role='. 3277: &HTML::Entities::encode($env{'request.role'},'"<>&'); 3278: if ($symb) { 3279: $cfile .= '&symb='.&HTML::Entities::encode($symb,'"<>&'); 3280: } elsif ($folderpath) { 3281: $cfile .= '&folderpath='.&HTML::Entities::encode($folderpath,'"<>&'); 3282: } 3283: if ($forceedit) { 3284: $cfile .= '&forceedit=1'; 3285: } 3286: if ($forcereg) { 3287: $cfile .= '&register=1'; 3288: } 3289: $jscall = "need_switchserver('".&Apache::loncommon::escape_single($cfile)."');"; 3290: } 3291: } else { 3292: unless ($cfile =~ m{^/priv/}) { 3293: if ($symb) { 3294: $cfile .= (($cfile=~/\?/)?'&':'?')."symb=$symb"; 3295: } elsif ($folderpath) { 3296: $cfile .= (($cfile=~/\?/)?'&':'?'). 3297: 'folderpath='.&HTML::Entities::encode(&escape($folderpath),'"<>&'); 3298: if ($title) { 3299: $cfile .= (($cfile=~/\?/)?'&':'?'). 3300: 'title='.&HTML::Entities::encode(&escape($title),'"<>&'); 3301: } 3302: if ($idx) { 3303: $cfile .= (($cfile=~/\?/)?'&':'?').'idx='.$idx; 3304: } 3305: if ($suppurl) { 3306: $cfile .= (($cfile=~/\?/)?'&':'?'). 3307: 'suppurl='.&HTML::Entities::encode(&escape($suppurl)); 3308: } 3309: } 3310: if ($forceedit) { 3311: $cfile .= (($cfile=~/\?/)?'&':'?').'forceedit=1'; 3312: } 3313: if ($forcereg) { 3314: $cfile .= (($cfile=~/\?/)?'&':'?').'register=1'; 3315: } 3316: if ($todocs) { 3317: $cfile .= (($cfile=~/\?/)?'&':'?').'todocs=1'; 3318: } 3319: } 3320: $jscall = "go('".&Apache::loncommon::escape_single($cfile)."')"; 3321: } 3322: return $jscall; 3323: } 3324: 3325: ############################################## 3326: ############################################## 3327: 3328: # javascript_valid_email 3329: # 3330: # Generates javascript to validate an e-mail address. 3331: # Returns a javascript function which accetps a form field as argumnent, and 3332: # returns false if field.value does not satisfy two regular expression matches 3333: # for a valid e-mail address. Backwards compatible with old browsers without 3334: # support for javascript RegExp (just checks for @ in field.value in this case). 3335: 3336: sub javascript_valid_email { 3337: my $scripttag .= <<'END'; 3338: function validmail(field) { 3339: var str = field.value; 3340: if (window.RegExp) { 3341: var reg1str = "(@.*@)|(\\.\\.)|(@\\.)|(\\.@)|(^\\.)"; 3342: var reg2str = "^.+\\@(\\[?)[a-zA-Z0-9\\-\\.]+\\.([a-zA-Z]{2,3}|[0-9]{1,3})(\\]?)$"; //" 3343: var reg1 = new RegExp(reg1str); 3344: var reg2 = new RegExp(reg2str); 3345: if (!reg1.test(str) && reg2.test(str)) { 3346: return true; 3347: } 3348: return false; 3349: } 3350: else 3351: { 3352: if(str.indexOf("@") >= 0) { 3353: return true; 3354: } 3355: return false; 3356: } 3357: } 3358: END 3359: return $scripttag; 3360: } 3361: 3362: 3363: # USAGE: htmltag(element, content, {attribute => value,...}); 3364: # 3365: # EXAMPLES: 3366: # - htmltag('a', 'this is an anchor', {href => 'www.example.com', 3367: # title => 'this is a title'}) 3368: # 3369: # - You might want to set up needed tags like: 3370: # 3371: # my $h3 = sub { return htmltag( "h3", @_ ) }; 3372: # 3373: # ... and use them: $h3->("This is a headline") 3374: # 3375: # - To set up a couple of tags, see sub inittags 3376: # 3377: # NOTES: 3378: # - Empty elements, such as <br/> are correctly terminated, 3379: # i.e. htmltag('br') returns <br/> 3380: # - Empty attributes (title="") are filtered out. 3381: # - The function will not check for deprecated attributes. 3382: # 3383: # OUTPUT: content enclosed in xhtml conform tags 3384: sub htmltag{ 3385: return 3386: qq|<$_[0]| 3387: . join( '', map { qq| $_="${$_[2]}{$_}"| if ${$_[2]}{$_} } keys(%{ $_[2] }) ) 3388: . ($_[1] ? qq|>$_[1]</$_[0]>| : qq|/>|). "\n"; 3389: }; 3390: 3391: 3392: # USAGE: inittags(@tags); 3393: # 3394: # EXAMPLES: 3395: # - my ($h1, $h2, $h3) = inittags( qw( h1 h2 h3 ) ) 3396: # $h1->("This is a headline") #Returns: <h1>This is a headline</h1> 3397: # 3398: # NOTES: See sub htmltag for further information. 3399: # 3400: # OUTPUT: List of subroutines. 3401: sub inittags { 3402: my @tags = @_; 3403: return map { my $tag = $_; 3404: sub { return htmltag( $tag, @_ ) } 3405: } @tags; 3406: } 3407: 3408: 3409: # USAGE: scripttag(scriptcode, [start|end|both]); 3410: # 3411: # EXAMPLES: 3412: # - scripttag("alert('Hello World!')", 'both') 3413: # returns: 3414: # <script type="text/javascript"> 3415: # // BEGIN LON-CAPA Internal 3416: # alert(Hello World!') 3417: # // END LON-CAPA Internal 3418: # </script> 3419: # 3420: # NOTES: 3421: # - works currently only for javascripts 3422: # 3423: # OUTPUT: 3424: # Scriptcode properly enclosed in <script> and CDATA tags (and LC 3425: # Internal markers if 2nd argument is given) 3426: sub scripttag { 3427: my ( $content, $marker ) = @_; 3428: return unless defined $content; 3429: 3430: my $begin = "\n// BEGIN LON-CAPA Internal\n"; 3431: my $end = "\n// END LON-CAPA Internal\n"; 3432: 3433: if ($marker) { 3434: $content = $begin . $content if $marker eq 'start' or $marker eq 'both'; 3435: $content .= $end if $marker eq 'end' or $marker eq 'both'; 3436: } 3437: 3438: $content = "\n// <![CDATA[\n$content\n// ]]>\n"; 3439: 3440: return htmltag('script', $content, {type => 'text/javascript'}); 3441: }; 3442: 3443: =pod 3444: 3445: =item &list_from_array( \@array, { listattr =>{}, itemattr =>{} } ) 3446: 3447: Constructs a XHTML list from \@array. 3448: 3449: input: 3450: 3451: =over 3452: 3453: =item \@array 3454: 3455: A reference to the array containing text that will be wrapped in <li></li> tags. 3456: 3457: =item { listattr => {}, itemattr =>{} } 3458: 3459: Attributes for <ul> and <li> passed in as hash references. 3460: See htmltag() for more details. 3461: 3462: =back 3463: 3464: returns: XHTML list as String. 3465: 3466: =cut 3467: 3468: # \@items, {listattr => { class => 'abc', id => 'xyx' }, itemattr => {class => 'abc', id => 'xyx'}} 3469: sub list_from_array { 3470: my ($items, $args) = @_; 3471: return unless (ref($items) eq 'ARRAY'); 3472: return unless scalar @$items; 3473: my ($ul, $li) = inittags( qw(ul li) ); 3474: my $listitems = join '', map { $li->($_, $args->{itemattr}) } @$items; 3475: return $ul->( $listitems, $args->{listattr} ); 3476: } 3477: 3478: 3479: ############################################## 3480: ############################################## 3481: 3482: # generate_menu 3483: # 3484: # Generates html markup for a menu. 3485: # 3486: # Inputs: 3487: # An array of following structure: 3488: # ({ categorytitle => 'Categorytitle', 3489: # items => [ 3490: # { 3491: # linktext => 'Text to be displayed', 3492: # url => 'URL the link is pointing to, i.e. /adm/site?action=dosomething', 3493: # permission => 'Contains permissions as returned from lonnet::allowed(), 3494: # must evaluate to true in order to activate the link', 3495: # icon => 'icon filename', 3496: # alttext => 'alt text for the icon', 3497: # help => 'Name of the corresponding helpfile', 3498: # linktitle => 'Description of the link (used for title tag)' 3499: # }, 3500: # ... 3501: # ] 3502: # }, 3503: # ... 3504: # ) 3505: # 3506: # Outputs: A scalar containing the html markup for the menu. 3507: 3508: sub generate_menu { 3509: my @menu = @_; 3510: # subs for specific html elements 3511: my ($h3, $div, $ul, $li, $a, $img) = inittags( qw(h3 div ul li a img) ); 3512: 3513: my @categories; # each element represents the entire markup for a category 3514: 3515: foreach my $category (@menu) { 3516: my @links; # contains the links for the current $category 3517: foreach my $link (@{$$category{items}}) { 3518: next unless $$link{permission}; 3519: 3520: # create the markup for the current $link and push it into @links. 3521: # each entry consists of an image and a text optionally followed 3522: # by a help link. 3523: my $src; 3524: if ($$link{icon} ne '') { 3525: $src = '/res/adm/pages/'.$$link{icon}; 3526: } 3527: push(@links,$li->( 3528: $a->( 3529: $img->("", { 3530: class => "LC_noBorder LC_middle", 3531: src => $src, 3532: alt => mt(defined($$link{alttext}) ? 3533: $$link{alttext} : $$link{linktext}) 3534: }), { 3535: href => $$link{url}, 3536: title => mt($$link{linktitle}), 3537: class => 'LC_menubuttons_link' 3538: }). 3539: $a->(mt($$link{linktext}), { 3540: href => $$link{url}, 3541: title => mt($$link{linktitle}), 3542: class => "LC_menubuttons_link" 3543: }). 3544: (defined($$link{help}) ? 3545: Apache::loncommon::help_open_topic($$link{help}) : ''), 3546: {class => "LC_menubuttons_inline_text"})); 3547: } 3548: 3549: # wrap categorytitle in <h3>, concatenate with 3550: # joined and in <ul> tags wrapped @links 3551: # and wrap everything in an enclosing <div> and push it into 3552: # @categories 3553: # such that each element looks like: 3554: # <div><h3>title</h3><ul><li>...</li>...</ul></div> 3555: # the category won't be added if there aren't any links 3556: push(@categories, 3557: $div->($h3->(mt($$category{categorytitle}), {class=>"LC_hcell"}). 3558: $ul->(join('' ,@links), {class =>"LC_ListStyleNormal" }), 3559: {class=>"LC_Box LC_400Box"})) if scalar(@links); 3560: } 3561: 3562: # wrap the joined @categories in another <div> (column layout) 3563: return $div->(join('', @categories), {class => "LC_columnSection"}); 3564: } 3565: 3566: ############################################## 3567: ############################################## 3568: 3569: =pod 3570: 3571: =item &start_funclist() 3572: 3573: Start list of available functions 3574: 3575: Typically used to offer a simple list of available functions 3576: at top or bottom of page. 3577: All available functions/actions for the current page 3578: should be included in this list. 3579: 3580: If the optional headline text is not provided, a default text will be used. 3581: 3582: 3583: Related routines: 3584: =over 4 3585: add_item_funclist 3586: end_funclist 3587: =back 3588: 3589: 3590: Inputs: (optional) headline text 3591: 3592: Returns: HTML code with function list start 3593: 3594: =cut 3595: 3596: ############################################## 3597: ############################################## 3598: 3599: sub start_funclist { 3600: my($legendtext)=@_; 3601: $legendtext=&mt('Functions') if !$legendtext; 3602: return '<ul class="LC_funclist"><li style="font-weight:bold; margin-left:0.8em;">'.$legendtext.'</li>'."\n"; 3603: } 3604: 3605: 3606: ############################################## 3607: ############################################## 3608: 3609: =pod 3610: 3611: =item &add_item_funclist() 3612: 3613: Adds an item to the list of available functions 3614: 3615: Related routines: 3616: =over 4 3617: start_funclist 3618: end_funclist 3619: =back 3620: 3621: Inputs: content item with text and link to function 3622: 3623: Returns: HTML code with list item for funclist 3624: 3625: =cut 3626: 3627: ############################################## 3628: ############################################## 3629: 3630: sub add_item_funclist { 3631: my($content) = @_; 3632: return '<li>'.$content.'</li>'."\n"; 3633: } 3634: 3635: =pod 3636: 3637: =item &end_funclist() 3638: 3639: End list of available functions 3640: 3641: Related routines: 3642: =over 4 3643: start_funclist 3644: add_item_funclist 3645: =back 3646: 3647: Inputs: ./. 3648: 3649: Returns: HTML code with function list end 3650: 3651: =cut 3652: 3653: sub end_funclist { 3654: return "</ul>\n"; 3655: } 3656: 3657: =pod 3658: 3659: =item &funclist_from_array( \@array, {legend => 'text for legend'} ) 3660: 3661: Constructs a XHTML list from \@array with the first item being visually 3662: highlighted and set to the value of legend or 'Functions' if legend is 3663: empty. 3664: 3665: =over 3666: 3667: =item \@array 3668: 3669: A reference to the array containing text that will be wrapped in <li></li> tags. 3670: 3671: =item { legend => 'text' } 3672: 3673: A string that's used as visually highlighted first item. 'Functions' is used if 3674: it's value evaluates to false. 3675: 3676: =back 3677: 3678: returns: XHTML list as string. 3679: 3680: =back 3681: 3682: =cut 3683: 3684: sub funclist_from_array { 3685: my ($items, $args) = @_; 3686: return unless(ref($items) eq 'ARRAY'); 3687: $args->{legend} ||= mt('Functions'); 3688: return list_from_array( [$args->{legend}, @$items], 3689: { listattr => {class => 'LC_funclist'} }); 3690: } 3691: 3692: =pod 3693: 3694: =over 3695: 3696: =item &actionbox( \@array ) 3697: 3698: Constructs a XHTML list from \@array with the first item being visually 3699: highlighted and set to the value 'Actions'. The list is wrapped in a division. 3700: 3701: The actionlist is used to offer contextual actions, mostly at the bottom 3702: of a page, on which the outcome of an processed action is shown, 3703: e.g. a file operation in Authoring Space. 3704: 3705: =over 3706: 3707: =item \@array 3708: 3709: A reference to the array containing text. Details: sub funclist_from_array 3710: 3711: =back 3712: 3713: Returns: XHTML div as string. 3714: 3715: =back 3716: 3717: =cut 3718: 3719: sub actionbox { 3720: my ($items) = @_; 3721: return unless(ref($items) eq 'ARRAY'); 3722: return 3723: '<div class="LC_actionbox">' 3724: .&funclist_from_array($items, {legend => &mt('Actions')}) 3725: .'</div>'; 3726: } 3727: 3728: 1; 3729: 3730: __END__