File:  [LON-CAPA] / loncom / interface / lonwishlist.pm
Revision 1.1: download - view: text, annotated - select for diffs
Tue Aug 10 14:30:20 2010 UTC (13 years, 10 months ago) by wenzelju
Branches: MAIN
CVS tags: HEAD
Add new module lonwishlist:
- This module will replace the bookmark-collection. Bookmark-collection is out of use because it's partly faulty and intricate to use.
- The wishlist will offer a possibility to link resources on a hierarchal list. The list can be modified by adding new links and folder, to structure the list. Existing entries can be renamed, moved, sorted and deleted. Additional information can be attached for each entry by adding a note. Linked resources from the wishlist can be imported into courses. The wishlist is only available for roles with access to the resource-pool.

Still under progress! Function for setting a link to wishlist, when viewing a resource, and import-functionality will follow!

    1: # The LearningOnline Network with CAPA
    2: # Routines to control the wishlist
    3: #
    4: # $Id: lonwishlist.pm,v 1.1 2010/08/10 14:30:20 wenzelju Exp $
    5: #
    6: # Copyright Michigan State University Board of Trustees
    7: #
    8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
    9: #
   10: # LON-CAPA is free software; you can redistribute it and/or modify
   11: # it under the terms of the GNU General Public License as published by
   12: # the Free Software Foundation; either version 2 of the License, or
   13: # (at your option) any later version.
   14: #
   15: # LON-CAPA is distributed in the hope that it will be useful,
   16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
   17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   18: # GNU General Public License for more details.
   19: #
   20: # You should have received a copy of the GNU General Public License
   21: # along with LON-CAPA; if not, write to the Free Software
   22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   23: #
   24: # /home/httpd/html/adm/gpl.txt
   25: #
   26: # http://www.lon-capa.org/
   27: #
   28: 
   29: 
   30: 
   31: package Apache::lonwishlist;
   32: 
   33: use strict;
   34: use Apache::Constants qw(:common);
   35: use Apache::lonnet;
   36: use Apache::loncommon();
   37: use Apache::lonhtmlcommon;
   38: use Apache::lonlocal;
   39: use Tree;
   40: 
   41: 
   42: # Global variables
   43: my $root;
   44: my @childrenRt;
   45: my %TreeHash;
   46: my %TreeToHash;
   47: my @allFolders;
   48: my @allNodes;
   49: my $indentConst = 20;
   50: 
   51: 
   52: # Read wishlist from user-data
   53: sub getWishlist {
   54:     my @wishlistkeys = &Apache::lonnet::getkeys('wishlist');
   55:     my %wishlist = &Apache::lonnet::get('wishlist',\@wishlistkeys);
   56:    foreach my $i (%wishlist) {
   57:         #File not found. This appears at the first time using the wishlist
   58:         #Create file and put 'root' into it
   59:        if ($i =~m/^error:No such file/) {
   60:            &Apache::lonnet::logthis($i.'! Create file by putting in the "root" of the directory tree.');
   61:            &Apache::lonnet::put('wishlist', {'root' => ''});
   62:            @wishlistkeys = &Apache::lonnet::getkeys('wishlist');
   63:            %wishlist = &Apache::lonnet::get('wishlist',\@wishlistkeys);
   64:        }
   65:        elsif ($i =~ /^(con_lost|error|no_such_host)/i) {
   66:            &Apache::lonnet::logthis('ERROR while attempting to get wishlist: '.$i);
   67:            return 'error';
   68:        }
   69:     }
   70: 
   71:     #If hash is empty, put 'root' into it, so we got a node to start the tree
   72:     if ((keys %wishlist) == 0) {
   73:         &Apache::lonnet::logthis('ERROR while attempting to get wishlist: no keys retrieved!');
   74:         return 'error';
   75:     }
   76:     
   77:     return %wishlist;
   78: }
   79: 
   80: 
   81: # Write wishlist to user-data
   82: sub putWishlist {
   83:     my $wishlist = shift;
   84:     &Apache::lonnet::put('wishlist',$wishlist);
   85: }
   86: 
   87: 
   88: # Removes all existing entrys for wishlist in user-data
   89: sub deleteWishlist {
   90:     my @wishlistkeys = &Apache::lonnet::getkeys('wishlist');
   91:     my %wishlist = &Apache::lonnet::del('wishlist',\@wishlistkeys);
   92: }
   93: 
   94: 
   95: # Create a new entry
   96: sub newEntry() {
   97:     my ($title, $path, $note) = @_;
   98:     my $date = gmtime();
   99:     # Create Entry-Object
  100:     my $entry = Entry->new(title => $title, path => $path, note => $note, date => $date);
  101:     # Create Tree-Object, this correspones a node in the wishlist-tree
  102:     my $tree = Tree->new($entry);
  103:     # Add this node to wishlist-tree
  104:     my $folderIndex = $env{'form.folders'};
  105:     if ($folderIndex ne '') {
  106:         @allFolders = ();
  107:         &getFoldersToArray(\@childrenRt);
  108:         my $folderToInsertOn = &Tree::getNodeByIndex($folderIndex,\@allFolders);
  109:         $folderToInsertOn->add_child($tree);
  110:     }
  111:     else {
  112:         $root->add_child($tree);
  113:     }
  114:     &saveChanges();
  115: }
  116: 
  117: 
  118: # Delete entries
  119: sub deleteEntries {
  120:     my $marked = shift;
  121:     &getNodesToArray(\@childrenRt);
  122: 
  123:     foreach my $m (@$marked) {
  124:         my $found = &Tree::getNodeByIndex($m, \@allNodes);
  125:         &Tree::removeNode($found);
  126:     }
  127:     @allNodes = ();
  128:     &saveChanges();
  129: }
  130: 
  131: 
  132: # Sort entries
  133: sub sortEntries {
  134:     my $indexNode = shift;
  135:     my $at = shift;
  136:     
  137:     &getNodesToArray(\@childrenRt);
  138:     my $foundNode = &Tree::getNodeByIndex($indexNode, \@allNodes);
  139: 
  140:     &Tree::moveNode($foundNode,$at,undef);
  141:     @allNodes = ();
  142: }
  143: 
  144: 
  145: # Move entries
  146: sub moveEntries {
  147:     my $indexNodesToMove = shift;
  148:     my $indexParent = shift;
  149:     my @nodesToMove = ();
  150: 
  151:     &getNodesToArray(\@childrenRt);
  152:     foreach my $index (@$indexNodesToMove) {
  153:         my $foundNode = &Tree::getNodeByIndex($index, \@allNodes);
  154:         push(@nodesToMove, $foundNode);
  155:     }
  156: 
  157:     foreach my $node (@nodesToMove) {
  158:         my $foundParent;
  159:         my $parentIsIn = 0;
  160:         foreach my $n (@nodesToMove) {
  161:             if ($node->parent()->value() ne "root") {
  162:                if ($node->parent()->value()->nindex() == $n->value()->nindex()) {
  163:                     $parentIsIn = 1;
  164:                 }
  165:             }
  166:         }
  167:         if (!$parentIsIn) {
  168:             if ($indexParent ne "root") {
  169:                 $foundParent = &Tree::getNodeByIndex($indexParent, \@allNodes);
  170:                 &Tree::moveNode($node,undef,$foundParent);
  171:             }
  172:             else {
  173:                 &Tree::moveNode($node,undef,$root);
  174:             }
  175:         }
  176:     }
  177:     @allNodes = ();
  178: }
  179: 
  180: 
  181: # Set a new title for an entry
  182: sub setNewTitle {
  183:     my ($nodeindex, $newTitle) = @_;
  184:     &getNodesToArray(\@childrenRt);
  185:     my $found = &Tree::getNodeByIndex($nodeindex, \@allNodes);
  186:     $found->value()->title($newTitle); 
  187:     @allNodes = ();
  188: }
  189: 
  190: 
  191: # Set a new note for an entry
  192: sub setNewNote {
  193:     my ($nodeindex, $newNote) = @_;
  194:     &getNodesToArray(\@childrenRt);
  195:     my $found = &Tree::getNodeByIndex($nodeindex, \@allNodes);
  196:     $found->value()->note($newNote); 
  197:     @allNodes = ();
  198: }
  199: 
  200: 
  201: # Save all changes
  202: sub saveChanges {
  203:     @childrenRt = $root->children();
  204:     &Tree::TreeIndex(\@childrenRt);
  205:     &Tree::setCountZero();
  206:     &Tree::RootToHash(\@childrenRt);
  207:     &Tree::TreeToHash(\@childrenRt);
  208:     &deleteWishlist();
  209:     &putWishlist(\%TreeToHash);
  210: 
  211: }
  212: 
  213: 
  214: # Return the names for all exiting folders in option-tags, so
  215: # a new link or a new folder can be created in an existing folder
  216: my $indent = 0;
  217: my $foldersOption;
  218: sub getFoldersForOption {
  219:     my $nodes = shift;
  220: 
  221:     foreach my $n (@$nodes) {
  222:         if ($n->value()->path() eq '') {
  223:             $foldersOption .= '<option value="'.$n->value()->nindex().'" style="margin-left:'.$indent.'px">'.
  224:                                    $n->value()->title().
  225:                                '</option>';
  226: 
  227:         my @children = $n->children();
  228:         if ($#children >=0) {
  229:             $indent += 10;
  230:             &getFoldersForOption(\@children);
  231:             $indent -= 10;
  232:             }
  233:         }
  234:     }
  235: }
  236: 
  237: 
  238: sub getfoldersOption {
  239:    if (&getWishlist ne 'error') {
  240:        %TreeHash = &getWishlist();
  241:        $root = &Tree::HashToTree();
  242:        @childrenRt = $root->children();
  243:        &getFoldersForOption(\@childrenRt);
  244:        my $options = '<option value="" selected="selected"></option>'.$foldersOption;
  245:        $foldersOption = '';
  246:        return $options;
  247:    }
  248:    else {
  249:        return '';
  250:    }
  251: }
  252: 
  253: 
  254: # Put all folder-nodes to an array
  255: sub getFoldersToArray {
  256:     my $children = shift;
  257:     foreach my $c (@$children) {
  258:         if ($c->value()->path() eq '') {
  259:             push(@allFolders,$c);
  260:         }
  261:         my @newchildren = $c->children();
  262:         if ($#newchildren >= 0) {
  263:             &getFoldersToArray(\@newchildren);
  264:         }
  265:     }
  266: }
  267: 
  268: 
  269: # Put all nodes to an array
  270: sub getNodesToArray {
  271:     my $children = shift;
  272:     foreach my $c (@$children) {
  273:         push(@allNodes,$c);
  274:         my @newchildren = $c->children();
  275:         if ($#newchildren >= 0) {
  276:             &getNodesToArray(\@newchildren);
  277:         }
  278:     }
  279: }
  280: 
  281: 
  282: # Return a script-tag containing Javascript-function
  283: # needed for wishlist actions like 'new link' ect.
  284: sub JSforWishlist {
  285:     my $startPagePopup = &Apache::loncommon::start_page('Wishlist',undef,
  286:                                                             {'only_body' => 1,
  287:                                                              'js_ready'  => 1,
  288:                                                              'bgcolor'   => '#FFFFFF',});
  289:     my $endPagePopup = &Apache::loncommon::end_page({'js_ready' => 1});
  290: 
  291:     @allFolders = ();
  292:     &getFoldersToArray(\@childrenRt);
  293:     &getFoldersForOption(\@childrenRt);
  294: 
  295:     # texthash
  296:     my %lt = &Apache::lonlocal::texthash(
  297:                  'nl' => 'New Link',
  298:                  'nf' => 'New Folder',
  299:                  'lt' => 'Link Title',
  300:                  'ft' => 'Folder Title',
  301:                  'pa' => 'Path',
  302:                  'nt' => 'Note',
  303:                  'si' => 'Save in',
  304:                  'cl' => 'Cancel');
  305: 
  306: 
  307:     my $inPageNewLink = '<h1>'.$lt{'nl'}.'</h1>'.
  308:                         '<form method="post" name="newlink" action="/adm/wishlist" target="wishlist" '.
  309:                         'onsubmit="return newlinksubmit();" >'.
  310:                         &Apache::lonhtmlcommon::start_pick_box().
  311:                         &Apache::lonhtmlcommon::row_title($lt{'lt'}).
  312:                         '<input type="text" name="title" size="45" value="" />'.
  313:                         &Apache::lonhtmlcommon::row_closure().
  314:                         &Apache::lonhtmlcommon::row_title($lt{'pa'}).
  315:                         '<input type="text" name="path" size="45" value="" />'.
  316:                         &Apache::lonhtmlcommon::row_closure().
  317:                         &Apache::lonhtmlcommon::row_title($lt{'nt'}).
  318:                         '<textarea name="note" rows="3" cols="35" style="width:100%"></textarea>'.
  319:                         &Apache::lonhtmlcommon::row_closure(1).
  320:                         &Apache::lonhtmlcommon::end_pick_box().
  321:                         '<br/><br/>'.
  322:                         '<input type="submit" value="'.$lt{'si'}.'" />'.
  323:                         '<select name="folders">'.
  324:                         '<option value="" selected="selected"></option>'.
  325:                         $foldersOption.
  326:                         '</select>'.
  327:                         '<input type="button" value="'.$lt{'cl'}.'" onclick="javascript:window.close();" />'.
  328:                         '</form>';
  329:     
  330:     my $inPageNewFolder = '<h1>'.$lt{'nf'}.'</h1>'.
  331:                           '<form method="post" name="newfolder" action="/adm/wishlist" target="wishlist" '.
  332:                           'onsubmit="return newfoldersubmit();" >'.
  333:                           &Apache::lonhtmlcommon::start_pick_box().
  334:                           &Apache::lonhtmlcommon::row_title($lt{'ft'}).
  335:                           '<input type="text" name="title" size="45" value="" /><br />'.
  336:                           &Apache::lonhtmlcommon::row_closure().
  337:                           &Apache::lonhtmlcommon::row_title($lt{'nt'}).
  338:                           '<textarea name="note" rows="3" cols="35" style="width:100%"></textarea><br />'.
  339:                           &Apache::lonhtmlcommon::row_closure(1).
  340:                           &Apache::lonhtmlcommon::end_pick_box().
  341:                           '<br/><br/>'.
  342:                           '<input type="submit" value="'.$lt{'si'}.'" />'.
  343:                           '<select name="folders">'.
  344:                           '<option value="" selected="selected"></option>'.
  345:                           $foldersOption.
  346:                           '</select>'.
  347:                           '<input type="button" value="'.$lt{'cl'}.'" onclick="javascript:window.close();" />'.
  348:                           '</form>';
  349: 
  350:     # Remove all \n for inserting on javascript document.write
  351:     $inPageNewLink =~ s/\n//g;
  352:     $inPageNewFolder =~ s/\n//g;
  353: 
  354:     my $warningLink = &mt('You must insert a title and a path!');
  355:     my $warningFolder = &mt('You must insert a title!');
  356:     my $warningDelete = &mt('Are you sure you want to delete the selected entries? Deleting a folder also deletes all entries within this folder!');
  357:     my $warningSave = &mt('You have unsaved changes. You can either save these changes now by clicking "ok" or click "cancel" if you do not want to save your changes.');
  358:     my $warningMove = &mt('You must select a destination folder!');
  359:     $foldersOption = '';
  360: 
  361:     my $js = &Apache::lonhtmlcommon::scripttag(<<JAVASCRIPT);
  362:     function newLink() {
  363:         newlinkWin=window.open('','newlinkWin','width=580,height=320,scrollbars=yes');
  364:         newlinkWin.document.write('$startPagePopup' 
  365:                               +'<script type="text\/javascript">'
  366:                               +'function newlinksubmit(){'
  367:                               +'var path = document.getElementsByName("path")[0].value;'
  368:                               +'var title = document.getElementsByName("title")[0].value;'
  369:                               +'if (!path || !title) {'
  370:                               +'alert("$warningLink");'
  371:                               +'return false;}'
  372:                               +'else {'
  373:                               +'window.close();'
  374:                               +'return true;}}'
  375:                               +'<\/scr'+'ipt>'
  376:                               +'$inPageNewLink'
  377:                               +'$endPagePopup');
  378:         newlinkWin.document.close();
  379:     }
  380: 
  381:     function newFolder() {
  382:         newfolderWin=window.open('','newfolderWin','width=580,height=270, scrollbars=yes');
  383:         newfolderWin.document.write('$startPagePopup' 
  384:                               +'<script type="text\/javascript">'
  385:                               +'function newfoldersubmit(){'
  386:                               +'var title = document.getElementsByName("title")[0].value;'
  387:                               +'if (!title) {'
  388:                               +'alert("$warningFolder");'
  389:                               +'return false;}'
  390:                               +'else {'
  391:                               +'window.close();'
  392:                               +'return true;}}'
  393:                               +'<\/scr'+'ipt>'
  394:                               +'$inPageNewFolder'
  395:                               +'$endPagePopup');
  396:         newfolderWin.document.close();
  397:     }
  398: 
  399:     function setFormAction(action,mode) {
  400:         var r = true;
  401:         setAction('');
  402:         if (action == 'delete') {
  403:             r = confirm("$warningDelete");
  404:             setAction('delete');
  405:         }
  406:         else if (action == 'save') {
  407:             var d = getDifferences();
  408:             if (d) {
  409:                 if (!confirm('$warningSave')) {
  410:                     setAction('noSave');
  411:                 }
  412:             }
  413:             r = true;
  414:         }
  415:         document.getElementsByName('list')[0].setAttribute("action", "/adm/wishlist?mode="+mode); 
  416:         if(r){
  417:             document.getElementsByName('list')[0].submit(); 
  418:         }
  419:     }
  420: 
  421:     function setAction(action) {
  422:         document.getElementById('action').value = action; 
  423:     }
  424: 
  425:     function getDifferences() {
  426:         var newtitles = document.getElementsByName('newtitle');
  427:         var i = 0;
  428:         for (i=0;i<newtitles.length;i++) {
  429:             var newt = newtitles[i].value;
  430:             var oldt = newtitles[i].alt;
  431:             if (newt != oldt) {
  432:                 return true;
  433:             }
  434:         }
  435:         var newnote = document.getElementsByName('newnote');
  436:         var i = 0;
  437:         for (i=0;i<newnote.length;i++) {
  438:             var newn = newnote[i].value;
  439:             var oldn = newnote[i].innerHTML;
  440:             if (newn != oldn) {
  441:                 return true;
  442:             }
  443:         }
  444:         return false;
  445:     }
  446: 
  447:     function onLoadAction(mode) {
  448:         window.name = 'wishlist';
  449:         if (mode == "edit") {
  450:             var deepestRows = getDeepestRows();
  451:             setDisplaySelect(deepestRows, '');
  452:         }
  453:     }
  454: 
  455:     function folderAction(rowid) {
  456:         var row = document.getElementById(rowid);
  457:         var indent = getIndent(row);
  458:         var displ;
  459:         var status;
  460:         if (getImage(row) == 'closed') {
  461:             displ = '';
  462:             status = 'open';
  463:         }
  464:         else {
  465:             displ = 'LC_hidden';
  466:             status = 'closed';
  467:         }
  468:         setImage(row,status);
  469:         if (getNextRow(row) != null) {
  470:             var nextIndent = getIndent(getNextRow(row));
  471:             row = getNextRow(row);
  472:             while (nextIndent > indent) {
  473:                 if (displ == '') {
  474:                     row.className = (row.className).replace('LC_hidden','');
  475:                 }
  476:                 else if (displ != '' && !((row.className).match('LC_hidden'))) {
  477:                     var oldClass = row.className;
  478:                     row.className = oldClass+' LC_hidden';
  479:                     setDisplayNote(row.id.replace('row','note'),'LC_hidden');
  480:                 }
  481:                 if (status == 'open' && getImage(row).match('closed')) {
  482:                     row = getNextRowWithIndent(row, getIndent(row));
  483:                 }
  484:                 else {
  485:                     row = getNextRow(row);
  486:                 } 
  487:                 if (row != null) {
  488:                     nextIndent = getIndent(row);
  489:                 } 
  490:                 else {
  491:                     nextIndent = indent;
  492:                 }
  493:             }
  494:         }
  495:         setClasses();
  496:         var newtitles = document.getElementsByName('newtitle');
  497:         if (newtitles.length>0) {
  498:             var deepestRows = getDeepestRows();
  499:             var otherRows = getOtherRows(deepestRows);
  500:             setDisplaySelect(deepestRows,'');
  501:             setDisplaySelect(otherRows,'LC_hidden');
  502:         }
  503:     }
  504: 
  505:     function selectAction(rowid) {
  506:         var row = document.getElementById(rowid);
  507:         var indent = getIndent(row);
  508:         var checked = getChecked(row);
  509:         var previousFolderRows = new Array();
  510:         if (indent != 0) {
  511:             previousFolderRows = getPreviousFolderRows(row);
  512:         }
  513:         if (getNextRow(row) != null) {
  514:             var nextIndent = getIndent(getNextRow(row));
  515:             row = getNextRow(row);
  516:                 while (nextIndent > indent) {
  517:                     setChecked(row,checked);
  518:                     if (status == 'open' && getImage(row).match('closed')) {
  519:                         row = getNextRowWithIndent(row, getIndent(row));
  520:                     }
  521:                     else {
  522:                         row = getNextRow(row);
  523:                     }
  524:                     if (row != null) {
  525:                         nextIndent = getIndent(row);
  526:                     }
  527:                     else {
  528:                         nextIndent = indent;
  529:                     }
  530:                 }
  531:         }
  532:         if (!checked) {
  533:             var i = 0;
  534:             for (i=0;i<previousFolderRows.length;i++) {
  535:                 setChecked(previousFolderRows[i], false);
  536:             }
  537:         }
  538:     }
  539: 
  540:     function getNextNote(row) {
  541:         var rowId = row.id;
  542:         var nextRowId = parseInt(rowId.substr(3,rowId.length))+1;
  543:         nextRowId = "note"+nextRowId;
  544:         var nextRow = document.getElementById(nextRowId);
  545:         return nextRow;
  546:     }
  547: 
  548:     function getNextRow(row) {
  549:         var rowId = row.id;
  550:         var nextRowId = parseInt(rowId.substr(3,rowId.length))+1;
  551:         nextRowId = "row"+nextRowId;
  552:         var nextRow = document.getElementById(nextRowId);
  553:         return nextRow;
  554:     }
  555: 
  556:     function getPreviousRow(row) {
  557:         var rowId = row.id;
  558:         var previousRowId =  parseInt(rowId.substr(3,rowId.length))-1;
  559:         previousRowId = "row"+previousRowId;
  560:         var previousRow =document.getElementById(previousRowId);
  561:         return previousRow;
  562:     }
  563: 
  564:     function getIndent(row) {
  565:         var childPADD = document.getElementById(row.id.replace('row','padd'));
  566:         indent = childPADD.style.paddingLeft;
  567:         indent = parseInt(indent.substr(0,(indent.length-2)));
  568:  
  569:         if (getImage(row).match('link')) {
  570:             indent -= $indentConst;
  571:         }
  572:         return indent;
  573:     }
  574: 
  575:     function getNextRowWithIndent(row, indent) {
  576:         var nextRow = getNextRow(row);
  577:         if (nextRow != null) {
  578:         var nextIndent = getIndent(nextRow);
  579:         while (nextIndent >= indent) {
  580:             if (nextIndent == indent) {
  581:                 return nextRow;
  582:             }
  583:             nextRow = getNextRow(nextRow);
  584:             if (nextRow == null) {
  585:                 return null;
  586:             }
  587:             nextIndent = getIndent(nextRow);
  588:         }
  589:         }
  590:         return nextRow;
  591:     }
  592: 
  593:     function getImage(row) {
  594:         var childIMG = document.getElementById(row.id.replace('row','img'));
  595:         if ((childIMG.src).match('closed')) {
  596:             return 'closed';
  597:         }
  598:         else if ((childIMG.src).match('open')) {
  599:             return 'open;'
  600:         }
  601:         else {
  602:             return 'link';
  603:         }
  604:     } 
  605: 
  606:     function setImage(row, status) {
  607:         var childIMG = document.getElementById(row.id.replace('row','img'));
  608:         var childIMGFolder = document.getElementById(row.id.replace('row','imgFolder'));
  609:         childIMG.src = "/adm/lonIcons/arrow."+status+".gif";
  610:         childIMGFolder.src="/adm/lonIcons/navmap.folder."+status+".gif"; 
  611:     }
  612: 
  613:     function getChecked(row) {
  614:         var childCHECK = document.getElementById(row.id.replace('row','check'));
  615:         var checked = childCHECK.checked;
  616:         return checked;
  617:     }
  618: 
  619:     function setChecked(row,checked) {
  620:         var childCHECK = document.getElementById(row.id.replace('row','check'));
  621:         childCHECK.checked = checked;
  622:     }
  623: 
  624:     function getPreviousFolderRows(row) {
  625:         var previousRow = getPreviousRow(row);
  626:         var indent = getIndent(previousRow);
  627:         var kindOfEntry = getImage(previousRow);
  628:         var rows = new Array();
  629:         if (kindOfEntry != 'link') {
  630:             rows.push(previousRow);
  631:         }
  632: 
  633:         while (indent >0) {
  634:             previousRow = getPreviousRow(previousRow);
  635:             if (previousRow != null) {
  636:                 indent = getIndent(previousRow);
  637:                 kindOfEntry = getImage(previousRow);
  638:                 if (kindOfEntry != 'link') {
  639:                     rows.push(previousRow);
  640:                 }
  641:             }
  642:             else {
  643:                 indent = 0; 
  644:             }
  645:         }
  646:         return rows;
  647:     }
  648: 
  649:     function getDeepestRows() {
  650:         var row = document.getElementById('row0');
  651:         var firstRow = row;
  652:         var indent = getIndent(row);
  653:         var maxIndent = indent;
  654:         while (getNextRow(row) != null) {
  655:             row = getNextRow(row);
  656:             indent = getIndent(row);
  657:             if (indent>maxIndent && !((row.className).match('LC_hidden'))) {
  658:                 maxIndent = indent;
  659:             }
  660:         }
  661:         var deepestRows = new Array();
  662:         row = firstRow;
  663:         var rowIndent;
  664:         while (getNextRow(row) != null) {
  665:             rowIndent = getIndent(row);
  666:             if (rowIndent == maxIndent) {
  667:                 deepestRows.push(row);
  668:             }
  669:             row = getNextRow(row);
  670:         }
  671:         rowIndent = getIndent(row);
  672:         if (rowIndent == maxIndent) {
  673:             deepestRows.push(row);
  674:         }
  675:         return deepestRows;
  676:     }
  677: 
  678:     function getOtherRows(deepestRows) {
  679:         var row = document.getElementById('row0');
  680:         var otherRows = new Array();
  681:         var isIn = false;
  682:         while (getNextRow(row) != null) {
  683:             var i = 0;
  684:             for (i=0; i < deepestRows.length; i++) {
  685:                 if (row.id == deepestRows[i].id) {
  686:                     isIn = true;
  687:                 }
  688:             }
  689:             if (!isIn) {
  690:                 otherRows.push(row);
  691:             }
  692:             row = getNextRow(row);
  693:             isIn = false;
  694:         }
  695:         for (i=0; i < deepestRows.length; i++) {
  696:             if (row.id == deepestRows[i].id) {
  697:                 isIn = true;
  698:             }
  699:         }
  700:         if (!isIn) {
  701:             otherRows.push(row);
  702:         }
  703:         return otherRows;
  704:     }
  705: 
  706:     function setDisplaySelect(deepestRows, displ) {
  707:         var i = 0;
  708:         for (i = 0; i < deepestRows.length; i++) {
  709:             var row = deepestRows[i];
  710:             var childSEL = document.getElementById(row.id.replace('row','sel'));
  711:             childSEL.className = displ;
  712:         } 
  713:     }
  714: 
  715:     function submitSelect() {
  716:        var list = document.getElementsByName('list')[0];
  717:        list.setAttribute("action","/adm/wishlist?mode=edit");
  718:        list.submit();
  719:     }
  720: 
  721:     function setDisplayNote(rowid, displ) {
  722:         var row = document.getElementById(rowid);
  723:         if (!displ) {
  724:             if ((row.className).match('LC_hidden')) {
  725:                 row.className = (row.className).replace('LC_hidden','');
  726:             }
  727:             else {
  728:                 var oldClass = row.className;
  729:                 row.className = oldClass+' LC_hidden';
  730:             }
  731:         }
  732:         else {
  733:             if (displ == '') {
  734:                 row.className = (row.className).replace('LC_hidden','');
  735:             }
  736:             else if (displ != '' && !((row.className).match('LC_hidden'))) {
  737:                 var oldClass = row.className;
  738:                 row.className = oldClass+' LC_hidden';
  739:             }
  740:         }
  741:         var noteText = document.getElementById(rowid.replace('note','noteText'));
  742:         var noteImg = document.getElementById(rowid.replace('note','noteImg'));
  743:         if (noteText.value) {
  744:             noteImg.src = "/res/adm/pages/anot2.png";
  745:         }
  746:         else {
  747:             noteImg.src = "/res/adm/pages/anot.png";
  748:         }
  749: 
  750:     }
  751: 
  752:     function setClasses() {
  753:         var row = document.getElementById("row0");
  754:         var note = document.getElementById("note0");
  755:         var LC_class = 0;
  756:         if (getNextRow(row) != null) {
  757:             while (getNextRow(row) != null) {
  758:                 if (!(row.className).match('LC_hidden')) {
  759:                     note.className = (note.className).replace('LC_even_row','');
  760:                     note.className = (note.className).replace('LC_odd_row','');
  761:                     if (LC_class) {
  762:                         row.className = 'LC_even_row';
  763:                         note.className = 'LC_even_row'+note.className;
  764:                     }
  765:                     else {
  766:                         row.className = 'LC_odd_row';
  767:                         note.className = 'LC_odd_row'+note.className;;
  768:                     }
  769:                     LC_class = !LC_class;
  770:                 }
  771:                 note = getNextNote(row);
  772:                 row = getNextRow(row);
  773:             }
  774:         }
  775:         if (!(row.className).match('LC_hidden')) {
  776:             note.className = (note.className).replace('LC_even_row','');
  777:             note.className = (note.className).replace('LC_odd_row','');
  778:             if (LC_class) {
  779:                 row.className = 'LC_even_row';
  780:                 note.className = 'LC_even_row'+note.className;
  781:             }
  782:             else {
  783:                 row.className = 'LC_odd_row';
  784:                 note.className = 'LC_odd_row'+note.className;
  785:             }
  786:         }
  787:     }
  788: 
  789:     function selectDestinationFolder() {
  790:         var mark = document.getElementsByName('mark');
  791:         var i = 0;
  792:         for (i = 0; i < mark.length; i++) {
  793:             if (mark[i].checked) {
  794:                 document.getElementsByName('list')[0].submit();
  795:                 return true;
  796:             }
  797:         }
  798:         alert('$warningMove');
  799:         return false;
  800:     }
  801: 
  802: JAVASCRIPT
  803:    return $js;
  804: }
  805: 
  806: 
  807: # HTML-Markup for table if in view-mode
  808: my $wishlistHTMLview;
  809: my $indent = $indentConst;
  810: sub wishlistView {
  811:     my $nodes = shift;
  812: 
  813:     foreach my $n (@$nodes) {
  814:         my $index = $n->value()->nindex();
  815: 
  816:         # start row, use data_table routines to set class to LC_even or LC_odd automatically. this row contains a checkbox, the title and the note-icon.
  817:         # only display the top level entries on load
  818:         $wishlistHTMLview .= ($n->parent()->value() eq 'root')?&Apache::loncommon::start_data_table_row('','row'.$index)
  819:                                                               :&Apache::loncommon::continue_data_table_row('LC_hidden','row'.$index);
  820: 
  821:  
  822:         # checkboxes
  823:         $wishlistHTMLview .= '<td><input type="checkbox" name="mark" id="check'.$index.'" value="'.$index.'" '.
  824:                              'onclick="selectAction('."'row".$index."'".')"/></td>';
  825: 
  826:         # entry is a folder
  827:         if ($n->value()->path() eq '') {
  828:             $wishlistHTMLview .= '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<0?0:($indent-$indentConst)).'px; min-width: 220px;">'.
  829:                                  '<a href="javascript:;" onclick="folderAction('."'row".$index."'".')" style="vertical-align:top">'.
  830:                                  '<img src="/adm/lonIcons/arrow.closed.gif" id="img'.$index.'" alt = "" class="LC_icon"/>'.
  831:                                  '<img src="/adm/lonIcons/navmap.folder.closed.gif" id="imgFolder'.$index.'" alt="folder"/>'.
  832:                                  $n->value()->title().'</a></td>';
  833:         }
  834:         # entry is a link
  835:         else {
  836:             $wishlistHTMLview .= '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<=0?$indentConst:$indent).'px; min-width: 220px;">'.
  837:                                  '<img src="/res/adm/pages/wishlist-link.png" id="img'.$index.'" alt="link" />'.
  838:                                  '<a href="'.$n->value()->path().'">'.$n->value()->title().'</a></td>';
  839:         }
  840: 
  841:         # note-icon, different icons for an entries with note and those without
  842:         my $noteIMG = 'anot.png';
  843: 
  844:         if ($n->value()->note() ne '') {
  845:             $noteIMG = 'anot2.png';
  846:         }
  847: 
  848:         $wishlistHTMLview .= '<td style="padding-left:10px;"><a href="javascript:;" onclick="setDisplayNote('."'note".$index."'".')">'.
  849:                              '<img id="noteImg'.$index.'" src="/res/adm/pages/'.$noteIMG.'" alt="'.&mt('Note').'" title="'.&mt('Note').'" '.
  850:                              ' class="LC_icon"/></a></td>';
  851: 
  852:         $wishlistHTMLview .= &Apache::loncommon::end_data_table_row();
  853: 
  854:         # start row containing the textarea for the note, do not display note on default
  855:         $wishlistHTMLview .= &Apache::loncommon::continue_data_table_row('LC_hidden','note'.$index).
  856:                              '<td></td><td>'.
  857:                              '<textarea id="noteText'.$index.'" cols="25" rows="3" style="width:100%" '.
  858:                              'name="newnote" >'.
  859:                              $n->value()->note().'</textarea></td><td></td>';
  860:         $wishlistHTMLview .= &Apache::loncommon::end_data_table_row();
  861: 
  862:         # if the entry is a folder, it could have other entries as content. if it has, call wishlistView for those entries 
  863:         my @children = $n->children();
  864:         if ($#children >=0) {
  865:             $indent += 20;
  866:             &wishlistView(\@children);
  867:             $indent -= 20;
  868:         }
  869:     }
  870: }
  871: 
  872: 
  873: # HTML-Markup for table if in edit-mode
  874: my $wishlistHTMLedit;
  875: my $indent = $indentConst;
  876: sub wishlistEdit {
  877:     my $nodes = shift;
  878:     my $curNode = 1;
  879: 
  880:     foreach my $n (@$nodes) {
  881:         my $index = $n->value()->nindex();
  882: 
  883:         # start row, use data_table routines to set class to LC_even or LC_odd automatically.
  884:         # this rows contains a checkbox, a select-field for sorting entries, the title in an input-field and the note-icon.
  885:         # only display the top level entries on load
  886:         $wishlistHTMLedit .= ($n->parent()->value() eq 'root')?&Apache::loncommon::start_data_table_row('','row'.$index)
  887:                                                               :&Apache::loncommon::continue_data_table_row('LC_hidden','row'.$index);
  888: 
  889:         # checkboxes
  890:         $wishlistHTMLedit .= '<td><input type="checkbox" name="mark" id="check'.$index.'" value="'.$index.'" '.
  891:                              'onclick="selectAction('."'row".$index."'".')"/></td>';
  892: 
  893:         # option-tags for sorting entries. we need the numbers from 1 to n with n being the number of entries on the same level as the current entry.
  894:         # set the number for the current entry into brackets 
  895:         my $options;
  896:         for (my $i = 1; $i < ((scalar @{$nodes})+1); $i++) {
  897:            if ($i == $curNode) {
  898:                $options .= '<option selected="selected" value="">('.$i.')</option>';
  899:            }
  900:            else {
  901:                $options .= '<option value="'.$i.'">'.$i.'</option>';
  902:            }
  903:         }
  904:         $curNode++;
  905: 
  906:         # entry is a folder
  907:         if ($n->value()->path() eq '') {
  908:             $wishlistHTMLedit .= '<td><select class="LC_hidden" name="sel" id="sel'.$index.'" onchange="submitSelect();">'.
  909:                                  $options.'</select></td>'.
  910:                                  '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<0?0:($indent-$indentConst)).'px;">'.
  911:                                  '<a href="javascript:;" onclick="folderAction('."'row".$index."'".')" style="vertical-align:top" >'.
  912:                                  '<img src="/adm/lonIcons/arrow.closed.gif" id="img'.$index.'" alt = ""  class="LC_icon"/>'.
  913:                                  '<img src="/adm/lonIcons/navmap.folder.closed.gif" id="imgFolder'.$index.'" alt="folder"/></a>';
  914: 
  915:         }
  916:         # entry is a link
  917:         else {
  918:             $wishlistHTMLedit .= '<td><select class="LC_hidden" name="sel" id="sel'.$index.'" onchange="submitSelect();">'.
  919:                                  $options.'</select></td>'.
  920:                                  '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<=0?$indentConst:$indent).'px;">'.
  921:                                  '<img src="/res/adm/pages/wishlist-link.png" id="img'.$index.'" alt="link"/>';
  922:         }
  923: 
  924:         # input-field for title
  925:         $wishlistHTMLedit .= '<input type="text" name="newtitle" value="'.$n->value()->title().'" alt = "'.$n->value()->title().'"/></td>';
  926: 
  927:         # note-icon, different icons for an entries with note and those without
  928:         my $noteIMG = 'anot.png';
  929: 
  930:         if ($n->value()->note() ne '') {
  931:             $noteIMG = 'anot2.png';
  932:         }
  933: 
  934:         $wishlistHTMLedit .= '<td style="padding-left:10px;"><a href="javascript:;" onclick="setDisplayNote('."'note".$index."'".')">'.
  935:                              '<img id="noteImg'.$index.'" src="/res/adm/pages/'.$noteIMG.'" alt="'.&mt('Note').'" title="'.&mt('Note').'" '.
  936:                              ' class="LC_icon"/></a></td>';
  937: 
  938:         $wishlistHTMLedit .= &Apache::loncommon::end_data_table_row();
  939: 
  940:         # start row containing the textarea for the note
  941:         $wishlistHTMLedit .= &Apache::loncommon::continue_data_table_row('LC_hidden','note'.$index).
  942:                              '<td></td><td></td><td>'.
  943:                              '<textarea id="noteText'.$index.'" cols="25" rows="3" style="width:100%" '.
  944:                              'name="newnote">'.
  945:                              $n->value()->note().'</textarea></td><td></td>';
  946:         $wishlistHTMLedit .= &Apache::loncommon::end_data_table_row();
  947: 
  948:         # if the entry is a folder, it could have other entries as content. if it has, call wishlistEdit for those entries 
  949:         my @children = $n->children();
  950:         if ($#children >=0) {
  951:             $indent += 20;
  952:             &wishlistEdit(\@children);
  953:             $indent -= 20;
  954:         }
  955:     }
  956: }
  957: 
  958: 
  959: 
  960: # HTML-Markup for table if in move-mode
  961: my $wishlistHTMLmove ='<tr id="root" class="LC_odd_row"><td><input type="radio" name="mark" id="radioRoot" value="root" /></td>'.
  962:                       '<td>'.&mt('Top level').'</td><td></td></tr>';
  963: my $indent = $indentConst;
  964: sub wishlistMove {
  965:     my $nodes = shift;
  966:     my $marked = shift;
  967: 
  968:     foreach my $n (@$nodes) {
  969:         my $index = $n->value()->nindex();
  970: 
  971:         #find out wether the current entry was marked to be moved.
  972:         my $isIn = 0;
  973:         foreach my $m (@$marked) {
  974:             if ($index == $m) {
  975:                $isIn = 1;
  976:             }
  977:         }
  978:         # start row and set class for even or odd row. this rows contains the title and the note-icon and can contain a radio-button
  979:         $wishlistHTMLmove .= &Apache::loncommon::start_data_table_row('','row'.$index);
  980: 
  981: 
  982:         # entry is a folder
  983:         if ($n->value()->path() eq '') {
  984:             # display a radio-button, if the folder was not selected to be moved
  985:             if (!$isIn) {
  986:                 $wishlistHTMLmove .= '<td><input type="radio" name="mark" id="radio'.$index.'" value="'.$index.'" /></td>'.
  987:                                      '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<0?0:($indent-$indentConst)).'px; min-width: 220px;">';
  988:             }
  989:             # higlight the title, if the folder was selected to be moved
  990:             else {
  991:                 $wishlistHTMLmove .= '<td></td>'.
  992:                                      '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<0?0:($indent-$indentConst)).'px; min-width: 220px;'.
  993:                                      'color:red;">';
  994:             }
  995:             #arrow- and folder-image, all folders are open, and title
  996:             $wishlistHTMLmove .= '<img src="/adm/lonIcons/arrow.open.gif" id="img'.$index.'" alt = "" />'.
  997:                                  '<img src="/adm/lonIcons/navmap.folder.open.gif" id="imgFolder'.$index.'" alt="folder"/>'.
  998:                                  $n->value()->title().'</td>';
  999:         }
 1000:         # entry is a link
 1001:         else {
 1002:             if (!$isIn) {
 1003:                 $wishlistHTMLmove .= '<td></td>'.
 1004:                                      '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<=0?$indentConst:$indent).'px; min-width: 220px;">';
 1005:             }
 1006:             # higlight the title, if the link was selected to be moved
 1007:             else {
 1008:                 $wishlistHTMLmove .= '<td></td>'.
 1009:                                      '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<=0?$indentConst:$indent).'px; min-width: 220px;'.
 1010:                                      'color:red;">';
 1011:             }
 1012:             # link-image and title
 1013:             $wishlistHTMLmove .= '<img src="/res/adm/pages/wishlist-link.png" id="img'.$index.'" alt="link"/>'.
 1014:                                  $n->value()->title().'</td>';
 1015:         }
 1016: 
 1017:         # note-icon, different icons for an entries with note and those without
 1018:         my $noteIMG = 'anot.png';
 1019: 
 1020:         if ($n->value()->note() ne '') {
 1021:             $noteIMG = 'anot2.png';
 1022:         }
 1023: 
 1024:         $wishlistHTMLmove .= '<td style="padding-left:10px;"><a href="javascript:;" onclick="setDisplayNote('."'note".$index."'".')">'.
 1025:                              '<img id="noteImg'.$index.'" src="/res/adm/pages/'.$noteIMG.'" alt="'.&mt('Note').'" title="'.&mt('Note').'" '.
 1026:                              ' class="LC_icon"/></a></td>';
 1027: 
 1028:         $wishlistHTMLmove .= &Apache::loncommon::end_data_table_row();
 1029: 
 1030:         # start row containing the textarea for the note, readonly in move-mode
 1031:         $wishlistHTMLmove .= &Apache::loncommon::continue_data_table_row('LC_hidden','note'.$index).
 1032:                              '<td></td><td>'.
 1033:                              '<textarea id="noteText'.$index.'" cols="25" rows="3" style="width:100%" '.
 1034:                              'name="newnote" readonly="readonly">'.
 1035:                              $n->value()->note().'</textarea></td><td></td>'.
 1036:                              &Apache::loncommon::end_data_table_row();
 1037: 
 1038:         # if the entry is a folder, it could have other entries as content. if it has, call wishlistMove for those entries 
 1039:         my @children = $n->children();
 1040:         if ($#children >=0) {
 1041:             $indent += 20;
 1042:             &wishlistMove(\@children, $marked);
 1043:             $indent -= 20;
 1044:         }
 1045:     }
 1046: }
 1047: 
 1048: # Returns the HTML-Markup for wishlist
 1049: sub makePage {
 1050:     my $mode = shift;
 1051:     my $marked = shift;
 1052: 
 1053:     # breadcrumbs and start_page
 1054:     &Apache::lonhtmlcommon::clear_breadcrumbs();
 1055:     &Apache::lonhtmlcommon::add_breadcrumb(
 1056:               { href => '/adm/wishlist?mode='.$mode,
 1057:                 text => 'Wishlist'});
 1058:     my $startPage = &Apache::loncommon::start_page('Wishlist',undef,
 1059:                                                      {'add_entries' => {
 1060:                                                         'onload' => 'javascript:onLoadAction('."'".$mode."'".');',
 1061:                                                         'onunload' => 'javascript:window.name = '."'loncapaclient'"}});
 1062: 
 1063:     my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs('Wishlist '.
 1064:                            '<a title="Online-Hilfe" href="/adm/help/Wishlist.hlp" target="_top">'.
 1065:                            '<img src="/adm/help/help.png" alt="'.&mt('Help').'" '.
 1066:                            'title="'.&mt('Help').'" class="LC_icon" /></a>');
 1067: 
 1068:     # get javascript-code for wishlist-interactions
 1069:     my $js = &JSforWishlist();
 1070: 
 1071:     # texthash for items in funtionlist
 1072:     my %lt = &Apache::lonlocal::texthash(
 1073:                  'ed' => 'Edit',
 1074:                  'vw' => 'View',
 1075:                  'al' => 'Add Link',
 1076:                  'af' => 'Add Folder',
 1077:                  'mv' => 'Move Selected',
 1078:                  'dl' => 'Delete Selected',
 1079:                  'sv' => 'Save');
 1080: 
 1081:     # start functionlist
 1082:     my $functions = &Apache::lonhtmlcommon::start_funclist();
 1083: 
 1084:     # icon for edit-mode, display when in view-mode
 1085:     if ($mode eq 'view') {
 1086:         $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
 1087:                           'onclick="setFormAction('."'save','edit'".'); list.submit();" class="LC_menubuttons_link">'.
 1088:                           '<img src="/res/adm/pages/edit-mode-22x22.png" alt="'.$lt{'ed'}.'" '.
 1089:                           'title="'.$lt{'ed'}.'" class="LC_icon"/> '.
 1090:                           '<span class="LC_menubuttons_inline_text">'.$lt{'ed'}.'</span></a>');
 1091:     }
 1092:     # icon for view-mode, display when in edit-mode
 1093:     else {
 1094:         $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
 1095:                           'onclick="setFormAction('."'save','view'".'); list.submit();" class="LC_menubuttons_link">'.
 1096:                           '<img src="/res/adm/pages/view-mode-22x22.png" alt="'.$lt{'vw'}.'" '.
 1097:                           'title="'.$lt{'vw'}.'" class="LC_icon"/> '.
 1098:                           '<span class="LC_menubuttons_inline_text">'.$lt{'vw'}.'</span></a>');
 1099:     }
 1100:     
 1101:     # icon for adding a new link
 1102:     $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
 1103:                       'onclick="newLink();" class="LC_menubuttons_link">'.
 1104:                       '<img src="/res/adm/pages/link-new-22x22.png" alt="'.$lt{'al'}.'" '.
 1105:                       'title="'.$lt{'al'}.'" class="LC_icon"/>'.
 1106:                       '<span class="LC_menubuttons_inline_text">'.$lt{'al'}.'</span></a>');
 1107: 
 1108:     # icon for adding a new folder
 1109:     $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
 1110:                       'onclick="newFolder();" class="LC_menubuttons_link">'.
 1111:                       '<img src="/res/adm/pages/folder-new-22x22.png" alt="'.$lt{'af'}.'" '.
 1112:                       'title="'.$lt{'af'}.'" class="LC_icon"/>'.
 1113:                       '<span class="LC_menubuttons_inline_text">'.$lt{'af'}.'</span></a>');
 1114: 
 1115:     # icon for moving entries
 1116:     $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
 1117:                       'onclick="setFormAction('."'move','move'".'); " class="LC_menubuttons_link">'.
 1118:                       '<img src="/res/adm/pages/move-22x22.png" alt="'.$lt{'mv'}.'" '.
 1119:                       'title="'.$lt{'mv'}.'" class="LC_icon" />'.
 1120:                       '<span class="LC_menubuttons_inline_text">'.$lt{'mv'}.'</span></a>');
 1121: 
 1122:     # icon for deleting entries
 1123:     $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
 1124:                       'onclick="setFormAction('."'delete','".$mode."'".'); " class="LC_menubuttons_link">'.
 1125:                       '<img src="/res/adm/pages/del.png" alt="'.$lt{'dl'}.'" '.
 1126:                       'title="'.$lt{'dl'}.'" class="LC_icon" />'.
 1127:                       '<span class="LC_menubuttons_inline_text">'.$lt{'dl'}.'</span></a>');
 1128: 
 1129:     # icon for saving changes
 1130:     $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
 1131:                       'onclick="setFormAction('."'','".$mode."'".'); " class="LC_menubuttons_link">'.
 1132:                       '<img src="/res/adm/pages/save-22x22.png" alt="'.$lt{'sv'}.'" '.
 1133:                       'title="'.$lt{'sv'}.'" class="LC_icon" />'.
 1134:                       '<span class="LC_menubuttons_inline_text">'.$lt{'sv'}.'</span></a>');
 1135: 
 1136:     # end funtionlist and generate subbox 
 1137:     $functions.= &Apache::lonhtmlcommon::end_funclist();
 1138:     my $subbox = &Apache::loncommon::head_subbox($functions);
 1139: 
 1140:     # start form 
 1141:     my $inner .= '<form name="list" action ="/adm/wishlist" method="post">'.
 1142:                  '<input type="hidden" id="action" name="action" value=""/>';
 1143:  
 1144:     # only display subbox in view- or edit-mode
 1145:     if ($mode ne 'move') {
 1146:         $inner .= $subbox;
 1147:     }
 1148: 
 1149:     # generate table-content depending on mode
 1150:     if ($mode eq 'edit') {
 1151:         &wishlistEdit(\@childrenRt);
 1152:         if ($wishlistHTMLedit ne '') {
 1153:             $inner .= &Apache::loncommon::start_data_table("LC_tableOfContent");
 1154:             $inner .= $wishlistHTMLedit;
 1155:             $inner .= &Apache::loncommon::end_data_table();
 1156:         }
 1157:         else {
 1158:             $inner .= '<span class="LC_info">'.&mt("Your wihlist ist currently empty.").'</span>';
 1159:         }
 1160:         $wishlistHTMLedit = '';
 1161:     }
 1162:     elsif ($mode eq 'view') {
 1163:         &wishlistView(\@childrenRt);
 1164:         if ($wishlistHTMLview ne '') {
 1165:             $inner .= '<table class="LC_data_table LC_tableOfContent">'.$wishlistHTMLview.'</table>';
 1166:         }
 1167:         else {
 1168:             $inner .= '<span class="LC_info">'.&mt("Your wihlist ist currently empty.").'</span>';
 1169:         }
 1170:         $wishlistHTMLview = '';
 1171:     }
 1172:     else {
 1173:         my $markStr = '';
 1174:         foreach my $m (@$marked) {
 1175:             $markStr .= $m.',';
 1176:         }
 1177:         if ($markStr) {
 1178:             $markStr = substr($markStr, 0, length($markStr)-1);
 1179:             $inner .= '<input type="hidden" value="'.$markStr.'" name="markedToMove"/>';
 1180:             $inner .= '<span class="LC_info">'.&mt('You have selected the red marked entries to be moved to another folder. '.
 1181:                                                    'Now choose the new destination folder.').'</span><br/><br/>';
 1182:             &wishlistMove(\@childrenRt, $marked);
 1183:             $inner .= '<table class="LC_data_table LC_tableOfContent">'.$wishlistHTMLmove.'</table><br/><br/>';
 1184:             $inner .= '<input type="button" value="'.&mt('Move').'" onclick="setFormAction('."'','view'".'); selectDestinationFolder()"/>'.
 1185:                       '<input type="button" value="'.&mt('Cancel').'" onclick="go('."'/adm/wishlist'".')"/>';
 1186: 
 1187:             $wishlistHTMLmove ='<tr id="root" class="LC_odd_row"><td><input type="radio" name="mark" id="radioRoot" value="root" /></td>'.
 1188:                                '<td>'.&mt('Top level').'</td><td></td></tr>';
 1189:         }
 1190:         else {
 1191:             $inner .= '<span class="LC_info">'.&mt("You haven't marked any entry to move.").'</span><br/>'.
 1192:                       '<input type="button" value="'.&mt('Back').'" onclick="go('."'/adm/wishlist'".')"/>';
 1193:         }
 1194:     }
 1195:     
 1196:     # end form 
 1197:     $inner .= '</form>';
 1198: 
 1199:     # end_page 
 1200:     my $endPage =  &Apache::loncommon::end_page();
 1201: 
 1202:     # put all page-elements togther
 1203:     my $page = $startPage.$breadcrumbs.$js.$inner.$endPage;
 1204: 
 1205:     return $page;
 1206: }
 1207: 
 1208: 
 1209: # Returns the HTML-Markup for the page, shown when a link was set
 1210: sub makePageSet {
 1211:     # start_page 
 1212:     my $startPage = &Apache::loncommon::start_page('Wishlist',undef,
 1213:                                                    {'only_body' => 1});
 1214:     
 1215:     # confirm success and offer link to wishlist
 1216:     my $message = &Apache::lonhtmlcommon::confirm_success(&mt('Link successfully set!'));
 1217:     $message = &Apache::loncommon::confirmwrapper($message);
 1218: 
 1219:     my $inner .= '<br>'.$message.'<br/><br/>'.
 1220:                  '<a href="javascript:;" onclick="opener.open('."'/adm/wishlist'".');window.close();">'.&mt('Go to wishlist').'</a>'.
 1221:                  '&nbsp;<a href="javascript:;" onclick="window.close();">'.&mt('Close this window').'</a>';
 1222: 
 1223:     # end_page 
 1224:     my $endPage =  &Apache::loncommon::end_page();
 1225: 
 1226:     # put all page-elements togther
 1227:     my $page = $startPage.$inner.$endPage;
 1228: 
 1229:     return $page;
 1230: }
 1231: 
 1232: 
 1233: # Returns the HTML-Markup for error-page
 1234: sub makeErrorPage {
 1235:     # breadcrumbs and start_page 
 1236:     &Apache::lonhtmlcommon::add_breadcrumb(
 1237:               { href => '/adm/wishlist',
 1238:                 text => 'Wishlist'});
 1239:     my $startPage = &Apache::loncommon::start_page('Wishlist');
 1240:     
 1241:     my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs('Wishlist '.
 1242:                            '<a title="Online-Hilfe" href="/adm/help/Wishlist.hlp" target="_top">'.
 1243:                            '<img src="/adm/help/help.png" alt="'.&mt('Help').'" '.
 1244:                            'title="'.&mt('Help').'" class="LC_icon" /></a>');
 1245:     &Apache::lonhtmlcommon::clear_breadcrumbs();
 1246: 
 1247:     # error-message
 1248:     my $inner .= '<span class="LC_error">'.&mt('An error occurred! Please try again later.').'</span>';
 1249: 
 1250:     # end_page 
 1251:     my $endPage =  &Apache::loncommon::end_page();
 1252: 
 1253:     # put all page-elements togther
 1254:     my $page = $startPage.$breadcrumbs.$inner.$endPage;
 1255: 
 1256:     return $page;
 1257: }
 1258: 
 1259: # ----------------------------------------------------- Main Handler, package lonwishlist
 1260: sub handler {
 1261:     my ($r) = @_;
 1262:     &Apache::loncommon::content_type($r,'text/html');
 1263:     $r->send_http_header;
 1264: 
 1265:     if (&getWishlist() ne 'error') {
 1266:         # get wishlist entries from user-data db-file and build a tree out of these entries
 1267:         %TreeHash = &getWishlist();
 1268:         $root = &Tree::HashToTree();
 1269:         @childrenRt = $root->children();
 1270: 
 1271:         # greate a new entry
 1272:         if ($env{'form.title'}) {
 1273:            &newEntry($env{'form.title'}, $env{'form.path'}, $env{'form.note'});
 1274:         }
 1275: 
 1276:         # get unprocessed_cgi (i.e. marked entries, mode ...) 
 1277:         &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['action','mark','markedToMove','mode','newtitle','note']);
 1278: 
 1279:         # change the order of entries within a level, that means sorting the entries
 1280:         my $changeOrder = 0;
 1281:         if (defined $env{'form.sel'}) {
 1282:             my @sel = &Apache::loncommon::get_env_multiple('form.sel');
 1283:             my $indexNode;
 1284:             my $at;
 1285:             for (my $s=0; $s<($#sel+1); $s++) {
 1286:                 if ($sel[$s] ne '') {
 1287:                     $indexNode = $s;
 1288:                     $at = $sel[$s]-1;
 1289:                 }
 1290:             }
 1291:             if ($at ne '') {
 1292:                 $changeOrder = 1;
 1293:                 &sortEntries($indexNode,$at);
 1294:                 &saveChanges();
 1295:             }
 1296:         }
 1297: 
 1298:         # get all marked (checkboxes) entries
 1299:         my @marked = ();
 1300:         if (defined $env{'form.mark'}) {
 1301:             @marked = &Apache::loncommon::get_env_multiple('form.mark');
 1302:         }
 1303: 
 1304:         # move entries from one folder to another
 1305:         if (defined $env{'form.markedToMove'}) {
 1306:            my $markedToMove = $env{'form.markedToMove'};
 1307:            my @ToMove = split(/\,/,$markedToMove);
 1308:            my $moveTo = $env{'form.mark'};
 1309:            if (defined $moveTo){ 
 1310:                &moveEntries(\@ToMove,$moveTo);
 1311:                &saveChanges();
 1312:            }
 1313:            $changeOrder = 1;
 1314:     
 1315:         }
 1316: 
 1317:         # delete entries
 1318:         if ($env{'form.action'} eq 'delete') {
 1319:             &deleteEntries(\@marked);
 1320:         }
 1321:    
 1322: 
 1323:         # get all titles and notes and save them
 1324:         # only save, if user wants to save changes
 1325:         # do not save, when current action is 'delete' or 'sort' or 'move' 
 1326:         my @newTitles = ();
 1327:         my @newNotes = ();
 1328:         if ((defined $env{'form.newtitle'} || defined $env{'form.newnote'}) && ($env{'form.action'} ne 'noSave') && ($env{'form.action'} ne 'delete') && !$changeOrder) {
 1329:             @newTitles = &Apache::loncommon::get_env_multiple('form.newtitle');
 1330:             @newNotes = &Apache::loncommon::get_env_multiple('form.newnote');
 1331:             my $node = 0;
 1332:             foreach my $t (@newTitles) {
 1333:                &setNewTitle($node, $t);
 1334:                $node++;
 1335:             }
 1336:             $node = 0;
 1337:             foreach my $n (@newNotes) {
 1338:                &setNewNote($node, $n);
 1339:                $node++;
 1340:             }
 1341:             &saveChanges();
 1342:         }
 1343: 
 1344:         # Create HTML-markup
 1345:         my $page;
 1346:         if ($env{'form.mode'} eq 'edit') {
 1347:             $page = &makePage("edit");
 1348:         }
 1349:         elsif ($env{'form.mode'} eq 'move') {
 1350:             $page = &makePage("move", \@marked);
 1351:         }
 1352:         elsif ($env{'form.mode'} eq 'set') {
 1353:             $page = &makePageSet();
 1354:         }
 1355:         else {
 1356:             $page = &makePage("view");
 1357:         }
 1358:         @marked = ();
 1359:         $r->print($page);
 1360:     }
 1361:     # An error occured, print an error-page
 1362:     else {
 1363:         my $errorPage = &makeErrorPage();
 1364:         $r->print($errorPage);
 1365:     }
 1366:     return OK;
 1367: }
 1368: 
 1369: # ----------------------------------------------------- package Tree
 1370: # Extend CPAN-Module Tree by function like 'moveNode' or 'deleteNode'
 1371: package Tree;
 1372: 
 1373: # returns the node with a given index from a list of nodes
 1374: sub getNodeByIndex {
 1375:     my $index = shift;
 1376:     my $nodes = shift;
 1377:     my $found;
 1378:     
 1379:     for my $n (@$nodes) {
 1380:         my $curIndex = $n->value()->nindex();
 1381:         if ($n->value()->nindex() == $index) {
 1382:             $found = $n;
 1383:         }
 1384:     }
 1385:     return $found;
 1386: }
 1387: 
 1388: # moves a given node to a new parent or change the position from a node
 1389: # within its siblings (sorting)
 1390: sub moveNode {
 1391:     my $node = shift;
 1392:     my $at = shift;
 1393:     my $newParent = shift;
 1394: 
 1395: 
 1396:     if (!$newParent) {
 1397:         $newParent = $node->parent();
 1398:     }
 1399: 
 1400:     $node->parent()->remove_child($node);
 1401: 
 1402:     if (defined $at) {
 1403:         $newParent->add_child({at => $at},$node);
 1404:     }
 1405:     else {
 1406:         $newParent->add_child($node);
 1407:     }
 1408:     
 1409:     # updating root's children
 1410:     @childrenRt = $root->children();
 1411: }
 1412: 
 1413: # removes a given node
 1414: sub removeNode() {
 1415:     my $node = shift;
 1416:     my @children = $node->children();
 1417: 
 1418:     if ($#children >= 0) {
 1419:         foreach my $c (@children) {
 1420:             &removeNode($c);
 1421:         }
 1422:     }
 1423:     $node->parent()->remove_child($node);
 1424: 
 1425:     # updating root's children
 1426:     @childrenRt = $root->children();
 1427: }
 1428: 
 1429: 
 1430: # set an index for every node in the tree, beginning with 0
 1431: my $count = 0;
 1432: sub TreeIndex {
 1433:     my $children = shift;
 1434: 
 1435:     foreach my $n (@$children) {
 1436:         my @children = $n->children();
 1437:         $n->value()->nindex($count);$count++;
 1438: 
 1439:         if ($#children>=0) {
 1440:             &TreeIndex(\@children);
 1441:         }
 1442:     }
 1443: }
 1444: 
 1445: # reset index counter
 1446: sub setCountZero {
 1447:     $count = 0;
 1448: }
 1449: 
 1450: 
 1451: # convert the tree to a hash
 1452: # each node is one hash-entry
 1453: # keys are the indices, values are all other attributes
 1454: # (containing tile, path, note, date and indices for all direct children)
 1455: # except for root: the key is root and values are
 1456: # just the indices of root's children
 1457: sub RootToHash {
 1458:     my $childrenRt = shift;
 1459:     my @indexarr = ();
 1460: 
 1461:     foreach my $c (@$childrenRt) {
 1462:        push (@indexarr, $c->value()->nindex());
 1463:     }
 1464:     $TreeToHash{'root'} = [@indexarr];
 1465: }
 1466: 
 1467: sub TreeToHash {
 1468:     my $childrenRt = shift;
 1469: 
 1470:     foreach my $n (@$childrenRt) {
 1471:         my @arrtmp = ();
 1472:         $arrtmp[0] = $n->value()->title();
 1473:         $arrtmp[1] = $n->value()->path();
 1474:         $arrtmp[2] = $n->value()->note();
 1475:         $arrtmp[3] = $n->value()->date();
 1476:         my @childrenRt = $n->children();
 1477:         my $co = 4;
 1478:         foreach my $c (@childrenRt) {
 1479:             my $i = $c->value()->nindex();
 1480:             $arrtmp[$co] = $i;
 1481:             $co++;
 1482:         }
 1483:         $TreeToHash{$n->value()->nindex} = [ @arrtmp]; 
 1484:         if ($#childrenRt>=0) {
 1485:             &TreeToHash(\@childrenRt);
 1486:         }
 1487:     }
 1488: }
 1489: 
 1490: 
 1491: # convert the hash to a tree
 1492: # build a tree-object for each entry in the hash
 1493: # afterwards call &buildTree to connect the tree-objects
 1494: sub HashToTree {
 1495:     my @TreeNodes = ();
 1496:     my $root;
 1497: 
 1498:     foreach my $key (keys %TreeHash) {
 1499:         if ($key eq 'root') {
 1500:             $root = Tree->new("root");
 1501:         }
 1502:         else {
 1503:         my @attributes = @{ $TreeHash{$key} };
 1504:         my $tmpNode;
 1505:             $tmpNode = Tree->new(Entry->new(title=>$attributes[0],
 1506:                                             path=>$attributes[1],
 1507:                                             note=>$attributes[2],
 1508:                                             date=>$attributes[3],
 1509:                                             nindex=>$key));
 1510:         push(@TreeNodes, $tmpNode);
 1511:         # shift all attributes except for
 1512:         # the indices representing the children of a node
 1513:         shift(@attributes);
 1514:         shift(@attributes);
 1515:         shift(@attributes);
 1516:         shift(@attributes);
 1517:         $TreeHash{$key} = [ @attributes ];
 1518:         }
 1519:     }
 1520:     # if there are nodes, build up the tree-structure
 1521:     if (defined $TreeHash{'root'}) {
 1522:         my @childrenRtIn = @{ $TreeHash{'root'} };
 1523:         &buildTree(\$root, \@childrenRtIn,\@TreeNodes,\%TreeHash);
 1524:     }
 1525:     return $root; 
 1526: }
 1527: 
 1528: 
 1529: # join the nodes to a tree
 1530: sub buildTree {
 1531:     my ($node, $childrenIn, $TreeNodes, $TreeHash) = @_;
 1532:     bless($node, 'Tree');
 1533:     foreach my $c (@$childrenIn) {
 1534:         my $tmpNode =  &getNodeByIndex($c,$TreeNodes);
 1535:         $$node->add_child($tmpNode);
 1536:         my @childrenIn = @{ $$TreeHash{$tmpNode->value()->nindex()} };
 1537:         &buildTree(\$tmpNode,\@childrenIn,$TreeNodes,$TreeHash);
 1538:     }
 1539: 
 1540: }
 1541: 
 1542: 
 1543: # ----------------------------------------------------- package Entry
 1544: # package that defines the entrys a wishlist could have
 1545: # i.e. folders and links
 1546: package Entry;
 1547: 
 1548: # constructor
 1549: sub new {
 1550:     my $invocant = shift;
 1551:     my $class = ref($invocant) || $invocant;
 1552:     my $self = { @_ }; #set attributes
 1553:     bless($self, $class);
 1554:     return $self;    
 1555: }
 1556: 
 1557: # getter and setter
 1558: sub title {
 1559:     my $self = shift;
 1560:     if ( @_ ) { $self->{title} = shift}
 1561:     return $self->{title};
 1562: }
 1563: 
 1564: sub date {
 1565:     my $self = shift;
 1566:     if ( @_ ) { $self->{date} = shift}
 1567:     return $self->{date};
 1568: }
 1569: 
 1570: sub note {
 1571:     my $self = shift;
 1572:     if ( @_ ) { $self->{note} = shift}
 1573:     return $self->{note};
 1574: }
 1575: 
 1576: sub path {
 1577:     my $self = shift;
 1578:     if ( @_ ) { $self->{path} = shift}
 1579:     return $self->{path};
 1580: }
 1581: 
 1582: sub nindex {
 1583:     my $self = shift;
 1584:     if ( @_ ) { $self->{nindex} = shift}
 1585:     return $self->{nindex};
 1586: }
 1587: 
 1588: 
 1589: 1;
 1590: __END__

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