File:  [LON-CAPA] / loncom / publisher / lonpubdir.pm
Revision 1.160: download - view: text, annotated - select for diffs
Tue Aug 5 19:32:23 2014 UTC (9 years, 10 months ago) by musolffc
Branches: MAIN
CVS tags: version_2_11_X, version_2_11_1, HEAD
Columns are sortable in the course quota list as well as well as the resource list in authoring space.  An up or down arrow is displayed next to the sorted column header indicating whether it is in ascending or descending order.

This resolves Bug #6704

    1: # The LearningOnline Network with CAPA
    2: # Authoring Space Directory Lister
    3: #
    4: # $Id: lonpubdir.pm,v 1.160 2014/08/05 19:32:23 musolffc Exp $
    5: #
    6: # Copyright Michigan State University Board of Trustees
    7: #
    8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
    9: #
   10: # LON-CAPA is free software; you can redistribute it and/or modify
   11: # it under the terms of the GNU General Public License as published by
   12: # the Free Software Foundation; either version 2 of the License, or
   13: # (at your option) any later version.
   14: #
   15: # LON-CAPA is distributed in the hope that it will be useful,
   16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
   17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   18: # GNU General Public License for more details.
   19: #
   20: # You should have received a copy of the GNU General Public License
   21: # along with LON-CAPA; if not, write to the Free Software
   22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   23: #
   24: # /home/httpd/html/adm/gpl.txt
   25: #
   26: # http://www.lon-capa.org/
   27: #
   28: ###
   29: 
   30: package Apache::lonpubdir;
   31: 
   32: use strict;
   33: use Apache::File;
   34: use File::Copy;
   35: use Apache::Constants qw(:common :http :methods);
   36: use Apache::loncommon();
   37: use Apache::lonhtmlcommon();
   38: use Apache::londiff();
   39: use Apache::lonlocal;
   40: use Apache::lonmsg;
   41: use Apache::lonmenu;
   42: use Apache::lonnet;
   43: use LONCAPA qw(:DEFAULT :match);
   44: 
   45: sub handler {
   46: 
   47:     my $r=shift;
   48: 
   49:     # Validate access to the construction space and get username:domain.
   50: 
   51:     my ($uname,$udom)=&Apache::lonnet::constructaccess($r->uri); 
   52:     unless (($uname) && ($udom)) {
   53:         return HTTP_NOT_ACCEPTABLE;
   54:     }
   55: 
   56: # ----------------------------------------------------------- Start page output
   57: 
   58:     my $fn=$r->filename;
   59:     $fn=~s/\/$//;
   60:     my $thisdisfn=$fn;
   61: 
   62:     my $docroot=$r->dir_config('lonDocRoot');     # Apache  londocument root.
   63:     if ($thisdisfn eq "$docroot/priv/$udom") {
   64:         if ((-d "/home/$uname/public_html/") && (!-e "$docroot/priv/$udom/$uname")) {
   65:             my ($version) = ($r->dir_config('lonVersion') =~ /^\'?(\d+\.\d+)\./);
   66:             &Apache::loncommon::content_type($r,'text/html');
   67:             $r->send_http_header;
   68: 
   69:             &Apache::lonhtmlcommon::clear_breadcrumbs();
   70:             $r->print(&Apache::loncommon::start_page('Authoring Space').
   71:                       '<div class="LC_error">'.
   72:                       '<br /><p>'.
   73:                       &mt('Your Authoring Space is currently in the location used by LON-CAPA version 2.10 and older, but your domain is using a newer LON-CAPA version ([_1]).',$version).'</p>'.
   74:                       '<p>'.
   75:                       &mt('Please ask your Domain Coordinator to move your Authoring Space to the new location.').
   76:                       '</p>'.
   77:                       '</div>'.
   78:                       &Apache::loncommon::end_page());
   79:             return OK;
   80:         }
   81:     }
   82:     $thisdisfn=~s/^\Q$docroot\E\/priv//;
   83:     
   84:     my $resdir=$docroot.'/res'.$thisdisfn; # Resource directory
   85:     my $targetdir='/res'.$thisdisfn; # Publication target directory.
   86:     my $linkdir='/priv'.$thisdisfn;      # Full URL name of constr space.
   87: 
   88:     my %bombs=&Apache::lonmsg::all_url_author_res_msg($uname,$udom);
   89: 
   90:     &startpage($r, $uname, $udom, $thisdisfn);  # Put out the start of page.
   91: 
   92:     if (!-d $fn) {
   93:         if (-e $fn) {
   94:             $r->print('<p class="LC_info">'.&mt('Requested item is a file not a directory.').'</p>');
   95:         } else {
   96:             $r->print('<p class="LC_info">'.&mt('The requested subdirectory does not exist.').'</p>');
   97:         }
   98:         $r->print(&Apache::loncommon::end_page());
   99:         return OK;
  100:     }
  101:     my @files;
  102:     if (opendir(DIR,$fn)) {
  103:         @files = grep(!/^\.+$/,readdir(DIR));
  104:         closedir(DIR);
  105:     } else {
  106:         $r->print('<p class="LC_error">'.&mt('Could not open directory.').'</p>');
  107:         $r->print(&Apache::loncommon::end_page());
  108:         return OK;
  109:     }
  110: 
  111:     &dircontrols($r,$uname,$udom,$thisdisfn);   # Put out actions for directory, 
  112:                                                 # browse/upload + new file page.
  113:     &resourceactions($r,$uname,$udom,$thisdisfn); # Put out form used for printing/deletion etc.
  114: 
  115:     my $numdir = 0;
  116:     my $numres = 0;
  117:   
  118:     if ((@files == 0) && ($thisdisfn =~ m{^/$match_domain/$match_username})) {
  119:         if ($thisdisfn =~ m{^/$match_domain/$match_username$}) {
  120:             $r->print('<p class="LC_info">'.&mt('This Authoring Space is currently empty.').'</p>');
  121:         } else {
  122:             $r->print('<p class="LC_info">'.&mt('This subdirectory is currently empty.').'</p>');
  123:         }
  124:         $r->print(&Apache::loncommon::end_page());
  125:         return OK;
  126:     }
  127: 
  128:     # Retrieving value for "sortby" and "sortorder" from QUERY_STRING
  129:     &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
  130:         ['sortby','sortorder']);
  131: 
  132:     # Sort by name as default, not reversed
  133:     if (! exists($env{'form.sortby'})) { $env{'form.sortby'} = 'filename' }
  134:     if (! exists($env{'form.sortorder'})) { $env{'form.sortorder'} = '' }
  135:     my $sortby = $env{'form.sortby'};
  136:     my $sortorder = $env{'form.sortorder'};
  137: 
  138:     # Order in which columns are displayed from left to right
  139:     my @order = ('filetype','actions','filename','title',
  140:                     'pubstatus','cmtime','size');
  141: 
  142:     # Up and down arrows to indicate sort order
  143:     my @arrows = ('&nbsp;&#9650;','&nbsp;&#9660;','');
  144: 
  145:     # Default sort order and column title
  146:     my %columns = (
  147:         filetype =>     {
  148:                             order => 'ascending',
  149:                             text  => &mt('Type'),
  150:                         },
  151:         actions =>      {
  152:                             # Not sortable
  153:                             text  => &mt('Actions'),
  154:                         },
  155:         filename =>     {
  156:                             order => 'ascending',
  157:                             text  => &mt('Name'),
  158:                         },
  159:         title =>        {
  160:                             order => 'ascending',
  161:                             text  => &mt('Title'),
  162:                         },
  163:         pubstatus =>    {
  164:                             order => 'ascending',
  165:                             text  => &mt('Status'),
  166:                             colspan => '2',
  167:                         },
  168:         cmtime =>       {
  169:                             order => 'descending',
  170:                             text  => &mt('Last Modified'),
  171:                         },
  172:         size =>         {
  173:                             order => 'ascending',
  174:                             text  => &mt('Size').' (kB)',
  175:                         },
  176:     ); 
  177: 
  178:     # Print column headers
  179:     my $output = '';
  180:     foreach my $key (@order) {
  181:         my $idx;
  182:         # Append an up or down arrow to sorted column
  183:         if ($sortby eq $key) {
  184:             $idx = ($columns{$key}{order} eq 'ascending') ? 0:1;
  185:             if ($sortorder eq 'rev') { $idx ++; }
  186:             $idx = $idx%2;
  187:         } else { $idx = 2; } # No arrow if column is not sorted
  188:         $output .= (($columns{$key}{order}) ?
  189:             '<th'.($columns{$key}{colspan} ? ' colspan="'.$columns{$key}{colspan}.'"' : '')
  190:             .'><a href="'.$linkdir.'/?sortby='.$key.'&sortorder='
  191:             .((($sortby eq $key) && ($sortorder ne 'rev')) ? 'rev' : '').'">'
  192:             .$columns{$key}{text}.$arrows[$idx].'</a></th>' :
  193:             '<th>'.$columns{$key}{text}.'</th>');
  194:     }
  195:     $r->print(&Apache::loncommon::start_data_table()
  196:         .&Apache::loncommon::start_data_table_header_row() . $output
  197:         .&Apache::loncommon::end_data_table_header_row()
  198:     );
  199: 
  200:     my $dirptr=16384;		# Mask indicating a directory in stat.cmode.
  201:     my $filehash = {};
  202:     foreach my $filename (@files) {
  203:         # Skip .DS_Store, .DAV and hidden files
  204:         my ($extension) = ($filename=~/\.(\w+)$/);
  205:         next if (($filename eq '.DS_Store')
  206:                 || ($filename eq '.DAV')
  207:                 || (&Apache::loncommon::fileembstyle($extension) eq 'hdn')
  208:                 || ($filename =~ /^\._/));
  209: 
  210:         my ($cmode,$csize,$cmtime)=(stat($fn.'/'.$filename))[2,7,9];
  211:         my $linkfilename = &HTML::Entities::encode('/priv'.$thisdisfn.'/'.$filename,'<>&"');
  212:         # Identify type of file according to icon used
  213:         my ($filetype) = (&Apache::loncommon::icon($filename) =~ m{/(\w+).gif$}); 
  214:         my $cstr_dir = $r->dir_config('lonDocRoot').'/priv'.$thisdisfn;
  215:         my $meta_same = &isMetaSame($cstr_dir, $resdir, $filename);
  216:         
  217:         # Store size, title, and status for files but not directories
  218:         my $size = (!($cmode&$dirptr)) ? $csize/1024. : 0;
  219:         my ($status, $pubstatus, $title, $fulltitle);
  220:         if (!($cmode&$dirptr)) {
  221:             ($status, $pubstatus) = &getStatus($resdir, $targetdir, $cstr_dir, 
  222:                 $filename, $linkfilename, $cmtime, $meta_same);
  223:             ($fulltitle, $title) = &getTitle($resdir, $targetdir, $filename, 
  224:                                         $linkfilename, $meta_same, \%bombs);
  225:         } else {
  226:             ($status, $pubstatus) = ('','');
  227:             ($fulltitle, $title) = ('','');
  228:         }
  229: 
  230:         # This hash will allow sorting
  231:         $filehash->{ $filename } = {
  232:             "cmtime"            => $cmtime,
  233:             "size"              => $size,
  234:             "cmode"             => $cmode,
  235:             "filetype"          => $filetype,
  236:             "title"             => $title,
  237:             "fulltitle"         => $fulltitle,
  238:             "status"            => $status,
  239:             "pubstatus"         => $pubstatus,
  240:             "linkfilename"      => $linkfilename,
  241:         }
  242:     }
  243:    
  244:     my @sorted_files;
  245:     # Sorting by something other than "Name".  Name is the secondary key.
  246:     if ($sortby =~ m{cmtime|size}) {    # Numeric fields
  247:         # First check if order should be reversed
  248:         if ($sortorder eq "rev") {
  249:             @sorted_files = sort {
  250:                 $filehash->{$a}->{$sortby} <=> $filehash->{$b}->{$sortby}
  251:                     or
  252:                 uc($a) cmp uc($b)
  253:             } (keys(%{$filehash}));
  254:         } else {
  255:             @sorted_files = sort {
  256:                 $filehash->{$b}->{$sortby} <=> $filehash->{$a}->{$sortby}
  257:                     or
  258:                 uc($a) cmp uc($b)
  259:             } (keys(%{$filehash}));
  260:         }
  261:     } elsif ($sortby =~ m{filetype|title|status}) {     # String fields
  262:         if ($sortorder eq "rev") {
  263:             @sorted_files = sort {
  264:                 $filehash->{$b}->{$sortby} cmp $filehash->{$a}->{$sortby}
  265:                     or
  266:                 uc($a) cmp uc($b)
  267:             } (keys(%{$filehash}));
  268:         } else {
  269:             @sorted_files = sort {
  270:                 $filehash->{$a}->{$sortby} cmp $filehash->{$b}->{$sortby}
  271:                     or
  272:                 uc($a) cmp uc($b)
  273:             } (keys(%{$filehash}));
  274:         }
  275: 
  276:     # Sort by "Name" is the default
  277:     } else { 
  278:         if ($sortorder eq "rev") {
  279:             @sorted_files = sort {uc($b) cmp uc($a)} (keys(%{$filehash}));
  280:         } else {
  281:             @sorted_files = sort {uc($a) cmp uc($b)} (keys(%{$filehash}));
  282:         }
  283:     }
  284: 
  285:     # Print the sorted resources
  286:     foreach my $filename (@sorted_files) {
  287:         if ($filehash->{$filename}->{"cmode"}&$dirptr) {        # Directories
  288:             &putdirectory($r, $thisdisfn, $linkdir, $filename, 
  289:                 $filehash->{$filename}->{"cmtime"}, 
  290:                 $targetdir, \%bombs, \$numdir);
  291:         } else {                                                # Files
  292:             &putresource($r, $udom, $uname, $filename, $thisdisfn, $resdir,
  293:                 $targetdir, $linkdir, $filehash->{$filename}->{"cmtime"}, 
  294:                 $filehash->{$filename}->{"size"}, \$numres, 
  295:                 $filehash->{$filename}->{"linkfilename"},
  296:                 $filehash->{$filename}->{"fulltitle"},
  297:                 $filehash->{$filename}->{"status"},
  298:                 $filehash->{$filename}->{"pubstatus"});
  299:         }
  300:     }
  301: 
  302:     $r->print( &Apache::loncommon::end_data_table()
  303:         .&Apache::loncommon::end_page() );
  304: 
  305:     return OK;
  306: }
  307: 
  308: 
  309: 
  310: #   Output the header of the page.  This includes:
  311: #   - The HTML header 
  312: #   - The H1/H3  stuff which includes the directory.
  313: #
  314: #     startpage($r, $uame, $udom, $thisdisfn);
  315: #      $r     - The apache request object.
  316: #      $uname - User name.
  317: #      $udom  - Domain name the user is logged in under.
  318: #      $thisdisfn - Displayable version of the filename.
  319: 
  320: sub startpage {
  321:     my ($r, $uname, $udom, $thisdisfn) = @_;
  322:     &Apache::loncommon::content_type($r,'text/html');
  323:     $r->send_http_header;
  324: 
  325:     my $formaction='/priv'.$thisdisfn.'/';
  326:     $formaction=~s|/+|/|g;
  327:     &Apache::lonhtmlcommon::store_recent('construct',$formaction,$formaction);
  328: 
  329:     &Apache::lonhtmlcommon::clear_breadcrumbs();
  330:     &Apache::lonhtmlcommon::add_breadcrumb({
  331:         'text'  => 'Authoring Space',
  332:         'href'  => &Apache::loncommon::authorspace($formaction),
  333:     });
  334:     # breadcrumbs (and tools) will be created 
  335:     # in start_page->bodytag->innerregister
  336: 
  337:     $env{'request.noversionuri'}=$formaction;
  338:     $r->print(&Apache::loncommon::start_page('Authoring Space'));
  339: 
  340:     my $londocroot = $Apache::lonnet::perlvar{'lonDocRoot'};
  341:     my $current_disk_usage = &Apache::lonnet::diskusage($udom,$uname,"$londocroot/priv/$udom/$uname");
  342:     my $disk_quota = &Apache::loncommon::get_user_quota($uname,$udom,'author'); #expressed in MB
  343:     $disk_quota = 1000 * $disk_quota; # convert from MB to kB
  344: 
  345:     $r->print(&Apache::loncommon::head_subbox(
  346:                      '<div style="float:right;padding-top:0;margin-top;0">'
  347:                     .&Apache::lonhtmlcommon::display_usage($current_disk_usage,$disk_quota)
  348:                     .'</div>'
  349:                     .&Apache::loncommon::CSTR_pageheader()));
  350: 
  351:     my $esc_thisdisfn = &Apache::loncommon::escape_single($thisdisfn);
  352:     my $doctitle = 'LON-CAPA '.&mt('Authoring Space');
  353:     my $newname = &mt('New Name');
  354:     my $pubdirscript=(<<ENDPUBDIRSCRIPT);
  355: <script type="text/javascript">
  356: top.document.title = '$esc_thisdisfn/ - $doctitle';
  357: // Store directory location for menu bar to find
  358: 
  359: parent.lastknownpriv='/priv$esc_thisdisfn/';
  360: 
  361: // Confirmation dialogues
  362: 
  363:     function currdiract(theform) {
  364:         if (theform.dirtask.options[theform.dirtask.selectedIndex].value == 'publish') {
  365:             document.publishdir.filename.value = theform.filename.value;
  366: 	    document.publishdir.submit();
  367:         }
  368:         if (theform.dirtask.options[theform.dirtask.selectedIndex].value == 'editmeta') {
  369:             top.location=theform.filename.value+'default.meta'
  370:         }
  371:         if (theform.dirtask.options[theform.dirtask.selectedIndex].value == 'printdir' ) {
  372:             document.printdir.postdata.value=theform.filename.value
  373:             document.printdir.submit();
  374:         }
  375:         if (theform.dirtask.options[theform.dirtask.selectedIndex].value == "delete") {
  376:               var delform = document.delresource
  377:               delform.filename.value = theform.filename.value
  378:               delform.submit()
  379:         }
  380:     }
  381:   
  382:     function checkUpload(theform) {
  383:         if (theform.file == '') {
  384:             alert("Please use 'Browse..' to choose a file first, before uploading")
  385:             return 
  386:         }
  387:         theform.submit()  
  388:     }
  389: 
  390:     function SetPubDir(theform,printForm) {
  391:         if (theform.diraction.options[theform.diraction.selectedIndex].value == "open") {
  392:             top.location = theform.openname.value
  393:             return
  394:         }
  395:         if (theform.diraction.options[theform.diraction.selectedIndex].value == "publish") {
  396:             theform.submit();
  397:         }
  398:         if (theform.diraction.options[theform.diraction.selectedIndex].value == "editmeta") {
  399:             top.location=theform.filename.value+'default.meta'
  400:         }
  401:         if (theform.diraction.options[theform.diraction.selectedIndex].value == "printdir") {
  402:             theform.action = '/adm/printout'
  403:             theform.postdata.value = theform.filename.value
  404:             theform.submit()
  405:         }
  406:         if (theform.diraction.options[theform.diraction.selectedIndex].value == "delete") {
  407:               var delform = document.delresource
  408:               delform.filename.value = theform.filename.value
  409:               delform.submit()
  410:         }
  411:         return
  412:     }
  413:     function SetResChoice(theform) {
  414:       var activity = theform.reschoice.options[theform.reschoice.selectedIndex].value
  415:       if ((activity == 'rename') || (activity == 'copy') || (activity == 'move')) {
  416:           changename(theform,activity)
  417:       }
  418:       if (activity == 'publish') {
  419:           var pubform = document.pubresource
  420:           pubform.filename.value = theform.filename.value
  421:           pubform.submit()
  422:       }
  423:       if (activity == 'delete') {
  424:           var delform = document.delresource
  425:           delform.filename.value = theform.filename.value
  426:           delform.submit()
  427:       }
  428:       if (activity == 'obsolete') {
  429:           var pubform = document.pubresource
  430:           pubform.filename.value = theform.filename.value
  431:           pubform.makeobsolete.value=1;
  432:           pubform.submit()
  433:       }
  434:       if (activity == 'print') {
  435:           document.printresource.postdata.value = theform.filename.value
  436:           document.printresource.submit()
  437:       }
  438:       if (activity == 'retrieve') {
  439:           document.retrieveres.filename.value = theform.filename.value
  440:           document.retrieveres.submit()
  441:       }
  442:       if (activity == 'cleanup') {
  443:           document.cleanup.filename.value = theform.filename.value
  444:           document.cleanup.submit()
  445:       }
  446:       return
  447:     }
  448:     function changename(theform,activity) {
  449:         var oldname=theform.dispfilename.value;
  450:         var newname=prompt('$newname',oldname);
  451:         if (newname == "" || !newname || newname == oldname)  {
  452:             return
  453:         }
  454:         document.moveresource.newfilename.value = newname
  455:         document.moveresource.filename.value = theform.filename.value
  456:         document.moveresource.action.value = activity
  457:         document.moveresource.submit();
  458:     }
  459: </script>
  460: ENDPUBDIRSCRIPT
  461:     $r->print($pubdirscript);
  462: }
  463: 
  464: sub dircontrols {
  465:     my ($r,$uname,$udom,$thisdisfn) = @_;
  466:     my %lt=&Apache::lonlocal::texthash(
  467:                                        cnpd => 'Cannot publish directory',
  468:                                        cnrd => 'Cannot retrieve directory',
  469:                                        mcdi => 'Must create new subdirectory inside a directory',
  470:                                        pubr => 'Publish this Resource',
  471:                                        pubd => 'Publish this Directory',
  472:                                        dedr => 'Delete Directory',
  473:                                        rtrv => 'Retrieve Old Version',
  474:                                        list => 'List Directory',
  475:                                        uplo => 'Upload file',  
  476:                                        dele => 'Delete',
  477:                                        edit => 'Edit Metadata', 
  478:                                        sela => 'Select Action',
  479:                                        nfil => 'New file',
  480:                                        nhtm => 'New HTML file',
  481:                                        nprb => 'New problem',
  482:                                        npag => 'New assembled page',
  483:                                        nseq => 'New assembled sequence',
  484:                                        ncrf => 'New custom rights file',
  485:                                        nsty => 'New style file',
  486:                                        nlib => 'New library file',
  487:                                        nbt  => 'New bridgetask file',
  488:                                        nsub => 'New subdirectory',
  489:                                        renm => 'Rename current file to',
  490:                                        move => 'Move current file to',
  491:                                        copy => 'Copy current file to',
  492:                                        type => 'Type Name Here',
  493:                                        go   => 'Go',
  494:                                        prnt => 'Print contents of directory',
  495:                                        crea => 'Create a new directory or LON-CAPA document',
  496: 				       acti => 'Actions for current directory',
  497: 				       updc => 'Upload a new document',
  498: 				       pick => 'Please select an action to perform using the new filename',
  499:                                       );
  500:     my $mytype = $lt{'type'}; # avoid conflict with " and ' in javascript
  501:     $r->print(<<END);
  502: <div class="LC_columnSection">
  503:   <div>
  504:     <form name="curractions" method="post" action="">
  505:       <fieldset>
  506:         <legend>$lt{'acti'}</legend>
  507:         <select name="dirtask" onchange="currdiract(this.form)">
  508:             <option>$lt{'sela'}</option>
  509:             <option value="publish">$lt{'pubd'}</option>
  510:             <option value="editmeta">$lt{'edit'}</option>
  511:             <option value="printdir">$lt{'prnt'}</option>
  512:             <option value="delete">$lt{'dedr'}</option>
  513:         </select>
  514:         <input type="hidden" name="filename" value="/priv$thisdisfn/" />
  515:       </fieldset>
  516:     </form>
  517:     <form name="publishdir" method="post" action="/adm/publish" target="_parent">
  518:       <input type="hidden" name="pubrec" value="" />
  519:       <input type="hidden" name="filename" value="" />
  520:     </form>
  521:     <form name="printdir" method="post" action="/adm/printout" target="_parent">
  522:       <input type="hidden" name="postdata" value="" />
  523:     </form>
  524:   </div>
  525: 
  526:   <div>
  527:     <form name="upublisher" enctype="multipart/form-data" method="post" action="/adm/upload" target="_parent">
  528:       <fieldset>
  529:         <legend>$lt{'updc'}</legend>
  530:         <input type="hidden" name="filename" value="/priv$thisdisfn/" />
  531:         <input type="file" name="upfile" size="20" />
  532:         <input type="button" value="$lt{'uplo'}"  onclick="checkUpload(this.form)" />
  533:       </fieldset>
  534:     </form>
  535:   </div>
  536: 
  537:   <div>
  538:     <form name="fileaction" method="post" action="/adm/cfile" target="_parent">
  539:       <fieldset>
  540:               <legend>$lt{'crea'}</legend>
  541: 	      <span class="LC_nobreak">
  542: 		<input type="hidden" name="filename" value="/priv$thisdisfn/" />
  543:                   <script type="text/javascript">
  544:                     function validate_go() {
  545:                         var selected = document.fileaction.action.selectedIndex;
  546:                         if (selected == 0) {
  547:                             alert('$lt{'pick'}');
  548:                         } else {
  549:                             document.fileaction.submit();
  550:                         }
  551:                     }
  552:                   </script>
  553: 		  <select name="action">
  554: 		    <option value="none">$lt{'sela'}</option>
  555: 		    <option value="newfile">$lt{'nfil'}:</option>
  556: 		    <option value="newhtmlfile">$lt{'nhtm'}:</option>
  557: 		    <option value="newproblemfile">$lt{'nprb'}:</option>
  558:                     <option value="newpagefile">$lt{'npag'}:</option>
  559:                     <option value="newsequencefile">$lt{'nseq'}:</option>
  560:                     <option value="newrightsfile">$lt{'ncrf'}:</option>
  561:                     <option value="newstyfile">$lt{'nsty'}:</option>
  562:                     <option value="newtaskfile">$lt{'nbt'}:</option>
  563:                     <option value="newlibraryfile">$lt{'nlib'}:</option>
  564: 	            <option value="newdir">$lt{'nsub'}:</option>
  565: 		  </select>&nbsp;<input type="text" name="newfilename" value="$lt{'type'}" onfocus="if (this.value == '$mytype') this.value=''" />&nbsp;<input type="button" value="Go" onclick="validate_go();" />
  566: 		 </span>
  567:       </fieldset>
  568:     </form>
  569:   </div>
  570: </div>
  571: END
  572: }
  573: 
  574: sub resourceactions {
  575:     my ($r,$uname,$udom,$thisdisfn) = @_;
  576:     $r->print(<<END);
  577:        <form name="moveresource" action="/adm/cfile" target="_parent" method="post">
  578:          <input type="hidden" name="filename" value="" />
  579:          <input type="hidden" name="newfilename" value="" />
  580:          <input type="hidden" name="action" value="" />
  581:        </form>
  582:        <form name="delresource" action="/adm/cfile" target="_parent" method="post">
  583:          <input type="hidden" name="filename" value="" />
  584:          <input type="hidden" name="action" value="delete" />
  585:        </form>
  586:        <form name="pubresource" action="/adm/publish" target="_parent" method="post">
  587:          <input type="hidden" name="filename" value="" />
  588:          <input type="hidden" name="makeobsolete" value="0" />
  589:        </form>
  590:        <form name="printresource" action="/adm/printout" target="_parent" method="post">
  591:            <input type="hidden" name="postdata" value="" />
  592:        </form>
  593:        <form name="retrieveres" action="/adm/retrieve" target="_parent" method="post">
  594:            <input type="hidden" name="filename" value="" />
  595:        </form>
  596:        <form name="cleanup" action="/adm/cleanup" target="_parent" method="post">
  597:            <input type="hidden" name="filename" value="" />
  598:        </form>
  599: END
  600: }
  601: 
  602: #
  603: #   Get the title string or "[untitled]" if the file has no title metadata:
  604: #   Without the latter substitution, it's impossible to examine metadata for
  605: #   untitled resources.  Resources may be legitimately untitled, to prevent
  606: #   searches from locating them.
  607: #
  608: #   $str = getTitleString($fullname);
  609: #       $fullname - Fully qualified filename to check.
  610: #
  611: sub getTitleString {
  612:     my $fullname = shift;
  613:     my $title    = &Apache::lonnet::metadata($fullname, 'title');
  614: 
  615:     unless ($title) {
  616: 	$title = "[".&mt('untitled')."]";
  617:     }
  618:     return $title;
  619: }
  620: 
  621: sub getCopyRightString {
  622:     my $fullname = shift;
  623:     return &Apache::lonnet::metadata($fullname, 'copyright');
  624: }
  625: 
  626: sub getSourceRightString {
  627:     my $fullname = shift;
  628:     return &Apache::lonnet::metadata($fullname, 'sourceavail');
  629: }
  630: #
  631: #  Put out a directory table row:
  632: #    putdirectory(r, base, here, dirname, modtime, targetdir, bombs, numdir)
  633: #      r         - Apache request object.
  634: #      reqfile   - File in request.
  635: #      here      - Where we are in directory tree.
  636: #      dirname   - Name of directory special file.
  637: #      modtime   - Encoded modification time.
  638: #      targetdir - Publication target directory.
  639: #      bombs     - Reference to hash of URLs with runtime error messages.
  640: #      numdir    - Reference to scalar used to track number of sub-directories
  641: #                  in directory (used in form name for each "actions" dropdown).
  642: #
  643: sub putdirectory {
  644:     my ($r, $reqfile, $here, $dirname, $modtime, $targetdir, $bombs, $numdir) = @_;
  645: 
  646: # construct the display filename: the directory name unless ..:
  647:    
  648:     my $actionitem;
  649:  
  650:     my $disfilename = $dirname;
  651: # Don't display directory itself, and there is no way up from root directory
  652:     unless ((($dirname eq '..') && ($reqfile=~/^\/[^\/]+\/[^\/]+$/)) || ($dirname eq '.')) {
  653:         my $kaputt=0;
  654:         if (ref($bombs) eq 'HASH') {
  655:             foreach my $key (keys(%{$bombs})) {
  656:                 my $currentdir = &Apache::lonnet::declutter("$targetdir/$disfilename");
  657:                 if (($key) =~ m{^\Q$currentdir\E/}) { $kaputt=1; last; }
  658:             }
  659:         }
  660: #
  661: # Get the metadata from that directory's default.meta to display titles
  662: #
  663: 	%Apache::lonpublisher::metadatafields=();
  664: 	%Apache::lonpublisher::metadatakeys=();
  665: 	&Apache::lonpublisher::metaeval(
  666:                  &Apache::lonnet::getfile($r->dir_config('lonDocRoot').$here.'/'.$dirname.'/default.meta')
  667:                                        );
  668:         if ($dirname eq '..') {
  669:             $actionitem = &mt('Go to ...');
  670:             $disfilename = '<i>'.&mt('Parent Directory').'</i>';
  671:         } else {
  672:             $actionitem = 
  673:                     '<form name="dirselect_'.$$numdir.
  674:                     '" action="/adm/publish" target="_parent">'.
  675:                     '<select name="diraction" onchange="SetPubDir(this.form,document)">'.
  676:                       '<option selected="selected">'.&mt('Select action').'</option>'.
  677:                       '<option value="open">'.&mt('Open').'</option>'.
  678:                       '<option value="publish">'.&mt('Publish').'</option>'.
  679:                       '<option value="editmeta">'.&mt('Edit Metadata').'</option>'.
  680:                       '<option value="printdir">'.&mt('Print directory').'</option>'.
  681:                       '<option value="delete">'.&mt('Delete directory').'</option>'.
  682:                     '</select>'.
  683:                      '<input type="hidden" name="filename" value="'.&HTML::Entities::encode($here.'/'.$dirname,'<>&"').'/" />'.
  684:                      '<input type="hidden" name="openname" value="'.$here.'/'.$dirname.'/" />'.
  685:                      '<input type="hidden" name="postdata" value="" />'.
  686:                    '</form>';
  687:             $$numdir ++;
  688:         }
  689: 	$r->print('<tr class="LC_browser_folder">'.
  690: 		  '<td><img src="'.
  691: 		  $Apache::lonnet::perlvar{'lonIconsURL'}.'/navmap.folder.closed.gif" alt="folder" /></td>'.
  692: 		  '<td>'.$actionitem.'</td>'.
  693: 		  '<td><span class="LC_filename"><a href="'.&HTML::Entities::encode($here.'/'.$dirname,'<>&"').'/" target="_parent">'.
  694: 		  $disfilename.'</a></span></td>'.
  695: 		        '<td colspan="3">'.($kaputt?&Apache::lonhtmlcommon::authorbombs($targetdir.'/'.$disfilename.'/'):'').$Apache::lonpublisher::metadatafields{'title'});
  696: 	if ($Apache::lonpublisher::metadatafields{'subject'} ne '') {
  697: 	    $r->print(' <i>'.
  698: 		      $Apache::lonpublisher::metadatafields{'subject'}.
  699: 		      '</i> ');
  700: 	}
  701: 	$r->print($Apache::lonpublisher::metadatafields{'keywords'}.'</td>'.
  702: 		  '<td>'.&Apache::lonlocal::locallocaltime($modtime).'</td>'.
  703: 	          '<td></td>'.
  704: 		  "</tr>\n");
  705:     }
  706:     return;
  707: }
  708: 
  709: sub getTitle {
  710:     my ($resdir, $targetdir, $filename, $linkfilename, $meta_same, $bombs) = @_;
  711:     my $title='';
  712:     my $titleString = &getTitleString($targetdir.'/'.$filename);
  713:     if (-e $resdir.'/'.$filename) {
  714: 	$title = '<a href="'.$targetdir.'/'.$filename.
  715: 	    '.meta" target="cat">'.$titleString.'</a>';
  716:         if (!$meta_same) {
  717: 	    $title = &mt('Metadata Modified').'<br />'.$title.
  718: 		'<br />'.
  719:                 &Apache::loncommon::modal_link(
  720:                     '/adm/diff?filename='.$linkfilename.'.meta'.'&amp;versiontwo=priv',
  721:                     &mt('Metadata Diffs'),600,500);
  722: 	    $title.="\n".'<br />'.
  723:                 &Apache::loncommon::modal_link(
  724:                     '/adm/retrieve?filename='.$linkfilename.'.meta&amp;inhibitmenu=yes&amp;add_modal=yes',
  725:                     &mt('Retrieve Metadata'),600,500);
  726:         } 
  727:     }
  728:     # Allow editing metadata of published and unpublished resources
  729:     $title .= "\n".'<br />' if ($title);
  730:     $title .= '<a href="'.$linkfilename.'.meta">'.
  731:               ($$bombs{&Apache::lonnet::declutter($targetdir.'/'.$filename)}?
  732:                   '<img src="/adm/lonMisc/bomb.gif" border="0" alt="'.&mt('bomb').'" />':
  733:                   &mt('Edit Metadata')).
  734:               '</a>';
  735: 
  736:     return ($title, $titleString);
  737: }
  738: 
  739: 
  740: sub isMetaSame {
  741:     my ($cstr_dir, $resdir, $filename) = @_;
  742:     my $meta_cmtime = (stat($cstr_dir.'/'.$filename.'.meta'))[9];
  743:     my $meta_rmtime = (stat($resdir.'/'.$filename.'.meta'))[9];
  744:     return (&Apache::londiff::are_different_files($resdir.'/'.$filename.'.meta',
  745:             $cstr_dir.'/'.$filename.'.meta') && $meta_rmtime < $meta_cmtime) 
  746:         ? 0 : 1;
  747: }
  748:     
  749: 
  750: sub getStatus {    
  751:     my ($resdir, $targetdir, $cstr_dir, $filename,  
  752:             $linkfilename, $cmtime, $meta_same) = @_;
  753:     my $pubstatus = 'unpublished';
  754:     my $status = &mt('Unpublished');
  755: 
  756:     if (-e $resdir.'/'.$filename) {
  757:         my $same = 0;
  758:         if ((stat($resdir.'/'.$filename))[9] >= $cmtime) {
  759:             $same = 1;
  760:         } else {
  761:            if (&Apache::londiff::are_different_files($resdir.'/'.$filename,
  762: 						     $cstr_dir.'/'.$filename)) {
  763:               $same = 0;
  764:            } else {
  765:               $same = 1;
  766:            }
  767:         }
  768: 
  769:         my $rights_status =
  770:             &mt(&getCopyRightString($targetdir.'/'.$filename)).', ';
  771: 
  772:         my %lt_SourceRight = &Apache::lonlocal::texthash(
  773:                'open'   => 'Source: open',
  774:                'closed' => 'Source: closed',
  775:         );
  776:         $rights_status .=
  777:             $lt_SourceRight{&getSourceRightString($targetdir.'/'.$filename)};
  778: 
  779: 	if ($same) {
  780: 	    if (&Apache::lonnet::metadata($targetdir.'/'.$filename,'obsolete')) {
  781:                 $pubstatus = 'obsolete';
  782: 		$status=&mt('Obsolete');
  783:             } else {
  784: 		if (!$meta_same) {
  785: 		    $pubstatus = 'metamodified';
  786: 		} else {
  787: 		    $pubstatus = 'published';
  788: 		}
  789: 		$status=&mt('Published').
  790: 		    '<br />'. $rights_status;
  791: 	    }
  792: 	} else {
  793:             $pubstatus = 'modified';
  794: 	    $status=&mt('Modified').
  795: 		'<br />'. $rights_status;
  796: 	    if (&Apache::loncommon::fileembstyle(($filename=~/\.(\w+)$/)) eq 'ssi') {
  797: 		$status.='<br />'.
  798:                          &Apache::loncommon::modal_link(
  799:                              '/adm/diff?filename='.$linkfilename.'&amp;versiontwo=priv',
  800:                              &mt('Diffs'),600,500);
  801: 	    }
  802: 	} 
  803: 
  804: 	$status.="\n".'<br />'.
  805:              &Apache::loncommon::modal_link(
  806:                  '/adm/retrieve?filename='.$linkfilename.'&amp;inhibitmenu=yes&amp;add_modal=yes',&mt('Retrieve'),600,500);
  807:     }
  808: 
  809:     return ($status, $pubstatus);
  810: }
  811: 
  812: 
  813: #
  814: #   Put a table row for a file resource.
  815: #
  816: sub putresource {
  817:     my ($r, $udom, $uname, $filename, $thisdisfn, $resdir, $targetdir, 
  818:             $linkdir, $cmtime, $size, $numres, $linkfilename, $title, 
  819:             $status, $pubstatus) = @_;
  820:     &Apache::lonnet::devalidate_cache_new('meta',$targetdir.'/'.$filename);
  821: 
  822:     my $editlink='';
  823:     my $editlink2='';
  824:     if ($filename=~/\.(xml|html|htm|xhtml|xhtm|sty)$/) {
  825: 	$editlink=' <br />(<a href="'.$linkdir.'/'.$filename.'?editmode=Edit&amp;problemmode=edit">'.&mt('Edit').'</a>)';
  826:     }
  827:     if ($filename=~/$LONCAPA::assess_re/) {
  828: 	$editlink=' (<a href="'.$linkdir.'/'.$filename.'?editmode=Edit&amp;problemmode=editxml">'.&mt('EditXML').'</a>)';
  829: 	$editlink2=' <br />(<a href="'.$linkdir.'/'.$filename.'?editmode=Edit&amp;problemmode=edit">'.&mt('Edit').'</a>)';
  830:     }
  831:     if ($filename=~/\.(problem|exam|quiz|assess|survey|form|library|xml|html|htm|xhtml|xhtm|sty)$/) {
  832: 	$editlink.=' (<a href="/adm/cleanup?filename='.$linkfilename.'" target="_parent">'.&mt('Clean Up').')</a>';
  833:     }
  834:     if ($filename=~/\.(zip|tar|bz2|gz|tar.gz|tar.bz2|tgz)$/) {
  835: 	$editlink=' (<a target="_parent" href="/adm/cfile?decompress='.$linkfilename.'">'.&mt('Decompress').'</a>)';
  836:     }
  837:     my $publish_button = (-e $resdir.'/'.$filename) ? &mt('Re-publish') : &mt('Publish');
  838:     my $pub_select = '';
  839:     &create_pubselect($r,\$pub_select,$udom,$uname,$thisdisfn,$filename,$resdir,$pubstatus,$publish_button,$numres);
  840:     $r->print(&Apache::loncommon::start_data_table_row().
  841: 	      '<td>'.($filename=~/[\#\~]$/?'&nbsp;':
  842: 		      '<img src="'.&Apache::loncommon::icon($filename).'" alt="" />').'</td>'.
  843:               '<td>'.$pub_select.'</td>'.
  844: 	      '<td><span class="LC_filename">'.
  845: 	      '<a href="'.$linkdir.'/'.$filename.'" target="_parent">'.
  846:                $filename.'</a></span>'.$editlink2.$editlink.
  847: 	      '</td>'.
  848: 	      '<td>'.$title.'</td>'.
  849:               '<td class="LC_browser_file_'.$pubstatus.'">&nbsp;&nbsp;</td>'. # Display publication status
  850:               '<td>'.$status.'</td>'.
  851: 	      '<td>'.&Apache::lonlocal::locallocaltime($cmtime).'</td>'.
  852: 	      '<td>'.sprintf("%.1f",$size).'</td>'.
  853: 	      &Apache::loncommon::end_data_table_row()
  854:     );
  855:     return;
  856: }
  857: 
  858: sub create_pubselect {
  859:     my ($r,$pub_select,$udom,$uname,$thisdisfn,$filename,$resdir,$pubstatus,$publish_button,$numres) = @_;
  860:     $$pub_select = '
  861: <form name="resselect_'.$$numres.'" action="">
  862: <select name="reschoice"  onchange="SetResChoice(this.form)">
  863: <option>'.&mt('Select action').'</option>'.
  864: '<option value="copy">'.&mt('Copy').'</option>';
  865:     if ($pubstatus eq 'obsolete' || $pubstatus eq 'unpublished') {
  866:         $$pub_select .= 
  867: '<option value="rename">'.&mt('Rename').'</option>'.
  868: '<option value="move">'.&mt('Move').'</option>'.
  869: '<option value="delete">'.&mt('Delete').'</option>';
  870:     } else {
  871:         $$pub_select .= '
  872: <option value="obsolete">'.&mt('Mark obsolete').'</option>';
  873:     }
  874: # check for versions
  875:     my $versions = &check_for_versions($r,'/'.$filename,$udom,$uname);
  876:     if ($versions > 0) {
  877:         $$pub_select .='
  878: <option value="retrieve">'.&mt('Retrieve old version').'</option>';
  879:     }
  880:     $$pub_select .= '
  881: <option value="publish">'.$publish_button.'</option>'.
  882: '<option value="cleanup">'.&mt('Clean up').'</option>'.
  883: '<option value="print">'.&mt('Print').'</option>'.
  884: '</select>
  885: <input type="hidden" name="filename" value="/priv'.
  886:  &HTML::Entities::encode($thisdisfn.'/'.$filename,'<>&"').'" />
  887:  <input type="hidden" name="dispfilename" value="'.
  888:  &HTML::Entities::encode($filename).'" /></form>';
  889:     $$numres ++;
  890: }
  891: 
  892: sub check_for_versions {
  893:     my ($r,$fn,$udom,$uname) = @_;
  894:     my $versions = 0;
  895:     my $docroot=$r->dir_config('lonDocRoot');
  896:     my $resfn=$docroot.'/res/'.$udom.'/'.$uname.$fn;
  897:     my $resdir=$resfn;
  898:     $resdir=~s/\/[^\/]+$/\//;
  899:     $fn=~/\/([^\/]+)\.(\w+)$/;
  900:     my $main=$1;
  901:     my $suffix=$2;
  902:     opendir(DIR,$resdir);
  903:     while (my $filename=readdir(DIR)) {
  904:         if ($filename=~/^\Q$main\E\.(\d+)\.\Q$suffix\E$/) {
  905:             $versions ++;        
  906:         }
  907:     }
  908:     closedir(DIR);
  909:     return $versions;
  910: }
  911: 
  912: 1;
  913: __END__
  914: 
  915: 
  916: =head1 NAME
  917: 
  918: Apache::lonpubdir - Authoring space directory lister
  919: 
  920: =head1 SYNOPSIS
  921: 
  922: Invoked (for various locations) by /etc/httpd/conf/srm.conf:
  923: 
  924:  <LocationMatch "^/+priv.*/$">
  925:  PerlAccessHandler       Apache::loncacc
  926:  SetHandler perl-script
  927:  PerlHandler Apache::lonpubdir
  928:  ErrorDocument     403 /adm/login
  929:  ErrorDocument     404 /adm/notfound.html
  930:  ErrorDocument     406 /adm/unauthorized.html
  931:  ErrorDocument	  500 /adm/errorhandler
  932:  </LocationMatch>
  933: 
  934:  <Location /adm/pubdir>
  935:  PerlAccessHandler       Apache::lonacc
  936:  SetHandler perl-script
  937:  PerlHandler Apache::lonpubdir
  938:  ErrorDocument     403 /adm/login
  939:  ErrorDocument     404 /adm/notfound.html
  940:  ErrorDocument     406 /adm/unauthorized.html
  941:  ErrorDocument	  500 /adm/errorhandler
  942:  </Location>
  943: 
  944: =head1 INTRODUCTION
  945: 
  946: This module publishes a directory of files.
  947: 
  948: This is part of the LearningOnline Network with CAPA project
  949: described at http://www.lon-capa.org.
  950: 
  951: =head1 HANDLER SUBROUTINE
  952: 
  953: This routine is called by Apache and mod_perl.
  954: 
  955: =over 4
  956: 
  957: =item *
  958: 
  959: read in information
  960: 
  961: =item *
  962: 
  963: start page output
  964: 
  965: =item *
  966: 
  967: run through list of files and attempt to publish unhidden files
  968: 
  969: =back
  970: 
  971: =head1 SUBROUTINES:
  972: 
  973: =over
  974: 
  975: =item startpage($r, $uame, $udom, $thisdisfn)
  976: 
  977: Output the header of the page.  This includes:
  978:  - The HTML header 
  979:  - The H1/H3  stuff which includes the directory.
  980:  
  981:     startpage($r, $uame, $udom, $thisdisfn);
  982:         $r     - The apache request object.
  983:         $uname - User name.
  984:         $udom  - Domain name the user is logged in under.
  985:         $thisdisfn - Displayable version of the filename.
  986: 
  987: =item getTitleString($fullname)
  988: 
  989:     Get the title string or "[untitled]" if the file has no title metadata:
  990:     Without the latter substitution, it's impossible to examine metadata for
  991:     untitled resources.  Resources may be legitimately untitled, to prevent
  992:     searches from locating them.
  993:     
  994:     $str = getTitleString($fullname);
  995:         $fullname - Fully qualified filename to check.
  996: 
  997: =item putdirectory($r, $base, $here, $dirname, $modtime, $targetdir, $bombs,
  998:                    $numdir)
  999: 
 1000:     Put out a directory table row:
 1001:     
 1002:         $r        - Apache request object.
 1003:         $reqfile  - File in request.
 1004:         $here     - Where we are in directory tree.
 1005:         $dirname  - Name of directory special file.
 1006:         $modtime  - Encoded modification time.
 1007:         targetdir - Publication target directory.
 1008:         bombs     - Reference to hash of URLs with runtime error messages.
 1009:         numdir    - Reference to scalar used to track number of sub-directories
 1010:                     in directory (used in form name for each "actions" dropdown).
 1011: 
 1012: =back
 1013: 
 1014: =cut

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>