[LON-CAPA-cvs] cvs: loncom / loncapa_apache.conf startup.pl /html/res/adm/pages edit-mode-22x22.png folder-new-22x22.png link-new-22x22.png move-22x22.png save-22x22.png view-mode-22x22.png wishlist-link.png wishlist.png /interface lonwishlist.pm mydesk.tab doc/loncapafiles loncapafiles.lpml

wenzelju wenzelju@source.lon-capa.org
Tue, 10 Aug 2010 14:30:46 -0000


This is a MIME encoded message

--wenzelju1281450646
Content-Type: text/plain

wenzelju		Tue Aug 10 14:30:46 2010 EDT

  Added files:                 
    /loncom/interface	lonwishlist.pm 
    /loncom/html/res/adm/pages	edit-mode-22x22.png folder-new-22x22.png 
                              	link-new-22x22.png move-22x22.png 
                              	save-22x22.png view-mode-22x22.png 
                              	wishlist-link.png wishlist.png 

  Modified files:              
    /loncom/interface	mydesk.tab 
    /loncom	loncapa_apache.conf startup.pl 
    /doc/loncapafiles	loncapafiles.lpml 
  Log:
  Add new module lonwishlist:
  - This module will replace the bookmark-collection. Bookmark-collection is out of use because it's partly faulty and intricate to use.
  - The wishlist will offer a possibility to link resources on a hierarchal list. The list can be modified by adding new links and folder, to structure the list. Existing entries can be renamed, moved, sorted and deleted. Additional information can be attached for each entry by adding a note. Linked resources from the wishlist can be imported into courses. The wishlist is only available for roles with access to the resource-pool.  
  
  Still under progress! Function for setting a link to wishlist, when viewing a resource, and import-functionality will follow!
  
  
  
--wenzelju1281450646
Content-Type: text/plain
Content-Disposition: attachment; filename="wenzelju-20100810143046.txt"

Index: loncom/interface/mydesk.tab
diff -u loncom/interface/mydesk.tab:1.136 loncom/interface/mydesk.tab:1.137
--- loncom/interface/mydesk.tab:1.136	Sun Jun  6 11:56:12 2010
+++ loncom/interface/mydesk.tab	Tue Aug 10 14:30:20 2010
@@ -199,6 +199,7 @@
 8:1:tools:reqcrsshc:rcrs.png:request[_1]:course[_16]:go('/adm/requestcourse');:Course/Community requests:rcr
 8:1:tools:reqcrsnsc:rcrs.png:request[_1]:course[_16]:go('/adm/requestcourse');:Course/Community requests:rol
 9:2:any:any:vbkm.png:view[_1]:bookmark[_1]:edit_bookmarks():Use or edit my bookmark collection:set
+9:3:pbre:$requested_domain:wishlist.png:view[_1]:wishlist[_1]:go('/adm/wishlist'):Wishlist:set
 10:1:clear
 10:1:any:any:anno.png:calendar[_1]:announce[_1]:go('/adm/announcements');:Announcements and my calendar:com
 10:1:course:any:anno.png:calendar[_1]:announce[_1]:go('/adm/announcements');:Course anouncements and my calendar:com
Index: loncom/loncapa_apache.conf
diff -u loncom/loncapa_apache.conf:1.196 loncom/loncapa_apache.conf:1.197
--- loncom/loncapa_apache.conf:1.196	Wed Jul  7 16:30:44 2010
+++ loncom/loncapa_apache.conf	Tue Aug 10 14:30:28 2010
@@ -1,7 +1,7 @@
 ##
 ## loncapa_apache.conf -- Apache HTTP LON-CAPA configuration file
 ##
-## $Id: loncapa_apache.conf,v 1.196 2010/07/07 16:30:44 raeburn Exp $
+## $Id: loncapa_apache.conf,v 1.197 2010/08/10 14:30:28 wenzelju Exp $
 ##
 
 #
@@ -1225,6 +1225,17 @@
 ErrorDocument	  500 /adm/errorhandler
 </Location>
 
+<Location /adm/wishlist>
+AuthType LONCAPA
+Require valid-user
+PerlAuthzHandler       Apache::lonacc
+SetHandler perl-script
+PerlHandler Apache::lonwishlist
+ErrorDocument     403 /adm/login
+ErrorDocument     406 /adm/roles
+ErrorDocument	  500 /adm/errorhandler
+</Location>
+
 <Location /adm/blockingstatus>
 PerlAccessHandler      Apache::publiccheck
 AuthType LONCAPA
Index: loncom/startup.pl
diff -u loncom/startup.pl:1.35 loncom/startup.pl:1.36
--- loncom/startup.pl:1.35	Mon Apr 26 21:10:13 2010
+++ loncom/startup.pl	Tue Aug 10 14:30:28 2010
@@ -1,5 +1,5 @@
 #!/usr/bin/perl
-# $Id: startup.pl,v 1.35 2010/04/26 21:10:13 faziophi Exp $
+# $Id: startup.pl,v 1.36 2010/08/10 14:30:28 wenzelju Exp $
 
 BEGIN {
     eval "use Apache2::compat();";
@@ -115,6 +115,7 @@
 use Apache::lonwrapper();
 use Apache::admannotations();
 use Apache::admbookmarks();
+use Apache::lonwishlist(); 
 use Apache::style();
 use Apache::lontex();
 use Apache::matchresponse();
Index: doc/loncapafiles/loncapafiles.lpml
diff -u doc/loncapafiles/loncapafiles.lpml:1.695 doc/loncapafiles/loncapafiles.lpml:1.696
--- doc/loncapafiles/loncapafiles.lpml:1.695	Sun Aug  8 02:01:03 2010
+++ doc/loncapafiles/loncapafiles.lpml	Tue Aug 10 14:30:37 2010
@@ -2,7 +2,7 @@
  "http://lpml.sourceforge.net/DTD/lpml.dtd">
 <!-- loncapafiles.lpml -->
 
-<!-- $Id: loncapafiles.lpml,v 1.695 2010/08/08 02:01:03 raeburn Exp $ -->
+<!-- $Id: loncapafiles.lpml,v 1.696 2010/08/10 14:30:37 wenzelju Exp $ -->
 
 <!--
 
@@ -6746,6 +6746,7 @@
 edit-copy.png;
 edit-find-replace.png;
 edit-find.png;
+edit-mode-22x22.png;
 edit-redo.png;
 edit-select-all.png;
 edit-undo.png;
@@ -6754,6 +6755,7 @@
 emblem-system.png;
 extres.png;
 folder-new.png;
+folder-new-22x22.png;
 format-justify-fill.png;
 fromfile.png;
 groupportfolio.png;
@@ -6763,12 +6765,14 @@
 grp_tools.png;
 ims.png;
 imsexport.png;
+link-new-22x22.png;
 list-add.png;
 log.png;
 mail-message-new.png;
 mail-reply-all.png;
 mail-reply-sender.png;
 messalog.png;
+move-22x22.png;
 myaboutme.png;
 navigation.png;
 network-workgroup.png;
@@ -6782,6 +6786,7 @@
 recover.png;
 remotecontrol.png;
 resversion.png;
+save-22x22.png;
 scoreupfrm.png;
 selfenrl-queue.png;
 sequence.png;
@@ -6796,6 +6801,9 @@
 uplcrs.png;
 uploadscores.png;
 verify.png;
+view-mode-22x22.png;
+wishlist.png;
+wishlist-link.png;
 </filenames>
 </fileglob>
 <file>
@@ -7367,6 +7375,16 @@
   </description>
 </file>
 <file>
+<source>loncom/interface/lonwishlist.pm</source>
+<target dist='default'>home/httpd/lib/perl/Apache/lonwishlist.pm</target>
+<categoryname>handler</categoryname>
+<description>
+Interface to modify the wishlist (add new links and folders,
+edit, move, sort or delete existing links and folders).
+Links from the wishlist can be imported into courses.</description>
+<status>works/unverified</status>
+</file>
+<file>
   <source>loncom/automation/Autocreate.pl</source>Autocreate.pl
   <target dist='default'>home/httpd/perl/Autocreate.pl</target>
   <categoryname>script</categoryname>

Index: loncom/interface/lonwishlist.pm
+++ loncom/interface/lonwishlist.pm
# The LearningOnline Network with CAPA
# Routines to control the wishlist
#
# $Id: lonwishlist.pm,v 1.1 2010/08/10 14:30:20 wenzelju Exp $
#
# Copyright Michigan State University Board of Trustees
#
# This file is part of the LearningOnline Network with CAPA (LON-CAPA).
#
# LON-CAPA is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# LON-CAPA is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with LON-CAPA; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# /home/httpd/html/adm/gpl.txt
#
# http://www.lon-capa.org/
#



package Apache::lonwishlist;

use strict;
use Apache::Constants qw(:common);
use Apache::lonnet;
use Apache::loncommon();
use Apache::lonhtmlcommon;
use Apache::lonlocal;
use Tree;


# Global variables
my $root;
my @childrenRt;
my %TreeHash;
my %TreeToHash;
my @allFolders;
my @allNodes;
my $indentConst = 20;


# Read wishlist from user-data
sub getWishlist {
    my @wishlistkeys = &Apache::lonnet::getkeys('wishlist');
    my %wishlist = &Apache::lonnet::get('wishlist',\@wishlistkeys);
   foreach my $i (%wishlist) {
        #File not found. This appears at the first time using the wishlist
        #Create file and put 'root' into it
       if ($i =~m/^error:No such file/) {
           &Apache::lonnet::logthis($i.'! Create file by putting in the "root" of the directory tree.');
           &Apache::lonnet::put('wishlist', {'root' => ''});
           @wishlistkeys = &Apache::lonnet::getkeys('wishlist');
           %wishlist = &Apache::lonnet::get('wishlist',\@wishlistkeys);
       }
       elsif ($i =~ /^(con_lost|error|no_such_host)/i) {
           &Apache::lonnet::logthis('ERROR while attempting to get wishlist: '.$i);
           return 'error';
       }
    }

    #If hash is empty, put 'root' into it, so we got a node to start the tree
    if ((keys %wishlist) == 0) {
        &Apache::lonnet::logthis('ERROR while attempting to get wishlist: no keys retrieved!');
        return 'error';
    }
    
    return %wishlist;
}


# Write wishlist to user-data
sub putWishlist {
    my $wishlist = shift;
    &Apache::lonnet::put('wishlist',$wishlist);
}


# Removes all existing entrys for wishlist in user-data
sub deleteWishlist {
    my @wishlistkeys = &Apache::lonnet::getkeys('wishlist');
    my %wishlist = &Apache::lonnet::del('wishlist',\@wishlistkeys);
}


# Create a new entry
sub newEntry() {
    my ($title, $path, $note) = @_;
    my $date = gmtime();
    # Create Entry-Object
    my $entry = Entry->new(title => $title, path => $path, note => $note, date => $date);
    # Create Tree-Object, this correspones a node in the wishlist-tree
    my $tree = Tree->new($entry);
    # Add this node to wishlist-tree
    my $folderIndex = $env{'form.folders'};
    if ($folderIndex ne '') {
        @allFolders = ();
        &getFoldersToArray(\@childrenRt);
        my $folderToInsertOn = &Tree::getNodeByIndex($folderIndex,\@allFolders);
        $folderToInsertOn->add_child($tree);
    }
    else {
        $root->add_child($tree);
    }
    &saveChanges();
}


# Delete entries
sub deleteEntries {
    my $marked = shift;
    &getNodesToArray(\@childrenRt);

    foreach my $m (@$marked) {
        my $found = &Tree::getNodeByIndex($m, \@allNodes);
        &Tree::removeNode($found);
    }
    @allNodes = ();
    &saveChanges();
}


# Sort entries
sub sortEntries {
    my $indexNode = shift;
    my $at = shift;
    
    &getNodesToArray(\@childrenRt);
    my $foundNode = &Tree::getNodeByIndex($indexNode, \@allNodes);

    &Tree::moveNode($foundNode,$at,undef);
    @allNodes = ();
}


# Move entries
sub moveEntries {
    my $indexNodesToMove = shift;
    my $indexParent = shift;
    my @nodesToMove = ();

    &getNodesToArray(\@childrenRt);
    foreach my $index (@$indexNodesToMove) {
        my $foundNode = &Tree::getNodeByIndex($index, \@allNodes);
        push(@nodesToMove, $foundNode);
    }

    foreach my $node (@nodesToMove) {
        my $foundParent;
        my $parentIsIn = 0;
        foreach my $n (@nodesToMove) {
            if ($node->parent()->value() ne "root") {
               if ($node->parent()->value()->nindex() == $n->value()->nindex()) {
                    $parentIsIn = 1;
                }
            }
        }
        if (!$parentIsIn) {
            if ($indexParent ne "root") {
                $foundParent = &Tree::getNodeByIndex($indexParent, \@allNodes);
                &Tree::moveNode($node,undef,$foundParent);
            }
            else {
                &Tree::moveNode($node,undef,$root);
            }
        }
    }
    @allNodes = ();
}


# Set a new title for an entry
sub setNewTitle {
    my ($nodeindex, $newTitle) = @_;
    &getNodesToArray(\@childrenRt);
    my $found = &Tree::getNodeByIndex($nodeindex, \@allNodes);
    $found->value()->title($newTitle); 
    @allNodes = ();
}


# Set a new note for an entry
sub setNewNote {
    my ($nodeindex, $newNote) = @_;
    &getNodesToArray(\@childrenRt);
    my $found = &Tree::getNodeByIndex($nodeindex, \@allNodes);
    $found->value()->note($newNote); 
    @allNodes = ();
}


# Save all changes
sub saveChanges {
    @childrenRt = $root->children();
    &Tree::TreeIndex(\@childrenRt);
    &Tree::setCountZero();
    &Tree::RootToHash(\@childrenRt);
    &Tree::TreeToHash(\@childrenRt);
    &deleteWishlist();
    &putWishlist(\%TreeToHash);

}


# 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
my $indent = 0;
my $foldersOption;
sub getFoldersForOption {
    my $nodes = shift;

    foreach my $n (@$nodes) {
        if ($n->value()->path() eq '') {
            $foldersOption .= '<option value="'.$n->value()->nindex().'" style="margin-left:'.$indent.'px">'.
                                   $n->value()->title().
                               '</option>';

        my @children = $n->children();
        if ($#children >=0) {
            $indent += 10;
            &getFoldersForOption(\@children);
            $indent -= 10;
            }
        }
    }
}


sub getfoldersOption {
   if (&getWishlist ne 'error') {
       %TreeHash = &getWishlist();
       $root = &Tree::HashToTree();
       @childrenRt = $root->children();
       &getFoldersForOption(\@childrenRt);
       my $options = '<option value="" selected="selected"></option>'.$foldersOption;
       $foldersOption = '';
       return $options;
   }
   else {
       return '';
   }
}


# Put all folder-nodes to an array
sub getFoldersToArray {
    my $children = shift;
    foreach my $c (@$children) {
        if ($c->value()->path() eq '') {
            push(@allFolders,$c);
        }
        my @newchildren = $c->children();
        if ($#newchildren >= 0) {
            &getFoldersToArray(\@newchildren);
        }
    }
}


# Put all nodes to an array
sub getNodesToArray {
    my $children = shift;
    foreach my $c (@$children) {
        push(@allNodes,$c);
        my @newchildren = $c->children();
        if ($#newchildren >= 0) {
            &getNodesToArray(\@newchildren);
        }
    }
}


# Return a script-tag containing Javascript-function
# needed for wishlist actions like 'new link' ect.
sub JSforWishlist {
    my $startPagePopup = &Apache::loncommon::start_page('Wishlist',undef,
                                                            {'only_body' => 1,
                                                             'js_ready'  => 1,
                                                             'bgcolor'   => '#FFFFFF',});
    my $endPagePopup = &Apache::loncommon::end_page({'js_ready' => 1});

    @allFolders = ();
    &getFoldersToArray(\@childrenRt);
    &getFoldersForOption(\@childrenRt);

    # texthash
    my %lt = &Apache::lonlocal::texthash(
                 'nl' => 'New Link',
                 'nf' => 'New Folder',
                 'lt' => 'Link Title',
                 'ft' => 'Folder Title',
                 'pa' => 'Path',
                 'nt' => 'Note',
                 'si' => 'Save in',
                 'cl' => 'Cancel');


    my $inPageNewLink = '<h1>'.$lt{'nl'}.'</h1>'.
                        '<form method="post" name="newlink" action="/adm/wishlist" target="wishlist" '.
                        'onsubmit="return newlinksubmit();" >'.
                        &Apache::lonhtmlcommon::start_pick_box().
                        &Apache::lonhtmlcommon::row_title($lt{'lt'}).
                        '<input type="text" name="title" size="45" value="" />'.
                        &Apache::lonhtmlcommon::row_closure().
                        &Apache::lonhtmlcommon::row_title($lt{'pa'}).
                        '<input type="text" name="path" size="45" value="" />'.
                        &Apache::lonhtmlcommon::row_closure().
                        &Apache::lonhtmlcommon::row_title($lt{'nt'}).
                        '<textarea name="note" rows="3" cols="35" style="width:100%"></textarea>'.
                        &Apache::lonhtmlcommon::row_closure(1).
                        &Apache::lonhtmlcommon::end_pick_box().
                        '<br/><br/>'.
                        '<input type="submit" value="'.$lt{'si'}.'" />'.
                        '<select name="folders">'.
                        '<option value="" selected="selected"></option>'.
                        $foldersOption.
                        '</select>'.
                        '<input type="button" value="'.$lt{'cl'}.'" onclick="javascript:window.close();" />'.
                        '</form>';
    
    my $inPageNewFolder = '<h1>'.$lt{'nf'}.'</h1>'.
                          '<form method="post" name="newfolder" action="/adm/wishlist" target="wishlist" '.
                          'onsubmit="return newfoldersubmit();" >'.
                          &Apache::lonhtmlcommon::start_pick_box().
                          &Apache::lonhtmlcommon::row_title($lt{'ft'}).
                          '<input type="text" name="title" size="45" value="" /><br />'.
                          &Apache::lonhtmlcommon::row_closure().
                          &Apache::lonhtmlcommon::row_title($lt{'nt'}).
                          '<textarea name="note" rows="3" cols="35" style="width:100%"></textarea><br />'.
                          &Apache::lonhtmlcommon::row_closure(1).
                          &Apache::lonhtmlcommon::end_pick_box().
                          '<br/><br/>'.
                          '<input type="submit" value="'.$lt{'si'}.'" />'.
                          '<select name="folders">'.
                          '<option value="" selected="selected"></option>'.
                          $foldersOption.
                          '</select>'.
                          '<input type="button" value="'.$lt{'cl'}.'" onclick="javascript:window.close();" />'.
                          '</form>';

    # Remove all \n for inserting on javascript document.write
    $inPageNewLink =~ s/\n//g;
    $inPageNewFolder =~ s/\n//g;

    my $warningLink = &mt('You must insert a title and a path!');
    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 $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.');
    my $warningMove = &mt('You must select a destination folder!');
    $foldersOption = '';

    my $js = &Apache::lonhtmlcommon::scripttag(<<JAVASCRIPT);
    function newLink() {
        newlinkWin=window.open('','newlinkWin','width=580,height=320,scrollbars=yes');
        newlinkWin.document.write('$startPagePopup' 
                              +'<script type="text\/javascript">'
                              +'function newlinksubmit(){'
                              +'var path = document.getElementsByName("path")[0].value;'
                              +'var title = document.getElementsByName("title")[0].value;'
                              +'if (!path || !title) {'
                              +'alert("$warningLink");'
                              +'return false;}'
                              +'else {'
                              +'window.close();'
                              +'return true;}}'
                              +'<\/scr'+'ipt>'
                              +'$inPageNewLink'
                              +'$endPagePopup');
        newlinkWin.document.close();
    }

    function newFolder() {
        newfolderWin=window.open('','newfolderWin','width=580,height=270, scrollbars=yes');
        newfolderWin.document.write('$startPagePopup' 
                              +'<script type="text\/javascript">'
                              +'function newfoldersubmit(){'
                              +'var title = document.getElementsByName("title")[0].value;'
                              +'if (!title) {'
                              +'alert("$warningFolder");'
                              +'return false;}'
                              +'else {'
                              +'window.close();'
                              +'return true;}}'
                              +'<\/scr'+'ipt>'
                              +'$inPageNewFolder'
                              +'$endPagePopup');
        newfolderWin.document.close();
    }

    function setFormAction(action,mode) {
        var r = true;
        setAction('');
        if (action == 'delete') {
            r = confirm("$warningDelete");
            setAction('delete');
        }
        else if (action == 'save') {
            var d = getDifferences();
            if (d) {
                if (!confirm('$warningSave')) {
                    setAction('noSave');
                }
            }
            r = true;
        }
        document.getElementsByName('list')[0].setAttribute("action", "/adm/wishlist?mode="+mode); 
        if(r){
            document.getElementsByName('list')[0].submit(); 
        }
    }

    function setAction(action) {
        document.getElementById('action').value = action; 
    }

    function getDifferences() {
        var newtitles = document.getElementsByName('newtitle');
        var i = 0;
        for (i=0;i<newtitles.length;i++) {
            var newt = newtitles[i].value;
            var oldt = newtitles[i].alt;
            if (newt != oldt) {
                return true;
            }
        }
        var newnote = document.getElementsByName('newnote');
        var i = 0;
        for (i=0;i<newnote.length;i++) {
            var newn = newnote[i].value;
            var oldn = newnote[i].innerHTML;
            if (newn != oldn) {
                return true;
            }
        }
        return false;
    }

    function onLoadAction(mode) {
        window.name = 'wishlist';
        if (mode == "edit") {
            var deepestRows = getDeepestRows();
            setDisplaySelect(deepestRows, '');
        }
    }

    function folderAction(rowid) {
        var row = document.getElementById(rowid);
        var indent = getIndent(row);
        var displ;
        var status;
        if (getImage(row) == 'closed') {
            displ = '';
            status = 'open';
        }
        else {
            displ = 'LC_hidden';
            status = 'closed';
        }
        setImage(row,status);
        if (getNextRow(row) != null) {
            var nextIndent = getIndent(getNextRow(row));
            row = getNextRow(row);
            while (nextIndent > indent) {
                if (displ == '') {
                    row.className = (row.className).replace('LC_hidden','');
                }
                else if (displ != '' && !((row.className).match('LC_hidden'))) {
                    var oldClass = row.className;
                    row.className = oldClass+' LC_hidden';
                    setDisplayNote(row.id.replace('row','note'),'LC_hidden');
                }
                if (status == 'open' && getImage(row).match('closed')) {
                    row = getNextRowWithIndent(row, getIndent(row));
                }
                else {
                    row = getNextRow(row);
                } 
                if (row != null) {
                    nextIndent = getIndent(row);
                } 
                else {
                    nextIndent = indent;
                }
            }
        }
        setClasses();
        var newtitles = document.getElementsByName('newtitle');
        if (newtitles.length>0) {
            var deepestRows = getDeepestRows();
            var otherRows = getOtherRows(deepestRows);
            setDisplaySelect(deepestRows,'');
            setDisplaySelect(otherRows,'LC_hidden');
        }
    }

    function selectAction(rowid) {
        var row = document.getElementById(rowid);
        var indent = getIndent(row);
        var checked = getChecked(row);
        var previousFolderRows = new Array();
        if (indent != 0) {
            previousFolderRows = getPreviousFolderRows(row);
        }
        if (getNextRow(row) != null) {
            var nextIndent = getIndent(getNextRow(row));
            row = getNextRow(row);
                while (nextIndent > indent) {
                    setChecked(row,checked);
                    if (status == 'open' && getImage(row).match('closed')) {
                        row = getNextRowWithIndent(row, getIndent(row));
                    }
                    else {
                        row = getNextRow(row);
                    }
                    if (row != null) {
                        nextIndent = getIndent(row);
                    }
                    else {
                        nextIndent = indent;
                    }
                }
        }
        if (!checked) {
            var i = 0;
            for (i=0;i<previousFolderRows.length;i++) {
                setChecked(previousFolderRows[i], false);
            }
        }
    }

    function getNextNote(row) {
        var rowId = row.id;
        var nextRowId = parseInt(rowId.substr(3,rowId.length))+1;
        nextRowId = "note"+nextRowId;
        var nextRow = document.getElementById(nextRowId);
        return nextRow;
    }

    function getNextRow(row) {
        var rowId = row.id;
        var nextRowId = parseInt(rowId.substr(3,rowId.length))+1;
        nextRowId = "row"+nextRowId;
        var nextRow = document.getElementById(nextRowId);
        return nextRow;
    }

    function getPreviousRow(row) {
        var rowId = row.id;
        var previousRowId =  parseInt(rowId.substr(3,rowId.length))-1;
        previousRowId = "row"+previousRowId;
        var previousRow =document.getElementById(previousRowId);
        return previousRow;
    }

    function getIndent(row) {
        var childPADD = document.getElementById(row.id.replace('row','padd'));
        indent = childPADD.style.paddingLeft;
        indent = parseInt(indent.substr(0,(indent.length-2)));
 
        if (getImage(row).match('link')) {
            indent -= $indentConst;
        }
        return indent;
    }

    function getNextRowWithIndent(row, indent) {
        var nextRow = getNextRow(row);
        if (nextRow != null) {
        var nextIndent = getIndent(nextRow);
        while (nextIndent >= indent) {
            if (nextIndent == indent) {
                return nextRow;
            }
            nextRow = getNextRow(nextRow);
            if (nextRow == null) {
                return null;
            }
            nextIndent = getIndent(nextRow);
        }
        }
        return nextRow;
    }

    function getImage(row) {
        var childIMG = document.getElementById(row.id.replace('row','img'));
        if ((childIMG.src).match('closed')) {
            return 'closed';
        }
        else if ((childIMG.src).match('open')) {
            return 'open;'
        }
        else {
            return 'link';
        }
    } 

    function setImage(row, status) {
        var childIMG = document.getElementById(row.id.replace('row','img'));
        var childIMGFolder = document.getElementById(row.id.replace('row','imgFolder'));
        childIMG.src = "/adm/lonIcons/arrow."+status+".gif";
        childIMGFolder.src="/adm/lonIcons/navmap.folder."+status+".gif"; 
    }

    function getChecked(row) {
        var childCHECK = document.getElementById(row.id.replace('row','check'));
        var checked = childCHECK.checked;
        return checked;
    }

    function setChecked(row,checked) {
        var childCHECK = document.getElementById(row.id.replace('row','check'));
        childCHECK.checked = checked;
    }

    function getPreviousFolderRows(row) {
        var previousRow = getPreviousRow(row);
        var indent = getIndent(previousRow);
        var kindOfEntry = getImage(previousRow);
        var rows = new Array();
        if (kindOfEntry != 'link') {
            rows.push(previousRow);
        }

        while (indent >0) {
            previousRow = getPreviousRow(previousRow);
            if (previousRow != null) {
                indent = getIndent(previousRow);
                kindOfEntry = getImage(previousRow);
                if (kindOfEntry != 'link') {
                    rows.push(previousRow);
                }
            }
            else {
                indent = 0; 
            }
        }
        return rows;
    }

    function getDeepestRows() {
        var row = document.getElementById('row0');
        var firstRow = row;
        var indent = getIndent(row);
        var maxIndent = indent;
        while (getNextRow(row) != null) {
            row = getNextRow(row);
            indent = getIndent(row);
            if (indent>maxIndent && !((row.className).match('LC_hidden'))) {
                maxIndent = indent;
            }
        }
        var deepestRows = new Array();
        row = firstRow;
        var rowIndent;
        while (getNextRow(row) != null) {
            rowIndent = getIndent(row);
            if (rowIndent == maxIndent) {
                deepestRows.push(row);
            }
            row = getNextRow(row);
        }
        rowIndent = getIndent(row);
        if (rowIndent == maxIndent) {
            deepestRows.push(row);
        }
        return deepestRows;
    }

    function getOtherRows(deepestRows) {
        var row = document.getElementById('row0');
        var otherRows = new Array();
        var isIn = false;
        while (getNextRow(row) != null) {
            var i = 0;
            for (i=0; i < deepestRows.length; i++) {
                if (row.id == deepestRows[i].id) {
                    isIn = true;
                }
            }
            if (!isIn) {
                otherRows.push(row);
            }
            row = getNextRow(row);
            isIn = false;
        }
        for (i=0; i < deepestRows.length; i++) {
            if (row.id == deepestRows[i].id) {
                isIn = true;
            }
        }
        if (!isIn) {
            otherRows.push(row);
        }
        return otherRows;
    }

    function setDisplaySelect(deepestRows, displ) {
        var i = 0;
        for (i = 0; i < deepestRows.length; i++) {
            var row = deepestRows[i];
            var childSEL = document.getElementById(row.id.replace('row','sel'));
            childSEL.className = displ;
        } 
    }

    function submitSelect() {
       var list = document.getElementsByName('list')[0];
       list.setAttribute("action","/adm/wishlist?mode=edit");
       list.submit();
    }

    function setDisplayNote(rowid, displ) {
        var row = document.getElementById(rowid);
        if (!displ) {
            if ((row.className).match('LC_hidden')) {
                row.className = (row.className).replace('LC_hidden','');
            }
            else {
                var oldClass = row.className;
                row.className = oldClass+' LC_hidden';
            }
        }
        else {
            if (displ == '') {
                row.className = (row.className).replace('LC_hidden','');
            }
            else if (displ != '' && !((row.className).match('LC_hidden'))) {
                var oldClass = row.className;
                row.className = oldClass+' LC_hidden';
            }
        }
        var noteText = document.getElementById(rowid.replace('note','noteText'));
        var noteImg = document.getElementById(rowid.replace('note','noteImg'));
        if (noteText.value) {
            noteImg.src = "/res/adm/pages/anot2.png";
        }
        else {
            noteImg.src = "/res/adm/pages/anot.png";
        }

    }

    function setClasses() {
        var row = document.getElementById("row0");
        var note = document.getElementById("note0");
        var LC_class = 0;
        if (getNextRow(row) != null) {
            while (getNextRow(row) != null) {
                if (!(row.className).match('LC_hidden')) {
                    note.className = (note.className).replace('LC_even_row','');
                    note.className = (note.className).replace('LC_odd_row','');
                    if (LC_class) {
                        row.className = 'LC_even_row';
                        note.className = 'LC_even_row'+note.className;
                    }
                    else {
                        row.className = 'LC_odd_row';
                        note.className = 'LC_odd_row'+note.className;;
                    }
                    LC_class = !LC_class;
                }
                note = getNextNote(row);
                row = getNextRow(row);
            }
        }
        if (!(row.className).match('LC_hidden')) {
            note.className = (note.className).replace('LC_even_row','');
            note.className = (note.className).replace('LC_odd_row','');
            if (LC_class) {
                row.className = 'LC_even_row';
                note.className = 'LC_even_row'+note.className;
            }
            else {
                row.className = 'LC_odd_row';
                note.className = 'LC_odd_row'+note.className;
            }
        }
    }

    function selectDestinationFolder() {
        var mark = document.getElementsByName('mark');
        var i = 0;
        for (i = 0; i < mark.length; i++) {
            if (mark[i].checked) {
                document.getElementsByName('list')[0].submit();
                return true;
            }
        }
        alert('$warningMove');
        return false;
    }

JAVASCRIPT
   return $js;
}


# HTML-Markup for table if in view-mode
my $wishlistHTMLview;
my $indent = $indentConst;
sub wishlistView {
    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
        $wishlistHTMLview .= ($n->parent()->value() eq 'root')?&Apache::loncommon::start_data_table_row('','row'.$index)
                                                              :&Apache::loncommon::continue_data_table_row('LC_hidden','row'.$index);

 
        # checkboxes
        $wishlistHTMLview .= '<td><input type="checkbox" name="mark" id="check'.$index.'" value="'.$index.'" '.
                             'onclick="selectAction('."'row".$index."'".')"/></td>';

        # entry is a folder
        if ($n->value()->path() eq '') {
            $wishlistHTMLview .= '<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 {
            $wishlistHTMLview .= '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<=0?$indentConst:$indent).'px; min-width: 220px;">'.
                                 '<img src="/res/adm/pages/wishlist-link.png" id="img'.$index.'" alt="link" />'.
                                 '<a href="'.$n->value()->path().'">'.$n->value()->title().'</a></td>';
        }

        # note-icon, different icons for an entries with note and those without
        my $noteIMG = 'anot.png';

        if ($n->value()->note() ne '') {
            $noteIMG = 'anot2.png';
        }

        $wishlistHTMLview .= '<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>';

        $wishlistHTMLview .= &Apache::loncommon::end_data_table_row();

        # start row containing the textarea for the note, do not display note on default
        $wishlistHTMLview .= &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" >'.
                             $n->value()->note().'</textarea></td><td></td>';
        $wishlistHTMLview .= &Apache::loncommon::end_data_table_row();

        # if the entry is a folder, it could have other entries as content. if it has, call wishlistView for those entries 
        my @children = $n->children();
        if ($#children >=0) {
            $indent += 20;
            &wishlistView(\@children);
            $indent -= 20;
        }
    }
}


# HTML-Markup for table if in edit-mode
my $wishlistHTMLedit;
my $indent = $indentConst;
sub wishlistEdit {
    my $nodes = shift;
    my $curNode = 1;

    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 rows contains a checkbox, a select-field for sorting entries, the title in an input-field and the note-icon.
        # only display the top level entries on load
        $wishlistHTMLedit .= ($n->parent()->value() eq 'root')?&Apache::loncommon::start_data_table_row('','row'.$index)
                                                              :&Apache::loncommon::continue_data_table_row('LC_hidden','row'.$index);

        # checkboxes
        $wishlistHTMLedit .= '<td><input type="checkbox" name="mark" id="check'.$index.'" value="'.$index.'" '.
                             'onclick="selectAction('."'row".$index."'".')"/></td>';

        # 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.
        # set the number for the current entry into brackets 
        my $options;
        for (my $i = 1; $i < ((scalar @{$nodes})+1); $i++) {
           if ($i == $curNode) {
               $options .= '<option selected="selected" value="">('.$i.')</option>';
           }
           else {
               $options .= '<option value="'.$i.'">'.$i.'</option>';
           }
        }
        $curNode++;

        # entry is a folder
        if ($n->value()->path() eq '') {
            $wishlistHTMLedit .= '<td><select class="LC_hidden" name="sel" id="sel'.$index.'" onchange="submitSelect();">'.
                                 $options.'</select></td>'.
                                 '<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" >'.
                                 '<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>';

        }
        # entry is a link
        else {
            $wishlistHTMLedit .= '<td><select class="LC_hidden" name="sel" id="sel'.$index.'" onchange="submitSelect();">'.
                                 $options.'</select></td>'.
                                 '<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"/>';
        }

        # 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
        my $noteIMG = 'anot.png';

        if ($n->value()->note() ne '') {
            $noteIMG = 'anot2.png';
        }

        $wishlistHTMLedit .= '<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>';

        $wishlistHTMLedit .= &Apache::loncommon::end_data_table_row();

        # start row containing the textarea for the note
        $wishlistHTMLedit .= &Apache::loncommon::continue_data_table_row('LC_hidden','note'.$index).
                             '<td></td><td></td><td>'.
                             '<textarea id="noteText'.$index.'" cols="25" rows="3" style="width:100%" '.
                             'name="newnote">'.
                             $n->value()->note().'</textarea></td><td></td>';
        $wishlistHTMLedit .= &Apache::loncommon::end_data_table_row();

        # if the entry is a folder, it could have other entries as content. if it has, call wishlistEdit for those entries 
        my @children = $n->children();
        if ($#children >=0) {
            $indent += 20;
            &wishlistEdit(\@children);
            $indent -= 20;
        }
    }
}



# HTML-Markup for table if in move-mode
my $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>';
my $indent = $indentConst;
sub wishlistMove {
    my $nodes = shift;
    my $marked = shift;

    foreach my $n (@$nodes) {
        my $index = $n->value()->nindex();

        #find out wether the current entry was marked to be moved.
        my $isIn = 0;
        foreach my $m (@$marked) {
            if ($index == $m) {
               $isIn = 1;
            }
        }
        # 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
        $wishlistHTMLmove .= &Apache::loncommon::start_data_table_row('','row'.$index);


        # entry is a folder
        if ($n->value()->path() eq '') {
            # display a radio-button, if the folder was not selected to be moved
            if (!$isIn) {
                $wishlistHTMLmove .= '<td><input type="radio" name="mark" id="radio'.$index.'" value="'.$index.'" /></td>'.
                                     '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<0?0:($indent-$indentConst)).'px; min-width: 220px;">';
            }
            # higlight the title, if the folder was selected to be moved
            else {
                $wishlistHTMLmove .= '<td></td>'.
                                     '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<0?0:($indent-$indentConst)).'px; min-width: 220px;'.
                                     'color:red;">';
            }
            #arrow- and folder-image, all folders are open, and title
            $wishlistHTMLmove .= '<img src="/adm/lonIcons/arrow.open.gif" id="img'.$index.'" alt = "" />'.
                                 '<img src="/adm/lonIcons/navmap.folder.open.gif" id="imgFolder'.$index.'" alt="folder"/>'.
                                 $n->value()->title().'</td>';
        }
        # entry is a link
        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
            else {
                $wishlistHTMLmove .= '<td></td>'.
                                     '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<=0?$indentConst:$indent).'px; min-width: 220px;'.
                                     'color:red;">';
            }
            # link-image and title
            $wishlistHTMLmove .= '<img src="/res/adm/pages/wishlist-link.png" id="img'.$index.'" alt="link"/>'.
                                 $n->value()->title().'</td>';
        }

        # note-icon, different icons for an entries with note and those without
        my $noteIMG = 'anot.png';

        if ($n->value()->note() ne '') {
            $noteIMG = 'anot2.png';
        }

        $wishlistHTMLmove .= '<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>';

        $wishlistHTMLmove .= &Apache::loncommon::end_data_table_row();

        # start row containing the textarea for the note, readonly in move-mode
        $wishlistHTMLmove .= &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>'.
                             &Apache::loncommon::end_data_table_row();

        # if the entry is a folder, it could have other entries as content. if it has, call wishlistMove for those entries 
        my @children = $n->children();
        if ($#children >=0) {
            $indent += 20;
            &wishlistMove(\@children, $marked);
            $indent -= 20;
        }
    }
}

# Returns the HTML-Markup for wishlist
sub makePage {
    my $mode = shift;
    my $marked = shift;

    # breadcrumbs and start_page
    &Apache::lonhtmlcommon::clear_breadcrumbs();
    &Apache::lonhtmlcommon::add_breadcrumb(
              { href => '/adm/wishlist?mode='.$mode,
                text => 'Wishlist'});
    my $startPage = &Apache::loncommon::start_page('Wishlist',undef,
                                                     {'add_entries' => {
                                                        'onload' => 'javascript:onLoadAction('."'".$mode."'".');',
                                                        'onunload' => 'javascript:window.name = '."'loncapaclient'"}});

    my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs('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
    my $js = &JSforWishlist();

    # texthash for items in funtionlist
    my %lt = &Apache::lonlocal::texthash(
                 'ed' => 'Edit',
                 'vw' => 'View',
                 'al' => 'Add Link',
                 'af' => 'Add Folder',
                 'mv' => 'Move Selected',
                 'dl' => 'Delete Selected',
                 'sv' => 'Save');

    # start functionlist
    my $functions = &Apache::lonhtmlcommon::start_funclist();

    # icon for edit-mode, display when in view-mode
    if ($mode eq 'view') {
        $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
                          'onclick="setFormAction('."'save','edit'".'); list.submit();" class="LC_menubuttons_link">'.
                          '<img src="/res/adm/pages/edit-mode-22x22.png" alt="'.$lt{'ed'}.'" '.
                          'title="'.$lt{'ed'}.'" class="LC_icon"/> '.
                          '<span class="LC_menubuttons_inline_text">'.$lt{'ed'}.'</span></a>');
    }
    # icon for view-mode, display when in edit-mode
    else {
        $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
                          'onclick="setFormAction('."'save','view'".'); list.submit();" class="LC_menubuttons_link">'.
                          '<img src="/res/adm/pages/view-mode-22x22.png" alt="'.$lt{'vw'}.'" '.
                          'title="'.$lt{'vw'}.'" class="LC_icon"/> '.
                          '<span class="LC_menubuttons_inline_text">'.$lt{'vw'}.'</span></a>');
    }
    
    # icon for adding a new link
    $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
                      'onclick="newLink();" class="LC_menubuttons_link">'.
                      '<img src="/res/adm/pages/link-new-22x22.png" alt="'.$lt{'al'}.'" '.
                      'title="'.$lt{'al'}.'" class="LC_icon"/>'.
                      '<span class="LC_menubuttons_inline_text">'.$lt{'al'}.'</span></a>');

    # icon for adding a new folder
    $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
                      'onclick="newFolder();" class="LC_menubuttons_link">'.
                      '<img src="/res/adm/pages/folder-new-22x22.png" alt="'.$lt{'af'}.'" '.
                      'title="'.$lt{'af'}.'" class="LC_icon"/>'.
                      '<span class="LC_menubuttons_inline_text">'.$lt{'af'}.'</span></a>');

    # icon for moving entries
    $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
                      'onclick="setFormAction('."'move','move'".'); " class="LC_menubuttons_link">'.
                      '<img src="/res/adm/pages/move-22x22.png" alt="'.$lt{'mv'}.'" '.
                      'title="'.$lt{'mv'}.'" class="LC_icon" />'.
                      '<span class="LC_menubuttons_inline_text">'.$lt{'mv'}.'</span></a>');

    # icon for deleting entries
    $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
                      'onclick="setFormAction('."'delete','".$mode."'".'); " class="LC_menubuttons_link">'.
                      '<img src="/res/adm/pages/del.png" alt="'.$lt{'dl'}.'" '.
                      'title="'.$lt{'dl'}.'" class="LC_icon" />'.
                      '<span class="LC_menubuttons_inline_text">'.$lt{'dl'}.'</span></a>');

    # icon for saving changes
    $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
                      'onclick="setFormAction('."'','".$mode."'".'); " class="LC_menubuttons_link">'.
                      '<img src="/res/adm/pages/save-22x22.png" alt="'.$lt{'sv'}.'" '.
                      'title="'.$lt{'sv'}.'" class="LC_icon" />'.
                      '<span class="LC_menubuttons_inline_text">'.$lt{'sv'}.'</span></a>');

    # end funtionlist and generate subbox 
    $functions.= &Apache::lonhtmlcommon::end_funclist();
    my $subbox = &Apache::loncommon::head_subbox($functions);

    # start form 
    my $inner .= '<form name="list" action ="/adm/wishlist" method="post">'.
                 '<input type="hidden" id="action" name="action" value=""/>';
 
    # only display subbox in view- or edit-mode
    if ($mode ne 'move') {
        $inner .= $subbox;
    }

    # generate table-content depending on mode
    if ($mode eq 'edit') {
        &wishlistEdit(\@childrenRt);
        if ($wishlistHTMLedit ne '') {
            $inner .= &Apache::loncommon::start_data_table("LC_tableOfContent");
            $inner .= $wishlistHTMLedit;
            $inner .= &Apache::loncommon::end_data_table();
        }
        else {
            $inner .= '<span class="LC_info">'.&mt("Your wihlist ist currently empty.").'</span>';
        }
        $wishlistHTMLedit = '';
    }
    elsif ($mode eq 'view') {
        &wishlistView(\@childrenRt);
        if ($wishlistHTMLview ne '') {
            $inner .= '<table class="LC_data_table LC_tableOfContent">'.$wishlistHTMLview.'</table>';
        }
        else {
            $inner .= '<span class="LC_info">'.&mt("Your wihlist ist currently empty.").'</span>';
        }
        $wishlistHTMLview = '';
    }
    else {
        my $markStr = '';
        foreach my $m (@$marked) {
            $markStr .= $m.',';
        }
        if ($markStr) {
            $markStr = substr($markStr, 0, length($markStr)-1);
            $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. '.
                                                   'Now choose the new destination folder.').'</span><br/><br/>';
            &wishlistMove(\@childrenRt, $marked);
            $inner .= '<table class="LC_data_table LC_tableOfContent">'.$wishlistHTMLmove.'</table><br/><br/>';
            $inner .= '<input type="button" value="'.&mt('Move').'" onclick="setFormAction('."'','view'".'); selectDestinationFolder()"/>'.
                      '<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>'.
                               '<td>'.&mt('Top level').'</td><td></td></tr>';
        }
        else {
            $inner .= '<span class="LC_info">'.&mt("You haven't marked any entry to move.").'</span><br/>'.
                      '<input type="button" value="'.&mt('Back').'" onclick="go('."'/adm/wishlist'".')"/>';
        }
    }
    
    # end form 
    $inner .= '</form>';

    # end_page 
    my $endPage =  &Apache::loncommon::end_page();

    # put all page-elements togther
    my $page = $startPage.$breadcrumbs.$js.$inner.$endPage;

    return $page;
}


# Returns the HTML-Markup for the page, shown when a link was set
sub makePageSet {
    # start_page 
    my $startPage = &Apache::loncommon::start_page('Wishlist',undef,
                                                   {'only_body' => 1});
    
    # confirm success and offer link to wishlist
    my $message = &Apache::lonhtmlcommon::confirm_success(&mt('Link successfully set!'));
    $message = &Apache::loncommon::confirmwrapper($message);

    my $inner .= '<br>'.$message.'<br/><br/>'.
                 '<a href="javascript:;" onclick="opener.open('."'/adm/wishlist'".');window.close();">'.&mt('Go to wishlist').'</a>'.
                 '&nbsp;<a href="javascript:;" onclick="window.close();">'.&mt('Close this window').'</a>';

    # end_page 
    my $endPage =  &Apache::loncommon::end_page();

    # put all page-elements togther
    my $page = $startPage.$inner.$endPage;

    return $page;
}


# Returns the HTML-Markup for error-page
sub makeErrorPage {
    # breadcrumbs and start_page 
    &Apache::lonhtmlcommon::add_breadcrumb(
              { href => '/adm/wishlist',
                text => 'Wishlist'});
    my $startPage = &Apache::loncommon::start_page('Wishlist');
    
    my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs('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>');
    &Apache::lonhtmlcommon::clear_breadcrumbs();

    # error-message
    my $inner .= '<span class="LC_error">'.&mt('An error occurred! Please try again later.').'</span>';

    # end_page 
    my $endPage =  &Apache::loncommon::end_page();

    # put all page-elements togther
    my $page = $startPage.$breadcrumbs.$inner.$endPage;

    return $page;
}

# ----------------------------------------------------- Main Handler, package lonwishlist
sub handler {
    my ($r) = @_;
    &Apache::loncommon::content_type($r,'text/html');
    $r->send_http_header;

    if (&getWishlist() ne 'error') {
        # get wishlist entries from user-data db-file and build a tree out of these entries
        %TreeHash = &getWishlist();
        $root = &Tree::HashToTree();
        @childrenRt = $root->children();

        # greate a new entry
        if ($env{'form.title'}) {
           &newEntry($env{'form.title'}, $env{'form.path'}, $env{'form.note'});
        }

        # get unprocessed_cgi (i.e. marked entries, mode ...) 
        &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['action','mark','markedToMove','mode','newtitle','note']);

        # change the order of entries within a level, that means sorting the entries
        my $changeOrder = 0;
        if (defined $env{'form.sel'}) {
            my @sel = &Apache::loncommon::get_env_multiple('form.sel');
            my $indexNode;
            my $at;
            for (my $s=0; $s<($#sel+1); $s++) {
                if ($sel[$s] ne '') {
                    $indexNode = $s;
                    $at = $sel[$s]-1;
                }
            }
            if ($at ne '') {
                $changeOrder = 1;
                &sortEntries($indexNode,$at);
                &saveChanges();
            }
        }

        # get all marked (checkboxes) entries
        my @marked = ();
        if (defined $env{'form.mark'}) {
            @marked = &Apache::loncommon::get_env_multiple('form.mark');
        }

        # move entries from one folder to another
        if (defined $env{'form.markedToMove'}) {
           my $markedToMove = $env{'form.markedToMove'};
           my @ToMove = split(/\,/,$markedToMove);
           my $moveTo = $env{'form.mark'};
           if (defined $moveTo){ 
               &moveEntries(\@ToMove,$moveTo);
               &saveChanges();
           }
           $changeOrder = 1;
    
        }

        # delete entries
        if ($env{'form.action'} eq 'delete') {
            &deleteEntries(\@marked);
        }
   

        # get all titles and notes and save them
        # only save, if user wants to save changes
        # do not save, when current action is 'delete' or 'sort' or 'move' 
        my @newTitles = ();
        my @newNotes = ();
        if ((defined $env{'form.newtitle'} || defined $env{'form.newnote'}) && ($env{'form.action'} ne 'noSave') && ($env{'form.action'} ne 'delete') && !$changeOrder) {
            @newTitles = &Apache::loncommon::get_env_multiple('form.newtitle');
            @newNotes = &Apache::loncommon::get_env_multiple('form.newnote');
            my $node = 0;
            foreach my $t (@newTitles) {
               &setNewTitle($node, $t);
               $node++;
            }
            $node = 0;
            foreach my $n (@newNotes) {
               &setNewNote($node, $n);
               $node++;
            }
            &saveChanges();
        }

        # Create HTML-markup
        my $page;
        if ($env{'form.mode'} eq 'edit') {
            $page = &makePage("edit");
        }
        elsif ($env{'form.mode'} eq 'move') {
            $page = &makePage("move", \@marked);
        }
        elsif ($env{'form.mode'} eq 'set') {
            $page = &makePageSet();
        }
        else {
            $page = &makePage("view");
        }
        @marked = ();
        $r->print($page);
    }
    # An error occured, print an error-page
    else {
        my $errorPage = &makeErrorPage();
        $r->print($errorPage);
    }
    return OK;
}

# ----------------------------------------------------- package Tree
# Extend CPAN-Module Tree by function like 'moveNode' or 'deleteNode'
package Tree;

# returns the node with a given index from a list of nodes
sub getNodeByIndex {
    my $index = shift;
    my $nodes = shift;
    my $found;
    
    for my $n (@$nodes) {
        my $curIndex = $n->value()->nindex();
        if ($n->value()->nindex() == $index) {
            $found = $n;
        }
    }
    return $found;
}

# moves a given node to a new parent or change the position from a node
# within its siblings (sorting)
sub moveNode {
    my $node = shift;
    my $at = shift;
    my $newParent = shift;


    if (!$newParent) {
        $newParent = $node->parent();
    }

    $node->parent()->remove_child($node);

    if (defined $at) {
        $newParent->add_child({at => $at},$node);
    }
    else {
        $newParent->add_child($node);
    }
    
    # updating root's children
    @childrenRt = $root->children();
}

# removes a given node
sub removeNode() {
    my $node = shift;
    my @children = $node->children();

    if ($#children >= 0) {
        foreach my $c (@children) {
            &removeNode($c);
        }
    }
    $node->parent()->remove_child($node);

    # updating root's children
    @childrenRt = $root->children();
}


# set an index for every node in the tree, beginning with 0
my $count = 0;
sub TreeIndex {
    my $children = shift;

    foreach my $n (@$children) {
        my @children = $n->children();
        $n->value()->nindex($count);$count++;

        if ($#children>=0) {
            &TreeIndex(\@children);
        }
    }
}

# reset index counter
sub setCountZero {
    $count = 0;
}


# convert the tree to a hash
# each node is one hash-entry
# keys are the indices, values are all other attributes
# (containing tile, path, note, date and indices for all direct children)
# except for root: the key is root and values are
# just the indices of root's children
sub RootToHash {
    my $childrenRt = shift;
    my @indexarr = ();

    foreach my $c (@$childrenRt) {
       push (@indexarr, $c->value()->nindex());
    }
    $TreeToHash{'root'} = [@indexarr];
}

sub TreeToHash {
    my $childrenRt = shift;

    foreach my $n (@$childrenRt) {
        my @arrtmp = ();
        $arrtmp[0] = $n->value()->title();
        $arrtmp[1] = $n->value()->path();
        $arrtmp[2] = $n->value()->note();
        $arrtmp[3] = $n->value()->date();
        my @childrenRt = $n->children();
        my $co = 4;
        foreach my $c (@childrenRt) {
            my $i = $c->value()->nindex();
            $arrtmp[$co] = $i;
            $co++;
        }
        $TreeToHash{$n->value()->nindex} = [ @arrtmp]; 
        if ($#childrenRt>=0) {
            &TreeToHash(\@childrenRt);
        }
    }
}


# convert the hash to a tree
# build a tree-object for each entry in the hash
# afterwards call &buildTree to connect the tree-objects
sub HashToTree {
    my @TreeNodes = ();
    my $root;

    foreach my $key (keys %TreeHash) {
        if ($key eq 'root') {
            $root = Tree->new("root");
        }
        else {
        my @attributes = @{ $TreeHash{$key} };
        my $tmpNode;
            $tmpNode = Tree->new(Entry->new(title=>$attributes[0],
                                            path=>$attributes[1],
                                            note=>$attributes[2],
                                            date=>$attributes[3],
                                            nindex=>$key));
        push(@TreeNodes, $tmpNode);
        # shift all attributes except for
        # the indices representing the children of a node
        shift(@attributes);
        shift(@attributes);
        shift(@attributes);
        shift(@attributes);
        $TreeHash{$key} = [ @attributes ];
        }
    }
    # if there are nodes, build up the tree-structure
    if (defined $TreeHash{'root'}) {
        my @childrenRtIn = @{ $TreeHash{'root'} };
        &buildTree(\$root, \@childrenRtIn,\@TreeNodes,\%TreeHash);
    }
    return $root; 
}


# join the nodes to a tree
sub buildTree {
    my ($node, $childrenIn, $TreeNodes, $TreeHash) = @_;
    bless($node, 'Tree');
    foreach my $c (@$childrenIn) {
        my $tmpNode =  &getNodeByIndex($c,$TreeNodes);
        $$node->add_child($tmpNode);
        my @childrenIn = @{ $$TreeHash{$tmpNode->value()->nindex()} };
        &buildTree(\$tmpNode,\@childrenIn,$TreeNodes,$TreeHash);
    }

}


# ----------------------------------------------------- package Entry
# package that defines the entrys a wishlist could have
# i.e. folders and links
package Entry;

# constructor
sub new {
    my $invocant = shift;
    my $class = ref($invocant) || $invocant;
    my $self = { @_ }; #set attributes
    bless($self, $class);
    return $self;    
}

# getter and setter
sub title {
    my $self = shift;
    if ( @_ ) { $self->{title} = shift}
    return $self->{title};
}

sub date {
    my $self = shift;
    if ( @_ ) { $self->{date} = shift}
    return $self->{date};
}

sub note {
    my $self = shift;
    if ( @_ ) { $self->{note} = shift}
    return $self->{note};
}

sub path {
    my $self = shift;
    if ( @_ ) { $self->{path} = shift}
    return $self->{path};
}

sub nindex {
    my $self = shift;
    if ( @_ ) { $self->{nindex} = shift}
    return $self->{nindex};
}


1;
__END__

--wenzelju1281450646--