--- loncom/interface/loncommon.pm 2008/01/15 16:53:27 1.634 +++ loncom/interface/loncommon.pm 2009/03/17 20:07:45 1.769 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # a pile of common routines # -# $Id: loncommon.pm,v 1.634 2008/01/15 16:53:27 www Exp $ +# $Id: loncommon.pm,v 1.769 2009/03/17 20:07:45 schulted Exp $ # # Copyright Michigan State University Board of Trustees # @@ -61,12 +61,15 @@ use POSIX qw(strftime mktime); use Apache::lonmenu(); use Apache::lonenc(); use Apache::lonlocal; +use Apache::lonnet(); use HTML::Entities; use Apache::lonhtmlcommon(); use Apache::loncoursedata(); use Apache::lontexconvert(); use Apache::lonclonecourse(); use LONCAPA qw(:DEFAULT :match); +use DateTime::TimeZone; +use DateTime::Locale::Catalog; # ---------------------------------------------- Designs use vars qw(%defaultdesign); @@ -78,6 +81,76 @@ my $readit; ## Global Variables ## + +# ----------------------------------------------- SSI with retries: +# + +=pod + +=head1 Server Side include with retries: + +=over 4 + +=item * &ssi_with_retries(resource,retries form) + +Performs an ssi with some number of retries. Retries continue either +until the result is ok or until the retry count supplied by the +caller is exhausted. + +Inputs: + +=over 4 + +resource - Identifies the resource to insert. + +retries - Count of the number of retries allowed. + +form - Hash that identifies the rendering options. + +=back + +Returns: + +=over 4 + +content - The content of the response. If retries were exhausted this is empty. + +response - The response from the last attempt (which may or may not have been successful. + +=back + +=back + +=cut + +sub ssi_with_retries { + my ($resource, $retries, %form) = @_; + + + my $ok = 0; # True if we got a good response. + my $content; + my $response; + + # Try to get the ssi done. within the retries count: + + do { + ($content, $response) = &Apache::lonnet::ssi($resource, %form); + $ok = $response->is_success; + if (!$ok) { + &Apache::lonnet::logthis("Failed ssi_with_retries on $resource: ".$response->is_success.', '.$response->code.', '.$response->message); + } + $retries--; + } while (!$ok && ($retries > 0)); + + if (!$ok) { + $content = ''; # On error return an empty content. + } + return ($content, $response); + +} + + + # ----------------------------------------------- Filetypes/Languages/Copyright my %language; my %supported_language; @@ -198,7 +271,7 @@ BEGIN { } } &Apache::lonnet::logthis( - "INFO: Read file types"); + "INFO: Read file types"); $readit=1; } # end of unless($readit) @@ -214,14 +287,14 @@ BEGIN { =over 4 -=item * browser_and_searcher_javascript () +=item * &browser_and_searcher_javascript() XXReturns a string containing javascript with two functions, C and C. Returned string does not contain EscriptE tags. -=item * openbrowser(formname,elementname,only,omit) [javascript] +=item * &openbrowser(formname,elementname,only,omit) [javascript] inputs: formname, elementname, only, omit @@ -234,7 +307,7 @@ with the given extension. Can be a comm Specifying 'omit' will restrict the browser to NOT displaying files with the given extension. Can be a comma separated list. -=item * opensearcher(formname, elementname) [javascript] +=item * &opensearcher(formname,elementname) [javascript] Inputs: formname, elementname @@ -319,7 +392,7 @@ sub storeresurl { unless ($resurl=~/^\/res/) { return 0; } $resurl=~s/\/$//; &Apache::lonnet::put('environment',{'lastresurl' => $resurl}); - &Apache::lonnet::appenv('environment.lastresurl' => $resurl); + &Apache::lonnet::appenv({'environment.lastresurl' => $resurl}); return 1; } @@ -377,6 +450,25 @@ sub selectstudent_link { return ''; } +sub authorbrowser_javascript { + return <<"ENDAUTHORBRW"; + +ENDAUTHORBRW +} + sub coursebrowser_javascript { my ($domainfilter,$sec_element,$formname)=@_; my $crs_or_grp_alert = &mt('Please select the type of LON-CAPA entity - Course or Group - for which you wish to add/modify a user role'); @@ -514,6 +606,12 @@ sub selectcourse_link { '","'.$udomele.'","'.$desc.'","'.$extra_element.'","'.$multflag.'","'.$selecttype.'");'."'>".&mt('Select Course').""; } +sub selectauthor_link { + my ($form,$udom)=@_; + return ''. + &mt('Select Author').''; +} + sub check_uncheck_jscript { my $jscript = <<"ENDSCRT"; function checkAll(field) { @@ -539,10 +637,82 @@ ENDSCRT return $jscript; } +sub select_timezone { + my ($name,$selected,$onchange,$includeempty)=@_; + my $output=' menus. The select menus will be linked in that @@ -707,7 +877,7 @@ END =pod -=item * help_open_topic($topic, $text, $stayOnPage, $width, $height) +=item * &help_open_topic($topic,$text,$stayOnPage,$width,$height) Returns a string corresponding to an HTML link to the given help $topic, where $topic corresponds to the name of a .tex file in @@ -753,19 +923,23 @@ sub help_open_topic { } # Add the text - if ($text ne "") { - $template .= - "". - "
$text"; + if ($text ne "") { + $template.='' + .'' + .$text.''; } - # Add the graphic + # (Always) Add the graphic my $title = &mt('Online Help'); - my $helpicon=&lonhttpdurl("/adm/help/gif/smallHelp.gif"); - $template .= <<"ENDTEMPLATE"; - (Help: $topic) -ENDTEMPLATE - if ($text ne '') { $template.='
' }; + my $helpicon=&lonhttpdurl("/adm/help/help.png"); + $template.=' ' + .''.&mt('Help: [_1]',$topic).''; + if ($text ne "") { + $template.=''; + } return $template; } @@ -773,21 +947,31 @@ ENDTEMPLATE # This is a quicky function for Latex cheatsheet editing, since it # appears in at least four places sub helpLatexCheatsheet { - my $other = shift; + my ($topic,$text,$not_author) = @_; + my $out; my $addOther = ''; - if ($other) { - $addOther = Apache::loncommon::help_open_topic($other, shift, - undef, undef, 600) . - ''; - } - return '
'. - $addOther . - &Apache::loncommon::help_open_topic("Greek_Symbols",'Greek Symbols', - undef,undef,600) - .''. - &Apache::loncommon::help_open_topic("Other_Symbols",'Other Symbols', - undef,undef,600) - .'
'; + if ($topic) { + $addOther = ''.&Apache::loncommon::help_open_topic($topic,&mt($text), + undef, undef, 600). + ' '; + } + $out = '' # Start cheatsheet + .$addOther + .'' + .&Apache::loncommon::help_open_topic('Greek_Symbols',&mt('Greek Symbols'), + undef,undef,600) + .' ' + .&Apache::loncommon::help_open_topic('Other_Symbols',&mt('Other Symbols'), + undef,undef,600) + .''; + unless ($not_author) { + $out .= ' ' + .&Apache::loncommon::help_open_topic('Authoring_Output_Tags',&mt('Output Tags'), + undef,undef,600) + .''; + } + $out .= ''; # End cheatsheet + return $out; } sub general_help { @@ -796,6 +980,8 @@ sub general_help { $helptopic='Authoring_Intro'; } elsif ($env{'request.role'}=~/^cc/) { $helptopic='Course_Coordination_Intro'; + } elsif ($env{'request.role'}=~/^dc/) { + $helptopic='Domain_Coordination_Intro'; } return $helptopic; } @@ -953,7 +1139,7 @@ sub help_open_bug { { $template .= "". - "
$text"; + "$text"; } # Add the graphic @@ -998,7 +1184,7 @@ sub help_open_faq { { $template .= "". - " ENDROLE - my $titleinfo = ''.$title.''; + my $titleinfo = '

'.$title.'

'; if ($customtitle) { $titleinfo = $customtitle; } @@ -3981,7 +4267,7 @@ ENDROLE $dc_info = '('.$dc_info.')'; } - if ($env{'environment.remote'} eq 'off') { + if (($env{'environment.remote'} eq 'off') || ($args->{'suppress_header_logos'})) { # No Remote if ($env{'request.state'} eq 'construct') { $forcereg=1; @@ -4004,11 +4290,11 @@ ENDROLE $lastitem = $thisdisfn; } $titleinfo = - &Apache::loncommon::help_open_menu('','',3,'Authoring'). - 'Construction Space: '. - '' - .&Apache::lonhtmlcommon::crumbs($uname.'/'.$parentpath,'_top','/priv','','+1',1)."$lastitem
" + .&Apache::lonhtmlcommon::crumbs($uname.'/'.$parentpath,'_top','/priv','','+1',1)."$lastitem
" .&Apache::lonhtmlcommon::select_recent('construct','recent','this.form.action=this.form.recent.value;this.form.submit()') .'' .&Apache::lonmenu::constspaceform(); @@ -4130,18 +4416,25 @@ sub make_attr_string { Returns a uniform footer for LON-CAPA web pages. -Inputs: none +Inputs: 1 - optional reference to an args hash +If in the hash, key for noredirectlink has a value which evaluates to true, +a 'Continue' link is not displayed if the page contains an +internal redirect in the section, +i.e., $env{'internal.head.redirect'} exists =cut sub endbodytag { + my ($args) = @_; my $endbodytag=''; $endbodytag=&Apache::lontexconvert::jsMath_process()."\n".$endbodytag; if ( exists( $env{'internal.head.redirect'} ) ) { - $endbodytag= - "
". - &mt('Continue').''. - $endbodytag; + if (!(ref($args) eq 'HASH' && $args->{'noredirectlink'})) { + $endbodytag= + "
". + &mt('Continue').''. + $endbodytag; + } } return $endbodytag; } @@ -4175,6 +4468,10 @@ sub standard_css { my $vlink = &designparm($function.'.vlink', $domain); my $link = &designparm($function.'.link', $domain); + my $loginbg = &designparm('login.sidebg',$domain); + my $bgcol = &designparm('login.bgcol',$domain); + my $textcol = &designparm('login.textcol',$domain); + my $sans = 'Verdana,Arial,Helvetica,sans-serif'; my $mono = 'monospace'; my $data_table_head = $tabbg; @@ -4192,6 +4489,7 @@ sub standard_css { my $mail_other_hover = '#669999'; my $table_header = '#DDDDDD'; my $feedback_link_bg = '#BBBBBB'; + my $lg_border_color = '#C8C8C8'; my $border = ($env{'browser.type'} eq 'explorer' || $env{'browser.type'} eq 'safari' ) ? '0px 2px 0px 2px' @@ -4199,22 +4497,45 @@ sub standard_css { return < td { + background-color: #CCCCCC; + font-weight: bold; + text-align: left; } table.LC_data_table tr.LC_odd_row > td, +table.LC_pick_box tr > td.LC_odd_row, table.LC_aboutme_port tr td { background-color: $data_table_light; padding: 2px; } table.LC_data_table tr.LC_even_row > td, +table.LC_pick_box tr > td.LC_even_row, table.LC_aboutme_port tr.LC_even_row td { background-color: $data_table_dark; + padding: 2px; } table.LC_data_table tr.LC_data_table_highlight td { background-color: $data_table_darker; } +table.LC_data_table tr td.LC_leftcol_header { + background-color: $data_table_head; + font-weight: bold; +} table.LC_data_table tr.LC_empty_row td, table.LC_nested tr.LC_empty_row td { background-color: #FFFFFF; @@ -4505,7 +4825,7 @@ table.LC_nested tr.LC_empty_row td { table.LC_nested_outer tr th { font-weight: bold; background-color: $data_table_head; - font-size: smaller; + font-size: small; border-bottom: 1px solid #000000; } table.LC_nested_outer tr td.LC_subheader { @@ -4516,7 +4836,7 @@ table.LC_nested_outer tr td.LC_subheader text-align: right; } table.LC_nested tr.LC_info_row td { - background-color: #CCC; + background-color: #CCCCCC; font-weight: bold; font-size: small; text-align: center; @@ -4526,7 +4846,7 @@ table.LC_nested_outer tr th.LC_left_item text-align: left; } table.LC_nested td { - background-color: #FFF; + background-color: #FFFFFF; font-size: small; } table.LC_nested_outer tr th.LC_right_item, @@ -4537,18 +4857,18 @@ table.LC_nested tr td.LC_right_item { } table.LC_nested tr.LC_odd_row td { - background-color: #EEE; + background-color: #EEEEEE; } table.LC_createuser { } table.LC_createuser tr.LC_section_row td { - font-size: smaller; + font-size: small; } table.LC_createuser tr.LC_info_row td { - background-color: #CCC; + background-color: #CCCCCC; font-weight: bold; text-align: center; } @@ -4600,71 +4920,46 @@ table.LC_mail_list tr.LC_mail_even { table.LC_mail_list tr.LC_mail_odd { } - -table#LC_portfolio_actions { - width: auto; - background: $pgbg; - border: 0px; - border-spacing: 2px 2px; - padding: 0px; - margin: 0px; - border-collapse: separate; -} -table#LC_portfolio_actions td.LC_label { - background: $tabbg; - text-align: right; +table.LC_data_table tr > td.LC_browser_file, +table.LC_data_table tr > td.LC_browser_file_published { + background: #CCFF88; } -table#LC_portfolio_actions td.LC_value { - background: $tabbg; +table.LC_data_table tr > td.LC_browser_file_locked, +table.LC_data_table tr > td.LC_browser_file_unpublished { + background: #FFAA99; } - -table#LC_cstr_controls { - width: 100%; - border-collapse: collapse; +table.LC_data_table tr > td.LC_browser_file_obsolete { + background: #AAAAAA; } -table#LC_cstr_controls tr td { - border: 4px solid $pgbg; - padding: 4px; - text-align: center; - background: $tabbg; +table.LC_data_table tr > td.LC_browser_file_modified, +table.LC_data_table tr > td.LC_browser_file_metamodified { + background: #FFFF77; } -table#LC_cstr_controls tr th { - border: 4px solid $pgbg; - background: $table_header; - text-align: center; - font-family: $sans; - font-size: smaller; +table.LC_data_table tr.LC_browser_folder > td { + background: #CCCCFF; } -table#LC_browser { - -} -table#LC_browser tr th { - background: $table_header; +table.LC_data_table tr > td.LC_roles_is { +/* background: #77FF77; */ } -table#LC_browser tr td { - padding: 2px; -} -table#LC_browser tr.LC_browser_file, -table#LC_browser tr.LC_browser_file_published { - background: #CCFF88; +table.LC_data_table tr > td.LC_roles_future { + background: #FFFF77; } -table#LC_browser tr.LC_browser_file_locked, -table#LC_browser tr.LC_browser_file_unpublished { - background: #FFAA99; +table.LC_data_table tr > td.LC_roles_will { + background: #FFAA77; } -table#LC_browser tr.LC_browser_file_obsolete { - background: #AAAAAA; +table.LC_data_table tr > td.LC_roles_expired { + background: #FF7777; } -table#LC_browser tr.LC_browser_file_modified, -table#LC_browser tr.LC_browser_file_metamodified { - background: #FFFF77; +table.LC_data_table tr > td.LC_roles_will_not { + background: #AAFF77; } -table#LC_browser tr.LC_browser_folder { - background: #CCCCFF; +table.LC_data_table tr > td.LC_roles_selected { + background: #11CC55; } + span.LC_current_location { - font-size: x-large; + font-size:larger; background: $pgbg; } @@ -4763,9 +5058,18 @@ table.LC_pick_box td.LC_pick_box_title { background: $tabbg; font-weight: bold; text-align: right; + vertical-align: top; width: 184px; padding: 8px; } +table.LC_pick_box td.LC_selfenroll_pick_box_title { + background: $tabbg; + font-weight: bold; + text-align: right; + width: 350px; + padding: 8px; +} + table.LC_pick_box td.LC_pick_box_value { text-align: left; padding: 8px; @@ -4996,13 +5300,6 @@ table.LC_docs_documents td.LC_docs_docum padding: 4px; } -.LC_docs_course_commands div { - float: left; - border: 4px solid #AAAAAA; - padding: 4px; - background: #DDDDCC; -} - .LC_docs_entry_move { border: 0px; border-collapse: collapse; @@ -5100,23 +5397,6 @@ div.LC_clear_float_footer { } -div.LC_grade_select_mode { - font-family: $sans; -} -div.LC_grade_select_mode div div { - margin: 5px; -} -div.LC_grade_select_mode_selector { - margin: 5px; - float: left; -} -div.LC_grade_select_mode_selector_header { - font: bold medium $sans; -} -div.LC_grade_select_mode_type { - clear: left; -} - div.LC_grade_show_user { margin-top: 20px; border: 1px solid black; @@ -5124,7 +5404,8 @@ div.LC_grade_show_user { div.LC_grade_user_name { background: #DDDDEE; border-bottom: 1px solid black; - font: bold large $sans; + font-weight: bold; + font-size: large; } div.LC_grade_show_user_odd_row div.LC_grade_user_name { background: #DDEEDD; @@ -5143,7 +5424,8 @@ div.LC_grade_show_problem_header, div.LC_grade_submissions_header, div.LC_grade_message_center_header, div.LC_grade_assign_header { - font: bold large $sans; + font-weight: bold; + font-size: large; } div.LC_grade_show_problem_problem, div.LC_grade_submissions_body, @@ -5154,7 +5436,8 @@ div.LC_grade_assign_body { background: #FFFFFF; } span.LC_grade_check_note { - font: normal medium $sans; + font-weight: normal; + font-size: medium; display: inline; position: absolute; right: 1em; @@ -5164,12 +5447,13 @@ table.LC_scantron_action { width: 100%; } table.LC_scantron_action tr th { - font: normal bold $sans; + font-weight:bold; + font-style:normal; } - -div.LC_edit_problem_header, +.LC_edit_problem_header, div.LC_edit_problem_footer { - font: normal medium $sans; + font-weight: normal; + font-size: medium; margin: 2px; } div.LC_edit_problem_header, @@ -5186,12 +5470,14 @@ div.LC_edit_problem_header_edit_row { margin-bottom: 5px; } div.LC_edit_problem_header_title { - font: larger bold $sans; + font-weight: bold; + font-size: larger; background: $tabbg; padding: 3px; } table.LC_edit_problem_header_title { - font: larger bold $sans; + font-size: larger; + font-weight: bold; width: 100%; border-color: $pgbg; border-style: solid; @@ -5217,6 +5503,502 @@ hr.LC_edit_problem_divide { height: 3px; border: 0px; } +img.stift{ + border-width:0; + vertical-align:middle; +} + +table#LC_mainmenu{ + margin-top:10px; + width:80%; + +} + +table#LC_mainmenu td.LC_mainmenu_col_fieldset{ + vertical-align: top; + width: 45%; +} +.LC_mainmenu_fieldset_category { + color: $font; + background: $pgbg; + font-family: $sans; + font-size: small; + font-weight: bold; +} + +div.LC_createcourse { + margin: 10px 10px 10px 10px; +} + +/* ---- Remove when done ---- +# The following styles is part of the redesign of LON-CAPA and are +# subject to change during this project. +# Don't rely on their current functionality as they might be +# changed or removed. +# --------------------------*/ + +a:hover, +ol.LC_smallMenu a:hover, +ol#LC_MenuBreadcrumbs a:hover, +ol#LC_PathBreadcrumbs a:hover, +ul#LC_TabMainMenuContent a:hover, +.LC_FormSectionClearButton input:hover +ul.LC_TabContent li:hover a{ + color:#BF2317; + text-decoration:none; +} + +h1 { + padding:5px 10px 5px 20px; + line-height:130%; +} + +h2,h3,h4,h5,h6 +{ + margin:5px 0px 5px 0px; + padding:0px; + line-height:130%; +} +.LC_hcell{ + padding:3px 15px 3px 15px; + margin:0px; + background-color:$tabbg; + border-bottom:solid 1px $lg_border_color; +} +.LC_noBorder { + border:0px; +} + +.LC_bgLightGrey{ + background:URL(/adm/lonIcons/lightGreyBG.png) repeat-x left bottom; +} + + +/* Main Header with discription of Person, Course, etc. */ +.LC_HeadRight { + text-align: right; + float: right; + margin: 0px; + padding: 0px; + right:0; + position:absolute; + overflow:hidden; +} + +.LC_Right { + float: right; + margin: 0px; + padding: 0px; +} + +p, .LC_ContentBox { + padding: 10px; + +} +.LC_FormSectionClearButton input { + background-color:transparent; + border:0px; + cursor:pointer; + text-decoration:underline; +} + +.LC_help_open_topic { + color: #FFFFFF; + background-color: #EEEEFF; + margin: 1px; + padding: 4px; + border: 1px solid #000033; + white-space: nowrap; +} + +dl,ul,div,fieldset { + margin: 10px 10px 10px 0px; + overflow:hidden; +} +ol.LC_smallMenu, ol#LC_PathBreadcrumbs { + margin: 0px; +} + +ol.LC_smallMenu li { + display: inline; + padding: 5px 5px 0px 10px; + vertical-align: top; +} + +ol.LC_smallMenu li img { + vertical-align: bottom; +} + +ol.LC_smallMenu a { + font-size: 90%; + color: RGB(80, 80, 80); + text-decoration: none; +} +ol#LC_TabMainMenuContent, ul.LC_TabContent , +ul.LC_TabContentBigger { + display:block; + list-style:none; + margin: 0px; + padding: 0px; +} + +ol#LC_TabMainMenuContent li, ul.LC_TabContent li, +ul.LC_TabContentBigger li{ + display: inline; + border-right: solid 1px $lg_border_color; + float:left; + line-height:140%; + white-space:nowrap; +} +ol#LC_TabMainMenuContent li{ + vertical-align: bottom; + border-bottom: solid 1px RGB(175, 175, 175); + padding: 5px 10px 5px 10px; + margin-right:5px; + margin-bottom:3px; + font-weight: bold; + background: url(/adm/lonIcons/lightGreyBG.png) repeat-x left top; +} + +ol#LC_TabMainMenuContent li a{ + color: RGB(47, 47, 47); + text-decoration: none; +} +ul.LC_TabContent { + min-height:1.6em; +} +ul.LC_TabContent li{ + vertical-align:middle; + padding:0px 10px 0px 10px; + background-color:$tabbg; + border-bottom:solid 1px $lg_border_color; +} +ul.LC_TabContent li a, ul.LC_TabContent li{ + color:rgb(47,47,47); + text-decoration:none; + font-size:95%; + font-weight:bold; + padding-right: 16px; +} +ul.LC_TabContent li:hover, ul.LC_TabContent li.active{ + background:#FFFFFF url(/adm/lonIcons/open.gif) no-repeat scroll right center; + border-bottom:solid 1px #FFFFFF; + padding-right: 16px; +} +ul.LC_TabContentBigger li{ + vertical-align:bottom; + border-top:solid 1px $lg_border_color; + border-left:solid 1px $lg_border_color; + padding:5px 10px 5px 10px; + margin-left:2px; + background:url(/adm/lonIcons/lightGreyBG.png) repeat-x left top; +} +ul.LC_TabContentBigger li:hover, ul.LC_TabContentBigger li.active{ + background:url(/adm/lonIcons/lightGreyBG.png) repeat-x right bottom; +} +ul.LC_TabContentBigger li, ul.LC_TabContentBigger li a{ + font-size:110%; + font-weight:bold; +} +#LC_CourseDocuments, #LC_SupplementalCourseDocuments +{ + margin:0px; +} + +.LC_hideThis +{ + display:none; + visibility:hidden; +} + +ol#LC_MenuBreadcrumbs, ol#LC_PathBreadcrumbs { + border-top: solid 1px RGB(255, 255, 255); + height: 20px; + line-height: 20px; + vertical-align: bottom; + margin: 0px 0px 30px 0px; + padding-left: 10px; + list-style-position: inside; + background: url(/adm/lonIcons/lightGreyBG.png) repeat-x left top; +} + +ol#LC_MenuBreadcrumbs li, ol#LC_PathBreadcrumbs li { +/* + background: url(/adm/lonIcons/arrow_white.png) no-repeat left center; +*/ + display: inline; + padding: 0px 0px 0px 10px; + vertical-align: bottom; + overflow:hidden; +} + +ol#LC_MenuBreadcrumbs li a { + text-decoration: none; + font-size:90%; +} +ol#LC_PathBreadcrumbs li a{ + text-decoration:none; + font-size:100%; + font-weight:bold; +} +.LC_ContentBoxSpecial +{ + border: solid 1px $lg_border_color; +} +.LC_ContentBoxSpecialContactInfo +{ + border: solid 1px $lg_border_color; + max-width:25%; + min-width:25%; +} +.LC_AboutMe_Image +{ + float:left; + margin-right:10px; +} +.LC_Clear_AboutMe_Image +{ + clear:left; +} +dl.LC_ListStyleClean dt { + padding-right: 5px; + display: table-header-group; +} + +dl.LC_ListStyleClean dd { + display: table-row; +} + +.LC_ListStyleClean, +.LC_ListStyleSimple, +.LC_ListStyleNormal, +.LC_ListStyleNormal_Border, +.LC_ListStyleSpecial + { + /*display:block; */ + list-style-position: inside; + list-style-type: none; + overflow: hidden; + padding: 0px; +} + +.LC_ListStyleSimple li, +.LC_ListStyleSimple dd, +.LC_ListStyleNormal li, +.LC_ListStyleNormal dd, +.LC_ListStyleSpecial li, +.LC_ListStyleSpecial dd + { + margin: 0px; + padding: 5px 5px 5px 10px; + clear: both; +} + +.LC_ListStyleClean li, +.LC_ListStyleClean dd { + padding-top: 0px; + padding-bottom: 0px; +} + +.LC_ListStyleSimple dd, +.LC_ListStyleSimple li{ + border-bottom: solid 1px $lg_border_color; +} + +.LC_ListStyleSpecial li, +.LC_ListStyleSpecial dd { + list-style-type: none; + background-color: RGB(220, 220, 220); + margin-bottom: 4px; +} + +table.LC_SimpleTable { + margin:5px; + border:solid 1px $lg_border_color; + } + +table.LC_SimpleTable tr { + padding:0px; + border:solid 1px $lg_border_color; +} +table.LC_SimpleTable thead{ + background:rgb(220,220,220); +} + +div.LC_columnSection { + display: block; + clear: both; + overflow: hidden; + margin:0px; +} + +div.LC_columnSection>* { + float: left; + margin: 10px 20px 10px 0px; + overflow:hidden; +} + +.ContentBoxSpecialTemplate +{ + border: solid 1px $lg_border_color; +} +.ContentBoxTemplate { + padding:10px; +} + +div.LC_columnSection > .ContentBoxTemplate, +div.LC_columnSection > .ContentBoxSpecialTemplate + { + width: 600px; +} + +.clear{ + clear: both; + line-height: 0px; + font-size: 0px; + height: 0px; +} + +.LC_loginpage_container { + text-align:left; + margin : 0 auto; + width:65%; + padding: 10px; + height: auto; + background-color:#FFFFFF; + border:1px solid #CCCCCC; +} + + +.LC_loginpage_loginContainer { + float:left; + width: 182px; + border:1px solid #CCCCCC; + background-color:$loginbg; +} + +.LC_loginpage_loginContainer h2{ + margin-top:0; + display:block; + background:$bgcol; + color:$textcol; + padding-left:5px; +} +.LC_loginpage_loginInfo { + margin-left:20px; + float:left; + width:30%; + border:1px solid #CCCCCC; + padding:10px; +} + +.LC_loginpage_loginDomain { + margin-right:20px; + width:20%; + float:left; + padding:10px; +} + +.LC_loginpage_space { + clear: both; + margin-bottom: 20px; + border-bottom: 1px solid #CCCCCC; +} + +table em{ + font-weight: bold; + font-style: normal; +} + +table.LC_tableOfContent{ + border:none; + border-spacing: 1; + padding: 3px; + background-color: #FFFFFF; + font-size: 90%; +} +table.LC_tableOfContent a { + background-color:transparent; + text-decoration: none; +} + +table.LC_tableOfContent tr.LC_trOdd{ + background-color: #EEEEEE; +} + +table.LC_tableOfContent img{ + border: none; + height: 1.3em; + vertical-align: text-bottom; + margin-right: 0.3em; +} + +a#LC_content_toolbar_firsthomework{ + background-image:url(/res/adm/pages/open-first-problem.gif); +} + +a#LC_content_toolbar_launchnav{ + background-image:url(/res/adm/pages/start-navigation.gif); +} + +a#LC_content_toolbar_closenav{ + background-image:url(/res/adm/pages/close-navigation.gif); +} + +a#LC_content_toolbar_everything{ + background-image:url(/res/adm/pages/show-all.gif); +} + +a#LC_content_toolbar_uncompleted{ + background-image:url(/res/adm/pages/show-incomplete-problems.gif); +} + +#LC_content_toolbar_clearbubbles{ + background-image:url(/res/adm/pages/mark-discussionentries-read.gif); +} + +a#LC_content_toolbar_changefolder{ + background : url(/res/adm/pages/close-all-folders.gif) top center ; +} + +a#LC_content_toolbar_changefolder_toggled{ + background-image:url(/res/adm/pages/open-all-folders.gif); +} + +ul#LC_toolbar li a:hover{ + background-position: bottom center; +} + +ul#LC_toolbar{ + padding:0; + margin: 2px; + list-style:none; + position:relative; + background-color:white; +} + +ul#LC_toolbar li{ + border:1px solid white; + padding:0; + margin: 0; + float: left; + display:inline; + vertical-align:middle; +} + +a.LC_toolbarItem{ + display:block; + padding:0; + margin:0; + height: 32px; + width: 32px; + color:white; + border:0 none; + background-repeat:no-repeat; + background-color:transparent; +} + + END } @@ -5310,10 +6092,7 @@ Inputs: none sub font_settings { my $headerstring=''; - if (($env{'browser.os'} eq 'mac') && (!$env{'browser.mathml'})) { - $headerstring.= - ''; - } elsif (!$env{'browser.mathml'} && $env{'browser.unicode'}) { + if (!$env{'browser.mathml'} && $env{'browser.unicode'}) { $headerstring.= ''; } @@ -5372,8 +6151,15 @@ sub endheadtag { Returns a uniform complete .. section for LON-CAPA web pages. -Inputs: $title - optional title for the page - $head_extra - optional extra HTML to put inside the +Inputs: + +=over 4 + +$title - optional title for the page + +$head_extra - optional extra HTML to put inside the + +=back =cut @@ -5388,44 +6174,54 @@ sub head { Returns a complete .. section for LON-CAPA web pages. -Inputs: $title - optional title for the page - $head_extra - optional extra HTML to incude inside the - $args - additional optional args supported are: - only_body -> is true will set &bodytag() onlybodytag +Inputs: + +=over 4 + +$title - optional title for the page + +$head_extra - optional extra HTML to incude inside the + +$args - additional optional args supported are: + +=over 8 + + only_body -> is true will set &bodytag() onlybodytag arg on - no_nav_bar -> is true will set &bodytag() notopbar arg on - add_entries -> additional attributes to add to the - domain -> force to color decorate a page for a + no_nav_bar -> is true will set &bodytag() notopbar arg on + add_entries -> additional attributes to add to the + domain -> force to color decorate a page for a specific domain - function -> force usage of a specific rolish color + function -> force usage of a specific rolish color scheme - redirect -> see &headtag() - bgcolor -> override the default page bg color - js_ready -> return a string ready for being used in + redirect -> see &headtag() + bgcolor -> override the default page bg color + js_ready -> return a string ready for being used in a javascript writeln - html_encode -> return a string ready for being used in + html_encode -> return a string ready for being used in a html attribute - force_register -> if is true will turn on the &bodytag() + force_register -> if is true will turn on the &bodytag() $forcereg arg - body_title -> alternate text to use instead of $title + body_title -> alternate text to use instead of $title in the title box that appears, this text is not auto translated like the $title is - frameset -> if true will start with a + frameset -> if true will start with a rather than - no_title -> if true the title bar won't be shown - skip_phases -> hash ref of + no_title -> if true the title bar won't be shown + skip_phases -> hash ref of head -> skip the generation body -> skip all generation - - no_inline_link -> if true and in remote mode, don't show the + no_inline_link -> if true and in remote mode, don't show the 'Switch To Inline Menu' link - - no_auto_mt_title -> prevent &mt()ing the title arg - - inherit_jsmath -> when creating popup window in a page, + no_auto_mt_title -> prevent &mt()ing the title arg + inherit_jsmath -> when creating popup window in a page, should it have jsmath forced on by the current page +=back + +=back + =cut sub start_page { @@ -5466,10 +6262,28 @@ sub start_page { } if ($args->{'js_ready'}) { - $result = &js_ready($result); + $result = &js_ready($result); } if ($args->{'html_encode'}) { - $result = &html_encode($result); + $result = &html_encode($result); + } + + #Breadcrumbs + if (exists($args->{'bread_crumbs'}) or exists($args->{'bread_crumbs_component'})) { + &Apache::lonhtmlcommon::clear_breadcrumbs(); + #if any br links exists, add them to the breadcrumbs + if (exists($args->{'bread_crumbs'}) and ref($args->{'bread_crumbs'}) eq 'ARRAY') { + foreach my $crumb (@{$args->{'bread_crumbs'}}){ + &Apache::lonhtmlcommon::add_breadcrumb($crumb); + } + } + + #if bread_crumbs_component exists show it as headline else show only the breadcrumbs + if(exists($args->{'bread_crumbs_component'})){ + $result .= &Apache::lonhtmlcommon::breadcrumbs($args->{'bread_crumbs_component'}); + }else{ + $result .= &Apache::lonhtmlcommon::breadcrumbs(); + } } return $result; } @@ -5512,7 +6326,7 @@ sub end_page { if ($args->{'frameset'}) { $result .= ''; } else { - $result .= &endbodytag(); + $result .= &endbodytag($args); } $result .= "\n"; @@ -5617,7 +6431,7 @@ sub simple_error_page { } sub start_data_table_empty_row { - $row_count[0]++; +# $row_count[0]++; return ''."\n";; } @@ -6231,21 +7045,37 @@ sub default_quota { ['quotas'],$udom); if (ref($quotahash{'quotas'}) eq 'HASH') { if ($inststatus ne '') { - my @statuses = split(/:/,$inststatus); + my @statuses = map { &unescape($_); } split(/:/,$inststatus); foreach my $item (@statuses) { - if ($quotahash{'quotas'}{$item} ne '') { - if ($defquota eq '') { - $defquota = $quotahash{'quotas'}{$item}; - $settingstatus = $item; - } elsif ($quotahash{'quotas'}{$item} > $defquota) { - $defquota = $quotahash{'quotas'}{$item}; - $settingstatus = $item; + if (ref($quotahash{'quotas'}{'defaultquota'}) eq 'HASH') { + if ($quotahash{'quotas'}{'defaultquota'}{$item} ne '') { + if ($defquota eq '') { + $defquota = $quotahash{'quotas'}{'defaultquota'}{$item}; + $settingstatus = $item; + } elsif ($quotahash{'quotas'}{'defaultquota'}{$item} > $defquota) { + $defquota = $quotahash{'quotas'}{'defaultquota'}{$item}; + $settingstatus = $item; + } + } + } else { + if ($quotahash{'quotas'}{$item} ne '') { + if ($defquota eq '') { + $defquota = $quotahash{'quotas'}{$item}; + $settingstatus = $item; + } elsif ($quotahash{'quotas'}{$item} > $defquota) { + $defquota = $quotahash{'quotas'}{$item}; + $settingstatus = $item; + } } } } } if ($defquota eq '') { - $defquota = $quotahash{'quotas'}{'default'}; + if (ref($quotahash{'quotas'}{'defaultquota'}) eq 'HASH') { + $defquota = $quotahash{'quotas'}{'defaultquota'}{'default'}; + } else { + $defquota = $quotahash{'quotas'}{'default'}; + } $settingstatus = 'default'; } } else { @@ -6651,12 +7481,16 @@ sub instrule_disallow_msg { $text{'action'} = 'IDs'; } } - $response = &mt("The $text{'item'} you chose $text{'match'} the format of $text{'items'} defined for [_1], but the $text{'item'} $text{'do'} not exist in the institutional directory.",$domdesc).'
'; + $response = &mt("The $text{'item'} you chose $text{'match'} the format of $text{'items'} defined for [_1], but the $text{'item'} $text{'do'} not exist in the institutional directory.",''.$domdesc.'').'
'; if ($mode eq 'upload') { if ($checkitem eq 'username') { $response .= &mt("You will need to modify your upload file so it will include $text{'action'} with a different format -- $text{'one'} that will not conflict with 'official' institutional $text{'items'}."); } elsif ($checkitem eq 'id') { - $response .= &mt("Either upload a file which includes $text{'action'} with a different format -- $text{'one'} that will not conflict with 'official' institutional $text{'items'}, or when associating fields with data columns, omit an association for the ID/Student Number field."); + $response .= &mt("Either upload a file which includes $text{'action'} with a different format -- $text{'one'} that will not conflict with 'official' institutional $text{'items'}, or when associating fields with data columns, omit an association for the Student/Employee ID field."); + } + } elsif ($mode eq 'selfcreate') { + if ($checkitem eq 'id') { + $response .= &mt("You must either choose $text{'action'} with a different format -- $text{'one'} that will not conflict with 'official' institutional $text{'items'}, or leave the ID field blank."); } } else { if ($checkitem eq 'username') { @@ -6677,10 +7511,74 @@ sub personal_data_fieldtitles { middlename => 'Middle Name', generation => 'Generation', gen => 'Generation', + inststatus => 'Affiliation', ); return %fieldtitles; } +sub sorted_inst_types { + my ($dom) = @_; + my ($usertypes,$order) = &Apache::lonnet::retrieve_inst_usertypes($dom); + my $othertitle = &mt('All users'); + if ($env{'request.course.id'}) { + $othertitle = &mt('Any users'); + } + my @types; + if (ref($order) eq 'ARRAY') { + @types = @{$order}; + } + if (@types == 0) { + if (ref($usertypes) eq 'HASH') { + @types = sort(keys(%{$usertypes})); + } + } + if (keys(%{$usertypes}) > 0) { + $othertitle = &mt('Other users'); + } + return ($othertitle,$usertypes,\@types); +} + +sub get_institutional_codes { + my ($settings,$allcourses,$LC_code) = @_; +# Get complete list of course sections to update + my @currsections = (); + my @currxlists = (); + my $coursecode = $$settings{'internal.coursecode'}; + + if ($$settings{'internal.sectionnums'} ne '') { + @currsections = split(/,/,$$settings{'internal.sectionnums'}); + } + + if ($$settings{'internal.crosslistings'} ne '') { + @currxlists = split(/,/,$$settings{'internal.crosslistings'}); + } + + if (@currxlists > 0) { + foreach (@currxlists) { + if (m/^([^:]+):(\w*)$/) { + unless (grep/^$1$/,@{$allcourses}) { + push @{$allcourses},$1; + $$LC_code{$1} = $2; + } + } + } + } + + if (@currsections > 0) { + foreach (@currsections) { + if (m/^(\w+):(\w*)$/) { + my $sec = $coursecode.$1; + my $lc_sec = $2; + unless (grep/^$sec$/,@{$allcourses}) { + push @{$allcourses},$sec; + $$LC_code{$sec} = $lc_sec; + } + } + } + } + return; +} + =pod =back @@ -6689,7 +7587,7 @@ sub personal_data_fieldtitles { =over 4 -=item * get_unprocessed_cgi($query,$possible_names) +=item * &get_unprocessed_cgi($query,$possible_names) Modify the %env hash to contain unprocessed CGI form parameters held in $query. The parameters listed in $possible_names (an array reference), @@ -6718,7 +7616,7 @@ sub get_unprocessed_cgi { =pod -=item * cacheheader() +=item * &cacheheader() returns cache-controlling header code @@ -6735,7 +7633,7 @@ sub cacheheader { =pod -=item * no_cache($r) +=item * &no_cache($r) specifies header code to not have cache @@ -6771,7 +7669,7 @@ sub content_type { =pod -=item * add_to_env($name,$value) +=item * &add_to_env($name,$value) adds $name to the %env hash with value $value, if $name already exists, the entry is converted to an array @@ -6798,7 +7696,7 @@ sub add_to_env { =pod -=item * get_env_multiple($name) +=item * &get_env_multiple($name) gets $name from the %env hash, it seemlessly handles the cases where multiple values may be defined and end up as an array ref. @@ -6821,6 +7719,232 @@ sub get_env_multiple { return(@values); } +sub ask_for_embedded_content { + my ($actionurl,$state,$allfiles,$codebase,$args)=@_; + my $upload_output = ' + '; + $upload_output .= $state; + $upload_output .= 'Upload embedded files:
'.&start_data_table(); + + my $num = 0; + foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%{$allfiles})) { + $upload_output .= &start_data_table_row(). + ''.&Apache::loncommon::end_data_table_row(); + $num++; + } + $upload_output .= &Apache::loncommon::end_data_table().'
+ + + '.&mt('(only files for which a location has been provided will be uploaded)').' + '; + return $upload_output; +} + +sub upload_embedded { + my ($context,$dirpath,$uname,$udom,$dir_root,$url_root,$group,$disk_quota, + $current_disk_usage) = @_; + my $output; + for (my $i=0; $i<$env{'form.number_embedded_items'}; $i++) { + next if (!exists($env{'form.embedded_item_'.$i.'.filename'})); + my $orig_uploaded_filename = + $env{'form.embedded_item_'.$i.'.filename'}; + + $env{'form.embedded_orig_'.$i} = + &unescape($env{'form.embedded_orig_'.$i}); + my ($path,$fname) = + ($env{'form.embedded_orig_'.$i} =~ m{(.*/)([^/]*)}); + # no path, whole string is fname + if (!$fname) { $fname = $env{'form.embedded_orig_'.$i} }; + + $path = $env{'form.currentpath'}.$path; + $fname = &Apache::lonnet::clean_filename($fname); + # See if there is anything left + next if ($fname eq ''); + + # Check if file already exists as a file or directory. + my ($state,$msg); + if ($context eq 'portfolio') { + my $port_path = $dirpath; + if ($group ne '') { + $port_path = "groups/$group/$port_path"; + } + ($state,$msg) = &check_for_upload($path,$fname,$group,'embedded_item_'.$i, + $dir_root,$port_path,$disk_quota, + $current_disk_usage,$uname,$udom); + if ($state eq 'will_exceed_quota' + || $state eq 'file_locked' + || $state eq 'file_exists' ) { + $output .= $msg; + next; + } + } elsif (($context eq 'author') || ($context eq 'testbank')) { + ($state,$msg) = &check_for_existing($path,$fname,'embedded_item_'.$i); + if ($state eq 'exists') { + $output .= $msg; + next; + } + } + # Check if extension is valid + if (($fname =~ /\.(\w+)$/) && + (&Apache::loncommon::fileembstyle($1) eq 'hdn')) { + $output .= &mt('Invalid file extension ([_1]) - reserved for LONCAPA use - rename the file with a different extension and re-upload. ',$1); + next; + } elsif (($fname =~ /\.(\w+)$/) && + (!defined(&Apache::loncommon::fileembstyle($1)))) { + $output .= &mt('Unrecognized file extension ([_1]) - rename the file with a proper extension and re-upload.',$1); + next; + } elsif ($fname=~/\.(\d+)\.(\w+)$/) { + $output .= &mt('File name not allowed - rename the file to remove the number immediately before the file extension([_1]) and re-upload.',$2); + next; + } + + $env{'form.embedded_item_'.$i.'.filename'}=$fname; + if ($context eq 'portfolio') { + my $result= + &Apache::lonnet::userfileupload('embedded_item_'.$i,'', + $dirpath.$path); + if ($result !~ m|^/uploaded/|) { + $output .= '' + .&mt('An error occurred ([_1]) while trying to upload [_2] for embedded element [_3].' + ,$result,$orig_uploaded_filename,$env{'form.embedded_orig_'.$i}) + .'
'; + next; + } else { + $output .= '

'.&mt('Uploaded [_1]',''. + $path.$fname.'').'

'; + } + } else { +# Save the file + my $target = $env{'form.embedded_item_'.$i}; + my $fullpath = $dir_root.$dirpath.'/'.$path; + my $dest = $fullpath.$fname; + my $url = $url_root.$dirpath.'/'.$path.$fname; + my @parts=split(/\//,$fullpath); + my $count; + my $filepath = $dir_root; + for ($count=4;$count<=$#parts;$count++) { + $filepath .= "/$parts[$count]"; + if ((-e $filepath)!=1) { + mkdir($filepath,0770); + } + } + my $fh; + if (!open($fh,'>'.$dest)) { + &Apache::lonnet::logthis('Failed to create '.$dest); + $output .= ''. + &mt('An error occurred while trying to upload [_1] for embedded element [_2].',$orig_uploaded_filename,$env{'form.embedded_orig_'.$i}). + '
'; + } else { + if (!print $fh $env{'form.embedded_item_'.$i}) { + &Apache::lonnet::logthis('Failed to write to '.$dest); + $output .= ''. + &mt('An error occurred while writing the file [_1] for embedded element [_2].',$orig_uploaded_filename,$env{'form.embedded_orig_'.$i}). + '
'; + } else { + if ($context eq 'testbank') { + $output .= &mt('Embedded file uploaded successfully:'). + ' '. + $orig_uploaded_filename.'
'; + } else { + $output .= ''. + &mt('View embedded file: [_1]',''. + $orig_uploaded_filename.'').'
'; + } + } + close($fh); + } + } + } + return $output; +} + +sub check_for_existing { + my ($path,$fname,$element) = @_; + my ($state,$msg); + if (-d $path.'/'.$fname) { + $state = 'exists'; + $msg = &mt('Unable to upload [_1]. A directory by that name was found in [_2].',''.$fname.'',$path); + } elsif (-e $path.'/'.$fname) { + $state = 'exists'; + $msg = &mt('Unable to upload [_1]. A file by that name was found in [_2].',''.$fname.'',$path); + } + if ($state eq 'exists') { + $msg = ''.$msg.'
'; + } + return ($state,$msg); +} + +sub check_for_upload { + my ($path,$fname,$group,$element,$portfolio_root,$port_path, + $disk_quota,$current_disk_usage,$uname,$udom) = @_; + my $filesize = (length($env{'form.'.$element})) / 1000; #express in k (1024?) + my $getpropath = 1; + my @dir_list = &Apache::lonnet::dirlist($portfolio_root.$path,$udom,$uname, + $getpropath); + my $found_file = 0; + my $locked_file = 0; + foreach my $line (@dir_list) { + my ($file_name)=split(/\&/,$line,2); + if ($file_name eq $fname){ + $file_name = $path.$file_name; + if ($group ne '') { + $file_name = $group.$file_name; + } + $found_file = 1; + if (&Apache::lonnet::is_locked($file_name,$udom,$uname) eq 'true') { + $locked_file = 1; + } + } + } + if (($current_disk_usage + $filesize) > $disk_quota){ + my $msg = ''. + &mt('Unable to upload [_1]. (size = [_2] kilobytes). Disk quota will be exceeded.',''.$fname.'',$filesize).''. + '
'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',$disk_quota,$current_disk_usage); + return ('will_exceed_quota',$msg); + } elsif ($found_file) { + if ($locked_file) { + my $msg = ''; + $msg .= &mt('Unable to upload [_1]. A locked file by that name was found in [_2].',''.$fname.'',''.$port_path.$env{'form.currentpath'}.''); + $msg .= '
'; + $msg .= &mt('You will be able to rename or delete existing [_1] after a grade has been assigned.',''.$fname.''); + return ('file_locked',$msg); + } else { + my $msg = ''; + $msg .= &mt('Unable to upload [_1]. A file by that name was found in [_2].',''.$fname.'',$port_path.$env{'form.currentpath'}); + $msg .= ''; + $msg .= '
'; + $msg .= &mt('To upload, rename or delete existing [_1] in [_2].',''.$fname.'', $port_path.$env{'form.currentpath'}); + return ('file_exists',$msg); + } + } +} + =pod @@ -6830,7 +7954,7 @@ sub get_env_multiple { =over 4 -=item * upfile_store($r) +=item * &upfile_store($r) Store uploaded file, $r should be the HTTP Request object, needs $env{'form.upfile'} @@ -6860,7 +7984,7 @@ sub upfile_store { =pod -=item * load_tmp_file($r) +=item * &load_tmp_file($r) Load uploaded file from tmp, $r should be the HTTP Request object, needs $env{'form.datatoken'}, @@ -6884,7 +8008,7 @@ sub load_tmp_file { =pod -=item * upfile_record_sep() +=item * &upfile_record_sep() Separate uploaded file into records returns array of records, @@ -6906,7 +8030,7 @@ sub upfile_record_sep { =pod -=item * record_sep($record) +=item * &record_sep($record) Separate a record into fields $record should be an item from the upfile_record_sep(), needs $env{'form.upfiletype'} @@ -6991,7 +8115,7 @@ sub record_sep { =pod -=item * upfile_select_html() +=item * &upfile_select_html() Return HTML code to select a file from the users machine and specify the file type. @@ -7009,7 +8133,7 @@ sub upfile_select_html { # xml => &mt('HTML/XML'), ); my $Str = ''. - '
Type: '; foreach my $type (sort(keys(%Types))) { $Str .= '\n"; } @@ -7038,7 +8162,7 @@ sub get_samples { =pod -=item * csv_print_samples($r,$records) +=item * &csv_print_samples($r,$records) Prints a table of sample values from each column uploaded $r is an Apache Request ref, $records is an arrayref from @@ -7050,7 +8174,7 @@ Apache Request ref, $records is an array ###################################################### sub csv_print_samples { my ($r,$records) = @_; - my $samples = &get_samples($records,3); + my $samples = &get_samples($records,5); $r->print(&mt('Samples').'
'.&start_data_table(). &start_data_table_header_row()); @@ -7074,7 +8198,7 @@ sub csv_print_samples { =pod -=item * csv_print_select_table($r,$records,$d) +=item * &csv_print_select_table($r,$records,$d) Prints a table to create associations between values and table columns. @@ -7097,7 +8221,7 @@ sub csv_print_select_table { &end_data_table_header_row()."\n"); foreach my $array_ref (@$d) { my ($value,$display,$defaultcol)=@{ $array_ref }; - $r->print(&start_data_table_row().''); + $r->print(&start_data_table_row().''); $r->print(''.&end_data_table_row()."\n"); $i++; @@ -7120,7 +8244,7 @@ sub csv_print_select_table { =pod -=item * csv_samples_select_table($r,$records,$d) +=item * &csv_samples_select_table($r,$records,$d) Prints a table of sample values from the upload and can make associate samples to internal names. @@ -7136,7 +8260,8 @@ sub csv_samples_select_table { my ($r,$records,$d) = @_; my $i=0; # - my $samples = &get_samples($records,3); + my $max_samples = 5; + my $samples = &get_samples($records,$max_samples); $r->print(&start_data_table(). &start_data_table_header_row().''. @@ -7152,7 +8277,7 @@ sub csv_samples_select_table { $display.''); } $r->print(''; + my $depth = 1; + push(@path,$parent); + $output .= &assign_category_rows($itemcount,\@cats,$depth,$parent,\@path,\@currcategories); + pop(@path); + $output .= ''; + $itemcount ++; + } + $output .= &Apache::loncommon::end_data_table(); + } + } + } + return $output; +} + +=pod + +=item *&assign_category_rows() + +Create a datatable row for display of nested categories in a domain, +with checkboxes to allow a course to be categorized,called recursively. + +Inputs: + +itemcount - track row number for alternating colors + +cats - reference to array of arrays/hashes which encapsulates hierarchy of + categories and subcategories. + +depth - current depth in hierarchy of categories and sub-categories - 0 indexed. + +parent - parent of current category item + +path - Array containing all categories back up through the hierarchy from the + current category to the top level. + +currcategories - reference to array of current categories assigned to the course + +Returns: $output (markup to be displayed). + +=cut + +sub assign_category_rows { + my ($itemcount,$cats,$depth,$parent,$path,$currcategories) = @_; + my ($text,$name,$item,$chgstr); + if (ref($cats) eq 'ARRAY') { + my $maxdepth = scalar(@{$cats}); + if (ref($cats->[$depth]) eq 'HASH') { + if (ref($cats->[$depth]{$parent}) eq 'ARRAY') { + my $numchildren = @{$cats->[$depth]{$parent}}; + my $css_class = $itemcount%2?' class="LC_odd_row"':''; + $text .= ''; + } + } + } + return $text; +} + +############################################################ +############################################################ + + sub commit_customrole { - my ($udom,$uname,$url,$three,$four,$five,$start,$end) = @_; + my ($udom,$uname,$url,$three,$four,$five,$start,$end,$context) = @_; my $output = &mt('Assigning custom role').' "'.$five.'" by '.$four.':'.$three.' in '.$url. ($start?', '.&mt('starting').' '.localtime($start):''). ($end?', ending '.localtime($end):'').': '. &Apache::lonnet::assigncustomrole( - $udom,$uname,$url,$three,$four,$five,$end,$start). + $udom,$uname,$url,$three,$four,$five,$end,$start,undef,undef,$context). '
'; return $output; } @@ -7855,7 +9323,7 @@ sub commit_standardrole { $output = &mt('Assigning').' '.$three.' in '.$url. ($start?', '.&mt('starting').' '.localtime($start):''). ($end?', '.&mt('ending').' '.localtime($end):'').': '; - my $result = &Apache::lonnet::assignrole($udom,$uname,$url,$three,$end,$start); + my $result = &Apache::lonnet::assignrole($udom,$uname,$url,$three,$end,$start,'','',$context); if ($context eq 'auto') { $output .= $result.$linefeed; } else { @@ -7890,7 +9358,7 @@ sub commit_studentrole { } $oldsecurl = $uurl; $expire_role_result = - &Apache::lonnet::assignrole($udom,$uname,$uurl,'st',$now); + &Apache::lonnet::assignrole($udom,$uname,$uurl,'st',$now,'','',$context); if ($env{'request.course.sec'} ne '') { if ($expire_role_result eq 'refused') { my @roles = ('st'); @@ -7913,7 +9381,7 @@ sub commit_studentrole { } } if (($expire_role_result eq 'ok') || ($secchange == 0)) { - $modify_section_result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,'','',$cid); + $modify_section_result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,'','',$cid,'',$context); if ($modify_section_result =~ /^ok/) { if ($secchange == 1) { if ($sec eq '') { @@ -8076,19 +9544,28 @@ sub construct_course { $outcome .= $clonemsg.$linefeed; my %oldcenv=&Apache::lonnet::dump('environment',$$crsudom,$$crsunum); # Copy all files - &Apache::lonclonecourse::copycoursefiles($cloneid,$$courseid); + &Apache::lonclonecourse::copycoursefiles($cloneid,$$courseid,$args->{'datemode'},$args->{'dateshift'}); # Restore URL $cenv{'url'}=$oldcenv{'url'}; # Restore title $cenv{'description'}=$oldcenv{'description'}; -# restore grading mode - if (defined($oldcenv{'grading'})) { - $cenv{'grading'}=$oldcenv{'grading'}; - } # Mark as cloned $cenv{'clonedfrom'}=$cloneid; - delete($cenv{'default_enrollment_start_date'}); - delete($cenv{'default_enrollment_end_date'}); +# Need to clone grading mode + my %newenv=&Apache::lonnet::get('environment',['grading'],$$crsudom,$$crsunum); + $cenv{'grading'}=$newenv{'grading'}; +# Do not clone these environment entries + &Apache::lonnet::del('environment', + ['default_enrollment_start_date', + 'default_enrollment_end_date', + 'question.email', + 'policy.email', + 'comment.email', + 'pch.users.denied', + 'plc.users.denied', + 'hidefromcat', + 'categories'], + $$crsudom,$$crsunum); } # @@ -8116,7 +9593,6 @@ sub construct_course { } else { $cenv{'internal.courseowner'} = $args->{'curruser'}; } - my @badclasses = (); # Used to accumulate sections/crosslistings that did not pass classlist access check for course owner. if ($args->{'crssections'}) { $cenv{'internal.sectionnums'} = ''; @@ -8316,10 +9792,10 @@ sub construct_course { $outcome .= ($fatal?$errtext:'read ok').' - '; my $title; my $url; if ($args->{'firstres'} eq 'syl') { - $title='Syllabus'; + $title=&mt('Syllabus'); $url='/public/'.$$crsudom.'/'.$$crsunum.'/syllabus'; } else { - $title='Navigate Contents'; + $title=&mt('Navigate Contents'); $url='/adm/navmaps'; } @@ -8373,28 +9849,14 @@ sub icon { return &lonhttpdurl($iconname); } -sub lonhttpd_port { - my $lonhttpd_port=$Apache::lonnet::perlvar{'lonhttpdPort'}; - if (!defined($lonhttpd_port)) { $lonhttpd_port='8080'; } - # IE doesn't like a secure page getting images from a non-secure - # port (when logging we haven't parsed the browser type so default - # back to secure - if ((!exists($env{'browser.type'}) || $env{'browser.type'} eq 'explorer') - && $ENV{'SERVER_PORT'} == 443) { - return 443; - } - return $lonhttpd_port; - -} - sub lonhttpdurl { +# +# Had been used for "small fry" static images on separate port 8080. +# Modify here if lightweight http functionality desired again. +# Currently eliminated due to increasing firewall issues. +# my ($url)=@_; - - my $lonhttpd_port = &lonhttpd_port(); - if ($lonhttpd_port == 443) { - return 'https://'.$ENV{'SERVER_NAME'}.$url; - } - return 'http://'.$ENV{'SERVER_NAME'}.':'.$lonhttpd_port.$url; + return $url; } sub connection_aborted { @@ -8472,7 +9934,7 @@ sub init_user_environment { } # Give them a new cookie my $id = ($args->{'robot'} ? 'robot'.$args->{'robot'} - : $now); + : $now.$$.int(rand(10000))); $cookie="$username\_$id\_$domain\_$authhost"; # Initialize roles @@ -8560,6 +10022,17 @@ sub init_user_environment { } } + foreach my $tool ('aboutme','blog','portfolio') { + $userenv{'availabletools.'.$tool} = + &Apache::lonnet::usertools_access($username,$domain,$tool,'reload'); + } + + foreach my $crstype ('official','unofficial') { + $userenv{'canrequest.'.$crstype} = + &Apache::lonnet::usertools_access($username,$domain,$crstype, + 'reload','requestcourses'); + } + $env{'user.environment'} = "$lonids/$cookie.id"; if (tie(my %disk_env,'GDBM_File',"$lonids/$cookie.id", @@ -8572,8 +10045,8 @@ sub init_user_environment { } untie(%disk_env); } else { - &Apache::lonnet::logthis("WARNING: ". - 'Could not create environment storage in lonauth: '.$!.''); + &Apache::lonnet::logthis("WARNING: ". + 'Could not create environment storage in lonauth: '.$!.''); return 'error: '.$!; } } @@ -8587,12 +10060,54 @@ sub init_user_environment { sub _add_to_env { my ($idf,$env_data,$prefix) = @_; - while (my ($key,$value) = each(%$env_data)) { - $idf->{$prefix.$key} = $value; - $env{$prefix.$key} = $value; + if (ref($env_data) eq 'HASH') { + while (my ($key,$value) = each(%$env_data)) { + $idf->{$prefix.$key} = $value; + $env{$prefix.$key} = $value; + } + } +} + +# --- Get the symbolic name of a problem and the url +sub get_symb { + my ($request,$silent) = @_; + (my $url=$env{'form.url'}) =~ s-^https?\://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--; + my $symb=($env{'form.symb'} ne '' ? $env{'form.symb'} : (&Apache::lonnet::symbread($url))); + if ($symb eq '') { + if (!$silent) { + $request->print("Unable to handle ambiguous references:$url:."); + return (); + } } + &Apache::lonenc::check_decrypt(\$symb); + return ($symb); } +# --------------------------------------------------------------Get annotation + +sub get_annotation { + my ($symb,$enc) = @_; + + my $key = $symb; + if (!$enc) { + $key = + &Apache::lonnet::clutter((&Apache::lonnet::decode_symb($symb))[2]); + } + my %annotation=&Apache::lonnet::get('nohist_annotations',[$key]); + return $annotation{$key}; +} + +sub clean_symb { + my ($symb,$delete_enc) = @_; + + &Apache::lonenc::check_decrypt(\$symb); + my $enc = $env{'request.enc'}; + if ($delete_enc) { + delete($env{'request.enc'}); + } + + return ($symb,$enc); +} =pod
$text"; + "$text"; } # Add the graphic @@ -1017,7 +1203,7 @@ ENDTEMPLATE =pod -=item * change_content_javascript(): +=item * &change_content_javascript(): This and the next function allow you to create small sections of an otherwise static HTML page that you can update on the fly with @@ -1072,7 +1258,7 @@ DOMBASED =pod -=item * changable_area($name, $origContent): +=item * &changable_area($name,$origContent): This provides a "changable area" that can be modified on the fly via the Javascript code provided in C. $name is @@ -1096,7 +1282,7 @@ sub changable_area { =pod -=item * viewport_geometry_js { +=item * &viewport_geometry_js Provides javascript object (Geometry) which can provide information about the viewport geometry for the client browser. @@ -1143,7 +1329,7 @@ GEOMETRY =pod -=item * viewport_size_js { +=item * &viewport_size_js() Provides a javascript function to set values of two form elements - width and height (elements are passed in as arguments to the javascript function) to the dimensions of the user's browser window. @@ -1167,7 +1353,7 @@ DIMS =pod -=item * resize_textarea_js +=item * &resize_textarea_js() emits the needed javascript to resize a textarea to be as big as possible @@ -1176,6 +1362,7 @@ the id of the element to resize, second surrounds everything that comes after the textarea, this routine needs to be attached to the for the onload and onresize events. +=back =cut @@ -1228,8 +1415,6 @@ RESIZE =pod -=back - =head1 Excel and CSV file utility routines =over 4 @@ -1241,7 +1426,7 @@ RESIZE =pod -=item * csv_translate($text) +=item * &csv_translate($text) Translate $text to allow it to be output as a 'comma separated values' format. @@ -1262,7 +1447,7 @@ sub csv_translate { =pod -=item * define_excel_formats +=item * &define_excel_formats() Define some commonly used Excel cell formats. @@ -1318,7 +1503,7 @@ sub define_excel_formats { =pod -=item * create_workbook +=item * &create_workbook() Create an Excel worksheet. If it fails, output message on the request object and return undefs. @@ -1361,7 +1546,7 @@ sub create_workbook { =pod -=item * create_text_file +=item * &create_text_file() Create a file to write to and eventually make available to the user. If file creation fails, outputs an error message on the request object and @@ -1386,9 +1571,9 @@ sub create_text_file { $fh = Apache::File->new('>/home/httpd'.$filename); if (! defined($fh)) { $r->log_error("Couldn't open $filename for output $!"); - $r->print("Problems occured in creating the output file. ". - "This error has been logged. ". - "Please alert your LON-CAPA administrator."); + $r->print(&mt('Problems occurred in creating the output file. ' + .'This error has been logged. ' + .'Please alert your LON-CAPA administrator.')); } return ($fh,$filename) } @@ -1429,7 +1614,7 @@ sub domain_select { =over 4 -=item * multiple_select_form($name,$value,$size,$hash,$order) +=item * &multiple_select_form($name,$value,$size,$hash,$order) Returns a string containing a "; + $output.="\n".' form to allow a user to select options from a hash option_name => displayed text. @@ -1512,17 +1697,17 @@ sub select_form { sub display_filter { if (!$env{'form.show'}) { $env{'form.show'}=10; } if (!$env{'form.displayfilter'}) { $env{'form.displayfilter'}='currentfolder'; } - return ' '. + ' '. &mt('Filter [_1]', &select_form($env{'form.displayfilter'}, 'displayfilter', ('currentfolder' => 'Current folder/page', 'containing' => 'Containing phrase', 'none' => 'None'))). - ''; + ''; } sub gradeleveldescription { @@ -1566,7 +1751,7 @@ sub select_level_form { =pod -=item * select_dom_form($defdom,$name,$includeempty,$showdomdesc) +=item * &select_dom_form($defdom,$name,$includeempty,$showdomdesc,$autosubmit) Returns a string containing a \n"; + my $selectdomain = "
'.$embed_file.''; + if ($args->{'ignore_remote_references'} + && $embed_file =~ m{^\w+://}) { + $upload_output.=''.&mt("URL points to other server.").''; + } elsif ($args->{'error_on_invalid_names'} + && $embed_file ne &Apache::lonnet::clean_filename($embed_file,{'keep_path' => 1,})) { + + $upload_output.=''.&mt("Invalid characters").''; + + } else { + $upload_output .=' + + '; + my $attrib = join(':',@{$$allfiles{$embed_file}}); + $upload_output .= + "\n\t\t". + ''; + if (exists($$codebase{$embed_file})) { + $upload_output .= + "\n\t\t". + ''; + } + } + $upload_output .= '
'.$display.''.$display.''. &mt('Field').''.&mt('Samples').''); - foreach my $line (0..2) { + foreach my $line (0..($max_samples-1)) { if (defined($samples->[$line]{$key})) { $r->print($samples->[$line]{$key}."
\n"); } @@ -7170,7 +8295,7 @@ sub csv_samples_select_table { =pod -=item clean_excel_name($name) +=item * &clean_excel_name($name) Returns a replacement for $name which does not contain any illegal characters. @@ -7189,7 +8314,7 @@ sub clean_excel_name { =pod -=item * check_if_partid_hidden($id,$symb,$udom,$uname) +=item * &check_if_partid_hidden($id,$symb,$udom,$uname) Returns either 1 or undef @@ -7230,7 +8355,7 @@ sub check_if_partid_hidden { =over 4 -=item get_cgi_id +=item * &get_cgi_id() Inputs: none @@ -7254,7 +8379,7 @@ sub get_cgi_id { =pod -=item DrawBarGraph +=item * &DrawBarGraph() Facilitates the plotting of data in a (stacked) bar graph. Puts plot definition data into the users environment in order for @@ -7389,7 +8514,7 @@ sub DrawBarGraph { $ValuesHash{$id.'.'.$key} = $value; } # - &Apache::lonnet::appenv(%ValuesHash); + &Apache::lonnet::appenv(\%ValuesHash); return ''; } @@ -7398,7 +8523,7 @@ sub DrawBarGraph { =pod -=item DrawXYGraph +=item * &DrawXYGraph() Facilitates the plotting of data in an XY graph. Puts plot definition data into the users environment in order for @@ -7479,7 +8604,7 @@ sub DrawXYGraph { $ValuesHash{$id.'.'.$key} = $value; } # - &Apache::lonnet::appenv(%ValuesHash); + &Apache::lonnet::appenv(\%ValuesHash); return ''; } @@ -7488,7 +8613,7 @@ sub DrawXYGraph { =pod -=item DrawXYYGraph +=item * &DrawXYYGraph() Facilitates the plotting of data in an XY graph with two Y axes. Puts plot definition data into the users environment in order for @@ -7581,7 +8706,7 @@ sub DrawXYYGraph { $ValuesHash{$id.'.'.$key} = $value; } # - &Apache::lonnet::appenv(%ValuesHash); + &Apache::lonnet::appenv(\%ValuesHash); return ''; } @@ -7598,7 +8723,7 @@ Bad place for them but what the hell. =over 4 -=item &chartlink +=item * &chartlink() Returns a link to the chart for a specific student. @@ -7637,9 +8762,9 @@ sub chartlink { =over 4 -=item &restore_course_settings +=item * &restore_course_settings() -=item &store_course_settings +=item * &store_course_settings() Restores/Store indicated form parameters from the course environment. Will not overwrite existing values of the form parameters. @@ -7713,7 +8838,7 @@ sub store_settings { 'got error:'.$put_result); } # Make sure these settings stick around in this session, too - &Apache::lonnet::appenv(%AppHash); + &Apache::lonnet::appenv(\%AppHash); return; } @@ -7750,11 +8875,12 @@ sub restore_settings { =over 4 -=item &build_recipient_list +=item * &build_recipient_list() -Build recipient lists for three types of e-mail: -(a) Error Reports, (b) Package Updates, (c) Help requests, generated by -lonerrorhandler.pm, CHECKRPMS and lonsupportreq.pm respectively. +Build recipient lists for four types of e-mail: +(a) Error Reports, (b) Package Updates, (c) lonstatus warnings/errors +(d) Help requests, generated by +lonerrorhandler.pm, CHECKRPMS, loncron, and lonsupportreq.pm respectively. Inputs: defmail (scalar - email address of default recipient), @@ -7763,7 +8889,9 @@ defdom (domain for which to retrieve con origmail (scalar - email address of recipient from loncapa.conf, i.e., predates configuration by DC via domainprefs.pm -Returns: comma separated list of addresses to which to send e-mail. +Returns: comma separated list of addresses to which to send e-mail. + +=back =cut @@ -7776,23 +8904,29 @@ sub build_recipient_list { my %domconfig = &Apache::lonnet::get_dom('configuration',['contacts'],$defdom); if (ref($domconfig{'contacts'}) eq 'HASH') { - if (ref($domconfig{'contacts'}{$mailing}) eq 'HASH') { - my @contacts = ('adminemail','supportemail'); - foreach my $item (@contacts) { - if ($domconfig{'contacts'}{$mailing}{$item}) { - my $addr = $domconfig{'contacts'}{$item}; - if (!grep(/^\Q$addr\E$/,@recipients)) { - push(@recipients,$addr); + if (exists($domconfig{'contacts'}{$mailing})) { + if (ref($domconfig{'contacts'}{$mailing}) eq 'HASH') { + my @contacts = ('adminemail','supportemail'); + foreach my $item (@contacts) { + if ($domconfig{'contacts'}{$mailing}{$item}) { + my $addr = $domconfig{'contacts'}{$item}; + if (!grep(/^\Q$addr\E$/,@recipients)) { + push(@recipients,$addr); + } } + $otheremails = $domconfig{'contacts'}{$mailing}{'others'}; } - $otheremails = $domconfig{'contacts'}{$mailing}{'others'}; } + } elsif ($origmail ne '') { + push(@recipients,$origmail); } } elsif ($origmail ne '') { push(@recipients,$origmail); } - if ($defmail ne '') { - push(@recipients,$defmail); + if (defined($defmail)) { + if ($defmail ne '') { + push(@recipients,$defmail); + } } if ($otheremails) { my @others; @@ -7814,13 +8948,347 @@ sub build_recipient_list { ############################################################ ############################################################ +=pod + +=head1 Course Catalog Routines + +=over 4 + +=item * &gather_categories() + +Converts category definitions - keys of categories hash stored in +coursecategories in configuration.db on the primary library server in a +domain - to an array. Also generates javascript and idx hash used to +generate Domain Coordinator interface for editing Course Categories. + +Inputs: + +categories (reference to hash of category definitions). + +cats (reference to array of arrays/hashes which encapsulates hierarchy of + categories and subcategories). + +idx (reference to hash of counters used in Domain Coordinator interface for + editing Course Categories). + +jsarray (reference to array of categories used to create Javascript arrays for + Domain Coordinator interface for editing Course Categories). + +Returns: nothing + +Side effects: populates cats, idx and jsarray. + +=cut + +sub gather_categories { + my ($categories,$cats,$idx,$jsarray) = @_; + my %counters; + my $num = 0; + foreach my $item (keys(%{$categories})) { + my ($cat,$container,$depth) = map { &unescape($_); } split(/:/,$item); + if ($container eq '' && $depth == 0) { + $cats->[$depth][$categories->{$item}] = $cat; + } else { + $cats->[$depth]{$container}[$categories->{$item}] = $cat; + } + my ($escitem,$tail) = split(/:/,$item,2); + if ($counters{$tail} eq '') { + $counters{$tail} = $num; + $num ++; + } + if (ref($idx) eq 'HASH') { + $idx->{$item} = $counters{$tail}; + } + if (ref($jsarray) eq 'ARRAY') { + push(@{$jsarray->[$counters{$tail}]},$item); + } + } + return; +} + +=pod + +=item * &extract_categories() + +Used to generate breadcrumb trails for course categories. + +Inputs: + +categories (reference to hash of category definitions). + +cats (reference to array of arrays/hashes which encapsulates hierarchy of + categories and subcategories). + +trails (reference to array of breacrumb trails for each category). + +allitems (reference to hash - key is category key + (format: escaped(name):escaped(parent category):depth in hierarchy). + +idx (reference to hash of counters used in Domain Coordinator interface for + editing Course Categories). + +jsarray (reference to array of categories used to create Javascript arrays for + Domain Coordinator interface for editing Course Categories). + +subcats (reference to hash of arrays containing all subcategories within each + category, -recursive) + +Returns: nothing + +Side effects: populates trails and allitems hash references. + +=cut + +sub extract_categories { + my ($categories,$cats,$trails,$allitems,$idx,$jsarray,$subcats) = @_; + if (ref($categories) eq 'HASH') { + &gather_categories($categories,$cats,$idx,$jsarray); + if (ref($cats->[0]) eq 'ARRAY') { + for (my $i=0; $i<@{$cats->[0]}; $i++) { + my $name = $cats->[0][$i]; + my $item = &escape($name).'::0'; + my $trailstr; + if ($name eq 'instcode') { + $trailstr = &mt('Official courses (with institutional codes)'); + } else { + $trailstr = $name; + } + if ($allitems->{$item} eq '') { + push(@{$trails},$trailstr); + $allitems->{$item} = scalar(@{$trails})-1; + } + my @parents = ($name); + if (ref($cats->[1]{$name}) eq 'ARRAY') { + for (my $j=0; $j<@{$cats->[1]{$name}}; $j++) { + my $category = $cats->[1]{$name}[$j]; + if (ref($subcats) eq 'HASH') { + push(@{$subcats->{$item}},&escape($category).':'.&escape($name).':1'); + } + &recurse_categories($cats,2,$category,$trails,$allitems,\@parents,$subcats); + } + } else { + if (ref($subcats) eq 'HASH') { + $subcats->{$item} = []; + } + } + } + } + } + return; +} + +=pod + +=item *&recurse_categories() + +Recursively used to generate breadcrumb trails for course categories. + +Inputs: + +cats (reference to array of arrays/hashes which encapsulates hierarchy of + categories and subcategories). + +depth (current depth in hierarchy of categories and sub-categories - 0 indexed). + +category (current course category, for which breadcrumb trail is being generated). + +trails (reference to array of breadcrumb trails for each category). + +allitems (reference to hash - key is category key + (format: escaped(name):escaped(parent category):depth in hierarchy). + +parents (array containing containers directories for current category, + back to top level). + +Returns: nothing + +Side effects: populates trails and allitems hash references + +=cut + +sub recurse_categories { + my ($cats,$depth,$category,$trails,$allitems,$parents,$subcats) = @_; + my $shallower = $depth - 1; + if (ref($cats->[$depth]{$category}) eq 'ARRAY') { + for (my $k=0; $k<@{$cats->[$depth]{$category}}; $k++) { + my $name = $cats->[$depth]{$category}[$k]; + my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower; + my $trailstr = join(' -> ',(@{$parents},$category)); + if ($allitems->{$item} eq '') { + push(@{$trails},$trailstr); + $allitems->{$item} = scalar(@{$trails})-1; + } + my $deeper = $depth+1; + push(@{$parents},$category); + if (ref($subcats) eq 'HASH') { + my $subcat = &escape($name).':'.$category.':'.$depth; + for (my $j=@{$parents}; $j>=0; $j--) { + my $higher; + if ($j > 0) { + $higher = &escape($parents->[$j]).':'. + &escape($parents->[$j-1]).':'.$j; + } else { + $higher = &escape($parents->[$j]).'::'.$j; + } + push(@{$subcats->{$higher}},$subcat); + } + } + &recurse_categories($cats,$deeper,$name,$trails,$allitems,$parents, + $subcats); + pop(@{$parents}); + } + } else { + my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower; + my $trailstr = join(' -> ',(@{$parents},$category)); + if ($allitems->{$item} eq '') { + push(@{$trails},$trailstr); + $allitems->{$item} = scalar(@{$trails})-1; + } + } + return; +} + +=pod + +=item *&assign_categories_table() + +Create a datatable for display of hierarchical categories in a domain, +with checkboxes to allow a course to be categorized. + +Inputs: + +cathash - reference to hash of categories defined for the domain (from + configuration.db) + +currcat - scalar with an & separated list of categories assigned to a course. + +Returns: $output (markup to be displayed) + +=cut + +sub assign_categories_table { + my ($cathash,$currcat) = @_; + my $output; + if (ref($cathash) eq 'HASH') { + my (@cats,@trails,%allitems,%idx,@jsarray,@path,$maxdepth); + &extract_categories($cathash,\@cats,\@trails,\%allitems,\%idx,\@jsarray); + $maxdepth = scalar(@cats); + if (@cats > 0) { + my $itemcount = 0; + if (ref($cats[0]) eq 'ARRAY') { + $output = &Apache::loncommon::start_data_table(); + my @currcategories; + if ($currcat ne '') { + @currcategories = split('&',$currcat); + } + for (my $i=0; $i<@{$cats[0]}; $i++) { + my $parent = $cats[0][$i]; + my $css_class = $itemcount%2?' class="LC_odd_row"':''; + next if ($parent eq 'instcode'); + my $item = &escape($parent).'::0'; + my $checked = ''; + if (@currcategories > 0) { + if (grep(/^\Q$item\E$/,@currcategories)) { + $checked = ' checked="checked" '; + } + } + $output .= '
'. + ''.$parent.''. + '
'; + for (my $j=0; $j<$numchildren; $j++) { + $name = $cats->[$depth]{$parent}[$j]; + $item = &escape($name).':'.&escape($parent).':'.$depth; + my $deeper = $depth+1; + my $checked = ''; + if (ref($currcategories) eq 'ARRAY') { + if (@{$currcategories} > 0) { + if (grep(/^\Q$item\E$/,@{$currcategories})) { + $checked = ' checked="checked" '; + } + } + } + $text .= ''; + } + $text .= '
'. + ''. + ''; + if (ref($path) eq 'ARRAY') { + push(@{$path},$name); + $text .= &assign_category_rows($itemcount,$cats,$deeper,$name,$path,$currcategories); + pop(@{$path}); + } + $text .= '