File:  [LON-CAPA] / loncom / interface / lonwishlist.pm
Revision 1.2: download - view: text, annotated - select for diffs
Mon Aug 16 08:58:39 2010 UTC (13 years, 8 months ago) by wenzelju
Branches: MAIN
CVS tags: HEAD
londocs, lonmenu, lonwishlist:
- Added import-functionality for wishlist.

lonwishlist:
- Replaced getkeys() and get() by dump().
- Preview of links in wishlist now in Popup.
- Some little style changes (inserted <p> etc.).

# The LearningOnline Network with CAPA
# Routines to control the wishlist
#
# $Id: lonwishlist.pm,v 1.2 2010/08/16 08:58:39 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 LONCAPA;
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 %wishlist = &Apache::lonnet::dump('wishlist');
    foreach my $i ( keys %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' => ''});
           %wishlist = &Apache::lonnet::dump('wishlist');
       }
       elsif ($i =~ /^(con_lost|error|no_such_host)/i) {
           &Apache::lonnet::logthis('ERROR while attempting to get wishlist: '.$i);
           return 'error';
       }
    }

    # 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) {
        &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 = ();

    # get all nodes that should be moved
    &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">('.&mt('Top level').')</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">('.&mt('Top level').')</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">('.&mt('Top level').')</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;
    }

    function preview(url) {
       var newWin = window.open(url+'?inhibitmenu=yes','preview','width=560,height=350,scrollbars=yes');
       newWin.focus();
    }

    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();
    }

    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
   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;">'.
                                 '<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>';
        }

        # 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 {
            # higlight the title, if the link was selected to be moved
            my $highlight = '';
            if ($isIn) {
               $highlight = 'style="color:red;"';
            }
            # link-image and title
            $wishlistHTMLmove .= '<td></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
        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;
        }
    }
}



# 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
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 eq 'view' || $mode eq 'edit') {
        $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 wishlist 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 wishlist 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 .= '<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></p>';
            &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 .= '<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'".')"/>';
        }
    }
    
    # end form 
    $inner .= '</form>';

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

    # put all page-elements together
    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 together
    my $page = $startPage.$inner.$endPage;

    return $page;
}


# Returns the HTML-Markup for the page, shown when links should be imported into a course
sub makePageImport {
    # start_page 
    my $startPage = &Apache::loncommon::start_page('Wishlist',undef,
                                                   {'only_body' => 1});
    
    # get javascript-code for wishlist-interactions
    my $js = &JSforWishlist();

    my $inner = '<h1>'.&mt('Import Resources from Wishlist').'</h1>';
    $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>';

    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
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 together
    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 'import') {
            $page = &makePageImport();
        }
        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'} && $TreeHash{'root'} ne '') {
        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__

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