File:  [LON-CAPA] / loncom / interface / lonwishlist.pm
Revision 1.3: download - view: text, annotated - select for diffs
Mon Aug 16 13:37:41 2010 UTC (13 years, 10 months ago) by wenzelju
Branches: MAIN
CVS tags: HEAD
- Only allow links to LON-CAPA-resources from resource-pool (paths starts with /res/...) or to external websites. Uses same filter for external websites as it is used in lonwrapper to display external resources. That means that only paths (not starting with /res/...) which does not contain a .problem, .quiz, .exam etc. are allowed. This is good for most cases but crashes as soon as a real external website contains one of this pattern in its URL. So maybe there's a better way to find out wether a given URL belongs to a LON-CAPA-server or not...?
- Allow users to edit the path of a link regarding the restriction from above.

    1: # The LearningOnline Network with CAPA
    2: # Routines to control the wishlist
    3: #
    4: # $Id: lonwishlist.pm,v 1.3 2010/08/16 13:37:41 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 LONCAPA;
   40: use Tree;
   41: 
   42: 
   43: # Global variables
   44: my $root;
   45: my @childrenRt;
   46: my %TreeHash;
   47: my %TreeToHash;
   48: my @allFolders;
   49: my @allNodes;
   50: my $indentConst = 20;
   51: 
   52: 
   53: # Read wishlist from user-data
   54: sub getWishlist {
   55:     my %wishlist = &Apache::lonnet::dump('wishlist');
   56:     foreach my $i ( keys %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:            %wishlist = &Apache::lonnet::dump('wishlist');
   63:        }
   64:        elsif ($i =~ /^(con_lost|error|no_such_host)/i) {
   65:            &Apache::lonnet::logthis('ERROR while attempting to get wishlist: '.$i);
   66:            return 'error';
   67:        }
   68:     }
   69: 
   70:     # if we got no keys in hash returned by dump(), return error.
   71:     # wishlist will not be loaded, instead the user will be asked to try again later
   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:     # get all nodes that should be moved
  152:     &getNodesToArray(\@childrenRt);
  153:     foreach my $index (@$indexNodesToMove) {
  154:         my $foundNode = &Tree::getNodeByIndex($index, \@allNodes);
  155:         push(@nodesToMove, $foundNode);
  156:     }
  157: 
  158:     foreach my $node (@nodesToMove) {
  159:         my $foundParent;
  160:         my $parentIsIn = 0;
  161:         foreach my $n (@nodesToMove) {
  162:             if ($node->parent()->value() ne "root") {
  163:                if ($node->parent()->value()->nindex() == $n->value()->nindex()) {
  164:                     $parentIsIn = 1;
  165:                 }
  166:             }
  167:         }
  168:         if (!$parentIsIn) {
  169:             if ($indexParent ne "root") {
  170:                 $foundParent = &Tree::getNodeByIndex($indexParent, \@allNodes);
  171:                 &Tree::moveNode($node,undef,$foundParent);
  172:             }
  173:             else {
  174:                 &Tree::moveNode($node,undef,$root);
  175:             }
  176:         }
  177:     }
  178:     @allNodes = ();
  179: }
  180: 
  181: 
  182: # Set a new title for an entry
  183: sub setNewTitle {
  184:     my ($nodeindex, $newTitle) = @_;
  185:     &getNodesToArray(\@childrenRt);
  186:     my $found = &Tree::getNodeByIndex($nodeindex, \@allNodes);
  187:     $found->value()->title($newTitle); 
  188:     @allNodes = ();
  189: }
  190: 
  191: 
  192: # Set a new path for an entry
  193: sub setNewPath {
  194:     my ($nodeindex, $newPath) = @_;
  195:     &getNodesToArray(\@childrenRt);
  196:     my $found = &Tree::getNodeByIndex($nodeindex, \@allNodes);
  197:     if ($found->value()->path()) {
  198:         $found->value()->path($newPath); 
  199:         return 1;
  200:     }
  201:     @allNodes = ();
  202:     return 0;
  203: }
  204: 
  205: 
  206: # Set a new note for an entry
  207: sub setNewNote {
  208:     my ($nodeindex, $newNote) = @_;
  209:     &getNodesToArray(\@childrenRt);
  210:     my $found = &Tree::getNodeByIndex($nodeindex, \@allNodes);
  211:     $found->value()->note($newNote); 
  212:     @allNodes = ();
  213: }
  214: 
  215: 
  216: # Save all changes
  217: sub saveChanges {
  218:     @childrenRt = $root->children();
  219:     &Tree::TreeIndex(\@childrenRt);
  220:     &Tree::setCountZero();
  221:     &Tree::RootToHash(\@childrenRt);
  222:     &Tree::TreeToHash(\@childrenRt);
  223:     &deleteWishlist();
  224:     &putWishlist(\%TreeToHash);
  225: 
  226: }
  227: 
  228: 
  229: # Return the names for all exiting folders in option-tags, so
  230: # a new link or a new folder can be created in an existing folder
  231: my $indent = 0;
  232: my $foldersOption;
  233: sub getFoldersForOption {
  234:     my $nodes = shift;
  235: 
  236:     foreach my $n (@$nodes) {
  237:         if ($n->value()->path() eq '') {
  238:             $foldersOption .= '<option value="'.$n->value()->nindex().'" style="margin-left:'.$indent.'px">'.
  239:                                    $n->value()->title().
  240:                                '</option>';
  241: 
  242:         my @children = $n->children();
  243:         if ($#children >=0) {
  244:             $indent += 10;
  245:             &getFoldersForOption(\@children);
  246:             $indent -= 10;
  247:             }
  248:         }
  249:     }
  250: }
  251: 
  252: 
  253: sub getfoldersOption {
  254:    if (&getWishlist ne 'error') {
  255:        %TreeHash = &getWishlist();
  256:        $root = &Tree::HashToTree();
  257:        @childrenRt = $root->children();
  258:        &getFoldersForOption(\@childrenRt);
  259:        my $options = '<option value="" selected="selected">('.&mt('Top level').')</option>'.$foldersOption;
  260:        $foldersOption = '';
  261:        return $options;
  262:    }
  263:    else {
  264:        return '';
  265:    }
  266: }
  267: 
  268: 
  269: # Put all folder-nodes to an array
  270: sub getFoldersToArray {
  271:     my $children = shift;
  272:     foreach my $c (@$children) {
  273:         if ($c->value()->path() eq '') {
  274:             push(@allFolders,$c);
  275:         }
  276:         my @newchildren = $c->children();
  277:         if ($#newchildren >= 0) {
  278:             &getFoldersToArray(\@newchildren);
  279:         }
  280:     }
  281: }
  282: 
  283: 
  284: # Put all nodes to an array
  285: sub getNodesToArray {
  286:     my $children = shift;
  287:     foreach my $c (@$children) {
  288:         push(@allNodes,$c);
  289:         my @newchildren = $c->children();
  290:         if ($#newchildren >= 0) {
  291:             &getNodesToArray(\@newchildren);
  292:         }
  293:     }
  294: }
  295: 
  296: 
  297: # Return a script-tag containing Javascript-function
  298: # needed for wishlist actions like 'new link' ect.
  299: sub JSforWishlist {
  300:     my $startPagePopup = &Apache::loncommon::start_page('Wishlist',undef,
  301:                                                             {'only_body' => 1,
  302:                                                              'js_ready'  => 1,
  303:                                                              'bgcolor'   => '#FFFFFF',});
  304:     my $endPagePopup = &Apache::loncommon::end_page({'js_ready' => 1});
  305: 
  306:     @allFolders = ();
  307:     &getFoldersToArray(\@childrenRt);
  308:     &getFoldersForOption(\@childrenRt);
  309: 
  310:     # texthash
  311:     my %lt = &Apache::lonlocal::texthash(
  312:                  'nl' => 'New Link',
  313:                  'nf' => 'New Folder',
  314:                  'lt' => 'Link Title',
  315:                  'ft' => 'Folder Title',
  316:                  'pa' => 'Path',
  317:                  'nt' => 'Note',
  318:                  'si' => 'Save in',
  319:                  'cl' => 'Cancel');
  320: 
  321: 
  322:     my $inPageNewLink = '<h1>'.$lt{'nl'}.'</h1>'.
  323:                         '<form method="post" name="newlink" action="/adm/wishlist" target="wishlist" '.
  324:                         'onsubmit="return newlinksubmit();" >'.
  325:                         &Apache::lonhtmlcommon::start_pick_box().
  326:                         &Apache::lonhtmlcommon::row_title($lt{'lt'}).
  327:                         '<input type="text" name="title" size="45" value="" />'.
  328:                         &Apache::lonhtmlcommon::row_closure().
  329:                         &Apache::lonhtmlcommon::row_title($lt{'pa'}).
  330:                         '<input type="text" name="path" size="45" value="" />'.
  331:                         &Apache::lonhtmlcommon::row_closure().
  332:                         &Apache::lonhtmlcommon::row_title($lt{'nt'}).
  333:                         '<textarea name="note" rows="3" cols="35" style="width:100%"></textarea>'.
  334:                         &Apache::lonhtmlcommon::row_closure(1).
  335:                         &Apache::lonhtmlcommon::end_pick_box().
  336:                         '<br/><br/>'.
  337:                         '<input type="submit" value="'.$lt{'si'}.'" />'.
  338:                         '<select name="folders">'.
  339:                         '<option value="" selected="selected">('.&mt('Top level').')</option>'.
  340:                         $foldersOption.
  341:                         '</select>'.
  342:                         '<input type="button" value="'.$lt{'cl'}.'" onclick="javascript:window.close();" />'.
  343:                         '</form>';
  344:     
  345:     my $inPageNewFolder = '<h1>'.$lt{'nf'}.'</h1>'.
  346:                           '<form method="post" name="newfolder" action="/adm/wishlist" target="wishlist" '.
  347:                           'onsubmit="return newfoldersubmit();" >'.
  348:                           &Apache::lonhtmlcommon::start_pick_box().
  349:                           &Apache::lonhtmlcommon::row_title($lt{'ft'}).
  350:                           '<input type="text" name="title" size="45" value="" /><br />'.
  351:                           &Apache::lonhtmlcommon::row_closure().
  352:                           &Apache::lonhtmlcommon::row_title($lt{'nt'}).
  353:                           '<textarea name="note" rows="3" cols="35" style="width:100%"></textarea><br />'.
  354:                           &Apache::lonhtmlcommon::row_closure(1).
  355:                           &Apache::lonhtmlcommon::end_pick_box().
  356:                           '<br/><br/>'.
  357:                           '<input type="submit" value="'.$lt{'si'}.'" />'.
  358:                           '<select name="folders">'.
  359:                           '<option value="" selected="selected">('.&mt('Top level').')</option>'.
  360:                           $foldersOption.
  361:                           '</select>'.
  362:                           '<input type="button" value="'.$lt{'cl'}.'" onclick="javascript:window.close();" />'.
  363:                           '</form>';
  364: 
  365:     # Remove all \n for inserting on javascript document.write
  366:     $inPageNewLink =~ s/\n//g;
  367:     $inPageNewFolder =~ s/\n//g;
  368: 
  369:     # it is checked, wether a path links to a LON-CAPA-resource or an external website. links to course-contents are not allowed
  370:     # because they probably will return a kind of 'no access' (unless the user is already in the course, the path links to).
  371:     # also importing these kind of links into a course does not make much sense.
  372:     # to find out if a path (not starting with /res/...) links to course-contents, the same filter as in lonwrapper is used,
  373:     # that means that it is checked wether a path contains .problem, .quiz, .exam etc.
  374:     # this is good for most cases but crashes as soon as a real external website contains one of this pattern in its URL.
  375:     # so maybe there's a better way to find out wether a given URL belongs to a LON-CAPA-server or not ...?
  376:     my $warningLinkNotAllowed1 = &mt('You can only insert links to LON-CAPA resources from the resource-pool '.
  377:                                     'or to external websites. Paths to LON-CAPA resources must be of the form /res/dom/usr... . '.
  378:                                     'Paths to external websites must contain the network protocol (e.g. http://...).');
  379:     my $warningLinkNotAllowed2 = &mt('The following link is not allowed: ');
  380:     my $warningLink = &mt('You must insert a title and a path!');
  381:     my $warningFolder = &mt('You must insert a title!');
  382:     my $warningDelete = &mt('Are you sure you want to delete the selected entries? Deleting a folder also deletes all entries within this folder!');
  383:     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.');
  384:     my $warningMove = &mt('You must select a destination folder!');
  385:     $foldersOption = '';
  386: 
  387:     my $js = &Apache::lonhtmlcommon::scripttag(<<JAVASCRIPT);
  388:     function newLink() {
  389:         newlinkWin=window.open('','newlinkWin','width=580,height=320,scrollbars=yes');
  390:         newlinkWin.document.write('$startPagePopup' 
  391:                               +'<script type="text\/javascript">'
  392:                               +'function newlinksubmit(){'
  393:                               +'var path = document.getElementsByName("path")[0].value;'
  394:                               +'var title = document.getElementsByName("title")[0].value;'
  395:                               +'if (!path || !title) {'
  396:                               +'alert("$warningLink");'
  397:                               +'return false;}'
  398:                               +'var linkOK = (path.match(/^http:(\\\\/\\\\/)/) || path.match(/^https:(\\\\/\\\\/)/))'
  399:                               +'&& !(path.match(/\\.problem/) || path.match(/\\.exam/)'
  400:                               +'|| path.match(/\\.quiz/) || path.match(/\\.assess/)'
  401:                               +'|| path.match(/\\.survey/) || path.match(/\\.form/)'
  402:                               +'|| path.match(/\\.library/) || path.match(/\\.page/)'
  403:                               +'|| path.match(/\\.sequence/));'
  404:                               +'if (!path.match(/^(\\\\/res\\\\/)/) && !linkOK) {'
  405:                               +'alert("$warningLinkNotAllowed1");'
  406:                               +'return false;}'
  407:                               +'else {'
  408:                               +'window.close();'
  409:                               +'return true;}}'
  410:                               +'<\/scr'+'ipt>'
  411:                               +'$inPageNewLink'
  412:                               +'$endPagePopup');
  413:         newlinkWin.document.close();
  414:     }
  415: 
  416:     function newFolder() {
  417:         newfolderWin=window.open('','newfolderWin','width=580,height=270, scrollbars=yes');
  418:         newfolderWin.document.write('$startPagePopup' 
  419:                               +'<script type="text\/javascript">'
  420:                               +'function newfoldersubmit(){'
  421:                               +'var title = document.getElementsByName("title")[0].value;'
  422:                               +'if (!title) {'
  423:                               +'alert("$warningFolder");'
  424:                               +'return false;}'
  425:                               +'else {'
  426:                               +'window.close();'
  427:                               +'return true;}}'
  428:                               +'<\/scr'+'ipt>'
  429:                               +'$inPageNewFolder'
  430:                               +'$endPagePopup');
  431:         newfolderWin.document.close();
  432:     }
  433: 
  434:     function setFormAction(action,mode) {
  435:         var r = true;
  436:         setAction('');
  437:         if (action == 'delete') {
  438:             r = confirm("$warningDelete");
  439:             setAction('delete');
  440:         }
  441:         else if (action == 'save') {
  442:             var d = getDifferences();
  443:             if (d) {
  444:                 if (!confirm('$warningSave')) {
  445:                     setAction('noSave');
  446:                     r = true;
  447:                 }
  448:                 else {
  449:                     r = linksOK();
  450:                 }
  451:             }
  452:         }
  453:         else if (action == 'saveOK') {
  454:             r = linksOK();
  455:         }
  456:         document.getElementsByName('list')[0].setAttribute("action", "/adm/wishlist?mode="+mode); 
  457:         if (r) {
  458:             document.getElementsByName('list')[0].submit(); 
  459:         }
  460:     }
  461: 
  462:     function setAction(action) {
  463:         document.getElementById('action').value = action; 
  464:     }
  465: 
  466:     function getDifferences() {
  467:         var newtitles = document.getElementsByName('newtitle');
  468:         var i = 0;
  469:         for (i=0;i<newtitles.length;i++) {
  470:             var newt = newtitles[i].value;
  471:             var oldt = newtitles[i].alt;
  472:             if (newt != oldt) {
  473:                 return true;
  474:             }
  475:         }
  476:         var newpath = document.getElementsByName('newpath');
  477:         var i = 0;
  478:         for (i=0;i<newpath.length;i++) {
  479:             var newp = newpath[i].value;
  480:             var oldp = newpath[i].alt;
  481:             if (newp != oldp) {
  482:                 return true;
  483:             }
  484:         }
  485:         var newnote = document.getElementsByName('newnote');
  486:         var i = 0;
  487:         for (i=0;i<newnote.length;i++) {
  488:             var newn = newnote[i].value;
  489:             var oldn = newnote[i].innerHTML;
  490:             if (newn != oldn) {
  491:                 return true;
  492:             }
  493:         }
  494:         return false;
  495:     }
  496: 
  497:     function linksOK() {
  498:         var newpath = document.getElementsByName('newpath');
  499:         var i = 0;
  500:         for (i=0;i<newpath.length;i++) {
  501:             var path = newpath[i].value;
  502:             var linkOK = (path.match(/^http:\\/\\//) || path.match(/^https:\\/\\//))
  503:                          && !(path.match(/\\.problem/) || path.match(/\\.exam/)
  504:                          || path.match(/\\.quiz/) || path.match(/\\.assess/)
  505:                          || path.match(/\\.survey/) || path.match(/\\.form/)
  506:                          || path.match(/\\.library/) || path.match(/\\.page/)
  507:                          || path.match(/\\.sequence/));
  508:             if (!path.match(/^(\\/res\\/)/) && !linkOK) {
  509:                 alert("$warningLinkNotAllowed1 $warningLinkNotAllowed2"+path);
  510:                 return false;
  511:             }
  512:          }
  513:         return true;
  514:     }
  515: 
  516:     function onLoadAction(mode) {
  517:         window.name = 'wishlist';
  518:         if (mode == "edit") {
  519:             var deepestRows = getDeepestRows();
  520:             setDisplaySelect(deepestRows, '');
  521:         }
  522:     }
  523: 
  524:     function folderAction(rowid) {
  525:         var row = document.getElementById(rowid);
  526:         var indent = getIndent(row);
  527:         var displ;
  528:         var status;
  529:         if (getImage(row) == 'closed') {
  530:             displ = '';
  531:             status = 'open';
  532:         }
  533:         else {
  534:             displ = 'LC_hidden';
  535:             status = 'closed';
  536:         }
  537:         setImage(row,status);
  538:         if (getNextRow(row) != null) {
  539:             var nextIndent = getIndent(getNextRow(row));
  540:             row = getNextRow(row);
  541:             while (nextIndent > indent) {
  542:                 if (displ == '') {
  543:                     row.className = (row.className).replace('LC_hidden','');
  544:                 }
  545:                 else if (displ != '' && !((row.className).match('LC_hidden'))) {
  546:                     var oldClass = row.className;
  547:                     row.className = oldClass+' LC_hidden';
  548:                     setDisplayNote(row.id.replace('row','note'),'LC_hidden');
  549:                 }
  550:                 if (status == 'open' && getImage(row).match('closed')) {
  551:                     row = getNextRowWithIndent(row, getIndent(row));
  552:                 }
  553:                 else {
  554:                     row = getNextRow(row);
  555:                 } 
  556:                 if (row != null) {
  557:                     nextIndent = getIndent(row);
  558:                 } 
  559:                 else {
  560:                     nextIndent = indent;
  561:                 }
  562:             }
  563:         }
  564:         setClasses();
  565:         var newtitles = document.getElementsByName('newtitle');
  566:         if (newtitles.length>0) {
  567:             var deepestRows = getDeepestRows();
  568:             var otherRows = getOtherRows(deepestRows);
  569:             setDisplaySelect(deepestRows,'');
  570:             setDisplaySelect(otherRows,'LC_hidden');
  571:         }
  572:     }
  573: 
  574:     function selectAction(rowid) {
  575:         var row = document.getElementById(rowid);
  576:         var indent = getIndent(row);
  577:         var checked = getChecked(row);
  578:         var previousFolderRows = new Array();
  579:         if (indent != 0) {
  580:             previousFolderRows = getPreviousFolderRows(row);
  581:         }
  582:         if (getNextRow(row) != null) {
  583:             var nextIndent = getIndent(getNextRow(row));
  584:             row = getNextRow(row);
  585:                 while (nextIndent > indent) {
  586:                     setChecked(row,checked);
  587:                     if (status == 'open' && getImage(row).match('closed')) {
  588:                         row = getNextRowWithIndent(row, getIndent(row));
  589:                     }
  590:                     else {
  591:                         row = getNextRow(row);
  592:                     }
  593:                     if (row != null) {
  594:                         nextIndent = getIndent(row);
  595:                     }
  596:                     else {
  597:                         nextIndent = indent;
  598:                     }
  599:                 }
  600:         }
  601:         if (!checked) {
  602:             var i = 0;
  603:             for (i=0;i<previousFolderRows.length;i++) {
  604:                 setChecked(previousFolderRows[i], false);
  605:             }
  606:         }
  607:     }
  608: 
  609:     function getNextNote(row) {
  610:         var rowId = row.id;
  611:         var nextRowId = parseInt(rowId.substr(3,rowId.length))+1;
  612:         nextRowId = "note"+nextRowId;
  613:         var nextRow = document.getElementById(nextRowId);
  614:         return nextRow;
  615:     }
  616: 
  617:     function getNextRow(row) {
  618:         var rowId = row.id;
  619:         var nextRowId = parseInt(rowId.substr(3,rowId.length))+1;
  620:         nextRowId = "row"+nextRowId;
  621:         var nextRow = document.getElementById(nextRowId);
  622:         return nextRow;
  623:     }
  624: 
  625:     function getPreviousRow(row) {
  626:         var rowId = row.id;
  627:         var previousRowId =  parseInt(rowId.substr(3,rowId.length))-1;
  628:         previousRowId = "row"+previousRowId;
  629:         var previousRow =document.getElementById(previousRowId);
  630:         return previousRow;
  631:     }
  632: 
  633:     function getIndent(row) {
  634:         var childPADD = document.getElementById(row.id.replace('row','padd'));
  635:         indent = childPADD.style.paddingLeft;
  636:         indent = parseInt(indent.substr(0,(indent.length-2)));
  637:  
  638:         if (getImage(row).match('link')) {
  639:             indent -= $indentConst;
  640:         }
  641:         return indent;
  642:     }
  643: 
  644:     function getNextRowWithIndent(row, indent) {
  645:         var nextRow = getNextRow(row);
  646:         if (nextRow != null) {
  647:         var nextIndent = getIndent(nextRow);
  648:         while (nextIndent >= indent) {
  649:             if (nextIndent == indent) {
  650:                 return nextRow;
  651:             }
  652:             nextRow = getNextRow(nextRow);
  653:             if (nextRow == null) {
  654:                 return null;
  655:             }
  656:             nextIndent = getIndent(nextRow);
  657:         }
  658:         }
  659:         return nextRow;
  660:     }
  661: 
  662:     function getImage(row) {
  663:         var childIMG = document.getElementById(row.id.replace('row','img'));
  664:         if ((childIMG.src).match('closed')) {
  665:             return 'closed';
  666:         }
  667:         else if ((childIMG.src).match('open')) {
  668:             return 'open;'
  669:         }
  670:         else {
  671:             return 'link';
  672:         }
  673:     } 
  674: 
  675:     function setImage(row, status) {
  676:         var childIMG = document.getElementById(row.id.replace('row','img'));
  677:         var childIMGFolder = document.getElementById(row.id.replace('row','imgFolder'));
  678:         childIMG.src = "/adm/lonIcons/arrow."+status+".gif";
  679:         childIMGFolder.src="/adm/lonIcons/navmap.folder."+status+".gif"; 
  680:     }
  681: 
  682:     function getChecked(row) {
  683:         var childCHECK = document.getElementById(row.id.replace('row','check'));
  684:         var checked = childCHECK.checked;
  685:         return checked;
  686:     }
  687: 
  688:     function setChecked(row,checked) {
  689:         var childCHECK = document.getElementById(row.id.replace('row','check'));
  690:         childCHECK.checked = checked;
  691:     }
  692: 
  693:     function getPreviousFolderRows(row) {
  694:         var previousRow = getPreviousRow(row);
  695:         var indent = getIndent(previousRow);
  696:         var kindOfEntry = getImage(previousRow);
  697:         var rows = new Array();
  698:         if (kindOfEntry != 'link') {
  699:             rows.push(previousRow);
  700:         }
  701: 
  702:         while (indent >0) {
  703:             previousRow = getPreviousRow(previousRow);
  704:             if (previousRow != null) {
  705:                 indent = getIndent(previousRow);
  706:                 kindOfEntry = getImage(previousRow);
  707:                 if (kindOfEntry != 'link') {
  708:                     rows.push(previousRow);
  709:                 }
  710:             }
  711:             else {
  712:                 indent = 0; 
  713:             }
  714:         }
  715:         return rows;
  716:     }
  717: 
  718:     function getDeepestRows() {
  719:         var row = document.getElementById('row0');
  720:         var firstRow = row;
  721:         var indent = getIndent(row);
  722:         var maxIndent = indent;
  723:         while (getNextRow(row) != null) {
  724:             row = getNextRow(row);
  725:             indent = getIndent(row);
  726:             if (indent>maxIndent && !((row.className).match('LC_hidden'))) {
  727:                 maxIndent = indent;
  728:             }
  729:         }
  730:         var deepestRows = new Array();
  731:         row = firstRow;
  732:         var rowIndent;
  733:         while (getNextRow(row) != null) {
  734:             rowIndent = getIndent(row);
  735:             if (rowIndent == maxIndent) {
  736:                 deepestRows.push(row);
  737:             }
  738:             row = getNextRow(row);
  739:         }
  740:         rowIndent = getIndent(row);
  741:         if (rowIndent == maxIndent) {
  742:             deepestRows.push(row);
  743:         }
  744:         return deepestRows;
  745:     }
  746: 
  747:     function getOtherRows(deepestRows) {
  748:         var row = document.getElementById('row0');
  749:         var otherRows = new Array();
  750:         var isIn = false;
  751:         while (getNextRow(row) != null) {
  752:             var i = 0;
  753:             for (i=0; i < deepestRows.length; i++) {
  754:                 if (row.id == deepestRows[i].id) {
  755:                     isIn = true;
  756:                 }
  757:             }
  758:             if (!isIn) {
  759:                 otherRows.push(row);
  760:             }
  761:             row = getNextRow(row);
  762:             isIn = false;
  763:         }
  764:         for (i=0; i < deepestRows.length; i++) {
  765:             if (row.id == deepestRows[i].id) {
  766:                 isIn = true;
  767:             }
  768:         }
  769:         if (!isIn) {
  770:             otherRows.push(row);
  771:         }
  772:         return otherRows;
  773:     }
  774: 
  775:     function setDisplaySelect(deepestRows, displ) {
  776:         var i = 0;
  777:         for (i = 0; i < deepestRows.length; i++) {
  778:             var row = deepestRows[i];
  779:             var childSEL = document.getElementById(row.id.replace('row','sel'));
  780:             childSEL.className = displ;
  781:         } 
  782:     }
  783: 
  784:     function submitSelect() {
  785:        var list = document.getElementsByName('list')[0];
  786:        list.setAttribute("action","/adm/wishlist?mode=edit");
  787:        list.submit();
  788:     }
  789: 
  790:     function setDisplayNote(rowid, displ) {
  791:         var row = document.getElementById(rowid);
  792:         if (!displ) {
  793:             if ((row.className).match('LC_hidden')) {
  794:                 row.className = (row.className).replace('LC_hidden','');
  795:             }
  796:             else {
  797:                 var oldClass = row.className;
  798:                 row.className = oldClass+' LC_hidden';
  799:             }
  800:         }
  801:         else {
  802:             if (displ == '') {
  803:                 row.className = (row.className).replace('LC_hidden','');
  804:             }
  805:             else if (displ != '' && !((row.className).match('LC_hidden'))) {
  806:                 var oldClass = row.className;
  807:                 row.className = oldClass+' LC_hidden';
  808:             }
  809:         }
  810:         var noteText = document.getElementById(rowid.replace('note','noteText'));
  811:         var noteImg = document.getElementById(rowid.replace('note','noteImg'));
  812:         if (noteText.value) {
  813:             noteImg.src = "/res/adm/pages/anot2.png";
  814:         }
  815:         else {
  816:             noteImg.src = "/res/adm/pages/anot.png";
  817:         }
  818: 
  819:     }
  820: 
  821:     function setClasses() {
  822:         var row = document.getElementById("row0");
  823:         var note = document.getElementById("note0");
  824:         var LC_class = 0;
  825:         if (getNextRow(row) != null) {
  826:             while (getNextRow(row) != null) {
  827:                 if (!(row.className).match('LC_hidden')) {
  828:                     note.className = (note.className).replace('LC_even_row','');
  829:                     note.className = (note.className).replace('LC_odd_row','');
  830:                     if (LC_class) {
  831:                         row.className = 'LC_even_row';
  832:                         note.className = 'LC_even_row'+note.className;
  833:                     }
  834:                     else {
  835:                         row.className = 'LC_odd_row';
  836:                         note.className = 'LC_odd_row'+note.className;;
  837:                     }
  838:                     LC_class = !LC_class;
  839:                 }
  840:                 note = getNextNote(row);
  841:                 row = getNextRow(row);
  842:             }
  843:         }
  844:         if (!(row.className).match('LC_hidden')) {
  845:             note.className = (note.className).replace('LC_even_row','');
  846:             note.className = (note.className).replace('LC_odd_row','');
  847:             if (LC_class) {
  848:                 row.className = 'LC_even_row';
  849:                 note.className = 'LC_even_row'+note.className;
  850:             }
  851:             else {
  852:                 row.className = 'LC_odd_row';
  853:                 note.className = 'LC_odd_row'+note.className;
  854:             }
  855:         }
  856:     }
  857: 
  858:     function selectDestinationFolder() {
  859:         var mark = document.getElementsByName('mark');
  860:         var i = 0;
  861:         for (i = 0; i < mark.length; i++) {
  862:             if (mark[i].checked) {
  863:                 document.getElementsByName('list')[0].submit();
  864:                 return true;
  865:             }
  866:         }
  867:         alert('$warningMove');
  868:         return false;
  869:     }
  870: 
  871:     function preview(url) {
  872:        var newWin;
  873:        if (!(url.match(/^http:\\/\\//) || url.match(/^https:\\/\\//))) {
  874:            newWin = window.open(url+'?inhibitmenu=yes','preview','width=560,height=350,scrollbars=yes');
  875:        }
  876:        else {
  877:            newWin = window.open(url,'preview','width=560,height=350,scrollbars=yes');
  878:        }
  879:        newWin.focus();
  880:     }
  881: 
  882:     function finish_import() {
  883:         opener.document.forms.simpleedit.importdetail.value='';
  884:         for (var num = 0; num < document.forms.groupsort.fnum.value; num++) {
  885:             if (eval("document.forms.groupsort.check"+num+".checked") && eval("document.forms.groupsort.filelink"+num+".value") != '') {
  886:                 opener.document.forms.simpleedit.importdetail.value+='&'+
  887:                 eval("document.forms.groupsort.title"+num+".value")+'='+
  888:                 eval("document.forms.groupsort.filelink"+num+".value")+'='+
  889:                 eval("document.forms.groupsort.id"+num+".value");
  890:             }
  891:         }
  892:         opener.document.forms.simpleedit.submit();
  893:         self.close();
  894:     }
  895: 
  896:     function checkAll() {
  897:         var checkboxes = document.getElementsByName('check');
  898:         for (var i = 0; i < checkboxes.length; i++) {
  899:             checkboxes[i].checked = "checked";
  900:         }
  901:     }
  902: 
  903:     function uncheckAll() {
  904:         var checkboxes = document.getElementsByName('check');
  905:         for (var i = 0; i < checkboxes.length; i++) {
  906:             checkboxes[i].checked = "";
  907:         }
  908:     }
  909: 
  910: JAVASCRIPT
  911:    return $js;
  912: }
  913: 
  914: 
  915: # HTML-Markup for table if in view-mode
  916: my $wishlistHTMLview;
  917: my $indent = $indentConst;
  918: sub wishlistView {
  919:     my $nodes = shift;
  920: 
  921:     foreach my $n (@$nodes) {
  922:         my $index = $n->value()->nindex();
  923: 
  924:         # 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.
  925:         # only display the top level entries on load
  926:         $wishlistHTMLview .= ($n->parent()->value() eq 'root')?&Apache::loncommon::start_data_table_row('','row'.$index)
  927:                                                               :&Apache::loncommon::continue_data_table_row('LC_hidden','row'.$index);
  928: 
  929:  
  930:         # checkboxes
  931:         $wishlistHTMLview .= '<td><input type="checkbox" name="mark" id="check'.$index.'" value="'.$index.'" '.
  932:                              'onclick="selectAction('."'row".$index."'".')"/></td>';
  933: 
  934:         # entry is a folder
  935:         if ($n->value()->path() eq '') {
  936:             $wishlistHTMLview .= '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<0?0:($indent-$indentConst)).'px; min-width: 220px;">'.
  937:                                  '<a href="javascript:;" onclick="folderAction('."'row".$index."'".')" style="vertical-align:top">'.
  938:                                  '<img src="/adm/lonIcons/arrow.closed.gif" id="img'.$index.'" alt = "" class="LC_icon"/>'.
  939:                                  '<img src="/adm/lonIcons/navmap.folder.closed.gif" id="imgFolder'.$index.'" alt="folder"/>'.
  940:                                  $n->value()->title().'</a></td>';
  941:         }
  942:         # entry is a link
  943:         else {
  944:             $wishlistHTMLview .= '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<=0?$indentConst:$indent).'px; min-width: 220px;">'.
  945:                                  '<a href="javascript:preview('."'".$n->value()->path()."'".');">'.
  946:                                  '<img src="/res/adm/pages/wishlist-link.png" id="img'.$index.'" alt="link" />'.
  947:                                  $n->value()->title().'</a></td>';
  948:         }
  949: 
  950:         # note-icon, different icons for an entries with note and those without
  951:         my $noteIMG = 'anot.png';
  952: 
  953:         if ($n->value()->note() ne '') {
  954:             $noteIMG = 'anot2.png';
  955:         }
  956: 
  957:         $wishlistHTMLview .= '<td style="padding-left:10px;"><a href="javascript:;" onclick="setDisplayNote('."'note".$index."'".')">'.
  958:                              '<img id="noteImg'.$index.'" src="/res/adm/pages/'.$noteIMG.'" alt="'.&mt('Note').'" title="'.&mt('Note').'" '.
  959:                              ' class="LC_icon"/></a></td>';
  960: 
  961:         $wishlistHTMLview .= &Apache::loncommon::end_data_table_row();
  962: 
  963:         # start row containing the textarea for the note, do not display note on default
  964:         $wishlistHTMLview .= &Apache::loncommon::continue_data_table_row('LC_hidden','note'.$index).
  965:                              '<td></td><td>'.
  966:                              '<textarea id="noteText'.$index.'" cols="25" rows="3" style="width:100%" '.
  967:                              'name="newnote" >'.
  968:                              $n->value()->note().'</textarea></td><td></td>';
  969:         $wishlistHTMLview .= &Apache::loncommon::end_data_table_row();
  970: 
  971:         # if the entry is a folder, it could have other entries as content. if it has, call wishlistView for those entries 
  972:         my @children = $n->children();
  973:         if ($#children >=0) {
  974:             $indent += 20;
  975:             &wishlistView(\@children);
  976:             $indent -= 20;
  977:         }
  978:     }
  979: }
  980: 
  981: 
  982: # HTML-Markup for table if in edit-mode
  983: my $wishlistHTMLedit;
  984: my $indent = $indentConst;
  985: sub wishlistEdit {
  986:     my $nodes = shift;
  987:     my $curNode = 1;
  988: 
  989:     foreach my $n (@$nodes) {
  990:         my $index = $n->value()->nindex();
  991: 
  992:         # start row, use data_table routines to set class to LC_even or LC_odd automatically.
  993:         # this rows contains a checkbox, a select-field for sorting entries, the title in an input-field and the note-icon.
  994:         # only display the top level entries on load
  995:         $wishlistHTMLedit .= ($n->parent()->value() eq 'root')?&Apache::loncommon::start_data_table_row('','row'.$index)
  996:                                                               :&Apache::loncommon::continue_data_table_row('LC_hidden','row'.$index);
  997: 
  998:         # checkboxes
  999:         $wishlistHTMLedit .= '<td><input type="checkbox" name="mark" id="check'.$index.'" value="'.$index.'" '.
 1000:                              'onclick="selectAction('."'row".$index."'".')"/></td>';
 1001: 
 1002:         # 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.
 1003:         # set the number for the current entry into brackets 
 1004:         my $options;
 1005:         for (my $i = 1; $i < ((scalar @{$nodes})+1); $i++) {
 1006:            if ($i == $curNode) {
 1007:                $options .= '<option selected="selected" value="">('.$i.')</option>';
 1008:            }
 1009:            else {
 1010:                $options .= '<option value="'.$i.'">'.$i.'</option>';
 1011:            }
 1012:         }
 1013:         $curNode++;
 1014: 
 1015:         # entry is a folder
 1016:         if ($n->value()->path() eq '') {
 1017:             $wishlistHTMLedit .= '<td><select class="LC_hidden" name="sel" id="sel'.$index.'" onchange="submitSelect();">'.
 1018:                                  $options.'</select></td>'.
 1019:                                  '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<0?0:($indent-$indentConst)).'px;">'.
 1020:                                  '<a href="javascript:;" onclick="folderAction('."'row".$index."'".')" style="vertical-align:top" >'.
 1021:                                  '<img src="/adm/lonIcons/arrow.closed.gif" id="img'.$index.'" alt = ""  class="LC_icon"/>'.
 1022:                                  '<img src="/adm/lonIcons/navmap.folder.closed.gif" id="imgFolder'.$index.'" alt="folder"/></a>'.
 1023:                                  '<input type="text" name="newtitle" value="'.$n->value()->title().'" alt = "'.$n->value()->title().'"/>'.
 1024:                                  '</td><td></td>';
 1025: 
 1026:         }
 1027:         # entry is a link
 1028:         else {
 1029:             $wishlistHTMLedit .= '<td><select class="LC_hidden" name="sel" id="sel'.$index.'" onchange="submitSelect();">'.
 1030:                                  $options.'</select></td>'.
 1031:                                  '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<=0?$indentConst:$indent).'px;">'.
 1032:                                  '<img src="/res/adm/pages/wishlist-link.png" id="img'.$index.'" alt="link"/>'.
 1033:                                  '<input type="text" name="newtitle" value="'.$n->value()->title().'" alt = "'.$n->value()->title().'"/></td>'.
 1034:                                  '<td><input type="text" name="newpath" value="'.$n->value()->path().'" alt = "'.$n->value()->path().'"/></td>';
 1035:         }
 1036:         
 1037:         # note-icon, different icons for an entries with note and those without
 1038:         my $noteIMG = 'anot.png';
 1039: 
 1040:         if ($n->value()->note() ne '') {
 1041:             $noteIMG = 'anot2.png';
 1042:         }
 1043: 
 1044:         $wishlistHTMLedit .= '<td style="padding-left:10px;"><a href="javascript:;" onclick="setDisplayNote('."'note".$index."'".')">'.
 1045:                              '<img id="noteImg'.$index.'" src="/res/adm/pages/'.$noteIMG.'" alt="'.&mt('Note').'" title="'.&mt('Note').'" '.
 1046:                              ' class="LC_icon"/></a></td>';
 1047: 
 1048:         $wishlistHTMLedit .= &Apache::loncommon::end_data_table_row();
 1049: 
 1050:         # start row containing the textarea for the note
 1051:         $wishlistHTMLedit .= &Apache::loncommon::continue_data_table_row('LC_hidden','note'.$index).
 1052:                              '<td></td><td></td><td colspan="2">'.
 1053:                              '<textarea id="noteText'.$index.'" cols="25" rows="3" style="width:100%" '.
 1054:                              'name="newnote">'.
 1055:                              $n->value()->note().'</textarea></td><td></td>';
 1056:         $wishlistHTMLedit .= &Apache::loncommon::end_data_table_row();
 1057: 
 1058:         # if the entry is a folder, it could have other entries as content. if it has, call wishlistEdit for those entries 
 1059:         my @children = $n->children();
 1060:         if ($#children >=0) {
 1061:             $indent += 20;
 1062:             &wishlistEdit(\@children);
 1063:             $indent -= 20;
 1064:         }
 1065:     }
 1066: }
 1067: 
 1068: 
 1069: 
 1070: # HTML-Markup for table if in move-mode
 1071: my $wishlistHTMLmove ='<tr id="root" class="LC_odd_row"><td><input type="radio" name="mark" id="radioRoot" value="root" /></td>'.
 1072:                       '<td>'.&mt('Top level').'</td><td></td></tr>';
 1073: my $indent = $indentConst;
 1074: sub wishlistMove {
 1075:     my $nodes = shift;
 1076:     my $marked = shift;
 1077: 
 1078:     foreach my $n (@$nodes) {
 1079:         my $index = $n->value()->nindex();
 1080: 
 1081:         #find out wether the current entry was marked to be moved.
 1082:         my $isIn = 0;
 1083:         foreach my $m (@$marked) {
 1084:             if ($index == $m) {
 1085:                $isIn = 1;
 1086:             }
 1087:         }
 1088:         # 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
 1089:         $wishlistHTMLmove .= &Apache::loncommon::start_data_table_row('','row'.$index);
 1090: 
 1091: 
 1092:         # entry is a folder
 1093:         if ($n->value()->path() eq '') {
 1094:             # display a radio-button, if the folder was not selected to be moved
 1095:             if (!$isIn) {
 1096:                 $wishlistHTMLmove .= '<td><input type="radio" name="mark" id="radio'.$index.'" value="'.$index.'" /></td>'.
 1097:                                      '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<0?0:($indent-$indentConst)).'px; min-width: 220px;">';
 1098:             }
 1099:             # higlight the title, if the folder was selected to be moved
 1100:             else {
 1101:                 $wishlistHTMLmove .= '<td></td>'.
 1102:                                      '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<0?0:($indent-$indentConst)).'px; min-width: 220px;'.
 1103:                                      'color:red;">';
 1104:             }
 1105:             #arrow- and folder-image, all folders are open, and title
 1106:             $wishlistHTMLmove .= '<img src="/adm/lonIcons/arrow.open.gif" id="img'.$index.'" alt = "" />'.
 1107:                                  '<img src="/adm/lonIcons/navmap.folder.open.gif" id="imgFolder'.$index.'" alt="folder"/>'.
 1108:                                  $n->value()->title().'</td>';
 1109:         }
 1110:         # entry is a link
 1111:         else {
 1112:             # higlight the title, if the link was selected to be moved
 1113:             my $highlight = '';
 1114:             if ($isIn) {
 1115:                $highlight = 'style="color:red;"';
 1116:             }
 1117:             # link-image and title
 1118:             $wishlistHTMLmove .= '<td></td>'.
 1119:                                  '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<=0?$indentConst:$indent).'px; min-width: 220px;">'.
 1120:                                  '<a href="javascript:preview('."'".$n->value()->path()."'".');" '.$highlight.'>'.
 1121:                                  '<img src="/res/adm/pages/wishlist-link.png" id="img'.$index.'" alt="link"/>'.
 1122:                                  $n->value()->title().'</a></td>';
 1123:         }
 1124: 
 1125:         # note-icon, different icons for an entries with note and those without
 1126:         my $noteIMG = 'anot.png';
 1127: 
 1128:         if ($n->value()->note() ne '') {
 1129:             $noteIMG = 'anot2.png';
 1130:         }
 1131: 
 1132:         $wishlistHTMLmove .= '<td style="padding-left:10px;"><a href="javascript:;" onclick="setDisplayNote('."'note".$index."'".')">'.
 1133:                              '<img id="noteImg'.$index.'" src="/res/adm/pages/'.$noteIMG.'" alt="'.&mt('Note').'" title="'.&mt('Note').'" '.
 1134:                              ' class="LC_icon"/></a></td>';
 1135: 
 1136:         $wishlistHTMLmove .= &Apache::loncommon::end_data_table_row();
 1137: 
 1138:         # start row containing the textarea for the note, readonly in move-mode
 1139:         $wishlistHTMLmove .= &Apache::loncommon::continue_data_table_row('LC_hidden','note'.$index).
 1140:                              '<td></td><td>'.
 1141:                              '<textarea id="noteText'.$index.'" cols="25" rows="3" style="width:100%" '.
 1142:                              'name="newnote" readonly="readonly">'.
 1143:                              $n->value()->note().'</textarea></td><td></td>'.
 1144:                              &Apache::loncommon::end_data_table_row();
 1145: 
 1146:         # if the entry is a folder, it could have other entries as content. if it has, call wishlistMove for those entries 
 1147:         my @children = $n->children();
 1148:         if ($#children >=0) {
 1149:             $indent += 20;
 1150:             &wishlistMove(\@children, $marked);
 1151:             $indent -= 20;
 1152:         }
 1153:     }
 1154: }
 1155: 
 1156: 
 1157: 
 1158: # HTML-Markup for table if in import-mode
 1159: my $wishlistHTMLimport;
 1160: my $indent = $indentConst;
 1161: my $form = 1;
 1162: sub wishlistImport {
 1163:     my $nodes = shift;
 1164: 
 1165:     foreach my $n (@$nodes) {
 1166:         my $index = $n->value()->nindex();
 1167: 
 1168:         # 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.
 1169:         # only display the top level entries on load
 1170:         $wishlistHTMLimport .= ($n->parent()->value() eq 'root')?&Apache::loncommon::start_data_table_row('','row'.$index)
 1171:                                                                 :&Apache::loncommon::continue_data_table_row('LC_hidden','row'.$index);
 1172: 
 1173:  
 1174:         # checkboxes
 1175:         $wishlistHTMLimport .= '<td>'.
 1176:                                '<input type="checkbox" name="check" id="check'.$index.'" value="'.$index.'" '.
 1177:                                'onclick="selectAction('."'row".$index."'".')"/>'.
 1178:                                '<input type="hidden" name="title'.$index.'" value="'.&escape($n->value()->title()).'">'.
 1179:                                '<input type="hidden" name="filelink'.$index.'" value="'.&escape($n->value()->path()).'">'.
 1180:                                '<input type="hidden" name="id'.$index.'">'.
 1181:                                '</td>';
 1182: 
 1183:         # entry is a folder
 1184:         if ($n->value()->path() eq '') {
 1185:             $wishlistHTMLimport .= '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<0?0:($indent-$indentConst)).'px; min-width: 220px;">'.
 1186:                                    '<a href="javascript:;" onclick="folderAction('."'row".$index."'".')" style="vertical-align:top">'.
 1187:                                    '<img src="/adm/lonIcons/arrow.closed.gif" id="img'.$index.'" alt = "" class="LC_icon"/>'.
 1188:                                    '<img src="/adm/lonIcons/navmap.folder.closed.gif" id="imgFolder'.$index.'" alt="folder"/>'.
 1189:                                    $n->value()->title().'</a></td>';
 1190:         }
 1191:         # entry is a link
 1192:         else {
 1193:             $wishlistHTMLimport .= '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<=0?$indentConst:$indent).'px; min-width: 220px;">'.
 1194:                                    '<a href="javascript:preview('."'".$n->value()->path()."'".');">'.
 1195:                                    '<img src="/res/adm/pages/wishlist-link.png" id="img'.$index.'" alt="link" />'.
 1196:                                    $n->value()->title().'</a></td>';
 1197:                                    $form++;
 1198:         }
 1199: 
 1200:         # note-icon, different icons for an entries with note and those without
 1201:         my $noteIMG = 'anot.png';
 1202: 
 1203:         if ($n->value()->note() ne '') {
 1204:             $noteIMG = 'anot2.png';
 1205:         }
 1206: 
 1207:         $wishlistHTMLimport .= '<td style="padding-left:10px;"><a href="javascript:;" onclick="setDisplayNote('."'note".$index."'".')">'.
 1208:                              '<img id="noteImg'.$index.'" src="/res/adm/pages/'.$noteIMG.'" alt="'.&mt('Note').'" title="'.&mt('Note').'" '.
 1209:                              ' class="LC_icon"/></a></td>';
 1210: 
 1211:         $wishlistHTMLimport .= &Apache::loncommon::end_data_table_row();
 1212: 
 1213:         # start row containing the textarea for the note, do not display note on default, readonly in import-mode
 1214:         $wishlistHTMLimport .= &Apache::loncommon::continue_data_table_row('LC_hidden','note'.$index).
 1215:                              '<td></td><td>'.
 1216:                              '<textarea id="noteText'.$index.'" cols="25" rows="3" style="width:100%" '.
 1217:                              'name="newnote" readonly="readonly">'.
 1218:                              $n->value()->note().'</textarea></td><td></td>';
 1219:         $wishlistHTMLimport .= &Apache::loncommon::end_data_table_row();
 1220: 
 1221:         # if the entry is a folder, it could have other entries as content. if it has, call wishlistImport for those entries 
 1222:         my @children = $n->children();
 1223:         if ($#children >=0) {
 1224:             $indent += 20;
 1225:             &wishlistImport(\@children);
 1226:             $indent -= 20;
 1227:         }
 1228:     }
 1229: }
 1230: 
 1231: # Returns the HTML-Markup for wishlist
 1232: sub makePage {
 1233:     my $mode = shift;
 1234:     my $marked = shift;
 1235: 
 1236:     # breadcrumbs and start_page
 1237:     &Apache::lonhtmlcommon::clear_breadcrumbs();
 1238:     &Apache::lonhtmlcommon::add_breadcrumb(
 1239:               { href => '/adm/wishlist?mode='.$mode,
 1240:                 text => 'Wishlist'});
 1241:     my $startPage = &Apache::loncommon::start_page('Wishlist',undef,
 1242:                                                      {'add_entries' => {
 1243:                                                         'onload' => 'javascript:onLoadAction('."'".$mode."'".');',
 1244:                                                         'onunload' => 'javascript:window.name = '."'loncapaclient'"}});
 1245: 
 1246:     my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs('Wishlist '.
 1247:                            '<a title="Online-Hilfe" href="/adm/help/Wishlist.hlp" target="_top">'.
 1248:                            '<img src="/adm/help/help.png" alt="'.&mt('Help').'" '.
 1249:                            'title="'.&mt('Help').'" class="LC_icon" /></a>');
 1250: 
 1251:     # get javascript-code for wishlist-interactions
 1252:     my $js = &JSforWishlist();
 1253: 
 1254:     # texthash for items in funtionlist
 1255:     my %lt = &Apache::lonlocal::texthash(
 1256:                  'ed' => 'Edit',
 1257:                  'vw' => 'View',
 1258:                  'al' => 'Add Link',
 1259:                  'af' => 'Add Folder',
 1260:                  'mv' => 'Move Selected',
 1261:                  'dl' => 'Delete Selected',
 1262:                  'sv' => 'Save');
 1263: 
 1264:     # start functionlist
 1265:     my $functions = &Apache::lonhtmlcommon::start_funclist();
 1266: 
 1267:     # icon for edit-mode, display when in view-mode
 1268:     if ($mode eq 'view') {
 1269:         $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
 1270:                           'onclick="setFormAction('."'save','edit'".');" class="LC_menubuttons_link">'.
 1271:                           '<img src="/res/adm/pages/edit-mode-22x22.png" alt="'.$lt{'ed'}.'" '.
 1272:                           'title="'.$lt{'ed'}.'" class="LC_icon"/> '.
 1273:                           '<span class="LC_menubuttons_inline_text">'.$lt{'ed'}.'</span></a>');
 1274:     }
 1275:     # icon for view-mode, display when in edit-mode
 1276:     else {
 1277:         $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
 1278:                           'onclick="setFormAction('."'save','view'".');" class="LC_menubuttons_link">'.
 1279:                           '<img src="/res/adm/pages/view-mode-22x22.png" alt="'.$lt{'vw'}.'" '.
 1280:                           'title="'.$lt{'vw'}.'" class="LC_icon"/> '.
 1281:                           '<span class="LC_menubuttons_inline_text">'.$lt{'vw'}.'</span></a>');
 1282:     }
 1283:     
 1284:     # icon for adding a new link
 1285:     $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
 1286:                       'onclick="newLink();" class="LC_menubuttons_link">'.
 1287:                       '<img src="/res/adm/pages/link-new-22x22.png" alt="'.$lt{'al'}.'" '.
 1288:                       'title="'.$lt{'al'}.'" class="LC_icon"/>'.
 1289:                       '<span class="LC_menubuttons_inline_text">'.$lt{'al'}.'</span></a>');
 1290: 
 1291:     # icon for adding a new folder
 1292:     $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
 1293:                       'onclick="newFolder();" class="LC_menubuttons_link">'.
 1294:                       '<img src="/res/adm/pages/folder-new-22x22.png" alt="'.$lt{'af'}.'" '.
 1295:                       'title="'.$lt{'af'}.'" class="LC_icon"/>'.
 1296:                       '<span class="LC_menubuttons_inline_text">'.$lt{'af'}.'</span></a>');
 1297: 
 1298:     # icon for moving entries
 1299:     $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
 1300:                       'onclick="setFormAction('."'move','move'".'); " class="LC_menubuttons_link">'.
 1301:                       '<img src="/res/adm/pages/move-22x22.png" alt="'.$lt{'mv'}.'" '.
 1302:                       'title="'.$lt{'mv'}.'" class="LC_icon" />'.
 1303:                       '<span class="LC_menubuttons_inline_text">'.$lt{'mv'}.'</span></a>');
 1304: 
 1305:     # icon for deleting entries
 1306:     $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
 1307:                       'onclick="setFormAction('."'delete','".$mode."'".'); " class="LC_menubuttons_link">'.
 1308:                       '<img src="/res/adm/pages/del.png" alt="'.$lt{'dl'}.'" '.
 1309:                       'title="'.$lt{'dl'}.'" class="LC_icon" />'.
 1310:                       '<span class="LC_menubuttons_inline_text">'.$lt{'dl'}.'</span></a>');
 1311: 
 1312:     # icon for saving changes
 1313:     $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
 1314:                       'onclick="setFormAction('."'saveOK','".$mode."'".'); " class="LC_menubuttons_link">'.
 1315:                       '<img src="/res/adm/pages/save-22x22.png" alt="'.$lt{'sv'}.'" '.
 1316:                       'title="'.$lt{'sv'}.'" class="LC_icon" />'.
 1317:                       '<span class="LC_menubuttons_inline_text">'.$lt{'sv'}.'</span></a>');
 1318: 
 1319:     # end funtionlist and generate subbox 
 1320:     $functions.= &Apache::lonhtmlcommon::end_funclist();
 1321:     my $subbox = &Apache::loncommon::head_subbox($functions);
 1322: 
 1323:     # start form 
 1324:     my $inner .= '<form name="list" action ="/adm/wishlist" method="post">'.
 1325:                  '<input type="hidden" id="action" name="action" value=""/>';
 1326:  
 1327:     # only display subbox in view- or edit-mode
 1328:     if ($mode eq 'view' || $mode eq 'edit') {
 1329:         $inner .= $subbox;
 1330:     }
 1331: 
 1332:     # generate table-content depending on mode
 1333:     if ($mode eq 'edit') {
 1334:         &wishlistEdit(\@childrenRt);
 1335:         if ($wishlistHTMLedit ne '') {
 1336:             $inner .= &Apache::loncommon::start_data_table("LC_tableOfContent");
 1337:             $inner .= $wishlistHTMLedit;
 1338:             $inner .= &Apache::loncommon::end_data_table();
 1339:         }
 1340:         else {
 1341:             $inner .= '<span class="LC_info">'.&mt("Your wishlist ist currently empty.").'</span>';
 1342:         }
 1343:         $wishlistHTMLedit = '';
 1344:     }
 1345:     elsif ($mode eq 'view') {
 1346:         &wishlistView(\@childrenRt);
 1347:         if ($wishlistHTMLview ne '') {
 1348:             $inner .= '<table class="LC_data_table LC_tableOfContent">'.$wishlistHTMLview.'</table>';
 1349:         }
 1350:         else {
 1351:             $inner .= '<span class="LC_info">'.&mt("Your wishlist ist currently empty.").'</span>';
 1352:         }
 1353:         $wishlistHTMLview = '';
 1354:     }
 1355:     else {
 1356:         my $markStr = '';
 1357:         foreach my $m (@$marked) {
 1358:             $markStr .= $m.',';
 1359:         }
 1360:         if ($markStr) {
 1361:             $markStr = substr($markStr, 0, length($markStr)-1);
 1362:             $inner .= '<input type="hidden" value="'.$markStr.'" name="markedToMove"/>';
 1363:             $inner .= '<p><span class="LC_info">'.&mt('You have selected the red marked entries to be moved to another folder. '.
 1364:                                                    'Now choose the new destination folder.').'</span></p>';
 1365:             &wishlistMove(\@childrenRt, $marked);
 1366:             $inner .= '<table class="LC_data_table LC_tableOfContent">'.$wishlistHTMLmove.'</table><br/><br/>';
 1367:             $inner .= '<input type="button" value="'.&mt('Move').'" onclick="setFormAction('."'','view'".'); selectDestinationFolder()"/>'.
 1368:                       '<input type="button" value="'.&mt('Cancel').'" onclick="go('."'/adm/wishlist'".')"/>';
 1369: 
 1370:             $wishlistHTMLmove ='<tr id="root" class="LC_odd_row"><td><input type="radio" name="mark" id="radioRoot" value="root" /></td>'.
 1371:                                '<td>'.&mt('Top level').'</td><td></td></tr>';
 1372:         }
 1373:         else {
 1374:             $inner .= '<p><span class="LC_info">'.&mt("You haven't marked any entry to move.").'</span></p>'.
 1375:                       '<input type="button" value="'.&mt('Back').'" onclick="go('."'/adm/wishlist'".')"/>';
 1376:         }
 1377:     }
 1378:     
 1379:     # end form 
 1380:     $inner .= '</form>';
 1381: 
 1382:     # end_page 
 1383:     my $endPage =  &Apache::loncommon::end_page();
 1384: 
 1385:     # put all page-elements together
 1386:     my $page = $startPage.$breadcrumbs.$js.$inner.$endPage;
 1387: 
 1388:     return $page;
 1389: }
 1390: 
 1391: 
 1392: # Returns the HTML-Markup for the page, shown when a link was set
 1393: sub makePageSet {
 1394:     # start_page 
 1395:     my $startPage = &Apache::loncommon::start_page('Wishlist',undef,
 1396:                                                    {'only_body' => 1});
 1397:     
 1398:     # confirm success and offer link to wishlist
 1399:     my $message = &Apache::lonhtmlcommon::confirm_success(&mt('Link successfully set!'));
 1400:     $message = &Apache::loncommon::confirmwrapper($message);
 1401: 
 1402:     my $inner .= '<br>'.$message.'<br/><br/>'.
 1403:                  '<a href="javascript:;" onclick="opener.open('."'/adm/wishlist'".');window.close();">'.&mt('Go to wishlist').'</a>'.
 1404:                  '&nbsp;<a href="javascript:;" onclick="window.close();">'.&mt('Close this window').'</a>';
 1405: 
 1406:     # end_page 
 1407:     my $endPage =  &Apache::loncommon::end_page();
 1408: 
 1409:     # put all page-elements together
 1410:     my $page = $startPage.$inner.$endPage;
 1411: 
 1412:     return $page;
 1413: }
 1414: 
 1415: 
 1416: # Returns the HTML-Markup for the page, shown when links should be imported into a course
 1417: sub makePageImport {
 1418:     # start_page 
 1419:     my $startPage = &Apache::loncommon::start_page('Wishlist',undef,
 1420:                                                    {'only_body' => 1});
 1421:     
 1422:     # get javascript-code for wishlist-interactions
 1423:     my $js = &JSforWishlist();
 1424: 
 1425:     my $inner = '<h1>'.&mt('Import Resources from Wishlist').'</h1>';
 1426:     $inner .= '<p><span class="LC_info">'.&mt("Please note that you  can use the checkboxes corresponding to a folder to ".
 1427:                                               "easily check all links within this folder. The folder structure itself can't be imported. ".
 1428:                                               "All checked links will be imported into the current folder of your course.").'</span></p>';
 1429: 
 1430:     my %wishlist = &getWishlist();
 1431:     my $fnum = (keys %wishlist)-1;
 1432: 
 1433:     $inner .= '<form method="post" name="groupsort">'.
 1434:               '<input type="hidden" value="'.$fnum.'" name="fnum">'.
 1435:               '<input type="button" onclick="javascript:checkAll()" id="checkallbutton" value="'.&mt('Check All').'">'.
 1436:               '<input type="button" onclick="javascript:uncheckAll()" id="uncheckallbutton" value="'.&mt('Uncheck All').'">'.
 1437:               '<input type="button" value="'.&mt('Import Checked').'" onclick="finish_import();">'.    
 1438:               '<input type="button" value="'.&mt('Cancel').'" onclick="window.close();"><br/><br/>'; 
 1439: 
 1440:     
 1441:     # wishlist-table
 1442:     &wishlistImport(\@childrenRt);
 1443:     if ($wishlistHTMLimport ne '') {
 1444:         $inner .= '<table class="LC_data_table LC_tableOfContent">'.$wishlistHTMLimport.'</table>';
 1445:     }
 1446:     else {
 1447:         $inner .= '<span class="LC_info">'.&mt("Your wishlist ist currently empty.").'</span>';
 1448:     }
 1449:     $wishlistHTMLimport = '';
 1450: 
 1451:     $inner .= '</form>';
 1452: 
 1453:     # end_page 
 1454:     my $endPage =  &Apache::loncommon::end_page();
 1455: 
 1456:     # put all page-elements together
 1457:     my $page = $startPage.$js.$inner.$endPage;
 1458: 
 1459:     return $page;
 1460: }
 1461: 
 1462: 
 1463: # Returns the HTML-Markup for error-page
 1464: sub makeErrorPage {
 1465:     # breadcrumbs and start_page 
 1466:     &Apache::lonhtmlcommon::add_breadcrumb(
 1467:               { href => '/adm/wishlist',
 1468:                 text => 'Wishlist'});
 1469:     my $startPage = &Apache::loncommon::start_page('Wishlist');
 1470:     
 1471:     my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs('Wishlist '.
 1472:                            '<a title="Online-Hilfe" href="/adm/help/Wishlist.hlp" target="_top">'.
 1473:                            '<img src="/adm/help/help.png" alt="'.&mt('Help').'" '.
 1474:                            'title="'.&mt('Help').'" class="LC_icon" /></a>');
 1475:     &Apache::lonhtmlcommon::clear_breadcrumbs();
 1476: 
 1477:     # error-message
 1478:     my $inner .= '<span class="LC_error">'.&mt('An error occurred! Please try again later.').'</span>';
 1479: 
 1480:     # end_page 
 1481:     my $endPage =  &Apache::loncommon::end_page();
 1482: 
 1483:     # put all page-elements together
 1484:     my $page = $startPage.$breadcrumbs.$inner.$endPage;
 1485: 
 1486:     return $page;
 1487: }
 1488: 
 1489: # ----------------------------------------------------- Main Handler, package lonwishlist
 1490: sub handler {
 1491:     my ($r) = @_;
 1492:     &Apache::loncommon::content_type($r,'text/html');
 1493:     $r->send_http_header;
 1494: 
 1495:     if (&getWishlist() ne 'error') {
 1496:         # get wishlist entries from user-data db-file and build a tree out of these entries
 1497:         %TreeHash = &getWishlist();
 1498:         $root = &Tree::HashToTree();
 1499:         @childrenRt = $root->children();
 1500: 
 1501:         # greate a new entry
 1502:         if ($env{'form.title'}) {
 1503:            &newEntry($env{'form.title'}, $env{'form.path'}, $env{'form.note'});
 1504:         }
 1505: 
 1506:         # get unprocessed_cgi (i.e. marked entries, mode ...) 
 1507:         &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['action','mark','markedToMove','mode','newtitle','note']);
 1508: 
 1509:         # change the order of entries within a level, that means sorting the entries
 1510:         my $changeOrder = 0;
 1511:         if (defined $env{'form.sel'}) {
 1512:             my @sel = &Apache::loncommon::get_env_multiple('form.sel');
 1513:             my $indexNode;
 1514:             my $at;
 1515:             for (my $s=0; $s<($#sel+1); $s++) {
 1516:                 if ($sel[$s] ne '') {
 1517:                     $indexNode = $s;
 1518:                     $at = $sel[$s]-1;
 1519:                 }
 1520:             }
 1521:             if ($at ne '') {
 1522:                 $changeOrder = 1;
 1523:                 &sortEntries($indexNode,$at);
 1524:                 &saveChanges();
 1525:             }
 1526:         }
 1527: 
 1528:         # get all marked (checkboxes) entries
 1529:         my @marked = ();
 1530:         if (defined $env{'form.mark'}) {
 1531:             @marked = &Apache::loncommon::get_env_multiple('form.mark');
 1532:         }
 1533: 
 1534:         # move entries from one folder to another
 1535:         if (defined $env{'form.markedToMove'}) {
 1536:            my $markedToMove = $env{'form.markedToMove'};
 1537:            my @ToMove = split(/\,/,$markedToMove);
 1538:            my $moveTo = $env{'form.mark'};
 1539:            if (defined $moveTo){ 
 1540:                &moveEntries(\@ToMove,$moveTo);
 1541:                &saveChanges();
 1542:            }
 1543:            $changeOrder = 1;
 1544:     
 1545:         }
 1546: 
 1547:         # delete entries
 1548:         if ($env{'form.action'} eq 'delete') {
 1549:             &deleteEntries(\@marked);
 1550:         }
 1551:    
 1552: 
 1553:         # get all titles and notes and save them
 1554:         # only save, if user wants to save changes
 1555:         # do not save, when current action is 'delete' or 'sort' or 'move' 
 1556:         my @newTitles = ();
 1557:         my @newPaths = ();
 1558:         my @newNotes = ();
 1559:         if ((defined $env{'form.newtitle'} || defined $env{'form.newpath'} || defined $env{'form.newnote'})
 1560:             && ($env{'form.action'} ne 'noSave') && ($env{'form.action'} ne 'delete') && !$changeOrder) {
 1561:             @newTitles = &Apache::loncommon::get_env_multiple('form.newtitle');
 1562:             @newPaths = &Apache::loncommon::get_env_multiple('form.newpath');
 1563:             @newNotes = &Apache::loncommon::get_env_multiple('form.newnote');
 1564:             my $node = 0;
 1565:             foreach my $t (@newTitles) {
 1566:                &setNewTitle($node, $t);
 1567:                $node++;
 1568:             }
 1569:             $node = 0;
 1570:             my $path = 0;
 1571:             for (my $i = 0; $i < ($#newTitles+1); $i++ ) {
 1572:                if (&setNewPath($node, $newPaths[$path])) {
 1573:                      $path++;
 1574:                }
 1575:                $node++;
 1576:             }
 1577:             $node = 0;
 1578:             foreach my $n (@newNotes) {
 1579:                &setNewNote($node, $n);
 1580:                $node++;
 1581:             }
 1582:             &saveChanges();
 1583:         }
 1584: 
 1585:         # Create HTML-markup
 1586:         my $page;
 1587:         if ($env{'form.mode'} eq 'edit') {
 1588:             $page = &makePage("edit");
 1589:         }
 1590:         elsif ($env{'form.mode'} eq 'move') {
 1591:             $page = &makePage("move", \@marked);
 1592:         }
 1593:         elsif ($env{'form.mode'} eq 'import') {
 1594:             $page = &makePageImport();
 1595:         }
 1596:         elsif ($env{'form.mode'} eq 'set') {
 1597:             $page = &makePageSet();
 1598:         }
 1599:         else {
 1600:             $page = &makePage("view");
 1601:         }
 1602:         @marked = ();
 1603:         $r->print($page);
 1604:     }
 1605:     # An error occured, print an error-page
 1606:     else {
 1607:         my $errorPage = &makeErrorPage();
 1608:         $r->print($errorPage);
 1609:     }
 1610:     return OK;
 1611: }
 1612: 
 1613: # ----------------------------------------------------- package Tree
 1614: # Extend CPAN-Module Tree by function like 'moveNode' or 'deleteNode'
 1615: package Tree;
 1616: 
 1617: # returns the node with a given index from a list of nodes
 1618: sub getNodeByIndex {
 1619:     my $index = shift;
 1620:     my $nodes = shift;
 1621:     my $found;
 1622:     
 1623:     for my $n (@$nodes) {
 1624:         my $curIndex = $n->value()->nindex();
 1625:         if ($n->value()->nindex() == $index) {
 1626:             $found = $n;
 1627:         }
 1628:     }
 1629:     return $found;
 1630: }
 1631: 
 1632: # moves a given node to a new parent or change the position from a node
 1633: # within its siblings (sorting)
 1634: sub moveNode {
 1635:     my $node = shift;
 1636:     my $at = shift;
 1637:     my $newParent = shift;
 1638: 
 1639: 
 1640:     if (!$newParent) {
 1641:         $newParent = $node->parent();
 1642:     }
 1643: 
 1644:     $node->parent()->remove_child($node);
 1645: 
 1646:     if (defined $at) {
 1647:         $newParent->add_child({at => $at},$node);
 1648:     }
 1649:     else {
 1650:         $newParent->add_child($node);
 1651:     }
 1652:     
 1653:     # updating root's children
 1654:     @childrenRt = $root->children();
 1655: }
 1656: 
 1657: # removes a given node
 1658: sub removeNode() {
 1659:     my $node = shift;
 1660:     my @children = $node->children();
 1661: 
 1662:     if ($#children >= 0) {
 1663:         foreach my $c (@children) {
 1664:             &removeNode($c);
 1665:         }
 1666:     }
 1667:     $node->parent()->remove_child($node);
 1668: 
 1669:     # updating root's children
 1670:     @childrenRt = $root->children();
 1671: }
 1672: 
 1673: 
 1674: # set an index for every node in the tree, beginning with 0
 1675: my $count = 0;
 1676: sub TreeIndex {
 1677:     my $children = shift;
 1678: 
 1679:     foreach my $n (@$children) {
 1680:         my @children = $n->children();
 1681:         $n->value()->nindex($count);$count++;
 1682: 
 1683:         if ($#children>=0) {
 1684:             &TreeIndex(\@children);
 1685:         }
 1686:     }
 1687: }
 1688: 
 1689: # reset index counter
 1690: sub setCountZero {
 1691:     $count = 0;
 1692: }
 1693: 
 1694: 
 1695: # convert the tree to a hash
 1696: # each node is one hash-entry
 1697: # keys are the indices, values are all other attributes
 1698: # (containing tile, path, note, date and indices for all direct children)
 1699: # except for root: the key is root and values are
 1700: # just the indices of root's children
 1701: sub RootToHash {
 1702:     my $childrenRt = shift;
 1703:     my @indexarr = ();
 1704: 
 1705:     foreach my $c (@$childrenRt) {
 1706:        push (@indexarr, $c->value()->nindex());
 1707:     }
 1708:     $TreeToHash{'root'} = [@indexarr];
 1709: }
 1710: 
 1711: sub TreeToHash {
 1712:     my $childrenRt = shift;
 1713: 
 1714:     foreach my $n (@$childrenRt) {
 1715:         my @arrtmp = ();
 1716:         $arrtmp[0] = $n->value()->title();
 1717:         $arrtmp[1] = $n->value()->path();
 1718:         $arrtmp[2] = $n->value()->note();
 1719:         $arrtmp[3] = $n->value()->date();
 1720:         my @childrenRt = $n->children();
 1721:         my $co = 4;
 1722:         foreach my $c (@childrenRt) {
 1723:             my $i = $c->value()->nindex();
 1724:             $arrtmp[$co] = $i;
 1725:             $co++;
 1726:         }
 1727:         $TreeToHash{$n->value()->nindex} = [ @arrtmp]; 
 1728:         if ($#childrenRt>=0) {
 1729:             &TreeToHash(\@childrenRt);
 1730:         }
 1731:     }
 1732: }
 1733: 
 1734: 
 1735: # convert the hash to a tree
 1736: # build a tree-object for each entry in the hash
 1737: # afterwards call &buildTree to connect the tree-objects
 1738: sub HashToTree {
 1739:     my @TreeNodes = ();
 1740:     my $root;
 1741: 
 1742:     foreach my $key (keys %TreeHash) {
 1743:         if ($key eq 'root') {
 1744:             $root = Tree->new("root");
 1745:         }
 1746:         else {
 1747:         my @attributes = @{ $TreeHash{$key} };
 1748:         my $tmpNode;
 1749:             $tmpNode = Tree->new(Entry->new(title=>$attributes[0],
 1750:                                             path=>$attributes[1],
 1751:                                             note=>$attributes[2],
 1752:                                             date=>$attributes[3],
 1753:                                             nindex=>$key));
 1754:         push(@TreeNodes, $tmpNode);
 1755:         # shift all attributes except for
 1756:         # the indices representing the children of a node
 1757:         shift(@attributes);
 1758:         shift(@attributes);
 1759:         shift(@attributes);
 1760:         shift(@attributes);
 1761:         $TreeHash{$key} = [ @attributes ];
 1762:         }
 1763:     }
 1764:     # if there are nodes, build up the tree-structure
 1765:     if (defined $TreeHash{'root'} && $TreeHash{'root'} ne '') {
 1766:         my @childrenRtIn = @{ $TreeHash{'root'} };
 1767:         &buildTree(\$root, \@childrenRtIn,\@TreeNodes,\%TreeHash);
 1768:     }
 1769:     return $root; 
 1770: }
 1771: 
 1772: 
 1773: # join the nodes to a tree
 1774: sub buildTree {
 1775:     my ($node, $childrenIn, $TreeNodes, $TreeHash) = @_;
 1776:     bless($node, 'Tree');
 1777:     foreach my $c (@$childrenIn) {
 1778:         my $tmpNode =  &getNodeByIndex($c,$TreeNodes);
 1779:         $$node->add_child($tmpNode);
 1780:         my @childrenIn = @{ $$TreeHash{$tmpNode->value()->nindex()} };
 1781:         &buildTree(\$tmpNode,\@childrenIn,$TreeNodes,$TreeHash);
 1782:     }
 1783: 
 1784: }
 1785: 
 1786: 
 1787: # ----------------------------------------------------- package Entry
 1788: # package that defines the entrys a wishlist could have
 1789: # i.e. folders and links
 1790: package Entry;
 1791: 
 1792: # constructor
 1793: sub new {
 1794:     my $invocant = shift;
 1795:     my $class = ref($invocant) || $invocant;
 1796:     my $self = { @_ }; #set attributes
 1797:     bless($self, $class);
 1798:     return $self;    
 1799: }
 1800: 
 1801: # getter and setter
 1802: sub title {
 1803:     my $self = shift;
 1804:     if ( @_ ) { $self->{title} = shift}
 1805:     return $self->{title};
 1806: }
 1807: 
 1808: sub date {
 1809:     my $self = shift;
 1810:     if ( @_ ) { $self->{date} = shift}
 1811:     return $self->{date};
 1812: }
 1813: 
 1814: sub note {
 1815:     my $self = shift;
 1816:     if ( @_ ) { $self->{note} = shift}
 1817:     return $self->{note};
 1818: }
 1819: 
 1820: sub path {
 1821:     my $self = shift;
 1822:     if ( @_ ) { $self->{path} = shift}
 1823:     return $self->{path};
 1824: }
 1825: 
 1826: sub nindex {
 1827:     my $self = shift;
 1828:     if ( @_ ) { $self->{nindex} = shift}
 1829:     return $self->{nindex};
 1830: }
 1831: 
 1832: 
 1833: 1;
 1834: __END__

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