# The LearningOnline Network # RSS Feeder # # $Id: lonrss.pm,v 1.61 2021/12/14 03:15:12 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::lonrss; use strict; use LONCAPA; use Apache::Constants qw(:common); use Apache::loncommon; use Apache::lonnet; use Apache::lontexconvert; use Apache::lonlocal; use Apache::lonhtmlcommon; use Apache::inputtags(); sub filterfeedname { my $filename=shift; $filename=~s/(\_rss\.html|\.rss)$//; $filename=~s/\W//g; $filename=~s/\_rssfeed$//; $filename=~s/^nohist\_//; return $filename; } sub feedname { return 'nohist_'.&filterfeedname(shift).'_rssfeed'; } sub displayfeedname { my ($rawname,$uname,$udom)=@_; my $filterfilename=&filterfeedname($rawname); # do we have a stored name? my %stored=&Apache::lonnet::get('nohist_all_rss_feeds',[$filterfilename,'feed_display_option_'.$filterfilename],$udom,$uname); if ($stored{$filterfilename}) { return ($stored{$filterfilename},$stored{'feed_display_option_'.$filterfilename}); } # no, construct a name my $name=$filterfilename; if ($name=~/^CourseBlog/) { $name=&mt('Course Blog'); if ($env{'course.'.$env{'request.course.id'}.'.type'} eq 'Community') { $name = &mt('Community Blog'); } if ($env{'course.'.$env{'request.course.id'}.'.description'}) { $name.=' '.$env{'course.'.$env{'request.course.id'}.'.description'}; } } else { $name=~s/\_/ /g; } return ($name,$stored{'feed_display_option_'.$filterfilename}); } sub namefeed { my ($rawname,$uname,$udom,$newname)=@_; return &Apache::lonnet::put('nohist_all_rss_feeds', { &filterfeedname($rawname) => $newname }, $udom,$uname); } sub changefeeddisplay { my ($rawname,$uname,$udom,$newstatus)=@_; return &Apache::lonnet::put('nohist_all_rss_feeds', { 'feed_display_option_'.&filterfeedname($rawname) => $newstatus }, $udom,$uname); } sub advertisefeeds { my ($uname,$udom,$edit,$count,$hidden)=@_; my $feeds=''; my %feednames=&Apache::lonnet::dump('nohist_all_rss_feeds',$udom,$uname); my $mode='public'; if ($edit) { $mode='adm'; } my $server = &Apache::lonnet::absolute_url(); foreach my $feed (sort(keys(%feednames))) { next if ($feed =~/^\s*$/ || $feed =~ /^error:/ || $feed =~ /^feed_display_option_/); if ($feednames{'feed_display_option_'.$feed} eq 'hidden') { if (ref($hidden)) { $$hidden ++; } if (ref($count)) { unless ($edit) { next; } } } else { if (ref($count)) { $$count ++; } } my $feedurl= $server.'/public/'.$udom.'/'.$uname.'/'.$feed.'.rss'; my $htmlurl= $server.'/'.$mode.'/'.$udom.'/'.$uname.'/'.$feed.'_rss.html'; if ($feednames{'feed_display_option_'.$feed} eq 'hidden') { if ($edit) { $feeds.='
  • '.$feednames{$feed}.'
    '.&mt('Hidden').': '.$htmlurl.'
  • '; } } else { $feeds.='
  • '.$feednames{$feed}. '
    '.($edit?&mt('Edit'):'HTML').': '.$feednames{$feed}.' HTML'. '
    '.&mt('Public RSS/podcast (subscribe to)').': '.$feednames{$feed}.' RSS/Podcast
  • '; } } if ($feeds) { return '

    '.&mt('Available RSS Feeds and Blogs').'

    '; } elsif (!$edit) { return '

    '.&mt('No available RSS Feeds and Blogs').'

    '; } } sub rss_link { my ($uname,$udom)=@_; my $result; my $server = &Apache::lonnet::absolute_url(); my %feednames=&Apache::lonnet::dump('nohist_all_rss_feeds',$udom,$uname); foreach my $feed (sort(keys(%feednames))) { next if ($feed =~/^\s*$/ || $feed =~ /^error:/ || $feed =~/^feed_display_option_/ ); my $url= $server.'/public/'.$udom.'/'.$uname.'/'.$feed.'.rss'; my $title = $feed; $title =~ s/_/ /g; $result.=qq| |; } return $result; } { my $feedcounter; sub get_new_feed_id { $feedcounter++; return time().'00000'.$$.'00000'.$feedcounter; } } sub addentry { my $id=&get_new_feed_id(); return &editentry($id,@_); } sub editentry { my ($id,$uname,$udom,$filename,$title,$description,$url,$status,$encurl,$enctype)=@_; if ($status eq 'deleted') { return &changestatus($id,$uname,$udom,$filename,$status); } my $feedname=&feedname($filename); &Apache::lonnet::put('nohist_all_rss_feeds', { &filterfeedname($filename) => (&displayfeedname($filename,$uname,$udom))[0] }, $udom,$uname); return &Apache::lonnet::put($feedname,{ $id.'_title' => $title, $id.'_description' => $description, $id.'_link' => $url, $id.'_enclosureurl' => $encurl, $id.'_enclosuretype' => $enctype, $id.'_status' => $status},$udom,$uname); } sub changestatus { my ($id,$uname,$udom,$filename,$status)=@_; my $feedname=&feedname($filename); if ($status eq 'deleted') { return &Apache::lonnet::del($feedname,[$id.'_title', $id.'_description', $id.'_link', $id.'_enclosureurl', $id.'_enclosuretype', $id.'_status'],$udom,$uname); } else { return &Apache::lonnet::put($feedname,{$id.'_status' => $status},$udom,$uname); } } sub changed_js { return < function changed(tform,id) { tform.elements[id+"_modified"].checked=true; } ENDSCRIPT } sub determine_enclosure_types { my ($url)=@_; my ($ending)=($url=~/\.(\w+)$/); return &Apache::loncommon::filemimetype($ending); } sub course_blog_link { my ($id,$title,$description,$url,$encurl,$enctype)=@_; if ($env{'request.course.id'}) { return &add_blog_entry_link($id, $env{'course.'.$env{'request.course.id'}.'.num'}, $env{'course.'.$env{'request.course.id'}.'.domain'}, 'Course_Announcements', $title,$description,$url,'public',$encurl,$enctype, &mt('Add to Course Announcements')); } else { return ''; } } sub add_blog_entry_link { my ($id,$uname,$udom,$filename,$title,$description,$url,$status,$encurl,$enctype,$linktext)=@_; return "".$linktext.''; } sub blocking_blogdisplay { my ($uname,$udom,$html,$filterfeedname,$clientip) = @_; my $user = &Apache::loncommon::plainname($uname,$udom); if ($html) { $user = &Apache::loncommon::aboutmewrapper($user,$uname,$udom); } else { $user = $user.' ('.$uname.':'.$udom.')'; } my %setters; my ($blocked,$output,$blockcause); my ($startblock,$endblock,$triggerblock,$by_ip,$blockdom) = &Apache::loncommon::blockcheck(\%setters,'blogs',$clientip); if ($startblock && $endblock) { $blockcause = 'user'; } elsif ($by_ip) { $blockcause = 'ip'; } else { if (($uname ne $env{'user.name'}) || ($udom ne $env{'user.domain'})) { ($startblock,$endblock) = &Apache::loncommon::blockcheck(\%setters,'blogs',$clientip, $uname,$udom); $blockcause = 'blogowner'; } } if (($startblock && $endblock) || ($by_ip)) { $blocked = 1; if ($startblock && $endblock) { my $showstart = &Apache::lonlocal::locallocaltime($startblock); my $showend = &Apache::lonlocal::locallocaltime($endblock); $output = &mt('Blogs belonging to [_1] are unavailable from [_2] to [_3].',$user,$showstart,$showend); } else { $output = &mt('Blogs are unavailable from your current IP address: [_1].',$clientip); } if ($html) {$output.='
    ';} if ($blockcause eq 'user') { $output .= &mt('This is because you are a student in one or more courses in which communication is being blocked.'); if ($html) { #$output .= '
    '. #&Apache::loncommon::build_block_table($startblock, # $endblock,\%setters); my ($blocked, $blocktext) = Apache::loncommon::blocking_status('blogs',$clientip); $output .= '

    '.$blocktext; } } elsif ($blockcause eq 'ip') { my $showdom = &Apache::lonnet::domain($blockdom); if ($showdom eq '') { $showdom = $blockdom; } $output .= &mt('This restriction was set by an administrator in the [_1] LON-CAPA domain',$showdom); } else { $output .= &mt('This is because the blog owner is a student in one or more courses in which communication is being blocked.'); } if (!$html) { my $id = &get_new_feed_id(); $output = '<item><title/><description>'.$output."</description><link/><guid isPermaLink='false'>".$id.$filterfeedname.'_'.$udom.'_'.$uname.'</guid></item>'; } } return ($blocked,$output); } sub handler { my ($r) = @_; my $edit=0; my $html=0; my (undef,$mode,$udom,$uname,$filename)=split(/\//,$r->uri); if (($mode eq 'adm') && ($env{'user.name'} eq $uname) && ($env{'user.domain'} eq $udom)) { $edit=1; $html=1; } if (($mode eq 'adm') && (&Apache::lonnet::allowed('mdc',$env{'request.course.id'})) && ($uname eq $env{'course.'.$env{'request.course.id'}.'.num'} && $udom eq $env{'course.'.$env{'request.course.id'}.'.domain'})) { $edit=1; $html=1; } if ($filename=~/\.html$/) { $html=1; } if ($html) { &Apache::loncommon::content_type($r,'text/html'); } else { # Workaround Mozilla/Firefox # &Apache::loncommon::content_type($r,'application/rss+xml'); &Apache::loncommon::content_type($r,'text/xml'); } $r->send_http_header; return OK if $r->header_only; my $filterfeedname=&filterfeedname($filename); my $feedname=&feedname($filename); my ($displayfeedname,$displayoption)=&displayfeedname($filename,$uname,$udom); my ($blocked,$blocktext,$disabled,$disabletext); if (!&Apache::lonnet::is_course($udom,$uname)) { my $clientip = &Apache::lonnet::get_requestor_ip($r); ($blocked,$blocktext) = &blocking_blogdisplay($uname,$udom,$html,$filterfeedname,$clientip); if (&Apache::lonnet::usertools_access($uname,$udom,'blog')) { $disabled = 0; } else { $disabled = 1; if ($html) { $disabletext = '<h2>'.&mt('No user blog available') .'</h2>'. &mt('This is a result of one of the following:').'<ul>'. '<li>'.&mt('The administrator of this domain has disabled blog functionality for this specific user.').'</li>'. '<li>'.&mt('The domain has been configured to disable, by default, blog functionality for all users in the domain.').'</li>'. '</ul>'; } else { $disabletext = &mt('No user blog available'); } } } if ($html) { # my $title = $displayfeedname?$displayfeedname:"Available RSS Feeds and Blogs"; my $title = "My Space"; my $rss_link = &Apache::lonrss::rss_link($uname,$udom); my $head_extra = $rss_link.'<script type="text/javascript" ' .'src="/res/adm/includes/file_upload.js"></script>'; my $brcrumb = [{href=>$rss_link,text=>"Available RSS Feeds and Blogs"}]; $r->print(&Apache::loncommon::start_page($title,$head_extra, {'bread_crumbs' => $brcrumb, 'domain' => $udom, 'force_register' => $env{'form.register'}}). &changed_js()); } else { # render RSS my $server = &Apache::lonnet::absolute_url(); $r->print("<rss version='2.0' xmlns:dc='http://purl.org/dc/elements/1.1'>\n<channel>". "\n".'<link>'.$server.'/public/'.$udom.'/'.$uname.'/'. $filterfeedname.'_rss.html</link>'. "\n<description>". &mt('An RSS Feed provided by the LON-CAPA Learning Content Management System'). '</description>'); } # This will be the entry id for new additions to the blog my $newid = &get_new_feed_id(); # Is this user for real? my $homeserver=&Apache::lonnet::homeserver($uname,$udom); if ($html && !$blocked && !$disabled) { # Any new feeds or renaming of feeds? if ($edit) { # Hide a feed? if ($env{'form.hidethisblog'}) { &changefeeddisplay($feedname,$uname,$udom,'hidden'); ($displayfeedname,$displayoption)=&displayfeedname($filename,$uname,$udom); } # Advertise a feed? if ($env{'form.advertisethisblog'}) { &changefeeddisplay($feedname,$uname,$udom,'public'); ($displayfeedname,$displayoption)=&displayfeedname($filename,$uname,$udom); } # New feed? if ($env{'form.namenewblog'}=~/\w/) { &namefeed($env{'form.namenewblog'},$uname,$udom,$env{'form.namenewblog'}); } # Old feed that is being renamed? if (($displayfeedname) && ($env{'form.newblogname'}=~/\w/)) { if ($env{'form.newblogname'} ne $displayfeedname) { &namefeed($feedname,$uname,$udom,$env{'form.newblogname'}); ($displayfeedname,$displayoption)=&displayfeedname($filename,$uname,$udom); } } } $r->print(&advertisefeeds($uname,$udom,$edit)); } if ($homeserver eq 'no_host') { $r->print(($html?'<h3>':'<title>').&mt('No feed available').($html?'</h3>':'')); } elsif ($blocked) { $r->print($blocktext); $r->print(($html?&Apache::loncommon::end_page():''."\n")); } elsif ($disabled) { $r->print($disabletext); $r->print(($html?&Apache::loncommon::end_page():''."\n")); } else { # is indeed a user # Course or user? my $name=''; if (&Apache::lonnet::is_course($udom,$uname)) { my %cenv=&Apache::lonnet::dump('environment',$udom,$uname); $name=$cenv{'description'}; } else { $name=&Apache::loncommon::nickname($uname,$udom); } # Add a new feed if (($html) && ($edit)) { $r->print('

    ' . &mt('New RSS Feed or Blog'). '

    '); $r->print('
    '); $r->print(&mt('Name').": "); $r->print(''); $r->print('
    '); } if ($displayfeedname) { # this is an existing feed # Anything to store? if ($edit) { # check if this was called with a query string &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['queryid']); if ($env{'form.queryid'}) { # yes, collect the remainder &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['title', 'description', 'url', 'status', 'enclosureurl', 'enclosuretype']); # my ($id,$uname,$udom,$filename,$title,$description,$url,$status,$encurl,$enctype)=@_; &editentry($env{'form.queryid'}, $uname,$udom,$filename, $env{'form.title'}, $env{'form.description'}, $env{'form.url'}, $env{'form.status'}, $env{'form.encurl'}, $env{'form.enctype'} ); } # store away the fields modified in the online form my %newsfeed=&Apache::lonnet::dump($feedname,$udom,$uname); foreach my $entry (sort(keys(%newsfeed)),$env{'form.newid'}.'_status') { if ($entry=~/^(\d+)\_status$/) { my $id=$1; if ($env{'form.'.$id.'_modified'}) { &editentry($id,$uname,$udom,$feedname, $env{'form.'.$id.'_title'}, $env{'form.'.$id.'_description'}, $env{'form.'.$id.'_link'}, $env{'form.'.$id.'_status'}, $env{'form.'.$id.'_enclosureurl'}, $env{'form.'.$id.'_enclosuretype'}, ); } } } # see if we have any uploaded or portfolio files my @uploadeditems=(); if ($env{'form.HWFILE0_0'}) { # We have an uploaded file - store it away. $uploadeditems[0]=&Apache::lonnet::userfileupload('HWFILE0_0',undef,'portfolio/podcasts'); } if ($env{'form.HWPORT0_0'}) { # Selected portfolio files foreach my $filename (split(/\,/,$env{'form.HWPORT0_0'})) { if ($filename) { # construct full path and remember push(@uploadeditems,'/uploaded/'.$env{'user.domain'}.'/'.$env{'user.name'}.'/portfolio'.$filename); } } } # the zeroth item should be stored together with the last displayed (newid) field if ($uploadeditems[0]) { my $id=$env{'form.newid'}; &editentry($id,$uname,$udom,$feedname, $env{'form.'.$id.'_title'}, $env{'form.'.$id.'_description'}, $env{'form.'.$id.'_link'}, $env{'form.'.$id.'_status'}, $uploadeditems[0], &Apache::loncommon::filemimetype(($uploadeditems[0]=~/\.(\w+)$/)[0]), ); &Apache::lonnet::make_public_indefinitely($uploadeditems[0]); } # if there are more files, they need new entries, since each can only have one enclosure for (my $i=1; $i<=$#uploadeditems; $i++) { my $id = &get_new_feed_id().$i; &editentry($id,$uname,$udom,$feedname, 'New Entry', '', '', 'public', $uploadeditems[$i], &Apache::loncommon::filemimetype(($uploadeditems[$i]=~/\.(\w+)$/)[0]), ); &Apache::lonnet::make_public_indefinitely($uploadeditems[$i]); } } #done storing # Render private items? my $viewpubliconly=1; $r->print("\n". ($html?'

    ':''). &mt('LON-CAPA Feed "[_1]" for [_2]',$displayfeedname,$name). ($displayoption eq 'hidden'?' ('.&mt('Hidden').')':''). ($html?'</h3>'.($edit?'<form method="post" name="lonhomework" enctype="multipart/form-data" action=""><br />'. &mt('Name of this Feed'). ': <input type="text" size="50" name="newblogname" value="'. $displayfeedname.'" />':'').'<ul>':'')); if (($env{'user.name'} eq $uname) && ($env{'user.domain'} eq $udom)) { $viewpubliconly=0; } # Get feed items my %newsfeed=&Apache::lonnet::dump($feedname,$udom,$uname); foreach my $entry (sort {$b cmp $a} (keys(%newsfeed)),$newid.'_status') { if ($entry=~/^(\d+)\_status$/) { # is an entry my $id=$1; if ($edit) { my %lt=&Apache::lonlocal::texthash('public' => 'public', 'private' => 'private', 'hidden' => 'hidden', 'delete' => 'delete', 'store' => 'Select', 'title' => 'Title', 'link' => 'Link', 'description' => 'Description', 'enc' => 'Podcasted enclosure'); my $uploadlink; if ($entry==$newid) { # Generate upload link only for last (new) entry # Calculate the quota space available in the user's portfolio my $disk_quota = &Apache::loncommon::get_user_quota($env{'user.name'}, $env{'user.domain'}); # expressed in MB my $portfolio_root = '/userfiles/portfolio'; my $getpropath = 1; my $current_disk_usage = &Apache::lonnet::diskusage( $env{'user.domain'}, $env{'user.name'}, $portfolio_root, $getpropath); # Expressed in kB # Convert to MB for use in file_selector() my $free_space = $disk_quota - ($current_disk_usage / 1024.); # Format this number since it will be displayed onscreen $free_space = sprintf("%.1f", $free_space); $uploadlink=&Apache::inputtags::file_selector(0,0,'*','both','',$free_space); } else { # Otherwise, display $uploadlink=''.$newsfeed{$id.'_enclosureurl'}.''. "". ""; } my %status=(); unless ($newsfeed{$id.'_status'}) { $newsfeed{$id.'_status'}='public'; } $status{$newsfeed{$id.'_status'}}='checked="checked"'; $r->print(<            
    $lt{'title'}:
    $lt{'description'}:

    $lt{'link'}:
    $lt{'enc'} - $uploadlink
    ENDEDIT } else { # not in edit mode, just displaying if (($newsfeed{$id.'_status'} ne 'public') && ($viewpubliconly)) { next; } if ($newsfeed{$id.'_status'} eq 'hidden') { next; } my $link = $newsfeed{$id.'_link'}; if ($link =~ m|^/| ) { $link = "http://".$ENV{'HTTP_HOST'}.$link; } $r->print("\n".($html?"\n
  • ":"\n").$newsfeed{$id.'_title'}. ($html?"</b><br />\n":"\n"). $newsfeed{$id.'_description'}. ($html?"
    ":"
    \n"). ($link?($html?"\n".&mt('Read more')."
    \n"):"\n"):'')); my $enclosure=$newsfeed{$id.'_enclosureurl'}; # Enclosure? Get stats if ($enclosure) { my @stat=&Apache::lonnet::stat_file($enclosure); if ($stat[7]) { # Has non-zero length (and exists) my $enclosuretype=$newsfeed{$id.'_enclosuretype'}; if ($enclosure =~ m|^/| ) { $enclosure = "http://".$ENV{'HTTP_HOST'}.$enclosure; } $r->print(($html?"".&mt('Enclosure')."":"' />")); } } if ($html) { # is HTML $r->print("\n
  • \n"); } else { # is RSS $r->print("\n".$id.$filterfeedname.'_'.$udom.'_'.$uname."\n"); } } # end of "in edit mode" } # end of rendering a real entry } # end of loop through all keys if ($html) { $r->print(''); if ($edit) { $r->print(''. ($displayoption eq 'hidden'?'': '')); } $r->print(''); } } # was a real display feedname $r->print(($html?&Apache::loncommon::end_page():''."\n")); } # a real user return OK; } # end handler 1; __END__