Diff for /loncom/interface/lonwishlist.pm between versions 1.1 and 1.6

version 1.1, 2010/08/10 14:30:20 version 1.6, 2010/08/20 08:13:41
Line 26 Line 26
 # http://www.lon-capa.org/  # http://www.lon-capa.org/
 #  #
   
   =pod
   
   =head1 NAME
   
   Apache::lonwishlist - Wishlist-Module
     
   =head1 SYNOPSIS
   
   The wishlist offers a possibility to store links to resources from the resource-pool and external websites in a hierarchical list.
   It is only available for user with access to the resource-pool. The list can be structured by folders.
   
   The wishlist-module uses the CPAN-module "Tree" for easily handling the directory-structure of the wishlist. Each node in the tree has an index to be referenced by.
   
   =cut
   
 package Apache::lonwishlist;  package Apache::lonwishlist;
   
Line 36  use Apache::lonnet; Line 49  use Apache::lonnet;
 use Apache::loncommon();  use Apache::loncommon();
 use Apache::lonhtmlcommon;  use Apache::lonhtmlcommon;
 use Apache::lonlocal;  use Apache::lonlocal;
   use LONCAPA;
 use Tree;  use Tree;
   
   
Line 48  my @allFolders; Line 62  my @allFolders;
 my @allNodes;  my @allNodes;
 my $indentConst = 20;  my $indentConst = 20;
   
   =pod
   
   =head2 Routines for getting and putting the wishlist data from and accordingly to users data.
   
   =over 4
   
   =item * &getWishlist()
   
        Get the wishlist-data via lonnet::dump() and returns the got data in a hash.
   
   
   =item * &putWishlist(wishlist)
   
        Parameter is a reference to a hash. Puts the wishlist-data contained in the given hash via lonnet::put() to user-data.
   
   
   =item * &deleteWishlist()
   
        Deletes all entries from the user-data for wishlist. Do this before putting in new data.
   
   
   =back
   
   =cut
   
   
 # Read wishlist from user-data  # Read wishlist from user-data
 sub getWishlist {  sub getWishlist {
     my @wishlistkeys = &Apache::lonnet::getkeys('wishlist');      my %wishlist = &Apache::lonnet::dump('wishlist');
     my %wishlist = &Apache::lonnet::get('wishlist',\@wishlistkeys);      foreach my $i ( keys %wishlist) {
    foreach my $i (%wishlist) {  
         #File not found. This appears at the first time using the wishlist          #File not found. This appears at the first time using the wishlist
         #Create file and put 'root' into it          #Create file and put 'root' into it
        if ($i =~m/^error:No such file/) {         if ($i =~m/^error:No such file/) {
            &Apache::lonnet::logthis($i.'! Create file by putting in the "root" of the directory tree.');             &Apache::lonnet::logthis($i.'! Create file by putting in the "root" of the directory tree.');
            &Apache::lonnet::put('wishlist', {'root' => ''});             &Apache::lonnet::put('wishlist', {'root' => ''});
            @wishlistkeys = &Apache::lonnet::getkeys('wishlist');             %wishlist = &Apache::lonnet::dump('wishlist');
            %wishlist = &Apache::lonnet::get('wishlist',\@wishlistkeys);  
        }         }
        elsif ($i =~ /^(con_lost|error|no_such_host)/i) {         elsif ($i =~ /^(con_lost|error|no_such_host)/i) {
            &Apache::lonnet::logthis('ERROR while attempting to get wishlist: '.$i);             &Apache::lonnet::logthis('ERROR while attempting to get wishlist: '.$i);
Line 68  sub getWishlist { Line 105  sub getWishlist {
        }         }
     }      }
   
     #If hash is empty, put 'root' into it, so we got a node to start the tree      # if we got no keys in hash returned by dump(), return error.
       # wishlist will not be loaded, instead the user will be asked to try again later
     if ((keys %wishlist) == 0) {      if ((keys %wishlist) == 0) {
         &Apache::lonnet::logthis('ERROR while attempting to get wishlist: no keys retrieved!');          &Apache::lonnet::logthis('ERROR while attempting to get wishlist: no keys retrieved!');
         return 'error';          return 'error';
Line 92  sub deleteWishlist { Line 130  sub deleteWishlist {
 }  }
   
   
   =pod
   
   =head2 Routines for changing the directory struture of the wishlist.
   
   =over 4
   
   =item * &newEntry(title, path, note)
   
        Creates a new entry in the wishlist containing the given informations. Additionally saves the date of creation in the entry.  
   
   
   =item * &deleteEntries(marked)
   
        Parameter is a reference to an array containing the indices of all nodes that should be removed from the tree. 
   
   
   =item * &sortEntries(indexNode, at)
   
        Changes the position of a node given by indexNode within its siblings. New position is given by at.
   
   
   =item * &moveEntries(indexNodesToMove, indexParent)
   
        Parameter is a reference to an array containing the indices of all nodes that should be moved. indexParent specifies the node that will become the new Parent for these nodes. 
   
   
   =item * &setNewTitle(nodeindex, newTitle)
   
        Sets the title for the node given by nodeindex to newTitle.
   
   
   =item * &setNewPath(nodeindex, newPath)
   
        Sets the path for the node given by nodeindex to newPath.
   
   
   =item * &setNewNote(nodeindex, newNote)
   
        Sets the note for the node given by nodeindex to newNote.     
   
   
   =item * &saveChanges()
   
        Prepares the wishlist-hash to save it via &putWishlist(wishlist).   
   
   
   =back
   
   =cut
   
   
 # Create a new entry  # Create a new entry
 sub newEntry() {  sub newEntry() {
     my ($title, $path, $note) = @_;      my ($title, $path, $note) = @_;
Line 148  sub moveEntries { Line 237  sub moveEntries {
     my $indexParent = shift;      my $indexParent = shift;
     my @nodesToMove = ();      my @nodesToMove = ();
   
       # get all nodes that should be moved
     &getNodesToArray(\@childrenRt);      &getNodesToArray(\@childrenRt);
     foreach my $index (@$indexNodesToMove) {      foreach my $index (@$indexNodesToMove) {
         my $foundNode = &Tree::getNodeByIndex($index, \@allNodes);          my $foundNode = &Tree::getNodeByIndex($index, \@allNodes);
Line 188  sub setNewTitle { Line 278  sub setNewTitle {
 }  }
   
   
   # Set a new path for an entry
   sub setNewPath {
       my ($nodeindex, $newPath) = @_;
       &getNodesToArray(\@childrenRt);
       my $found = &Tree::getNodeByIndex($nodeindex, \@allNodes);
       if ($found->value()->path()) {
           $found->value()->path($newPath); 
           return 1;
       }
       @allNodes = ();
       return 0;
   }
   
   
 # Set a new note for an entry  # Set a new note for an entry
 sub setNewNote {  sub setNewNote {
     my ($nodeindex, $newNote) = @_;      my ($nodeindex, $newNote) = @_;
Line 211  sub saveChanges { Line 315  sub saveChanges {
 }  }
   
   
   =pod
   
   =head2 Routines for handling the directory structure
   
   =over 4
   
   =item * &getFoldersForOption(nodes)
   
        Return the titles for all exiting folders in an option-tag, used to offer the users a possibility to create a new link or folder in an existing folder.
        Recursive call starting with all children of the root of the tree (parameter nodes is reference to an array containing the nodes of the current level). 
   
   
   =item * &getfoldersOption()
   
        Returns the option-tag build by &getFoldersForOption(nodes). Use it to transfer this to other modules (e.g. lonmenu.pm). 
   
   
   =item * &getFoldersToArray(children)
   
        Puts all nodes that represent folders in the wishlist into an array. 
        Recursive call starting with all children of the root of the tree (parameter nodes is reference to an array containing the nodes of the current level).     
   
   
   =item * &getNodesToArray(children)
   
        Puts all existing nodes into an array (apart from the root node, because this one does not represent an entry in the wishlist).
        Recursive call starting with all children of the root of the tree (parameter nodes is reference to an array containing the nodes of the current level).     
    
   
    =back
   
   =cut
   
   
 # Return the names for all exiting folders in option-tags, so  # Return the names for all exiting folders in option-tags, so
 # a new link or a new folder can be created in an existing folder  # a new link or a new folder can be created in an existing folder
 my $indent = 0;  my $indent = 0;
Line 241  sub getfoldersOption { Line 379  sub getfoldersOption {
        $root = &Tree::HashToTree();         $root = &Tree::HashToTree();
        @childrenRt = $root->children();         @childrenRt = $root->children();
        &getFoldersForOption(\@childrenRt);         &getFoldersForOption(\@childrenRt);
        my $options = '<option value="" selected="selected"></option>'.$foldersOption;         my $options = '<option value="" selected="selected">('.&mt('Top level').')</option>'.$foldersOption;
        $foldersOption = '';         $foldersOption = '';
        return $options;         return $options;
    }     }
Line 279  sub getNodesToArray { Line 417  sub getNodesToArray {
 }  }
   
   
   =pod
   
   =head2 Routines for the user-interface of the wishlist
   
   =over 4
   
   =item * &JSforWishlist()
   
        Returns JavaScript-functions needed for wishlist actions like open and close folders.
   
   
   =item * &wishlistView(nodes)
   
        Returns the table-HTML-markup for the wishlist in mode "view".
        Recursive call starting with all children of the root of the tree (parameter nodes is reference to an array containing the nodes of the current level).     
   
   
   =item * &wishlistEdit(nodes)
   
        Returns the table-HTML-markup for the wishlist in mode "edit".
        Recursive call starting with all children of the root of the tree (parameter nodes is reference to an array containing the nodes of the current level).     
   
   
   =item * &wishlistMove(nodes, marked)
   
        Returns the table-HTML-markup for the wishlist in mode "move". Highlights all entry "selected to move" contained in marked (reference to array).
        Recursive call starting with all children of the root of the tree (parameter nodes is reference to an array containing the nodes of the current level).     
   
   
   =item * &wishlistImport(nodes)
   
        Returns the table-HTML-markup for the wishlist in mode "import".
        Recursive call starting with all children of the root of the tree (parameter nodes is reference to an array containing the nodes of the current level).     
    
   
   =item * &makePage(mode, marked)
   
        Returns the HTML-markup for the whole wishlist depending on mode. If mode is "move" we need the marked entries to be highlighted a "selected to move". 
        Calls &wishlistView(nodes), &wishlistEdit(nodes) or &wishlistMove(nodes, marked).
    
   
   =item * &makePageSet()
   
        Returns the HTML-Markup for the page shown when a link was set by using the icon when viewing a resource.
   
   
   =item * &makePageImport()
   
        Returns the HTML-Markup for the page shown when links should be imported into courses.
    
   
   =item * &makeErrorPage ()
   
        Returns the HTML-Markup for an error-page shown if the wishlist could not be loaded.
    
   
   =back
   
   =cut
   
   
 # Return a script-tag containing Javascript-function  # Return a script-tag containing Javascript-function
 # needed for wishlist actions like 'new link' ect.  # needed for wishlist actions like 'new link' ect.
 sub JSforWishlist {  sub JSforWishlist {
Line 321  sub JSforWishlist { Line 520  sub JSforWishlist {
                         '<br/><br/>'.                          '<br/><br/>'.
                         '<input type="submit" value="'.$lt{'si'}.'" />'.                          '<input type="submit" value="'.$lt{'si'}.'" />'.
                         '<select name="folders">'.                          '<select name="folders">'.
                         '<option value="" selected="selected"></option>'.                          '<option value="" selected="selected">('.&mt('Top level').')</option>'.
                         $foldersOption.                          $foldersOption.
                         '</select>'.                          '</select>'.
                         '<input type="button" value="'.$lt{'cl'}.'" onclick="javascript:window.close();" />'.                          '<input type="button" value="'.$lt{'cl'}.'" onclick="javascript:window.close();" />'.
Line 341  sub JSforWishlist { Line 540  sub JSforWishlist {
                           '<br/><br/>'.                            '<br/><br/>'.
                           '<input type="submit" value="'.$lt{'si'}.'" />'.                            '<input type="submit" value="'.$lt{'si'}.'" />'.
                           '<select name="folders">'.                            '<select name="folders">'.
                           '<option value="" selected="selected"></option>'.                            '<option value="" selected="selected">('.&mt('Top level').')</option>'.
                           $foldersOption.                            $foldersOption.
                           '</select>'.                            '</select>'.
                           '<input type="button" value="'.$lt{'cl'}.'" onclick="javascript:window.close();" />'.                            '<input type="button" value="'.$lt{'cl'}.'" onclick="javascript:window.close();" />'.
Line 351  sub JSforWishlist { Line 550  sub JSforWishlist {
     $inPageNewLink =~ s/\n//g;      $inPageNewLink =~ s/\n//g;
     $inPageNewFolder =~ s/\n//g;      $inPageNewFolder =~ s/\n//g;
   
       # it is checked, wether a path links to a LON-CAPA-resource or an external website. links to course-contents are not allowed
       # because they probably will return a kind of 'no access' (unless the user is already in the course, the path links to).
       # also importing these kind of links into a course does not make much sense.
       # to find out if a path (not starting with /res/...) links to course-contents, the same filter as in lonwrapper is used,
       # that means that it is checked wether a path contains .problem, .quiz, .exam etc.
       # 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 ...?
       my $warningLinkNotAllowed1 = &mt('You can only insert links to LON-CAPA resources from the resource-pool '.
                                       'or to external websites. Paths to LON-CAPA resources must be of the form /res/dom/usr... . '.
                                       'Paths to external websites must contain the network protocol (e.g. http://...).');
       my $warningLinkNotAllowed2 = &mt('The following link is not allowed: ');
     my $warningLink = &mt('You must insert a title and a path!');      my $warningLink = &mt('You must insert a title and a path!');
     my $warningFolder = &mt('You must insert a title!');      my $warningFolder = &mt('You must insert a title!');
     my $warningDelete = &mt('Are you sure you want to delete the selected entries? Deleting a folder also deletes all entries within this folder!');      my $warningDelete = &mt('Are you sure you want to delete the selected entries? Deleting a folder also deletes all entries within this folder!');
Line 369  sub JSforWishlist { Line 579  sub JSforWishlist {
                               +'if (!path || !title) {'                                +'if (!path || !title) {'
                               +'alert("$warningLink");'                                +'alert("$warningLink");'
                               +'return false;}'                                +'return false;}'
                                 +'var linkOK = (path.match(/^http:(\\\\/\\\\/)/) || path.match(/^https:(\\\\/\\\\/)/))'
                                 +'&& !(path.match(/\\.problem/) || path.match(/\\.exam/)'
                                 +'|| path.match(/\\.quiz/) || path.match(/\\.assess/)'
                                 +'|| path.match(/\\.survey/) || path.match(/\\.form/)'
                                 +'|| path.match(/\\.library/) || path.match(/\\.page/)'
                                 +'|| path.match(/\\.sequence/));'
                                 +'if (!path.match(/^(\\\\/res\\\\/)/) && !linkOK) {'
                                 +'alert("$warningLinkNotAllowed1");'
                                 +'return false;}'
                               +'else {'                                +'else {'
                               +'window.close();'                                +'window.close();'
                               +'return true;}}'                                +'return true;}}'
Line 408  sub JSforWishlist { Line 627  sub JSforWishlist {
             if (d) {              if (d) {
                 if (!confirm('$warningSave')) {                  if (!confirm('$warningSave')) {
                     setAction('noSave');                      setAction('noSave');
                       r = true;
                   }
                   else {
                       r = linksOK();
                 }                  }
             }              }
             r = true;          }
           else if (action == 'saveOK') {
               r = linksOK();
           }
           else if (action == 'move') {
               r = selectDestinationFolder();
         }          }
         document.getElementsByName('list')[0].setAttribute("action", "/adm/wishlist?mode="+mode);           document.getElementsByName('list')[0].setAttribute("action", "/adm/wishlist?mode="+mode); 
         if(r){          if (r) {
             document.getElementsByName('list')[0].submit();               document.getElementsByName('list')[0].submit(); 
         }          }
     }      }
Line 432  sub JSforWishlist { Line 660  sub JSforWishlist {
                 return true;                  return true;
             }              }
         }          }
           var newpath = document.getElementsByName('newpath');
           var i = 0;
           for (i=0;i<newpath.length;i++) {
               var newp = newpath[i].value;
               var oldp = newpath[i].alt;
               if (newp != oldp) {
                   return true;
               }
           }
         var newnote = document.getElementsByName('newnote');          var newnote = document.getElementsByName('newnote');
         var i = 0;          var i = 0;
         for (i=0;i<newnote.length;i++) {          for (i=0;i<newnote.length;i++) {
Line 444  sub JSforWishlist { Line 681  sub JSforWishlist {
         return false;          return false;
     }      }
   
       function linksOK() {
           var newpath = document.getElementsByName('newpath');
           var i = 0;
           for (i=0;i<newpath.length;i++) {
               var path = newpath[i].value;
               var linkOK = (path.match(/^http:\\/\\//) || path.match(/^https:\\/\\//))
                            && !(path.match(/\\.problem/) || path.match(/\\.exam/)
                            || path.match(/\\.quiz/) || path.match(/\\.assess/)
                            || path.match(/\\.survey/) || path.match(/\\.form/)
                            || path.match(/\\.library/) || path.match(/\\.page/)
                            || path.match(/\\.sequence/));
               if (!path.match(/^(\\/res\\/)/) && !linkOK) {
                   alert("$warningLinkNotAllowed1 $warningLinkNotAllowed2"+path);
                   return false;
               }
            }
           return true;
       }
   
     function onLoadAction(mode) {      function onLoadAction(mode) {
         window.name = 'wishlist';          window.name = 'wishlist';
         if (mode == "edit") {          if (mode == "edit") {
Line 799  sub JSforWishlist { Line 1055  sub JSforWishlist {
         return false;          return false;
     }      }
   
       function preview(url) {
          var newWin;
          if (!(url.match(/^http:\\/\\//) || url.match(/^https:\\/\\//))) {
              newWin = window.open(url+'?inhibitmenu=yes','preview','width=560,height=350,scrollbars=yes');
          }
          else {
              newWin = window.open(url,'preview','width=560,height=350,scrollbars=yes');
          }
          newWin.focus();
       }
   
       function checkAll() {
           var checkboxes = document.getElementsByName('check');
           for (var i = 0; i < checkboxes.length; i++) {
               checkboxes[i].checked = "checked";
           }
       }
   
       function uncheckAll() {
           var checkboxes = document.getElementsByName('check');
           for (var i = 0; i < checkboxes.length; i++) {
               checkboxes[i].checked = "";
           }
       }
   
 JAVASCRIPT  JAVASCRIPT
    return $js;     return $js;
 }  }
   
   sub JSforImport{
       my $rat = shift;
   
       my $js;
       if ($rat eq 'simple' || $rat eq '') {
           $js = &Apache::lonhtmlcommon::scripttag(<<JAVASCRIPT);
           function finish_import() {
               opener.document.forms.simpleedit.importdetail.value='';
               for (var num = 0; num < document.forms.groupsort.fnum.value; num++) {
                   if (eval("document.forms.groupsort.check"+num+".checked") && eval("document.forms.groupsort.filelink"+num+".value") != '') {
                       opener.document.forms.simpleedit.importdetail.value+='&'+
                       eval("document.forms.groupsort.title"+num+".value")+'='+
                       eval("document.forms.groupsort.filelink"+num+".value")+'='+
                       eval("document.forms.groupsort.id"+num+".value");
                   }
               }
               opener.document.forms.simpleedit.submit();
               self.close();
           }
   JAVASCRIPT
       }
       else {
           $js = &Apache::lonhtmlcommon::scripttag(<<JAVASCRIPT);
           function finish_import() {
               var linkflag=false;
               for (var num=0; num<document.forms.groupsort.fnum.value; num++) {
                   if (eval("document.forms.groupsort.check"+num+".checked") && eval("document.forms.groupsort.filelink"+num+".value") != '') {
                       insertRowInLastRow();
                       placeResourceInLastRow(
                           eval("document.forms.groupsort.title"+num+".value"),
                           eval("document.forms.groupsort.filelink"+num+".value"),
                           eval("document.forms.groupsort.id"+num+".value"),
                           linkflag
                           );
                       linkflag=true;
                   }
               }
               opener.editmode=0;
               opener.notclear=0;
               opener.linkmode=0;
               opener.draw();
               self.close();
           }
   
           function insertRowInLastRow() {
               opener.insertrow(opener.maxrow);
               opener.addobj(opener.maxrow,'e&2');
           }
   
           function placeResourceInLastRow (title,url,id,linkflag) {
               opener.mostrecent=opener.newresource(opener.maxrow,2,opener.unescape(title),
                                 opener.unescape(url),'false','normal',id);
               opener.save();
               if (linkflag) {
                   opener.joinres(opener.linkmode,opener.mostrecent,0);
               }
               opener.linkmode=opener.mostrecent;
           }
   JAVASCRIPT
       }
       return $js;
   }
   
 # HTML-Markup for table if in view-mode  # HTML-Markup for table if in view-mode
 my $wishlistHTMLview;  my $wishlistHTMLview;
Line 834  sub wishlistView { Line 1177  sub wishlistView {
         # entry is a link          # entry is a link
         else {          else {
             $wishlistHTMLview .= '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<=0?$indentConst:$indent).'px; min-width: 220px;">'.              $wishlistHTMLview .= '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<=0?$indentConst:$indent).'px; min-width: 220px;">'.
                                    '<a href="javascript:preview('."'".$n->value()->path()."'".');">'.
                                  '<img src="/res/adm/pages/wishlist-link.png" id="img'.$index.'" alt="link" />'.                                   '<img src="/res/adm/pages/wishlist-link.png" id="img'.$index.'" alt="link" />'.
                                  '<a href="'.$n->value()->path().'">'.$n->value()->title().'</a></td>';                                   $n->value()->title().'</a></td>';
         }          }
   
         # note-icon, different icons for an entries with note and those without          # note-icon, different icons for an entries with note and those without
Line 910  sub wishlistEdit { Line 1254  sub wishlistEdit {
                                  '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<0?0:($indent-$indentConst)).'px;">'.                                   '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<0?0:($indent-$indentConst)).'px;">'.
                                  '<a href="javascript:;" onclick="folderAction('."'row".$index."'".')" style="vertical-align:top" >'.                                   '<a href="javascript:;" onclick="folderAction('."'row".$index."'".')" style="vertical-align:top" >'.
                                  '<img src="/adm/lonIcons/arrow.closed.gif" id="img'.$index.'" alt = ""  class="LC_icon"/>'.                                   '<img src="/adm/lonIcons/arrow.closed.gif" id="img'.$index.'" alt = ""  class="LC_icon"/>'.
                                  '<img src="/adm/lonIcons/navmap.folder.closed.gif" id="imgFolder'.$index.'" alt="folder"/></a>';                                   '<img src="/adm/lonIcons/navmap.folder.closed.gif" id="imgFolder'.$index.'" alt="folder"/></a>'.
                                    '<input type="text" name="newtitle" value="'.$n->value()->title().'" alt = "'.$n->value()->title().'"/>'.
                                    '</td><td></td>';
   
         }          }
         # entry is a link          # entry is a link
Line 918  sub wishlistEdit { Line 1264  sub wishlistEdit {
             $wishlistHTMLedit .= '<td><select class="LC_hidden" name="sel" id="sel'.$index.'" onchange="submitSelect();">'.              $wishlistHTMLedit .= '<td><select class="LC_hidden" name="sel" id="sel'.$index.'" onchange="submitSelect();">'.
                                  $options.'</select></td>'.                                   $options.'</select></td>'.
                                  '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<=0?$indentConst:$indent).'px;">'.                                   '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<=0?$indentConst:$indent).'px;">'.
                                  '<img src="/res/adm/pages/wishlist-link.png" id="img'.$index.'" alt="link"/>';                                   '<img src="/res/adm/pages/wishlist-link.png" id="img'.$index.'" alt="link"/>'.
                                    '<input type="text" name="newtitle" value="'.$n->value()->title().'" alt = "'.$n->value()->title().'"/></td>'.
                                    '<td><input type="text" name="newpath" value="'.$n->value()->path().'" alt = "'.$n->value()->path().'"/></td>';
         }          }
           
         # input-field for title  
         $wishlistHTMLedit .= '<input type="text" name="newtitle" value="'.$n->value()->title().'" alt = "'.$n->value()->title().'"/></td>';  
   
         # note-icon, different icons for an entries with note and those without          # note-icon, different icons for an entries with note and those without
         my $noteIMG = 'anot.png';          my $noteIMG = 'anot.png';
   
Line 939  sub wishlistEdit { Line 1284  sub wishlistEdit {
   
         # start row containing the textarea for the note          # start row containing the textarea for the note
         $wishlistHTMLedit .= &Apache::loncommon::continue_data_table_row('LC_hidden','note'.$index).          $wishlistHTMLedit .= &Apache::loncommon::continue_data_table_row('LC_hidden','note'.$index).
                              '<td></td><td></td><td>'.                               '<td></td><td></td><td colspan="2">'.
                              '<textarea id="noteText'.$index.'" cols="25" rows="3" style="width:100%" '.                               '<textarea id="noteText'.$index.'" cols="25" rows="3" style="width:100%" '.
                              'name="newnote">'.                               'name="newnote">'.
                              $n->value()->note().'</textarea></td><td></td>';                               $n->value()->note().'</textarea></td><td></td>';
Line 999  sub wishlistMove { Line 1344  sub wishlistMove {
         }          }
         # entry is a link          # entry is a link
         else {          else {
             if (!$isIn) {  
                 $wishlistHTMLmove .= '<td></td>'.  
                                      '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<=0?$indentConst:$indent).'px; min-width: 220px;">';  
             }  
             # higlight the title, if the link was selected to be moved              # higlight the title, if the link was selected to be moved
             else {              my $highlight = '';
                 $wishlistHTMLmove .= '<td></td>'.              if ($isIn) {
                                      '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<=0?$indentConst:$indent).'px; min-width: 220px;'.                 $highlight = 'style="color:red;"';
                                      'color:red;">';  
             }              }
             # link-image and title              # link-image and title
             $wishlistHTMLmove .= '<img src="/res/adm/pages/wishlist-link.png" id="img'.$index.'" alt="link"/>'.              $wishlistHTMLmove .= '<td></td>'.
                                  $n->value()->title().'</td>';                                   '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<=0?$indentConst:$indent).'px; min-width: 220px;">'.
                                    '<a href="javascript:preview('."'".$n->value()->path()."'".');" '.$highlight.'>'.
                                    '<img src="/res/adm/pages/wishlist-link.png" id="img'.$index.'" alt="link"/>'.
                                    $n->value()->title().'</a></td>';
         }          }
   
         # note-icon, different icons for an entries with note and those without          # note-icon, different icons for an entries with note and those without
Line 1045  sub wishlistMove { Line 1388  sub wishlistMove {
     }      }
 }  }
   
   
   
   # HTML-Markup for table if in import-mode
   my $wishlistHTMLimport;
   my $indent = $indentConst;
   my $form = 1;
   sub wishlistImport {
       my $nodes = shift;
   
       foreach my $n (@$nodes) {
           my $index = $n->value()->nindex();
   
           # 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.
           # only display the top level entries on load
           $wishlistHTMLimport .= ($n->parent()->value() eq 'root')?&Apache::loncommon::start_data_table_row('','row'.$index)
                                                                   :&Apache::loncommon::continue_data_table_row('LC_hidden','row'.$index);
   
    
           # checkboxes
           $wishlistHTMLimport .= '<td>'.
                                  '<input type="checkbox" name="check" id="check'.$index.'" value="'.$index.'" '.
                                  'onclick="selectAction('."'row".$index."'".')"/>'.
                                  '<input type="hidden" name="title'.$index.'" value="'.&escape($n->value()->title()).'">'.
                                  '<input type="hidden" name="filelink'.$index.'" value="'.&escape($n->value()->path()).'">'.
                                  '<input type="hidden" name="id'.$index.'">'.
                                  '</td>';
   
           # entry is a folder
           if ($n->value()->path() eq '') {
               $wishlistHTMLimport .= '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<0?0:($indent-$indentConst)).'px; min-width: 220px;">'.
                                      '<a href="javascript:;" onclick="folderAction('."'row".$index."'".')" style="vertical-align:top">'.
                                      '<img src="/adm/lonIcons/arrow.closed.gif" id="img'.$index.'" alt = "" class="LC_icon"/>'.
                                      '<img src="/adm/lonIcons/navmap.folder.closed.gif" id="imgFolder'.$index.'" alt="folder"/>'.
                                      $n->value()->title().'</a></td>';
           }
           # entry is a link
           else {
               $wishlistHTMLimport .= '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<=0?$indentConst:$indent).'px; min-width: 220px;">'.
                                      '<a href="javascript:preview('."'".$n->value()->path()."'".');">'.
                                      '<img src="/res/adm/pages/wishlist-link.png" id="img'.$index.'" alt="link" />'.
                                      $n->value()->title().'</a></td>';
                                      $form++;
           }
   
           # note-icon, different icons for an entries with note and those without
           my $noteIMG = 'anot.png';
   
           if ($n->value()->note() ne '') {
               $noteIMG = 'anot2.png';
           }
   
           $wishlistHTMLimport .= '<td style="padding-left:10px;"><a href="javascript:;" onclick="setDisplayNote('."'note".$index."'".')">'.
                                '<img id="noteImg'.$index.'" src="/res/adm/pages/'.$noteIMG.'" alt="'.&mt('Note').'" title="'.&mt('Note').'" '.
                                ' class="LC_icon"/></a></td>';
   
           $wishlistHTMLimport .= &Apache::loncommon::end_data_table_row();
   
           # start row containing the textarea for the note, do not display note on default, readonly in import-mode
           $wishlistHTMLimport .= &Apache::loncommon::continue_data_table_row('LC_hidden','note'.$index).
                                '<td></td><td>'.
                                '<textarea id="noteText'.$index.'" cols="25" rows="3" style="width:100%" '.
                                'name="newnote" readonly="readonly">'.
                                $n->value()->note().'</textarea></td><td></td>';
           $wishlistHTMLimport .= &Apache::loncommon::end_data_table_row();
   
           # if the entry is a folder, it could have other entries as content. if it has, call wishlistImport for those entries 
           my @children = $n->children();
           if ($#children >=0) {
               $indent += 20;
               &wishlistImport(\@children);
               $indent -= 20;
           }
       }
   }
   
 # Returns the HTML-Markup for wishlist  # Returns the HTML-Markup for wishlist
 sub makePage {  sub makePage {
     my $mode = shift;      my $mode = shift;
Line 1060  sub makePage { Line 1478  sub makePage {
                                                         'onload' => 'javascript:onLoadAction('."'".$mode."'".');',                                                          'onload' => 'javascript:onLoadAction('."'".$mode."'".');',
                                                         'onunload' => 'javascript:window.name = '."'loncapaclient'"}});                                                          'onunload' => 'javascript:window.name = '."'loncapaclient'"}});
   
     my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs('Wishlist '.      my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs('Wishlist '.&Apache::loncommon::help_open_topic('Wishlist'));
                            '<a title="Online-Hilfe" href="/adm/help/Wishlist.hlp" target="_top">'.  
                            '<img src="/adm/help/help.png" alt="'.&mt('Help').'" '.  
                            'title="'.&mt('Help').'" class="LC_icon" /></a>');  
   
     # get javascript-code for wishlist-interactions      # get javascript-code for wishlist-interactions
     my $js = &JSforWishlist();      my $js = &JSforWishlist();
Line 1084  sub makePage { Line 1499  sub makePage {
     # icon for edit-mode, display when in view-mode      # icon for edit-mode, display when in view-mode
     if ($mode eq 'view') {      if ($mode eq 'view') {
         $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.          $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
                           'onclick="setFormAction('."'save','edit'".'); list.submit();" class="LC_menubuttons_link">'.                            'onclick="setFormAction('."'save','edit'".');" class="LC_menubuttons_link">'.
                           '<img src="/res/adm/pages/edit-mode-22x22.png" alt="'.$lt{'ed'}.'" '.                            '<img src="/res/adm/pages/edit-mode-22x22.png" alt="'.$lt{'ed'}.'" '.
                           'title="'.$lt{'ed'}.'" class="LC_icon"/> '.                            'title="'.$lt{'ed'}.'" class="LC_icon"/> '.
                           '<span class="LC_menubuttons_inline_text">'.$lt{'ed'}.'</span></a>');                            '<span class="LC_menubuttons_inline_text">'.$lt{'ed'}.'</span></a>');
Line 1092  sub makePage { Line 1507  sub makePage {
     # icon for view-mode, display when in edit-mode      # icon for view-mode, display when in edit-mode
     else {      else {
         $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.          $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
                           'onclick="setFormAction('."'save','view'".'); list.submit();" class="LC_menubuttons_link">'.                            'onclick="setFormAction('."'save','view'".');" class="LC_menubuttons_link">'.
                           '<img src="/res/adm/pages/view-mode-22x22.png" alt="'.$lt{'vw'}.'" '.                            '<img src="/res/adm/pages/view-mode-22x22.png" alt="'.$lt{'vw'}.'" '.
                           'title="'.$lt{'vw'}.'" class="LC_icon"/> '.                            'title="'.$lt{'vw'}.'" class="LC_icon"/> '.
                           '<span class="LC_menubuttons_inline_text">'.$lt{'vw'}.'</span></a>');                            '<span class="LC_menubuttons_inline_text">'.$lt{'vw'}.'</span></a>');
Line 1128  sub makePage { Line 1543  sub makePage {
   
     # icon for saving changes      # icon for saving changes
     $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.      $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
                       'onclick="setFormAction('."'','".$mode."'".'); " class="LC_menubuttons_link">'.                        'onclick="setFormAction('."'saveOK','".$mode."'".'); " class="LC_menubuttons_link">'.
                       '<img src="/res/adm/pages/save-22x22.png" alt="'.$lt{'sv'}.'" '.                        '<img src="/res/adm/pages/save-22x22.png" alt="'.$lt{'sv'}.'" '.
                       'title="'.$lt{'sv'}.'" class="LC_icon" />'.                        'title="'.$lt{'sv'}.'" class="LC_icon" />'.
                       '<span class="LC_menubuttons_inline_text">'.$lt{'sv'}.'</span></a>');                        '<span class="LC_menubuttons_inline_text">'.$lt{'sv'}.'</span></a>');
Line 1142  sub makePage { Line 1557  sub makePage {
                  '<input type="hidden" id="action" name="action" value=""/>';                   '<input type="hidden" id="action" name="action" value=""/>';
     
     # only display subbox in view- or edit-mode      # only display subbox in view- or edit-mode
     if ($mode ne 'move') {      if ($mode eq 'view' || $mode eq 'edit') {
         $inner .= $subbox;          $inner .= $subbox;
     }      }
   
Line 1155  sub makePage { Line 1570  sub makePage {
             $inner .= &Apache::loncommon::end_data_table();              $inner .= &Apache::loncommon::end_data_table();
         }          }
         else {          else {
             $inner .= '<span class="LC_info">'.&mt("Your wihlist ist currently empty.").'</span>';              $inner .= '<span class="LC_info">'.&mt("Your wishlist ist currently empty.").'</span>';
         }          }
         $wishlistHTMLedit = '';          $wishlistHTMLedit = '';
     }      }
Line 1165  sub makePage { Line 1580  sub makePage {
             $inner .= '<table class="LC_data_table LC_tableOfContent">'.$wishlistHTMLview.'</table>';              $inner .= '<table class="LC_data_table LC_tableOfContent">'.$wishlistHTMLview.'</table>';
         }          }
         else {          else {
             $inner .= '<span class="LC_info">'.&mt("Your wihlist ist currently empty.").'</span>';              $inner .= '<span class="LC_info">'.&mt("Your wishlist ist currently empty.").'</span>';
         }          }
         $wishlistHTMLview = '';          $wishlistHTMLview = '';
     }      }
Line 1177  sub makePage { Line 1592  sub makePage {
         if ($markStr) {          if ($markStr) {
             $markStr = substr($markStr, 0, length($markStr)-1);              $markStr = substr($markStr, 0, length($markStr)-1);
             $inner .= '<input type="hidden" value="'.$markStr.'" name="markedToMove"/>';              $inner .= '<input type="hidden" value="'.$markStr.'" name="markedToMove"/>';
             $inner .= '<span class="LC_info">'.&mt('You have selected the red marked entries to be moved to another folder. '.              $inner .= '<p><span class="LC_info">'.&mt('You have selected the red marked entries to be moved to another folder. '.
                                                    'Now choose the new destination folder.').'</span><br/><br/>';                                                     'Now choose the new destination folder.').'</span></p>';
             &wishlistMove(\@childrenRt, $marked);              &wishlistMove(\@childrenRt, $marked);
             $inner .= '<table class="LC_data_table LC_tableOfContent">'.$wishlistHTMLmove.'</table><br/><br/>';              $inner .= '<table class="LC_data_table LC_tableOfContent">'.$wishlistHTMLmove.'</table><br/><br/>';
             $inner .= '<input type="button" value="'.&mt('Move').'" onclick="setFormAction('."'','view'".'); selectDestinationFolder()"/>'.              $inner .= '<input type="button" value="'.&mt('Move').'" onclick="setFormAction('."'move','view'".');"/>'.
                       '<input type="button" value="'.&mt('Cancel').'" onclick="go('."'/adm/wishlist'".')"/>';                        '<input type="button" value="'.&mt('Cancel').'" onclick="go('."'/adm/wishlist'".')"/>';
   
             $wishlistHTMLmove ='<tr id="root" class="LC_odd_row"><td><input type="radio" name="mark" id="radioRoot" value="root" /></td>'.              $wishlistHTMLmove ='<tr id="root" class="LC_odd_row"><td><input type="radio" name="mark" id="radioRoot" value="root" /></td>'.
                                '<td>'.&mt('Top level').'</td><td></td></tr>';                                 '<td>'.&mt('Top level').'</td><td></td></tr>';
         }          }
         else {          else {
             $inner .= '<span class="LC_info">'.&mt("You haven't marked any entry to move.").'</span><br/>'.              $inner .= '<p><span class="LC_info">'.&mt("You haven't marked any entry to move.").'</span></p>'.
                       '<input type="button" value="'.&mt('Back').'" onclick="go('."'/adm/wishlist'".')"/>';                        '<input type="button" value="'.&mt('Back').'" onclick="go('."'/adm/wishlist'".')"/>';
         }          }
     }      }
Line 1199  sub makePage { Line 1614  sub makePage {
     # end_page       # end_page 
     my $endPage =  &Apache::loncommon::end_page();      my $endPage =  &Apache::loncommon::end_page();
   
     # put all page-elements togther      # put all page-elements together
     my $page = $startPage.$breadcrumbs.$js.$inner.$endPage;      my $page = $startPage.$breadcrumbs.$js.$inner.$endPage;
   
     return $page;      return $page;
Line 1223  sub makePageSet { Line 1638  sub makePageSet {
     # end_page       # end_page 
     my $endPage =  &Apache::loncommon::end_page();      my $endPage =  &Apache::loncommon::end_page();
   
     # put all page-elements togther      # put all page-elements together
     my $page = $startPage.$inner.$endPage;      my $page = $startPage.$inner.$endPage;
   
     return $page;      return $page;
 }  }
   
   
   # Returns the HTML-Markup for the page, shown when links should be imported into a course
   sub makePageImport {
       my $rat = shift;
       # start_page 
       my $startPage = &Apache::loncommon::start_page('Wishlist',undef,
                                                      {'only_body' => 1});
       
       # get javascript-code for wishlist-interactions
       my $js = &JSforWishlist();
       $js .= &JSforImport($rat);
   
       my $inner = '<h1>'.&mt('Import Resources from Wishlist').'</h1>';
       if (!$rat) {
           $inner .= '<p><span class="LC_info">'.&mt("Please note that you  can use the checkboxes corresponding to a folder to ".
                                                     "easily check all links within this folder. The folder structure itself can't be imported. ".
                                                     "All checked links will be imported into the current folder of your course.").'</span></p>';
       }
       else {
           $inner .= '<p><span class="LC_info">'.&mt("Please note that you  can use the checkboxes corresponding to a folder to ".
                                                     "easily check all links within this folder. The folder structure itself can't be imported. ")
                                                     .'</span></p>';
       }
       my %wishlist = &getWishlist();
       my $fnum = (keys %wishlist)-1;
   
       $inner .= '<form method="post" name="groupsort">'.
                 '<input type="hidden" value="'.$fnum.'" name="fnum">'.
                 '<input type="button" onclick="javascript:checkAll()" id="checkallbutton" value="'.&mt('Check All').'">'.
                 '<input type="button" onclick="javascript:uncheckAll()" id="uncheckallbutton" value="'.&mt('Uncheck All').'">'.
                 '<input type="button" value="'.&mt('Import Checked').'" onclick="finish_import();">'.    
                 '<input type="button" value="'.&mt('Cancel').'" onclick="window.close();"><br/><br/>'; 
   
       
       # wishlist-table
       &wishlistImport(\@childrenRt);
       if ($wishlistHTMLimport ne '') {
           $inner .= '<table class="LC_data_table LC_tableOfContent">'.$wishlistHTMLimport.'</table>';
       }
       else {
           $inner .= '<span class="LC_info">'.&mt("Your wishlist ist currently empty.").'</span>';
       }
       $wishlistHTMLimport = '';
   
       $inner .= '</form>';
   
       # end_page 
       my $endPage =  &Apache::loncommon::end_page();
   
       # put all page-elements together
       my $page = $startPage.$js.$inner.$endPage;
   
       return $page;
   }
   
   
 # Returns the HTML-Markup for error-page  # Returns the HTML-Markup for error-page
 sub makeErrorPage {  sub makeErrorPage {
     # breadcrumbs and start_page       # breadcrumbs and start_page 
Line 1250  sub makeErrorPage { Line 1720  sub makeErrorPage {
     # end_page       # end_page 
     my $endPage =  &Apache::loncommon::end_page();      my $endPage =  &Apache::loncommon::end_page();
   
     # put all page-elements togther      # put all page-elements together
     my $page = $startPage.$breadcrumbs.$inner.$endPage;      my $page = $startPage.$breadcrumbs.$inner.$endPage;
   
     return $page;      return $page;
Line 1274  sub handler { Line 1744  sub handler {
         }          }
   
         # get unprocessed_cgi (i.e. marked entries, mode ...)           # get unprocessed_cgi (i.e. marked entries, mode ...) 
         &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['action','mark','markedToMove','mode','newtitle','note']);          &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['action','mark','markedToMove','mode','newtitle','note','rat']);
   
         # change the order of entries within a level, that means sorting the entries          # change the order of entries within a level, that means sorting the entries
         my $changeOrder = 0;          my $changeOrder = 0;
Line 1324  sub handler { Line 1794  sub handler {
         # only save, if user wants to save changes          # only save, if user wants to save changes
         # do not save, when current action is 'delete' or 'sort' or 'move'           # do not save, when current action is 'delete' or 'sort' or 'move' 
         my @newTitles = ();          my @newTitles = ();
           my @newPaths = ();
         my @newNotes = ();          my @newNotes = ();
         if ((defined $env{'form.newtitle'} || defined $env{'form.newnote'}) && ($env{'form.action'} ne 'noSave') && ($env{'form.action'} ne 'delete') && !$changeOrder) {          if ((defined $env{'form.newtitle'} || defined $env{'form.newpath'} || defined $env{'form.newnote'})
               && ($env{'form.action'} ne 'noSave') && ($env{'form.action'} ne 'delete') && !$changeOrder) {
             @newTitles = &Apache::loncommon::get_env_multiple('form.newtitle');              @newTitles = &Apache::loncommon::get_env_multiple('form.newtitle');
               @newPaths = &Apache::loncommon::get_env_multiple('form.newpath');
             @newNotes = &Apache::loncommon::get_env_multiple('form.newnote');              @newNotes = &Apache::loncommon::get_env_multiple('form.newnote');
             my $node = 0;              my $node = 0;
             foreach my $t (@newTitles) {              foreach my $t (@newTitles) {
Line 1334  sub handler { Line 1807  sub handler {
                $node++;                 $node++;
             }              }
             $node = 0;              $node = 0;
               my $path = 0;
               for (my $i = 0; $i < ($#newTitles+1); $i++ ) {
                  if (&setNewPath($node, $newPaths[$path])) {
                        $path++;
                  }
                  $node++;
               }
               $node = 0;
             foreach my $n (@newNotes) {              foreach my $n (@newNotes) {
                &setNewNote($node, $n);                 &setNewNote($node, $n);
                $node++;                 $node++;
Line 1349  sub handler { Line 1830  sub handler {
         elsif ($env{'form.mode'} eq 'move') {          elsif ($env{'form.mode'} eq 'move') {
             $page = &makePage("move", \@marked);              $page = &makePage("move", \@marked);
         }          }
           elsif ($env{'form.mode'} eq 'import') {
               $page = &makePageImport($env{'form.rat'});
           }
         elsif ($env{'form.mode'} eq 'set') {          elsif ($env{'form.mode'} eq 'set') {
             $page = &makePageSet();              $page = &makePageSet();
         }          }
Line 1370  sub handler { Line 1854  sub handler {
 # Extend CPAN-Module Tree by function like 'moveNode' or 'deleteNode'  # Extend CPAN-Module Tree by function like 'moveNode' or 'deleteNode'
 package Tree;  package Tree;
   
   =pod
   
   =head2 Routines from package Tree
   
   =over 4
   
   =item * &getNodeByIndex(index, nodes)
   
        Searches for a node, specified by the index, in nodes (reference to array) and returns it. 
    
   
   =item * &moveNode(node, at, newParent)
   
        Moves a given node to a new parent (if new parents is defined) or change the position from a node within its siblings (means sorting, at must be defined).
   
   
   =item * &removeNode(node)
   
        Removes a node given by node from the tree.
   
   
   =item * &TreeIndex(children)
   
        Sets an index for every node in the tree, beginning with 0.
        Recursive call starting with all children of the root of the tree (parameter children is reference to an array containing the nodes of the current level).     
   
   
   =item * &setCountZero()
   
        Resets index counter.
   
   
   =item * &RootToHash(childrenRt)
   
        Converts the root-node to a hash-entry: the key is root and values are just the indices of root's children.
      
   
   =item * &TreeToHash(childrenRt)
   
        Converts all other nodes in the tree to hash. Each node is one hash-entry where the keys are the index of a node and the values are all other attributes (containing tile, path, note, date and indices for all direct children).
        Recursive call starting with all children of the root of the tree (parameter childrenRT is reference to an array containing the nodes of the current level).     
   
   
   =item * &HashToTree()
   
        Converts the hash to a tree. Builds a tree-object for each entry in the hash. Afterwards call &buildTree(node, childrenIn, TreeNodes, TreeHash) to connect the tree-objects.
   
   
   =item * &buildTree(node, childrenIn, TreeNodes, TreeHash)
   
        Joins the nodes to a tree.
        Recursive call starting with root and all children of root (parameter childrenIn is reference to an array containing the nodes indices of the current level).
      
   
   =back
   
   =cut
   
   
 # returns the node with a given index from a list of nodes  # returns the node with a given index from a list of nodes
 sub getNodeByIndex {  sub getNodeByIndex {
     my $index = shift;      my $index = shift;
Line 1518  sub HashToTree { Line 2061  sub HashToTree {
         }          }
     }      }
     # if there are nodes, build up the tree-structure      # if there are nodes, build up the tree-structure
     if (defined $TreeHash{'root'}) {      if (defined $TreeHash{'root'} && $TreeHash{'root'} ne '') {
         my @childrenRtIn = @{ $TreeHash{'root'} };          my @childrenRtIn = @{ $TreeHash{'root'} };
         &buildTree(\$root, \@childrenRtIn,\@TreeNodes,\%TreeHash);          &buildTree(\$root, \@childrenRtIn,\@TreeNodes,\%TreeHash);
     }      }

Removed from v.1.1  
changed lines
  Added in v.1.6


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