# The LearningOnline Network
# Syllabus
#
# $Id: lonsyllabus.pm,v 1.118 2013/05/03 21:57:13 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
# This file is part of the LearningOnline Network with CAPA (LON-CAPA).
#
# LON-CAPA is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# LON-CAPA is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with LON-CAPA; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# /home/httpd/html/adm/gpl.txt
#
# http://www.lon-capa.org/
#
package Apache::lonsyllabus;
use strict;
use Apache::lontemplate;
use Apache::Constants qw(:common);
use Apache::loncommon;
use Apache::lonnet;
use Apache::lontexconvert;
use Apache::lonfeedback;
use Apache::lonannounce;
use Apache::lonlocal;
use Apache::lonhtmlcommon;
use Apache::lonspeller();
use HTML::Entities();
sub handler {
my $r = shift;
&Apache::loncommon::content_type($r,'text/html');
$r->send_http_header;
return OK if $r->header_only;
my $target=$env{'form.grade_target'};
# --------------------------------------------------- Get course info from URL
my (undef,undef,$cdom,$cnum)=split(/\//,$r->uri);
# ------------------------------------------------------------ Get query string
&Apache::loncommon::get_unprocessed_cgi
($ENV{'QUERY_STRING'},['register','forceedit','todocs',
'folderpath','title']);
# ----------------------------------------------------- Is this even a course?
my $homeserver=&Apache::lonnet::homeserver($cnum,$cdom);
if ($homeserver eq 'no_host') {
&Apache::loncommon::content_type($r,'text/html');
$r->send_http_header;
&Apache::loncommon::simple_error_page($r,'No syllabus available',
'No syllabus available');
return OK;
} elsif (!&Apache::lonnet::is_course($cdom,$cnum)) {
&Apache::loncommon::content_type($r,'text/html');
$r->send_http_header;
&Apache::loncommon::simple_error_page($r,'No syllabus available',
'The course/community for which the syllabus was requested does not exist.');
return OK;
}
# ------------------------------------- There is such a course, get environment
my %courseenv=&Apache::lonnet::dump('environment',$cdom,$cnum);
# ------------------------------------------------------------ Print the screen
if ($target eq 'tex') {
$r->print(&Apache::lonprintout::print_latex_header($env{'form.latex_type'}));
}
# --------------------------------------------------------------- Force Student
my ($forceedit,$forcestudent);
if ($env{'form.forceedit'}) { $forceedit=1; }
if (!$forceedit) {
$forcestudent=1;
}
# --------------------------------------------------------------- Check Privileges
my $allowed = 0;
if ($env{'user.environment'}) {
# does this user have privileges to post, etc?
if ($env{'request.course.id'}
&& $cdom eq $env{'course.'.$env{'request.course.id'}.'.domain'}
&& $cnum eq $env{'course.'.$env{'request.course.id'}.'.num'}) {
$allowed=&Apache::lonnet::allowed('mdc',$env{'request.course.id'});
if ($forcestudent or $target eq 'tex') { $allowed=0; }
}
}
# -------------------------------------------------- Let's see who handles this
my $external=$courseenv{'externalsyllabus'};
my $uploaded=$courseenv{'uploadedsyllabus'};
if ($uploaded =~/\w/) {
if ($external =~ m{\Q$uploaded\E$}) {
undef($external);
}
unless ($allowed && $forceedit) {
my $file=&Apache::lonnet::filelocation("",$uploaded);
if ($file =~ /\.(sty|css|js|txt|tex|html?)$/) {
my $filetype;
if ($file =~ /\.(sty|css|js|txt|tex)$/) {
$filetype=$1;
} else {
$filetype='html';
}
my $result = '';
my $filecontents=&Apache::lonnet::getfile($file);
if ($filecontents eq -1) {
$r->print(&mt('Syllabus file unavailable'));
} elsif ($filetype eq 'html') {
my %mystyle;
&Apache::structuretags::reset_problem_globals();
my $oldfile = $env{'request.filename'};
$env{'request.filename'} = $uploaded;
$result = &Apache::lonxml::xmlparse($r,'web',$filecontents,
'',%mystyle);
&Apache::structuretags::reset_problem_globals();
&Apache::lonhomework::finished_parsing();
$env{'request.filename'} = $oldfile;
&Apache::lonxml::add_messages(\$result);
$r->print($result);
}
} else {
$r->print(&Apache::lonwrapper::wrapper($uploaded));
}
return OK;
}
} elsif ($external=~/\w/) {
unless ($allowed && $forceedit) {
$r->print(&Apache::lonwrapper::wrapper($external));
return OK;
}
}
# ------------------------------ The buck stops here: internal syllabus display
# --------------------------------------------------------- The syllabus fields
my %syllabusfields=&Apache::lonlocal::texthash(
'aaa_instructorinfo' => 'Instructor Information',
'bbb_description' => 'Course Description',
'ccc_prereq' => 'Prerequisites',
'cdc_classhours' => 'Class Hours',
'ddd_officehours' => 'Office Hours',
'eee_helproom' => 'Helproom Hours',
'efe_projectinfo' => 'Project Information',
'fff_examinfo' => 'Exam Information',
'fgf_deadlines' => 'Deadlines',
'ggg_grading' => 'Grading Information',
'hhh_readings' => 'Readings',
'iii_coursepack' => 'Coursepack',
'jjj_weblinks' => 'Web Links',
'kkk_textbook' => 'Textbook',
'lll_includeurl' => 'URLs To Include in Syllabus');
# ----------------------------------------------------------------- Make header
if ($target ne 'tex') {
my $rss_link = &Apache::lonrss::rss_link($cnum,$cdom);
my $js;
if ($env{'form.backto'} eq 'coursecatalog') {
$js .= <<"ENDSCRIPT";
ENDSCRIPT
}
if ($allowed && $forceedit) {
my $check_uncheck = &Apache::loncommon::check_uncheck_jscript();
my $invurl = &mt('Invalid URL');
my $urlregexp = <<'ENDREGEXP';
/^([a-z]([a-z]|\d|\+|-|\.)*):(\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?((\[(|(v[\da-f]{1,}\.(([a-z]|\d|-|\.|_|~)|[!\$&'\(\)\*\+,;=]|:)+))\])|((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=])*)(:\d*)?)(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*|(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)|((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)|((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)){0})(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i
ENDREGEXP
$js .= <<"ENDSCRIPT";
ENDSCRIPT
}
my $args = {'function' => undef,
'domain' => $cdom};
my $forcereg;
if ($env{'form.register'}) {
$forcereg = 1;
$args->{'force_register'} = $forcereg;
}
if ($env{'form.backto'} eq 'coursecatalog') {
&Apache::lonhtmlcommon::clear_breadcrumbs();
my $brcrum = [{href=>"javascript:ToCatalog();",
text=>&mt('Course/Community Catalog'),
no_mt=>1}
];
if ($env{'form.coursenum'} ne '') {
push(@{$brcrum},
{href=>"javascript:ToCatalog('details')",
text=>"Course details"});
}
push(@{$brcrum},
{href=>$r->uri,
text=>"Course syllabus"});
$args->{'bread_crumbs'} = $brcrum;
} elsif ($env{'form.folderpath'} =~ /^supplemental/) {
my $crstype = &Apache::loncommon::course_type();
my $title = $env{'form.title'};
if ($title eq '') {
$title = &mt('Syllabus');
}
my $brcrum =
&Apache::lonhtmlcommon::docs_breadcrumbs(undef,$crstype,undef,$title,1);
if (ref($brcrum) eq 'ARRAY') {
$args->{'bread_crumbs'} = $brcrum;
}
}
my $start_page =
&Apache::loncommon::start_page("Syllabus", $rss_link.$js,$args);
$r->print($start_page);
}
# ---------------------------------------------------------- Load syllabus info
my %syllabus=&Apache::lonnet::dump('syllabus',$cdom,$cnum);
my %displayfields;
# This handler might be called anonymously ...
# ----------------------------------------------------- Only if not public call
if ($allowed) {
#store what the user typed in to the template
if ($env{'form.storesyl'}) {
foreach my $syl_field (keys(%syllabusfields)) {
my $field=$env{'form.'.$syl_field};
chomp($field);
$field=~s/\s+$//s;
$field=~s/^\s+//s;
$field=~s/\
$//s;
$field=&Apache::lonfeedback::clear_out_html($field,1);
#here it will be stored
$syllabus{$syl_field}=$field;
if ($syl_field eq 'lll_includeurl') { # clean up included URLs
my $field='';
foreach my $value (split(/\n/,$syllabus{$syl_field})) {
my $url=$value;
# get rid of leading and trailing spaces
$url=~s/^\s+//;
$url=~s/\s+$//;
if ($url=~m|^https?\://([^/]+)/(.+)$|) {
my $host = $1;
my $remainder=$2;
# remove the hostname from internal URLs
my $hostname = &Apache::lonnet::hostname($host);
my %all_hostnames = &Apache::lonnet::all_hostnames();
foreach my $possible_host (keys(%all_hostnames)) {
if ($possible_host =~ /\Q$hostname\E/i) {
$url=$remainder;
}
}
}
# norm internal URLs
unless ($url=~/^https?\:/) {
$url=&Apache::lonnet::clutter($url);
}
# re-assemble field
if ($url) {
$field.=$url."\n";
}
}
$syllabus{$syl_field}=$field;
}
}
$syllabus{'uploaded.domain'}=$env{'user.domain'};
$syllabus{'uploaded.name'}=$env{'user.name'};
$syllabus{'uploaded.lastmodified'}=time;
&Apache::lonnet::put('syllabus',\%syllabus,$cdom,$cnum);
my %storehash;
if ($courseenv{'uploadedsyllabus'}) {
&Apache::lonnet::delenv('course.'.$env{'request.course.id'}.'.uploadedsyllabus');
$storehash{'uploadedsyllabus'} = '';
}
if ($courseenv{'externalsyllabus'}) {
&Apache::lonnet::delenv('course.'.$env{'request.course.id'}.'.externalsyllabus');
$storehash{'externalsyllabus'} = '';
}
if ($courseenv{'externalsyllabus'} || $courseenv{'uploadedsyllabus'}) {
&Apache::lonnet::put('environment',\%storehash,$cdom,$cnum);
undef($uploaded);
undef($external);
}
} elsif ($env{'form.storeurl'}) {
if ($env{'form.externalsyllabus'} =~ m{^https?://}) {
if ($env{'form.externalsyllabus'} eq $external) {
$r->print('
' .&mt('This syllabus can be publicly viewed at [_1]' ,''.$protocol.'://'.&Apache::lonnet::hostname($homeserver).$r->uri.'') .' '.&Apache::loncommon::help_open_topic('Syllabus_ExtLink') .'
' .'');} else {$r->print('\par ');} $r->print(&mt('No syllabus information provided.')); if ($target ne 'tex') {$r->print('
');} } if ($target ne 'tex') { $r->print(''.&mt('Uploaded [_1]', ''.$stored.''). '
'; } else { my ($filename) = ($env{'form.syllabusfile.filename'} =~ m{([^/]+)$}); $$upload_output = ''.&mt('This file contains embedded multimedia objects, which need to be uploaded.').'
'.$embedded; } elsif ($numpathchanges) { $$upload_output .= $embedded; } else { $$upload_output .= $embedded; &Apache::loncommon::modify_html_refs('syllabus','portfolio/syllabus', $cnum,$cdom,'/userfiles',$url); } } else { $$upload_output .= &mt('Embedded item(s) already present, so no additional upload(s) required').'