File:  [LON-CAPA] / loncom / interface / lonwishlist.pm
Revision 1.9: download - view: text, annotated - select for diffs
Thu Jan 27 14:38:44 2011 UTC (13 years, 5 months ago) by wenzelju
Branches: MAIN
CVS tags: HEAD
- Split lonwishlist into two modules, one for displaying (handler) and one utility-module, to avoid problems with Apache::constants when calling methods from lonwishlist (e.g. need to call lonwishlist-method in lonmenu and lonmenu is used in printout.pl -> outside of mod_perl).
- Moving entries in wishlist: corrected warning shown when no entry is selected
- Some  code optimizations
- Still some things do not work perfect -> in progress

    1: # The LearningOnline Network with CAPA
    2: # Utility-routines for wishlist
    3: #
    4: # $Id: lonwishlist.pm,v 1.9 2011/01/27 14:38:44 wenzelju Exp $
    5: #
    6: # Copyright Michigan State University Board of Trustees
    7: #
    8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
    9: #
   10: # LON-CAPA is free software; you can redistribute it and/or modify
   11: # it under the terms of the GNU General Public License as published by
   12: # the Free Software Foundation; either version 2 of the License, or
   13: # (at your option) any later version.
   14: #
   15: # LON-CAPA is distributed in the hope that it will be useful,
   16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
   17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   18: # GNU General Public License for more details.
   19: #
   20: # You should have received a copy of the GNU General Public License
   21: # along with LON-CAPA; if not, write to the Free Software
   22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   23: #
   24: # /home/httpd/html/adm/gpl.txt
   25: #
   26: # http://www.lon-capa.org/
   27: #
   28: 
   29: =pod
   30: 
   31: =head1 NAME
   32: 
   33: Apache::lonwishlist - Wishlist-Module
   34:   
   35: =head1 SYNOPSIS
   36: 
   37: The wishlist offers a possibility to store links to resources from the resource-pool and external websites in a hierarchical list.
   38: It is only available for user with access to the resource-pool. The list can be structured by folders.
   39: 
   40: The wishlist-module uses the CPAN-module "Tree" for easily handling the directory-structure of the wishlist. Each node in the tree has an index to be referenced by.
   41: 
   42: =back
   43: 
   44: =cut
   45: 
   46: package Apache::lonwishlist;
   47: 
   48: use strict;
   49: use Apache::lonnet;
   50: use Apache::loncommon();
   51: use Apache::lonhtmlcommon;
   52: use Apache::lonlocal;
   53: use LONCAPA;
   54: use Tree;
   55: 
   56: 
   57: # Global variables
   58: my $root;
   59: my @childrenRt;
   60: my %TreeHash;
   61: my %TreeToHash;
   62: my @allFolders;
   63: my @allNodes;
   64: my $indentConst = 20;
   65: my $foldersOption;
   66: 
   67: =pod
   68: 
   69: =head2 Routines for getting and putting the wishlist data from and accordingly to users data.
   70: 
   71: =over 4
   72: 
   73: =item * &getWishlist()
   74: 
   75:      Get the wishlist-data via lonnet::getkeys() and lonnet::get() and returns the got data in a hash.
   76: 
   77: 
   78: =item * &putWishlist(wishlist)
   79: 
   80:      Parameter is a reference to a hash. Puts the wishlist-data contained in the given hash via lonnet::put() to user-data.
   81: 
   82: 
   83: =item * &deleteWishlist()
   84: 
   85:      Deletes all entries from the user-data for wishlist. Do this before putting in new data.
   86: 
   87: 
   88: =back
   89: 
   90: =cut
   91: 
   92: 
   93: # Read wishlist from user-data
   94: sub getWishlist {
   95:     my @keys = &Apache::lonnet::getkeys('wishlist');
   96:     my %wishlist = &Apache::lonnet::get('wishlist',\@keys);
   97:     foreach my $i ( keys %wishlist) {
   98:         #File not found. This appears at the first time using the wishlist
   99:         #Create file and put 'root' into it
  100:        if ($i =~m/^error:No such file/) {
  101:            &Apache::lonnet::logthis($i.'! Create file by putting in the "root" of the directory tree.');
  102:            &Apache::lonnet::put('wishlist', {'root' => ''});
  103:            my $options = '<option value="" selected="selected">('.&mt('Top level').')</option>';
  104:            &Apache::lonnet::put('wishlist', {'folders' => $options});
  105:            @keys = &Apache::lonnet::getkeys('wishlist');
  106:            %wishlist = &Apache::lonnet::get('wishlist',\@keys);
  107:        }
  108:        elsif ($i =~ /^(con_lost|error|no_such_host)/i) {
  109:            &Apache::lonnet::logthis('ERROR while attempting to get wishlist: '.$i);
  110:            return 'error';
  111:        }
  112:     }
  113: 
  114:     # if we got no keys in hash returned by get(), return error.
  115:     # wishlist will not be loaded, instead the user will be asked to try again later
  116:     if ((keys %wishlist) == 0) {
  117:         &Apache::lonnet::logthis('ERROR while attempting to get wishlist: no keys retrieved!');
  118:         return 'error';
  119:     }
  120:     
  121:     return %wishlist;
  122: }
  123: 
  124: 
  125: # Write wishlist to user-data
  126: sub putWishlist {
  127:     my $wishlist = shift;
  128:     &Apache::lonnet::put('wishlist',$wishlist);
  129: }
  130: 
  131: 
  132: # Removes all existing entrys for wishlist in user-data
  133: sub deleteWishlist {
  134:     my @wishlistkeys = &Apache::lonnet::getkeys('wishlist');
  135:     my %wishlist = &Apache::lonnet::del('wishlist',\@wishlistkeys);
  136: }
  137: 
  138: 
  139: =pod
  140: 
  141: =head2 Routines for changing the directory struture of the wishlist.
  142: 
  143: =over 4
  144: 
  145: =item * &newEntry(title, path, note)
  146: 
  147:      Creates a new entry in the wishlist containing the given informations. Additionally saves the date of creation in the entry.  
  148: 
  149: 
  150: =item * &deleteEntries(marked)
  151: 
  152:      Parameter is a reference to an array containing the indices of all nodes that should be removed from the tree. 
  153: 
  154: 
  155: =item * &sortEntries(indexNode, at)
  156: 
  157:      Changes the position of a node given by indexNode within its siblings. New position is given by at.
  158: 
  159: 
  160: =item * &moveEntries(indexNodesToMove, indexParent)
  161: 
  162:      Parameter is a reference to an array containing the indices of all nodes that should be moved. indexParent specifies the node that will become the new Parent for these nodes. 
  163: 
  164: 
  165: =item * &setNewTitle(nodeindex, newTitle)
  166: 
  167:      Sets the title for the node given by nodeindex to newTitle.
  168: 
  169: 
  170: =item * &setNewPath(nodeindex, newPath)
  171: 
  172:      Sets the path for the node given by nodeindex to newPath.
  173: 
  174: 
  175: =item * &setNewNote(nodeindex, newNote)
  176: 
  177:      Sets the note for the node given by nodeindex to newNote.     
  178: 
  179: 
  180: =item * &saveChanges()
  181: 
  182:      Prepares the wishlist-hash to save it via &putWishlist(wishlist).   
  183: 
  184: 
  185: =back
  186: 
  187: =cut
  188: 
  189: 
  190: # Create a new entry
  191: sub newEntry() {
  192:     my ($rootgiven, $title, $path, $note) = @_;
  193: 
  194:     $root = $rootgiven;
  195:     @childrenRt = $root->children();
  196: 
  197:     my $date = gmtime();
  198:     # Create Entry-Object
  199:     my $entry = Entry->new(title => $title, path => $path, note => $note, date => $date);
  200:     # Create Tree-Object, this correspones a node in the wishlist-tree
  201:     my $tree = Tree->new($entry);
  202:     # Add this node to wishlist-tree
  203:     my $folderIndex = $env{'form.folders'};
  204:     if ($folderIndex ne '') {
  205:         @allFolders = ();
  206:         &getFoldersToArray(\@childrenRt);
  207:         my $folderToInsertOn = &Apache::Tree::getNodeByIndex($folderIndex,\@allFolders);
  208:         $folderToInsertOn->add_child($tree);
  209:     }
  210:     else {
  211:         $root->add_child($tree);
  212:     }
  213:     return &saveChanges();
  214: }
  215: 
  216: 
  217: # Delete entries
  218: sub deleteEntries {
  219:     my $rootgiven = shift;
  220:     my $marked = shift;
  221: 
  222:     $root = $rootgiven;
  223:     @childrenRt = $root->children();
  224: 
  225:     &getNodesToArray(\@childrenRt);
  226:     foreach my $m (@$marked) {
  227:         my $found = &Apache::Tree::getNodeByIndex($m, \@allNodes);
  228:         # be sure, that entry exists (may have been deleted before, e.g. in an other browsertab)
  229:         if (defined $found) {
  230:             &Apache::Tree::removeNode($found);
  231:         }
  232:     }
  233:     @allNodes = ();
  234:     return &saveChanges();
  235: }
  236: 
  237: 
  238: # Sort entries
  239: sub sortEntries {
  240:     my $rootgiven = shift;
  241:     my $indexNode = shift;
  242:     my $at = shift;
  243: 
  244:     $root = $rootgiven;
  245:     @childrenRt = $root->children();
  246:     
  247:     &getNodesToArray(\@childrenRt);
  248:     my $foundNode = &Apache::Tree::getNodeByIndex($indexNode, \@allNodes);
  249: 
  250:     &Apache::Tree::moveNode($foundNode,$at,undef);
  251:     @allNodes = ();
  252:     return &saveChanges();
  253: }
  254: 
  255: 
  256: # Move entries
  257: sub moveEntries {
  258:     my $rootgiven = shift;
  259:     my $indexNodesToMove = shift;
  260:     my $indexParent = shift;
  261:     my @nodesToMove = ();
  262: 
  263:     $root = $rootgiven;
  264:     @childrenRt = $root->children();
  265: 
  266:     # get all nodes that should be moved
  267:     &getNodesToArray(\@childrenRt);
  268:     foreach my $index (@$indexNodesToMove) {
  269:         my $foundNode = &Apache::Tree::getNodeByIndex($index, \@allNodes);
  270:         push(@nodesToMove, $foundNode);
  271:     }
  272: 
  273:     foreach my $node (@nodesToMove) {
  274:         my $foundParent;
  275:         my $parentIsIn = 0;
  276:         foreach my $n (@nodesToMove) {
  277:             if ($node->parent()->value() ne "root") {
  278:                if ($node->parent()->value()->nindex() == $n->value()->nindex()) {
  279:                     $parentIsIn = 1;
  280:                 }
  281:             }
  282:         }
  283:         if (!$parentIsIn) {
  284:             if ($indexParent ne "root") {
  285:                 $foundParent = &Apache::Tree::getNodeByIndex($indexParent, \@allNodes);
  286:                 &Apache::Tree::moveNode($node,undef,$foundParent);
  287:             }
  288:             else {
  289:                 &Apache::Tree::moveNode($node,undef,$root);
  290:             }
  291:         }
  292:     }
  293:     @allNodes = ();
  294:     return &saveChanges();
  295: }
  296: 
  297: 
  298: # Set a new title for an entry
  299: sub setNewTitle {
  300:     my ($rootgiven, $nodeindex, $newTitle) = @_;
  301: 
  302:     $root = $rootgiven;
  303:     @childrenRt = $root->children();
  304: 
  305:     &getNodesToArray(\@childrenRt);
  306:     my $found = &Apache::Tree::getNodeByIndex($nodeindex, \@allNodes);
  307:     $found->value()->title($newTitle); 
  308:     @allNodes = ();
  309:     return &saveChanges();
  310: }
  311: 
  312: 
  313: # Set a new path for an entry
  314: sub setNewPath {
  315:     my ($rootgiven, $nodeindex, $newPath) = @_;
  316: 
  317:     $root = $rootgiven;
  318:     @childrenRt = $root->children();
  319: 
  320:     &getNodesToArray(\@childrenRt);
  321:     my $found = &Apache::Tree::getNodeByIndex($nodeindex, \@allNodes);
  322:     if ($found->value()->path()) {
  323:         $found->value()->path($newPath); 
  324:         return &saveChanges();
  325:     }
  326:     @allNodes = ();
  327:     return 0;
  328: }
  329: 
  330: 
  331: # Set a new note for an entry
  332: sub setNewNote {
  333:     my ($rootgiven, $nodeindex, $newNote) = @_;
  334: 
  335:     $root = $rootgiven;
  336:     @childrenRt = $root->children();
  337: 
  338:     &getNodesToArray(\@childrenRt);
  339:     my $found = &Apache::Tree::getNodeByIndex($nodeindex, \@allNodes);
  340:     $found->value()->note($newNote); 
  341:     @allNodes = ();
  342:     return &saveChanges();
  343: }
  344: 
  345: 
  346: # Save all changes
  347: sub saveChanges {
  348:     @childrenRt = $root->children();
  349:     &Apache::Tree::TreeIndex(\@childrenRt);
  350:     &Apache::Tree::setCountZero();
  351:     &Apache::Tree::RootToHash(\@childrenRt);
  352:     &Apache::Tree::TreeToHash(\@childrenRt);
  353:     &deleteWishlist();
  354:     &putWishlist(\%TreeToHash);
  355:     return $root;
  356: 
  357: }
  358: 
  359: 
  360: =pod
  361: 
  362: =head2 Routines for handling the directory structure
  363: 
  364: =over 4
  365: 
  366: =item * &getFoldersForOption(nodes)
  367: 
  368:      Return the titles for all exiting folders in an option-tag, used to offer the users a possibility to create a new link or folder in an existing folder.
  369:      Recursive call starting with all children of the root of the tree (parameter nodes is reference to an array containing the nodes of the current level). 
  370: 
  371: 
  372: =item * &getFoldersToArray(children)
  373: 
  374:      Puts all nodes that represent folders in the wishlist into an array. 
  375:      Recursive call starting with all children of the root of the tree (parameter nodes is reference to an array containing the nodes of the current level).     
  376: 
  377: 
  378: =item * &getNodesToArray(children)
  379: 
  380:      Puts all existing nodes into an array (apart from the root node, because this one does not represent an entry in the wishlist).
  381:      Recursive call starting with all children of the root of the tree (parameter nodes is reference to an array containing the nodes of the current level).     
  382:  
  383: 
  384: =back
  385: 
  386: =cut
  387: 
  388: 
  389: # Return the names for all exiting folders in option-tags, so
  390: # a new link or a new folder can be created in an existing folder
  391: my $indent = 0;
  392: sub getFoldersForOption {
  393:     my $nodes = shift;
  394: 
  395:     foreach my $n (@$nodes) {
  396:         if ($n->value()->path() eq '') {
  397:             $foldersOption .= '<option value="'.$n->value()->nindex().'" style="margin-left:'.$indent.'px">'.
  398:                                    $n->value()->title().
  399:                                '</option>';
  400: 
  401:         my @children = $n->children();
  402:         if ($#children >=0) {
  403:             $indent += 10;
  404:             &getFoldersForOption(\@children);
  405:             $indent -= 10;
  406:             }
  407:         }
  408:     }
  409: }
  410: 
  411: 
  412: # Put all folder-nodes to an array
  413: sub getFoldersToArray {
  414:     my $children = shift;
  415:     foreach my $c (@$children) {
  416:         if ($c->value()->path() eq '') {
  417:             push(@allFolders,$c);
  418:         }
  419:         my @newchildren = $c->children();
  420:         if ($#newchildren >= 0) {
  421:             &getFoldersToArray(\@newchildren);
  422:         }
  423:     }
  424: }
  425: 
  426: 
  427: # Put all nodes to an array
  428: sub getNodesToArray {
  429:     my $children = shift;
  430:     foreach my $c (@$children) {
  431:         push(@allNodes,$c);
  432:         my @newchildren = $c->children();
  433:         if ($#newchildren >= 0) {
  434:             &getNodesToArray(\@newchildren);
  435:         }
  436:     }
  437: }
  438: 
  439: 
  440: =pod
  441: 
  442: =head2 Routines for the user-interface of the wishlist
  443: 
  444: =over 4
  445: 
  446: =item * &JSforWishlist()
  447: 
  448:      Returns JavaScript-functions needed for wishlist actions like open and close folders.
  449: 
  450: 
  451: =item * &wishlistView(nodes)
  452: 
  453:      Returns the table-HTML-markup for the wishlist in mode "view".
  454:      Recursive call starting with all children of the root of the tree (parameter nodes is reference to an array containing the nodes of the current level).     
  455: 
  456: 
  457: =item * &wishlistEdit(nodes)
  458: 
  459:      Returns the table-HTML-markup for the wishlist in mode "edit".
  460:      Recursive call starting with all children of the root of the tree (parameter nodes is reference to an array containing the nodes of the current level).     
  461: 
  462: 
  463: =item * &wishlistMove(nodes, marked)
  464: 
  465:      Returns the table-HTML-markup for the wishlist in mode "move". Highlights all entry "selected to move" contained in marked (reference to array).
  466:      Recursive call starting with all children of the root of the tree (parameter nodes is reference to an array containing the nodes of the current level).     
  467: 
  468: 
  469: =item * &wishlistImport(nodes)
  470: 
  471:      Returns the table-HTML-markup for the wishlist in mode "import".
  472:      Recursive call starting with all children of the root of the tree (parameter nodes is reference to an array containing the nodes of the current level).     
  473:  
  474: 
  475: =item * &makePage(mode, marked)
  476: 
  477:      Returns the HTML-markup for the whole wishlist depending on mode. If mode is "move" we need the marked entries to be highlighted a "selected to move". 
  478:      Calls &wishlistView(nodes), &wishlistEdit(nodes) or &wishlistMove(nodes, marked).
  479:  
  480: 
  481: =item * &makePageSet()
  482: 
  483:      Returns the HTML-Markup for the page shown when a link was set by using the icon when viewing a resource.
  484: 
  485: 
  486: =item * &makePageImport()
  487: 
  488:      Returns the HTML-Markup for the page shown when links should be imported into courses.
  489:  
  490: 
  491: =item * &makeErrorPage ()
  492: 
  493:      Returns the HTML-Markup for an error-page shown if the wishlist could not be loaded.
  494:  
  495: 
  496: =back
  497: 
  498: =cut
  499: 
  500: 
  501: # Return a script-tag containing Javascript-function
  502: # needed for wishlist actions like 'new link' ect.
  503: sub JSforWishlist {
  504:     my $startPagePopup = &Apache::loncommon::start_page('Wishlist',undef,
  505:                                                             {'only_body' => 1,
  506:                                                              'js_ready'  => 1,
  507:                                                              'bgcolor'   => '#FFFFFF',});
  508:     my $endPagePopup = &Apache::loncommon::end_page({'js_ready' => 1});
  509: 
  510:     @allFolders = ();
  511:     &getFoldersToArray(\@childrenRt);
  512:     &getFoldersForOption(\@childrenRt);
  513: 
  514:     # texthash
  515:     my %lt = &Apache::lonlocal::texthash(
  516:                  'nl' => 'New Link',
  517:                  'nf' => 'New Folder',
  518:                  'lt' => 'Link Title',
  519:                  'ft' => 'Folder Title',
  520:                  'pa' => 'Path',
  521:                  'nt' => 'Note',
  522:                  'si' => 'Save in',
  523:                  'cl' => 'Cancel');
  524: 
  525: 
  526:     my $inPageNewLink = '<h1>'.$lt{'nl'}.'</h1>'.
  527:                         '<form method="post" name="newlink" action="/adm/wishlist" target="wishlist" '.
  528:                         'onsubmit="return newlinksubmit();" >'.
  529:                         &Apache::lonhtmlcommon::start_pick_box().
  530:                         &Apache::lonhtmlcommon::row_title($lt{'lt'}).
  531:                         '<input type="text" name="title" size="45" value="" />'.
  532:                         &Apache::lonhtmlcommon::row_closure().
  533:                         &Apache::lonhtmlcommon::row_title($lt{'pa'}).
  534:                         '<input type="text" name="path" size="45" value="" />'.
  535:                         &Apache::lonhtmlcommon::row_closure().
  536:                         &Apache::lonhtmlcommon::row_title($lt{'nt'}).
  537:                         '<textarea name="note" rows="3" cols="35" style="width:100%"></textarea>'.
  538:                         &Apache::lonhtmlcommon::row_closure(1).
  539:                         &Apache::lonhtmlcommon::end_pick_box().
  540:                         '<br/><br/>'.
  541:                         '<input type="submit" value="'.$lt{'si'}.'" />'.
  542:                         '<select name="folders">'.
  543:                         '<option value="" selected="selected">('.&mt('Top level').')</option>'.
  544:                         $foldersOption.
  545:                         '</select>'.
  546:                         '<input type="button" value="'.$lt{'cl'}.'" onclick="javascript:window.close();" />'.
  547:                         '</form>';
  548:     
  549:     my $inPageNewFolder = '<h1>'.$lt{'nf'}.'</h1>'.
  550:                           '<form method="post" name="newfolder" action="/adm/wishlist" target="wishlist" '.
  551:                           'onsubmit="return newfoldersubmit();" >'.
  552:                           &Apache::lonhtmlcommon::start_pick_box().
  553:                           &Apache::lonhtmlcommon::row_title($lt{'ft'}).
  554:                           '<input type="text" name="title" size="45" value="" /><br />'.
  555:                           &Apache::lonhtmlcommon::row_closure().
  556:                           &Apache::lonhtmlcommon::row_title($lt{'nt'}).
  557:                           '<textarea name="note" rows="3" cols="35" style="width:100%"></textarea><br />'.
  558:                           &Apache::lonhtmlcommon::row_closure(1).
  559:                           &Apache::lonhtmlcommon::end_pick_box().
  560:                           '<br/><br/>'.
  561:                           '<input type="submit" value="'.$lt{'si'}.'" />'.
  562:                           '<select name="folders">'.
  563:                           '<option value="" selected="selected">('.&mt('Top level').')</option>'.
  564:                           $foldersOption.
  565:                           '</select>'.
  566:                           '<input type="button" value="'.$lt{'cl'}.'" onclick="javascript:window.close();" />'.
  567:                           '</form>';
  568: 
  569:     # Remove all \n for inserting on javascript document.write
  570:     $inPageNewLink =~ s/\n//g;
  571:     $inPageNewFolder =~ s/\n//g;
  572: 
  573:     # it is checked, wether a path links to a LON-CAPA-resource or an external website. links to course-contents are not allowed
  574:     # because they probably will return a kind of 'no access' (unless the user is already in the course, the path links to).
  575:     # also importing these kind of links into a course does not make much sense.
  576:     # to find out if a path (not starting with /res/...) links to course-contents, the same filter as in lonwrapper is used,
  577:     # that means that it is checked wether a path contains .problem, .quiz, .exam etc.
  578:     # this is good for most cases but crashes as soon as a real external website contains one of this pattern in its URL.
  579:     # so maybe there's a better way to find out wether a given URL belongs to a LON-CAPA-server or not ...?
  580:     my $warningLinkNotAllowed1 = &mt('You can only insert links to LON-CAPA resources from the resource-pool '.
  581:                                     'or to external websites. Paths to LON-CAPA resources must be of the form /res/dom/usr... . '.
  582:                                     'Paths to external websites must contain the network protocol (e.g. http://...).');
  583:     my $warningLinkNotAllowed2 = &mt('The following link is not allowed: ');
  584:     my $warningLink = &mt('You must insert a title and a path!');
  585:     my $warningFolder = &mt('You must insert a title!');
  586:     my $warningDelete = &mt('Are you sure you want to delete the selected entries? Deleting a folder also deletes all entries within this folder!');
  587:     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.');
  588:     my $warningMoveS = &mt('You must select at minimum one entry to move!');
  589:     my $warningMoveD = &mt('You must select a destination folder!');
  590:     $foldersOption = '';
  591: 
  592:     my $js = &Apache::lonhtmlcommon::scripttag(<<JAVASCRIPT);
  593:     function newLink() {
  594:         newlinkWin=window.open('','newlinkWin','width=580,height=320,scrollbars=yes');
  595:         newlinkWin.document.write('$startPagePopup' 
  596:                               +'<script type="text\/javascript">'
  597:                               +'function newlinksubmit(){'
  598:                               +'var path = document.getElementsByName("path")[0].value;'
  599:                               +'var title = document.getElementsByName("title")[0].value;'
  600:                               +'if (!path || !title) {'
  601:                               +'alert("$warningLink");'
  602:                               +'return false;}'
  603:                               +'var linkOK = (path.match(/^http:(\\\\/\\\\/)/) || path.match(/^https:(\\\\/\\\\/)/))'
  604:                               +'&& !(path.match(/\\.problem/) || path.match(/\\.exam/)'
  605:                               +'|| path.match(/\\.quiz/) || path.match(/\\.assess/)'
  606:                               +'|| path.match(/\\.survey/) || path.match(/\\.form/)'
  607:                               +'|| path.match(/\\.library/) || path.match(/\\.page/)'
  608:                               +'|| path.match(/\\.sequence/));'
  609:                               +'if (!path.match(/^(\\\\/res\\\\/)/) && !linkOK) {'
  610:                               +'alert("$warningLinkNotAllowed1");'
  611:                               +'return false;}'
  612:                               +'else {'
  613:                               +'window.close();'
  614:                               +'return true;}}'
  615:                               +'<\/scr'+'ipt>'
  616:                               +'$inPageNewLink'
  617:                               +'$endPagePopup');
  618:         newlinkWin.document.close();
  619:     }
  620: 
  621:     function newFolder() {
  622:         newfolderWin=window.open('','newfolderWin','width=580,height=270, scrollbars=yes');
  623:         newfolderWin.document.write('$startPagePopup' 
  624:                               +'<script type="text\/javascript">'
  625:                               +'function newfoldersubmit(){'
  626:                               +'var title = document.getElementsByName("title")[0].value;'
  627:                               +'if (!title) {'
  628:                               +'alert("$warningFolder");'
  629:                               +'return false;}'
  630:                               +'else {'
  631:                               +'window.close();'
  632:                               +'return true;}}'
  633:                               +'<\/scr'+'ipt>'
  634:                               +'$inPageNewFolder'
  635:                               +'$endPagePopup');
  636:         newfolderWin.document.close();
  637:     }
  638: 
  639:     function setFormAction(action,mode) {
  640:         var r = true;
  641:         setAction('');
  642:         if (action == 'delete') {
  643:             r = confirm("$warningDelete");
  644:             setAction('delete');
  645:         }
  646:         else if (action == 'save') {
  647:             var d = getDifferences();
  648:             if (d) {
  649:                 if (!confirm('$warningSave')) {
  650:                     setAction('noSave');
  651:                     r = true;
  652:                 }
  653:                 else {
  654:                     r = linksOK();
  655:                 }
  656:             }
  657:         }
  658:         else if (action == 'saveOK') {
  659:             r = linksOK();
  660:         }
  661:         else if (action == 'move') {
  662:             r = selectDestinationFolder(mode);
  663:         }
  664:         document.getElementsByName('list')[0].setAttribute("action", "/adm/wishlist?mode="+mode); 
  665:         if (r) {
  666:             document.getElementsByName('list')[0].submit(); 
  667:         }
  668:     }
  669: 
  670:     function setAction(action) {
  671:         document.getElementById('action').value = action; 
  672:     }
  673: 
  674:     function getDifferences() {
  675:         var newtitles = document.getElementsByName('newtitle');
  676:         var i = 0;
  677:         for (i=0;i<newtitles.length;i++) {
  678:             var newt = newtitles[i].value;
  679:             var oldt = newtitles[i].alt;
  680:             if (newt != oldt) {
  681:                 return true;
  682:             }
  683:         }
  684:         var newpath = document.getElementsByName('newpath');
  685:         var i = 0;
  686:         for (i=0;i<newpath.length;i++) {
  687:             var newp = newpath[i].value;
  688:             var oldp = newpath[i].alt;
  689:             if (newp != oldp) {
  690:                 return true;
  691:             }
  692:         }
  693:         var newnote = document.getElementsByName('newnote');
  694:         var i = 0;
  695:         for (i=0;i<newnote.length;i++) {
  696:             var newn = newnote[i].value;
  697:             var oldn = newnote[i].innerHTML;
  698:             if (newn != oldn) {
  699:                 return true;
  700:             }
  701:         }
  702:         return false;
  703:     }
  704: 
  705:     function linksOK() {
  706:         var newpath = document.getElementsByName('newpath');
  707:         var i = 0;
  708:         for (i=0;i<newpath.length;i++) {
  709:             var path = newpath[i].value;
  710:             var linkOK = (path.match(/^http:\\/\\//) || path.match(/^https:\\/\\//))
  711:                          && !(path.match(/\\.problem/) || path.match(/\\.exam/)
  712:                          || path.match(/\\.quiz/) || path.match(/\\.assess/)
  713:                          || path.match(/\\.survey/) || path.match(/\\.form/)
  714:                          || path.match(/\\.library/) || path.match(/\\.page/)
  715:                          || path.match(/\\.sequence/));
  716:             if (!path.match(/^(\\/res\\/)/) && !linkOK) {
  717:                 alert("$warningLinkNotAllowed1 $warningLinkNotAllowed2"+path);
  718:                 return false;
  719:             }
  720:          }
  721:         return true;
  722:     }
  723: 
  724:     function onLoadAction(mode) {
  725:         window.name = 'wishlist';
  726:         if (mode == "edit") {
  727:             var deepestRows = getDeepestRows();
  728:             setDisplaySelect(deepestRows, '');
  729:         }
  730:     }
  731: 
  732:     function folderAction(rowid) {
  733:         var row = document.getElementById(rowid);
  734:         var indent = getIndent(row);
  735:         var displ;
  736:         var status;
  737:         if (getImage(row) == 'closed') {
  738:             displ = '';
  739:             status = 'open';
  740:         }
  741:         else {
  742:             displ = 'LC_hidden';
  743:             status = 'closed';
  744:         }
  745:         setImage(row,status);
  746:         if (getNextRow(row) != null) {
  747:             var nextIndent = getIndent(getNextRow(row));
  748:             row = getNextRow(row);
  749:             while (nextIndent > indent) {
  750:                 if (displ == '') {
  751:                     row.className = (row.className).replace('LC_hidden','');
  752:                 }
  753:                 else if (displ != '' && !((row.className).match('LC_hidden'))) {
  754:                     var oldClass = row.className;
  755:                     row.className = oldClass+' LC_hidden';
  756:                     setDisplayNote(row.id.replace('row','note'),'LC_hidden');
  757:                 }
  758:                 if (status == 'open' && getImage(row).match('closed')) {
  759:                     row = getNextRowWithIndent(row, getIndent(row));
  760:                 }
  761:                 else {
  762:                     row = getNextRow(row);
  763:                 } 
  764:                 if (row != null) {
  765:                     nextIndent = getIndent(row);
  766:                 } 
  767:                 else {
  768:                     nextIndent = indent;
  769:                 }
  770:             }
  771:         }
  772:         setClasses();
  773:         var newtitles = document.getElementsByName('newtitle');
  774:         if (newtitles.length>0) {
  775:             var deepestRows = getDeepestRows();
  776:             var otherRows = getOtherRows(deepestRows);
  777:             setDisplaySelect(deepestRows,'');
  778:             setDisplaySelect(otherRows,'LC_hidden');
  779:         }
  780:     }
  781: 
  782:     function selectAction(rowid) {
  783:         var row = document.getElementById(rowid);
  784:         var indent = getIndent(row);
  785:         var checked = getChecked(row);
  786:         var previousFolderRows = new Array();
  787:         if (indent != 0) {
  788:             previousFolderRows = getPreviousFolderRows(row);
  789:         }
  790:         if (getNextRow(row) != null) {
  791:             var nextIndent = getIndent(getNextRow(row));
  792:             row = getNextRow(row);
  793:                 while (nextIndent > indent) {
  794:                     setChecked(row,checked);
  795:                     if (status == 'open' && getImage(row).match('closed')) {
  796:                         row = getNextRowWithIndent(row, getIndent(row));
  797:                     }
  798:                     else {
  799:                         row = getNextRow(row);
  800:                     }
  801:                     if (row != null) {
  802:                         nextIndent = getIndent(row);
  803:                     }
  804:                     else {
  805:                         nextIndent = indent;
  806:                     }
  807:                 }
  808:         }
  809:         if (!checked) {
  810:             var i = 0;
  811:             for (i=0;i<previousFolderRows.length;i++) {
  812:                 setChecked(previousFolderRows[i], false);
  813:             }
  814:         }
  815:     }
  816: 
  817:     function getNextNote(row) {
  818:         var rowId = row.id;
  819:         var nextRowId = parseInt(rowId.substr(3,rowId.length))+1;
  820:         nextRowId = "note"+nextRowId;
  821:         var nextRow = document.getElementById(nextRowId);
  822:         return nextRow;
  823:     }
  824: 
  825:     function getNextRow(row) {
  826:         var rowId = row.id;
  827:         var nextRowId = parseInt(rowId.substr(3,rowId.length))+1;
  828:         nextRowId = "row"+nextRowId;
  829:         var nextRow = document.getElementById(nextRowId);
  830:         return nextRow;
  831:     }
  832: 
  833:     function getPreviousRow(row) {
  834:         var rowId = row.id;
  835:         var previousRowId =  parseInt(rowId.substr(3,rowId.length))-1;
  836:         previousRowId = "row"+previousRowId;
  837:         var previousRow =document.getElementById(previousRowId);
  838:         return previousRow;
  839:     }
  840: 
  841:     function getIndent(row) {
  842:         var childPADD = document.getElementById(row.id.replace('row','padd'));
  843:         indent = childPADD.style.paddingLeft;
  844:         indent = parseInt(indent.substr(0,(indent.length-2)));
  845:  
  846:         if (getImage(row).match('link')) {
  847:             indent -= $indentConst;
  848:         }
  849:         return indent;
  850:     }
  851: 
  852:     function getNextRowWithIndent(row, indent) {
  853:         var nextRow = getNextRow(row);
  854:         if (nextRow != null) {
  855:         var nextIndent = getIndent(nextRow);
  856:         while (nextIndent >= indent) {
  857:             if (nextIndent == indent) {
  858:                 return nextRow;
  859:             }
  860:             nextRow = getNextRow(nextRow);
  861:             if (nextRow == null) {
  862:                 return null;
  863:             }
  864:             nextIndent = getIndent(nextRow);
  865:         }
  866:         }
  867:         return nextRow;
  868:     }
  869: 
  870:     function getImage(row) {
  871:         var childIMG = document.getElementById(row.id.replace('row','img'));
  872:         if ((childIMG.src).match('closed')) {
  873:             return 'closed';
  874:         }
  875:         else if ((childIMG.src).match('open')) {
  876:             return 'open;'
  877:         }
  878:         else {
  879:             return 'link';
  880:         }
  881:     } 
  882: 
  883:     function setImage(row, status) {
  884:         var childIMG = document.getElementById(row.id.replace('row','img'));
  885:         var childIMGFolder = document.getElementById(row.id.replace('row','imgFolder'));
  886:         childIMG.src = "/adm/lonIcons/arrow."+status+".gif";
  887:         childIMGFolder.src="/adm/lonIcons/navmap.folder."+status+".gif"; 
  888:     }
  889: 
  890:     function getChecked(row) {
  891:         var childCHECK = document.getElementById(row.id.replace('row','check'));
  892:         var checked = childCHECK.checked;
  893:         return checked;
  894:     }
  895: 
  896:     function setChecked(row,checked) {
  897:         var childCHECK = document.getElementById(row.id.replace('row','check'));
  898:         childCHECK.checked = checked;
  899:     }
  900: 
  901:     function getPreviousFolderRows(row) {
  902:         var previousRow = getPreviousRow(row);
  903:         var indent = getIndent(previousRow);
  904:         var kindOfEntry = getImage(previousRow);
  905:         var rows = new Array();
  906:         if (kindOfEntry != 'link') {
  907:             rows.push(previousRow);
  908:         }
  909: 
  910:         while (indent >0) {
  911:             previousRow = getPreviousRow(previousRow);
  912:             if (previousRow != null) {
  913:                 indent = getIndent(previousRow);
  914:                 kindOfEntry = getImage(previousRow);
  915:                 if (kindOfEntry != 'link') {
  916:                     rows.push(previousRow);
  917:                 }
  918:             }
  919:             else {
  920:                 indent = 0; 
  921:             }
  922:         }
  923:         return rows;
  924:     }
  925: 
  926:     function getDeepestRows() {
  927:         var row = document.getElementById('row0');
  928:         var firstRow = row;
  929:         var indent = getIndent(row);
  930:         var maxIndent = indent;
  931:         while (getNextRow(row) != null) {
  932:             row = getNextRow(row);
  933:             indent = getIndent(row);
  934:             if (indent>maxIndent && !((row.className).match('LC_hidden'))) {
  935:                 maxIndent = indent;
  936:             }
  937:         }
  938:         var deepestRows = new Array();
  939:         row = firstRow;
  940:         var rowIndent;
  941:         while (getNextRow(row) != null) {
  942:             rowIndent = getIndent(row);
  943:             if (rowIndent == maxIndent) {
  944:                 deepestRows.push(row);
  945:             }
  946:             row = getNextRow(row);
  947:         }
  948:         rowIndent = getIndent(row);
  949:         if (rowIndent == maxIndent) {
  950:             deepestRows.push(row);
  951:         }
  952:         return deepestRows;
  953:     }
  954: 
  955:     function getOtherRows(deepestRows) {
  956:         var row = document.getElementById('row0');
  957:         var otherRows = new Array();
  958:         var isIn = false;
  959:         while (getNextRow(row) != null) {
  960:             var i = 0;
  961:             for (i=0; i < deepestRows.length; i++) {
  962:                 if (row.id == deepestRows[i].id) {
  963:                     isIn = true;
  964:                 }
  965:             }
  966:             if (!isIn) {
  967:                 otherRows.push(row);
  968:             }
  969:             row = getNextRow(row);
  970:             isIn = false;
  971:         }
  972:         for (i=0; i < deepestRows.length; i++) {
  973:             if (row.id == deepestRows[i].id) {
  974:                 isIn = true;
  975:             }
  976:         }
  977:         if (!isIn) {
  978:             otherRows.push(row);
  979:         }
  980:         return otherRows;
  981:     }
  982: 
  983:     function setDisplaySelect(deepestRows, displ) {
  984:         var i = 0;
  985:         for (i = 0; i < deepestRows.length; i++) {
  986:             var row = deepestRows[i];
  987:             var childSEL = document.getElementById(row.id.replace('row','sel'));
  988:             childSEL.className = displ;
  989:         } 
  990:     }
  991: 
  992:     function submitSelect() {
  993:        var list = document.getElementsByName('list')[0];
  994:        list.setAttribute("action","/adm/wishlist?mode=edit");
  995:        list.submit();
  996:     }
  997: 
  998:     function setDisplayNote(rowid, displ) {
  999:         var row = document.getElementById(rowid);
 1000:         if (!displ) {
 1001:             if ((row.className).match('LC_hidden')) {
 1002:                 row.className = (row.className).replace('LC_hidden','');
 1003:             }
 1004:             else {
 1005:                 var oldClass = row.className;
 1006:                 row.className = oldClass+' LC_hidden';
 1007:             }
 1008:         }
 1009:         else {
 1010:             if (displ == '') {
 1011:                 row.className = (row.className).replace('LC_hidden','');
 1012:             }
 1013:             else if (displ != '' && !((row.className).match('LC_hidden'))) {
 1014:                 var oldClass = row.className;
 1015:                 row.className = oldClass+' LC_hidden';
 1016:             }
 1017:         }
 1018:         var noteText = document.getElementById(rowid.replace('note','noteText'));
 1019:         var noteImg = document.getElementById(rowid.replace('note','noteImg'));
 1020:         if (noteText.value) {
 1021:             noteImg.src = "/res/adm/pages/anot2.png";
 1022:         }
 1023:         else {
 1024:             noteImg.src = "/res/adm/pages/anot.png";
 1025:         }
 1026: 
 1027:     }
 1028: 
 1029:     function setClasses() {
 1030:         var row = document.getElementById("row0");
 1031:         var note = document.getElementById("note0");
 1032:         var LC_class = 0;
 1033:         if (getNextRow(row) != null) {
 1034:             while (getNextRow(row) != null) {
 1035:                 if (!(row.className).match('LC_hidden')) {
 1036:                     note.className = (note.className).replace('LC_even_row','');
 1037:                     note.className = (note.className).replace('LC_odd_row','');
 1038:                     if (LC_class) {
 1039:                         row.className = 'LC_even_row';
 1040:                         note.className = 'LC_even_row'+note.className;
 1041:                     }
 1042:                     else {
 1043:                         row.className = 'LC_odd_row';
 1044:                         note.className = 'LC_odd_row'+note.className;;
 1045:                     }
 1046:                     LC_class = !LC_class;
 1047:                 }
 1048:                 note = getNextNote(row);
 1049:                 row = getNextRow(row);
 1050:             }
 1051:         }
 1052:         if (!(row.className).match('LC_hidden')) {
 1053:             note.className = (note.className).replace('LC_even_row','');
 1054:             note.className = (note.className).replace('LC_odd_row','');
 1055:             if (LC_class) {
 1056:                 row.className = 'LC_even_row';
 1057:                 note.className = 'LC_even_row'+note.className;
 1058:             }
 1059:             else {
 1060:                 row.className = 'LC_odd_row';
 1061:                 note.className = 'LC_odd_row'+note.className;
 1062:             }
 1063:         }
 1064:     }
 1065: 
 1066:     function selectDestinationFolder(mode) {
 1067:         var mark = document.getElementsByName('mark');
 1068:         var i = 0;
 1069:         for (i = 0; i < mark.length; i++) {
 1070:             if (mark[i].checked) {
 1071:                 document.getElementsByName('list')[0].submit();
 1072:                 return true;
 1073:             }
 1074:         }
 1075:         if (mode == 'move') {
 1076:             alert('$warningMoveS');
 1077:         }
 1078:         else {
 1079:             alert('$warningMoveD');
 1080:         }
 1081:         return false;
 1082:     }
 1083: 
 1084:     function preview(url) {
 1085:        var newWin;
 1086:        if (!(url.match(/^http:\\/\\//) || url.match(/^https:\\/\\//))) {
 1087:            newWin = window.open(url+'?inhibitmenu=yes','preview','width=560,height=350,scrollbars=yes');
 1088:        }
 1089:        else {
 1090:            newWin = window.open(url,'preview','width=560,height=350,scrollbars=yes');
 1091:        }
 1092:        newWin.focus();
 1093:     }
 1094: 
 1095:     function checkAll() {
 1096:         var checkboxes = document.getElementsByName('check');
 1097:         for (var i = 0; i < checkboxes.length; i++) {
 1098:             checkboxes[i].checked = "checked";
 1099:         }
 1100:     }
 1101: 
 1102:     function uncheckAll() {
 1103:         var checkboxes = document.getElementsByName('check');
 1104:         for (var i = 0; i < checkboxes.length; i++) {
 1105:             checkboxes[i].checked = "";
 1106:         }
 1107:     }
 1108: 
 1109: JAVASCRIPT
 1110:    return $js;
 1111: }
 1112: 
 1113: sub JSforImport{
 1114:     my $rat = shift;
 1115: 
 1116:     my $js;
 1117:     if ($rat eq 'simple' || $rat eq '') {
 1118:         $js = &Apache::lonhtmlcommon::scripttag(<<JAVASCRIPT);
 1119:         function finish_import() {
 1120:             opener.document.forms.simpleedit.importdetail.value='';
 1121:             for (var num = 0; num < document.forms.groupsort.fnum.value; num++) {
 1122:                 if (eval("document.forms.groupsort.check"+num+".checked") && eval("document.forms.groupsort.filelink"+num+".value") != '') {
 1123:                     opener.document.forms.simpleedit.importdetail.value+='&'+
 1124:                     eval("document.forms.groupsort.title"+num+".value")+'='+
 1125:                     eval("document.forms.groupsort.filelink"+num+".value")+'='+
 1126:                     eval("document.forms.groupsort.id"+num+".value");
 1127:                 }
 1128:             }
 1129:             opener.document.forms.simpleedit.submit();
 1130:             self.close();
 1131:         }
 1132: JAVASCRIPT
 1133:     }
 1134:     else {
 1135:         $js = &Apache::lonhtmlcommon::scripttag(<<JAVASCRIPT);
 1136:         function finish_import() {
 1137:             var linkflag=false;
 1138:             for (var num=0; num<document.forms.groupsort.fnum.value; num++) {
 1139:                 if (eval("document.forms.groupsort.check"+num+".checked") && eval("document.forms.groupsort.filelink"+num+".value") != '') {
 1140:                     insertRowInLastRow();
 1141:                     placeResourceInLastRow(
 1142:                         eval("document.forms.groupsort.title"+num+".value"),
 1143:                         eval("document.forms.groupsort.filelink"+num+".value"),
 1144:                         eval("document.forms.groupsort.id"+num+".value"),
 1145:                         linkflag
 1146:                         );
 1147:                     linkflag=true;
 1148:                 }
 1149:             }
 1150:             opener.editmode=0;
 1151:             opener.notclear=0;
 1152:             opener.linkmode=0;
 1153:             opener.draw();
 1154:             self.close();
 1155:         }
 1156: 
 1157:         function insertRowInLastRow() {
 1158:             opener.insertrow(opener.maxrow);
 1159:             opener.addobj(opener.maxrow,'e&2');
 1160:         }
 1161: 
 1162:         function placeResourceInLastRow (title,url,id,linkflag) {
 1163:             opener.mostrecent=opener.newresource(opener.maxrow,2,opener.unescape(title),
 1164:                               opener.unescape(url),'false','normal',id);
 1165:             opener.save();
 1166:             if (linkflag) {
 1167:                 opener.joinres(opener.linkmode,opener.mostrecent,0);
 1168:             }
 1169:             opener.linkmode=opener.mostrecent;
 1170:         }
 1171: JAVASCRIPT
 1172:     }
 1173:     return $js;
 1174: }
 1175: 
 1176: # HTML-Markup for table if in view-mode
 1177: my $wishlistHTMLview;
 1178: my $indent = $indentConst;
 1179: sub wishlistView {
 1180:     my $nodes = shift;
 1181: 
 1182:     foreach my $n (@$nodes) {
 1183:         my $index = $n->value()->nindex();
 1184: 
 1185:         # 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.
 1186:         # only display the top level entries on load
 1187:         $wishlistHTMLview .= ($n->parent()->value() eq 'root')?&Apache::loncommon::start_data_table_row('','row'.$index)
 1188:                                                               :&Apache::loncommon::continue_data_table_row('LC_hidden','row'.$index);
 1189: 
 1190:  
 1191:         # checkboxes
 1192:         $wishlistHTMLview .= '<td><input type="checkbox" name="mark" id="check'.$index.'" value="'.$index.'" '.
 1193:                              'onclick="selectAction('."'row".$index."'".')"/></td>';
 1194: 
 1195:         # entry is a folder
 1196:         if ($n->value()->path() eq '') {
 1197:             $wishlistHTMLview .= '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<0?0:($indent-$indentConst)).'px; min-width: 220px;">'.
 1198:                                  '<a href="javascript:;" onclick="folderAction('."'row".$index."'".')" style="vertical-align:top">'.
 1199:                                  '<img src="/adm/lonIcons/arrow.closed.gif" id="img'.$index.'" alt = "" class="LC_icon"/>'.
 1200:                                  '<img src="/adm/lonIcons/navmap.folder.closed.gif" id="imgFolder'.$index.'" alt="folder"/>'.
 1201:                                  $n->value()->title().'</a></td>';
 1202:         }
 1203:         # entry is a link
 1204:         else {
 1205:             $wishlistHTMLview .= '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<=0?$indentConst:$indent).'px; min-width: 220px;">'.
 1206:                                  '<a href="javascript:preview('."'".$n->value()->path()."'".');">'.
 1207:                                  '<img src="/res/adm/pages/wishlist-link.png" id="img'.$index.'" alt="link" />'.
 1208:                                  $n->value()->title().'</a></td>';
 1209:         }
 1210: 
 1211:         # note-icon, different icons for an entries with note and those without
 1212:         my $noteIMG = 'anot.png';
 1213: 
 1214:         if ($n->value()->note() ne '') {
 1215:             $noteIMG = 'anot2.png';
 1216:         }
 1217: 
 1218:         $wishlistHTMLview .= '<td style="padding-left:10px;"><a href="javascript:;" onclick="setDisplayNote('."'note".$index."'".')">'.
 1219:                              '<img id="noteImg'.$index.'" src="/res/adm/pages/'.$noteIMG.'" alt="'.&mt('Note').'" title="'.&mt('Note').'" '.
 1220:                              ' class="LC_icon"/></a></td>';
 1221: 
 1222:         $wishlistHTMLview .= &Apache::loncommon::end_data_table_row();
 1223: 
 1224:         # start row containing the textarea for the note, do not display note on default
 1225:         $wishlistHTMLview .= &Apache::loncommon::continue_data_table_row('LC_hidden','note'.$index).
 1226:                              '<td></td><td>'.
 1227:                              '<textarea id="noteText'.$index.'" cols="25" rows="3" style="width:100%" '.
 1228:                              'name="newnote" >'.
 1229:                              $n->value()->note().'</textarea></td><td></td>';
 1230:         $wishlistHTMLview .= &Apache::loncommon::end_data_table_row();
 1231: 
 1232:         # if the entry is a folder, it could have other entries as content. if it has, call wishlistView for those entries 
 1233:         my @children = $n->children();
 1234:         if ($#children >=0) {
 1235:             $indent += 20;
 1236:             &wishlistView(\@children);
 1237:             $indent -= 20;
 1238:         }
 1239:     }
 1240: }
 1241: 
 1242: 
 1243: # HTML-Markup for table if in edit-mode
 1244: my $wishlistHTMLedit;
 1245: my $indent = $indentConst;
 1246: sub wishlistEdit {
 1247:     my $nodes = shift;
 1248:     my $curNode = 1;
 1249: 
 1250:     foreach my $n (@$nodes) {
 1251:         my $index = $n->value()->nindex();
 1252: 
 1253:         # start row, use data_table routines to set class to LC_even or LC_odd automatically.
 1254:         # this rows contains a checkbox, a select-field for sorting entries, the title in an input-field and the note-icon.
 1255:         # only display the top level entries on load
 1256:         $wishlistHTMLedit .= ($n->parent()->value() eq 'root')?&Apache::loncommon::start_data_table_row('','row'.$index)
 1257:                                                               :&Apache::loncommon::continue_data_table_row('LC_hidden','row'.$index);
 1258: 
 1259:         # checkboxes
 1260:         $wishlistHTMLedit .= '<td><input type="checkbox" name="mark" id="check'.$index.'" value="'.$index.'" '.
 1261:                              'onclick="selectAction('."'row".$index."'".')"/></td>';
 1262: 
 1263:         # 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.
 1264:         # set the number for the current entry into brackets 
 1265:         my $options;
 1266:         for (my $i = 1; $i < ((scalar @{$nodes})+1); $i++) {
 1267:            if ($i == $curNode) {
 1268:                $options .= '<option selected="selected" value="">('.$i.')</option>';
 1269:            }
 1270:            else {
 1271:                $options .= '<option value="'.$i.'">'.$i.'</option>';
 1272:            }
 1273:         }
 1274:         $curNode++;
 1275: 
 1276:         # entry is a folder
 1277:         if ($n->value()->path() eq '') {
 1278:             $wishlistHTMLedit .= '<td><select class="LC_hidden" name="sel" id="sel'.$index.'" onchange="submitSelect();">'.
 1279:                                  $options.'</select></td>'.
 1280:                                  '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<0?0:($indent-$indentConst)).'px;">'.
 1281:                                  '<a href="javascript:;" onclick="folderAction('."'row".$index."'".')" style="vertical-align:top" >'.
 1282:                                  '<img src="/adm/lonIcons/arrow.closed.gif" id="img'.$index.'" alt = ""  class="LC_icon"/>'.
 1283:                                  '<img src="/adm/lonIcons/navmap.folder.closed.gif" id="imgFolder'.$index.'" alt="folder"/></a>'.
 1284:                                  '<input type="text" name="newtitle" value="'.$n->value()->title().'" alt = "'.$n->value()->title().'"/>'.
 1285:                                  '</td><td></td>';
 1286: 
 1287:         }
 1288:         # entry is a link
 1289:         else {
 1290:             $wishlistHTMLedit .= '<td><select class="LC_hidden" name="sel" id="sel'.$index.'" onchange="submitSelect();">'.
 1291:                                  $options.'</select></td>'.
 1292:                                  '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<=0?$indentConst:$indent).'px;">'.
 1293:                                  '<img src="/res/adm/pages/wishlist-link.png" id="img'.$index.'" alt="link"/>'.
 1294:                                  '<input type="text" name="newtitle" value="'.$n->value()->title().'" alt = "'.$n->value()->title().'"/></td>'.
 1295:                                  '<td><input type="text" name="newpath" value="'.$n->value()->path().'" alt = "'.$n->value()->path().'"/></td>';
 1296:         }
 1297:         
 1298:         # note-icon, different icons for an entries with note and those without
 1299:         my $noteIMG = 'anot.png';
 1300: 
 1301:         if ($n->value()->note() ne '') {
 1302:             $noteIMG = 'anot2.png';
 1303:         }
 1304: 
 1305:         $wishlistHTMLedit .= '<td style="padding-left:10px;"><a href="javascript:;" onclick="setDisplayNote('."'note".$index."'".')">'.
 1306:                              '<img id="noteImg'.$index.'" src="/res/adm/pages/'.$noteIMG.'" alt="'.&mt('Note').'" title="'.&mt('Note').'" '.
 1307:                              ' class="LC_icon"/></a></td>';
 1308: 
 1309:         $wishlistHTMLedit .= &Apache::loncommon::end_data_table_row();
 1310: 
 1311:         # start row containing the textarea for the note
 1312:         $wishlistHTMLedit .= &Apache::loncommon::continue_data_table_row('LC_hidden','note'.$index).
 1313:                              '<td></td><td></td><td colspan="2">'.
 1314:                              '<textarea id="noteText'.$index.'" cols="25" rows="3" style="width:100%" '.
 1315:                              'name="newnote">'.
 1316:                              $n->value()->note().'</textarea></td><td></td>';
 1317:         $wishlistHTMLedit .= &Apache::loncommon::end_data_table_row();
 1318: 
 1319:         # if the entry is a folder, it could have other entries as content. if it has, call wishlistEdit for those entries 
 1320:         my @children = $n->children();
 1321:         if ($#children >=0) {
 1322:             $indent += 20;
 1323:             &wishlistEdit(\@children);
 1324:             $indent -= 20;
 1325:         }
 1326:     }
 1327: }
 1328: 
 1329: 
 1330: 
 1331: # HTML-Markup for table if in move-mode
 1332: my $wishlistHTMLmove ='<tr id="root" class="LC_odd_row"><td><input type="radio" name="mark" id="radioRoot" value="root" /></td>'.
 1333:                       '<td>'.&mt('Top level').'</td><td></td></tr>';
 1334: my $indent = $indentConst;
 1335: sub wishlistMove {
 1336:     my $nodes = shift;
 1337:     my $marked = shift;
 1338: 
 1339:     foreach my $n (@$nodes) {
 1340:         my $index = $n->value()->nindex();
 1341: 
 1342:         #find out wether the current entry was marked to be moved.
 1343:         my $isIn = 0;
 1344:         foreach my $m (@$marked) {
 1345:             if ($index == $m) {
 1346:                $isIn = 1;
 1347:             }
 1348:         }
 1349:         # 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
 1350:         $wishlistHTMLmove .= &Apache::loncommon::start_data_table_row('','row'.$index);
 1351: 
 1352: 
 1353:         # entry is a folder
 1354:         if ($n->value()->path() eq '') {
 1355:             # display a radio-button, if the folder was not selected to be moved
 1356:             if (!$isIn) {
 1357:                 $wishlistHTMLmove .= '<td><input type="radio" name="mark" id="radio'.$index.'" value="'.$index.'" /></td>'.
 1358:                                      '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<0?0:($indent-$indentConst)).'px; min-width: 220px;">';
 1359:             }
 1360:             # higlight the title, if the folder was selected to be moved
 1361:             else {
 1362:                 $wishlistHTMLmove .= '<td></td>'.
 1363:                                      '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<0?0:($indent-$indentConst)).'px; min-width: 220px;'.
 1364:                                      'color:red;">';
 1365:             }
 1366:             #arrow- and folder-image, all folders are open, and title
 1367:             $wishlistHTMLmove .= '<img src="/adm/lonIcons/arrow.open.gif" id="img'.$index.'" alt = "" />'.
 1368:                                  '<img src="/adm/lonIcons/navmap.folder.open.gif" id="imgFolder'.$index.'" alt="folder"/>'.
 1369:                                  $n->value()->title().'</td>';
 1370:         }
 1371:         # entry is a link
 1372:         else {
 1373:             # higlight the title, if the link was selected to be moved
 1374:             my $highlight = '';
 1375:             if ($isIn) {
 1376:                $highlight = 'style="color:red;"';
 1377:             }
 1378:             # link-image and title
 1379:             $wishlistHTMLmove .= '<td></td>'.
 1380:                                  '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<=0?$indentConst:$indent).'px; min-width: 220px;">'.
 1381:                                  '<a href="javascript:preview('."'".$n->value()->path()."'".');" '.$highlight.'>'.
 1382:                                  '<img src="/res/adm/pages/wishlist-link.png" id="img'.$index.'" alt="link"/>'.
 1383:                                  $n->value()->title().'</a></td>';
 1384:         }
 1385: 
 1386:         # note-icon, different icons for an entries with note and those without
 1387:         my $noteIMG = 'anot.png';
 1388: 
 1389:         if ($n->value()->note() ne '') {
 1390:             $noteIMG = 'anot2.png';
 1391:         }
 1392: 
 1393:         $wishlistHTMLmove .= '<td style="padding-left:10px;"><a href="javascript:;" onclick="setDisplayNote('."'note".$index."'".')">'.
 1394:                              '<img id="noteImg'.$index.'" src="/res/adm/pages/'.$noteIMG.'" alt="'.&mt('Note').'" title="'.&mt('Note').'" '.
 1395:                              ' class="LC_icon"/></a></td>';
 1396: 
 1397:         $wishlistHTMLmove .= &Apache::loncommon::end_data_table_row();
 1398: 
 1399:         # start row containing the textarea for the note, readonly in move-mode
 1400:         $wishlistHTMLmove .= &Apache::loncommon::continue_data_table_row('LC_hidden','note'.$index).
 1401:                              '<td></td><td>'.
 1402:                              '<textarea id="noteText'.$index.'" cols="25" rows="3" style="width:100%" '.
 1403:                              'name="newnote" readonly="readonly">'.
 1404:                              $n->value()->note().'</textarea></td><td></td>'.
 1405:                              &Apache::loncommon::end_data_table_row();
 1406: 
 1407:         # if the entry is a folder, it could have other entries as content. if it has, call wishlistMove for those entries 
 1408:         my @children = $n->children();
 1409:         if ($#children >=0) {
 1410:             $indent += 20;
 1411:             &wishlistMove(\@children, $marked);
 1412:             $indent -= 20;
 1413:         }
 1414:     }
 1415: }
 1416: 
 1417: 
 1418: 
 1419: # HTML-Markup for table if in import-mode
 1420: my $wishlistHTMLimport;
 1421: my $indent = $indentConst;
 1422: my $form = 1;
 1423: sub wishlistImport {
 1424:     my $nodes = shift;
 1425: 
 1426:     foreach my $n (@$nodes) {
 1427:         my $index = $n->value()->nindex();
 1428: 
 1429:         # 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.
 1430:         # only display the top level entries on load
 1431:         $wishlistHTMLimport .= ($n->parent()->value() eq 'root')?&Apache::loncommon::start_data_table_row('','row'.$index)
 1432:                                                                 :&Apache::loncommon::continue_data_table_row('LC_hidden','row'.$index);
 1433: 
 1434:  
 1435:         # checkboxes
 1436:         $wishlistHTMLimport .= '<td>'.
 1437:                                '<input type="checkbox" name="check" id="check'.$index.'" value="'.$index.'" '.
 1438:                                'onclick="selectAction('."'row".$index."'".')"/>'.
 1439:                                '<input type="hidden" name="title'.$index.'" value="'.&escape($n->value()->title()).'">'.
 1440:                                '<input type="hidden" name="filelink'.$index.'" value="'.&escape($n->value()->path()).'">'.
 1441:                                '<input type="hidden" name="id'.$index.'">'.
 1442:                                '</td>';
 1443: 
 1444:         # entry is a folder
 1445:         if ($n->value()->path() eq '') {
 1446:             $wishlistHTMLimport .= '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<0?0:($indent-$indentConst)).'px; min-width: 220px;">'.
 1447:                                    '<a href="javascript:;" onclick="folderAction('."'row".$index."'".')" style="vertical-align:top">'.
 1448:                                    '<img src="/adm/lonIcons/arrow.closed.gif" id="img'.$index.'" alt = "" class="LC_icon"/>'.
 1449:                                    '<img src="/adm/lonIcons/navmap.folder.closed.gif" id="imgFolder'.$index.'" alt="folder"/>'.
 1450:                                    $n->value()->title().'</a></td>';
 1451:         }
 1452:         # entry is a link
 1453:         else {
 1454:             $wishlistHTMLimport .= '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<=0?$indentConst:$indent).'px; min-width: 220px;">'.
 1455:                                    '<a href="javascript:preview('."'".$n->value()->path()."'".');">'.
 1456:                                    '<img src="/res/adm/pages/wishlist-link.png" id="img'.$index.'" alt="link" />'.
 1457:                                    $n->value()->title().'</a></td>';
 1458:                                    $form++;
 1459:         }
 1460: 
 1461:         # note-icon, different icons for an entries with note and those without
 1462:         my $noteIMG = 'anot.png';
 1463: 
 1464:         if ($n->value()->note() ne '') {
 1465:             $noteIMG = 'anot2.png';
 1466:         }
 1467: 
 1468:         $wishlistHTMLimport .= '<td style="padding-left:10px;"><a href="javascript:;" onclick="setDisplayNote('."'note".$index."'".')">'.
 1469:                              '<img id="noteImg'.$index.'" src="/res/adm/pages/'.$noteIMG.'" alt="'.&mt('Note').'" title="'.&mt('Note').'" '.
 1470:                              ' class="LC_icon"/></a></td>';
 1471: 
 1472:         $wishlistHTMLimport .= &Apache::loncommon::end_data_table_row();
 1473: 
 1474:         # start row containing the textarea for the note, do not display note on default, readonly in import-mode
 1475:         $wishlistHTMLimport .= &Apache::loncommon::continue_data_table_row('LC_hidden','note'.$index).
 1476:                              '<td></td><td>'.
 1477:                              '<textarea id="noteText'.$index.'" cols="25" rows="3" style="width:100%" '.
 1478:                              'name="newnote" readonly="readonly">'.
 1479:                              $n->value()->note().'</textarea></td><td></td>';
 1480:         $wishlistHTMLimport .= &Apache::loncommon::end_data_table_row();
 1481: 
 1482:         # if the entry is a folder, it could have other entries as content. if it has, call wishlistImport for those entries 
 1483:         my @children = $n->children();
 1484:         if ($#children >=0) {
 1485:             $indent += 20;
 1486:             &wishlistImport(\@children);
 1487:             $indent -= 20;
 1488:         }
 1489:     }
 1490: }
 1491: 
 1492: # Returns the HTML-Markup for wishlist
 1493: sub makePage {
 1494:     my $rootgiven = shift;
 1495:     my $mode = shift;
 1496:     my $marked = shift;
 1497: 
 1498:     $root = $rootgiven;
 1499:     @childrenRt = $root->children();
 1500: 
 1501:     # breadcrumbs and start_page
 1502:     &Apache::lonhtmlcommon::clear_breadcrumbs();
 1503:     &Apache::lonhtmlcommon::add_breadcrumb(
 1504:               { href => '/adm/wishlist?mode='.$mode,
 1505:                 text => 'Wishlist'});
 1506:     my $startPage = &Apache::loncommon::start_page('Wishlist',undef,
 1507:                                                      {'add_entries' => {
 1508:                                                         'onload' => 'javascript:onLoadAction('."'".$mode."'".');',
 1509:                                                         'onunload' => 'javascript:window.name = '."'loncapaclient'"}});
 1510: 
 1511:     my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs(&mt('Wishlist').&Apache::loncommon::help_open_topic('Wishlist'));
 1512: 
 1513:     # get javascript-code for wishlist-interactions
 1514:     my $js = &JSforWishlist();
 1515: 
 1516:     # texthash for items in funtionlist
 1517:     my %lt = &Apache::lonlocal::texthash(
 1518:                  'ed' => 'Edit',
 1519:                  'vw' => 'View',
 1520:                  'al' => 'Add Link',
 1521:                  'af' => 'Add Folder',
 1522:                  'mv' => 'Move Selected',
 1523:                  'dl' => 'Delete Selected',
 1524:                  'sv' => 'Save');
 1525: 
 1526:     # start functionlist
 1527:     my $functions = &Apache::lonhtmlcommon::start_funclist();
 1528: 
 1529:     # icon for edit-mode, display when in view-mode
 1530:     if ($mode eq 'view') {
 1531:         $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
 1532:                           'onclick="setFormAction('."'save','edit'".');" class="LC_menubuttons_link">'.
 1533:                           '<img src="/res/adm/pages/edit-mode-22x22.png" alt="'.$lt{'ed'}.'" '.
 1534:                           'title="'.$lt{'ed'}.'" class="LC_icon"/> '.
 1535:                           '<span class="LC_menubuttons_inline_text">'.$lt{'ed'}.'</span></a>');
 1536:     }
 1537:     # icon for view-mode, display when in edit-mode
 1538:     else {
 1539:         $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
 1540:                           'onclick="setFormAction('."'save','view'".');" class="LC_menubuttons_link">'.
 1541:                           '<img src="/res/adm/pages/view-mode-22x22.png" alt="'.$lt{'vw'}.'" '.
 1542:                           'title="'.$lt{'vw'}.'" class="LC_icon"/> '.
 1543:                           '<span class="LC_menubuttons_inline_text">'.$lt{'vw'}.'</span></a>');
 1544:     }
 1545:     
 1546:     # icon for adding a new link
 1547:     $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
 1548:                       'onclick="newLink();" class="LC_menubuttons_link">'.
 1549:                       '<img src="/res/adm/pages/link-new-22x22.png" alt="'.$lt{'al'}.'" '.
 1550:                       'title="'.$lt{'al'}.'" class="LC_icon"/>'.
 1551:                       '<span class="LC_menubuttons_inline_text">'.$lt{'al'}.'</span></a>');
 1552: 
 1553:     # icon for adding a new folder
 1554:     $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
 1555:                       'onclick="newFolder();" class="LC_menubuttons_link">'.
 1556:                       '<img src="/res/adm/pages/folder-new-22x22.png" alt="'.$lt{'af'}.'" '.
 1557:                       'title="'.$lt{'af'}.'" class="LC_icon"/>'.
 1558:                       '<span class="LC_menubuttons_inline_text">'.$lt{'af'}.'</span></a>');
 1559: 
 1560:     # icon for moving entries
 1561:     $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
 1562:                       'onclick="setFormAction('."'move','move'".'); " class="LC_menubuttons_link">'.
 1563:                       '<img src="/res/adm/pages/move-22x22.png" alt="'.$lt{'mv'}.'" '.
 1564:                       'title="'.$lt{'mv'}.'" class="LC_icon" />'.
 1565:                       '<span class="LC_menubuttons_inline_text">'.$lt{'mv'}.'</span></a>');
 1566: 
 1567:     # icon for deleting entries
 1568:     $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
 1569:                       'onclick="setFormAction('."'delete','".$mode."'".'); " class="LC_menubuttons_link">'.
 1570:                       '<img src="/res/adm/pages/del.png" alt="'.$lt{'dl'}.'" '.
 1571:                       'title="'.$lt{'dl'}.'" class="LC_icon" />'.
 1572:                       '<span class="LC_menubuttons_inline_text">'.$lt{'dl'}.'</span></a>');
 1573: 
 1574:     # icon for saving changes
 1575:     $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
 1576:                       'onclick="setFormAction('."'saveOK','".$mode."'".'); " class="LC_menubuttons_link">'.
 1577:                       '<img src="/res/adm/pages/save-22x22.png" alt="'.$lt{'sv'}.'" '.
 1578:                       'title="'.$lt{'sv'}.'" class="LC_icon" />'.
 1579:                       '<span class="LC_menubuttons_inline_text">'.$lt{'sv'}.'</span></a>');
 1580: 
 1581:     # end funtionlist and generate subbox 
 1582:     $functions.= &Apache::lonhtmlcommon::end_funclist();
 1583:     my $subbox = &Apache::loncommon::head_subbox($functions);
 1584: 
 1585:     # start form 
 1586:     my $inner .= '<form name="list" action ="/adm/wishlist" method="post">'.
 1587:                  '<input type="hidden" id="action" name="action" value=""/>';
 1588:  
 1589:     # only display subbox in view- or edit-mode
 1590:     if ($mode eq 'view' || $mode eq 'edit') {
 1591:         $inner .= $subbox;
 1592:     }
 1593: 
 1594:     # generate table-content depending on mode
 1595:     if ($mode eq 'edit') {
 1596:         &wishlistEdit(\@childrenRt);
 1597:         if ($wishlistHTMLedit ne '') {
 1598:             $inner .= &Apache::loncommon::start_data_table("LC_tableOfContent");
 1599:             $inner .= $wishlistHTMLedit;
 1600:             $inner .= &Apache::loncommon::end_data_table();
 1601:         }
 1602:         else {
 1603:             $inner .= '<span class="LC_info">'.&mt("Your wishlist ist currently empty.").'</span>';
 1604:         }
 1605:         $wishlistHTMLedit = '';
 1606:     }
 1607:     elsif ($mode eq 'view') {
 1608:         &wishlistView(\@childrenRt);
 1609:         if ($wishlistHTMLview ne '') {
 1610:             $inner .= '<table class="LC_data_table LC_tableOfContent">'.$wishlistHTMLview.'</table>';
 1611:         }
 1612:         else {
 1613:             $inner .= '<span class="LC_info">'.&mt("Your wishlist ist currently empty.").'</span>';
 1614:         }
 1615:         $wishlistHTMLview = '';
 1616:     }
 1617:     else {
 1618:         my $markStr = '';
 1619:         foreach my $m (@$marked) {
 1620:             $markStr .= $m.',';
 1621:         }
 1622:         if ($markStr) {
 1623:             $markStr = substr($markStr, 0, length($markStr)-1);
 1624:             $inner .= '<input type="hidden" value="'.$markStr.'" name="markedToMove"/>';
 1625:             $inner .= '<p><span class="LC_info">'.&mt('You have selected the red marked entries to be moved to another folder. '.
 1626:                                                    'Now choose the new destination folder.').'</span></p>';
 1627:             &wishlistMove(\@childrenRt, $marked);
 1628:             $inner .= '<table class="LC_data_table LC_tableOfContent">'.$wishlistHTMLmove.'</table><br/><br/>';
 1629:             $inner .= '<input type="button" value="'.&mt('Move').'" onclick="setFormAction('."'move','view'".');"/>'.
 1630:                       '<input type="button" value="'.&mt('Cancel').'" onclick="go('."'/adm/wishlist'".')"/>';
 1631: 
 1632:             $wishlistHTMLmove ='<tr id="root" class="LC_odd_row"><td><input type="radio" name="mark" id="radioRoot" value="root" /></td>'.
 1633:                                '<td>'.&mt('Top level').'</td><td></td></tr>';
 1634:         }
 1635:         else {
 1636:             $inner .= '<p><span class="LC_info">'.&mt("You haven't marked any entry to move.").'</span></p>'.
 1637:                       '<input type="button" value="'.&mt('Back').'" onclick="go('."'/adm/wishlist'".')"/>';
 1638:         }
 1639:     }
 1640:     
 1641:     # end form 
 1642:     $inner .= '</form>';
 1643: 
 1644:     # end_page 
 1645:     my $endPage =  &Apache::loncommon::end_page();
 1646: 
 1647:     # put all page-elements together
 1648:     my $page = $startPage.$breadcrumbs.$js.$inner.$endPage;
 1649: 
 1650:     return $page;
 1651: }
 1652: 
 1653: 
 1654: # Returns the HTML-Markup for the PopUp, shown when a new link should set, when NOT
 1655: # beeing in the wishlist-interface (method is called in lonmenu and lonsearchcat)
 1656: sub makePopUpNewLink{
 1657: 
 1658:     # Get all existing folders to offer posibility to set a new link
 1659:     # into a folder
 1660:     my %TreeHashLink = &Apache::lonwishlist::getWishlist();
 1661:     my $rootLink = &Apache::Tree::HashToTree(\%TreeHashLink);
 1662:     my @childrenRtLink = $rootLink->children();
 1663: 
 1664:     $foldersOption = '';
 1665:     @allFolders = ();
 1666:     &getFoldersToArray(\@childrenRtLink);
 1667:     &getFoldersForOption(\@childrenRtLink);
 1668: 
 1669:     my $options = '<option value="" selected="selected">('.&mt('Top level').')</option>'.$foldersOption;
 1670:     $foldersOption = '';
 1671:     @allFolders = ();
 1672: 
 1673:     # HTML-Markup for 'Set a link for this resource to wishlist'
 1674:     # this is written via JavaScript document.write (function set_wishlistlink) 
 1675:     # it is split into 3 parts and the inputfields for title and path are left out
 1676:     # these fields are inserted later to set the values for title and path
 1677:     # automatically via JavaScript (document.title and location.pathname) 
 1678: 
 1679:     my $start_page_wishlistlink = 
 1680:         &Apache::loncommon::start_page('Set link to wishlist',undef,
 1681: 				       {'only_body' => 1,
 1682: 					'js_ready'  => 1,
 1683: 					'bgcolor'   => '#FFFFFF',});
 1684: 
 1685:     my $warningLink = &mt('You must insert a title!');
 1686: 
 1687:     my $in_page_wishlistlink1 = '<h1>'.&mt('Set a link to wishlist').'</h1>'.
 1688:                                 '<form method="post" name="newlink" action="/adm/wishlist?mode=set" '.
 1689:                                 'onsubmit="return newlinksubmit();" >'.
 1690:                                 &Apache::lonhtmlcommon::start_pick_box().
 1691:                                 &Apache::lonhtmlcommon::row_title(&mt('Link Title'));
 1692: 
 1693:     my $in_page_wishlistlink2 = &Apache::lonhtmlcommon::row_closure().
 1694:                                 &Apache::lonhtmlcommon::row_title(&mt('Path'));
 1695: 
 1696:     my $in_page_wishlistlink3 = &Apache::lonhtmlcommon::row_closure().
 1697:                                 &Apache::lonhtmlcommon::row_title(&mt('Note')).
 1698:                                 '<textarea name="note" rows="3" cols="35" style="width:100%"></textarea>'.
 1699:                                 &Apache::lonhtmlcommon::row_closure(1).
 1700:                                 &Apache::lonhtmlcommon::end_pick_box().
 1701:                                 '<br/><br/>'.
 1702:                                 '<input type="submit" value="'.&mt('Save in').'" />'.
 1703:                                 '<select name="folders">'.
 1704:                                 $options.
 1705:                                 '</select>'.
 1706:                                 '<input type="button" value="'.&mt('cancel').'" onclick="javascript:window.close();" />'.
 1707:                                 '</form>';
 1708:     $options = '';
 1709: 
 1710:     # remove all \n for inserting on javascript document.write
 1711:     $in_page_wishlistlink1 =~ s/\n//g;
 1712:     $in_page_wishlistlink2 =~ s/\n//g;
 1713:     $in_page_wishlistlink3 =~ s/\n//g;
 1714: 
 1715:     my $end_page_wishlistlink = 
 1716:        &Apache::loncommon::end_page({'js_ready' => 1});
 1717: 
 1718:     # Add JavaScript-function to set link for a ressource to wishlist
 1719:     my $js.=<<SCRIPT;
 1720:     if(!title){
 1721:         title=document.title;
 1722:     }
 1723:     if(!path){
 1724:         path=location.pathname;
 1725:     }
 1726:     title = title.replace(/^LON-CAPA /,'');
 1727:     wishlistlink=window.open('','wishlistNewLink','width=560,height=350,scrollbars=0');
 1728:     wishlistlink.document.write(
 1729:     '$start_page_wishlistlink'
 1730:     +'<script type="text\/javascript">'
 1731:     +'function newlinksubmit(){'
 1732:     +'var title = document.getElementsByName("title")[0].value;'
 1733:     +'if (!title) {'
 1734:     +'alert("$warningLink");'
 1735:     +'return false;}'
 1736:     +'return true;}'
 1737:     +'<\/scr'+'ipt>'
 1738:     +'$in_page_wishlistlink1'
 1739:     +'<input type="text" name="title" size="45" value="'+title+'"/>'
 1740:     +'$in_page_wishlistlink2'
 1741:     +'<input type="text" name="path" size="45" value="'+path+'" '
 1742:     +'readonly="readonly" style="background-color: #DDDDDD"/>'
 1743:     +'$in_page_wishlistlink3'
 1744:     +'$end_page_wishlistlink' );
 1745:     wishlistlink.document.close();
 1746: SCRIPT
 1747: 
 1748:     return $js;
 1749: }
 1750: 
 1751: # Returns the HTML-Markup for the page, shown when a link was set
 1752: sub makePageSet {
 1753:     # start_page 
 1754:     my $startPage = &Apache::loncommon::start_page('Wishlist',undef,
 1755:                                                    {'only_body' => 1});
 1756:     
 1757:     # confirm success and offer link to wishlist
 1758:     my $message = &Apache::lonhtmlcommon::confirm_success(&mt('Link successfully set!'));
 1759:     $message = &Apache::loncommon::confirmwrapper($message);
 1760: 
 1761:     my $inner .= '<br>'.$message.'<br/><br/>'.
 1762:                  '<a href="javascript:;" onclick="opener.open('."'/adm/wishlist'".');window.close();">'.&mt('Go to wishlist').'</a>'.
 1763:                  '&nbsp;<a href="javascript:;" onclick="window.close();">'.&mt('Close this window').'</a>';
 1764: 
 1765:     # end_page 
 1766:     my $endPage =  &Apache::loncommon::end_page();
 1767: 
 1768:     # put all page-elements together
 1769:     my $page = $startPage.$inner.$endPage;
 1770: 
 1771:     return $page;
 1772: }
 1773: 
 1774: 
 1775: # Returns the HTML-Markup for the page, shown when links should be imported into a course
 1776: sub makePageImport {
 1777:     my $rootgiven = shift;
 1778:     my $rat = shift;
 1779: 
 1780:     $root = $rootgiven;
 1781:     @childrenRt = $root->children();
 1782:     # start_page 
 1783:     my $startPage = &Apache::loncommon::start_page('Wishlist',undef,
 1784:                                                    {'only_body' => 1});
 1785:     
 1786:     # get javascript-code for wishlist-interactions
 1787:     my $js = &JSforWishlist();
 1788:     $js .= &JSforImport($rat);
 1789: 
 1790:     my $inner = '<h1>'.&mt('Import Resources from Wishlist').'</h1>';
 1791:     if (!$rat) {
 1792:         $inner .= '<p><span class="LC_info">'.&mt("Please note that you  can use the checkboxes corresponding to a folder to ".
 1793:                                                   "easily check all links within this folder. The folder structure itself can't be imported. ".
 1794:                                                   "All checked links will be imported into the current folder of your course.").'</span></p>';
 1795:     }
 1796:     else {
 1797:         $inner .= '<p><span class="LC_info">'.&mt("Please note that you  can use the checkboxes corresponding to a folder to ".
 1798:                                                   "easily check all links within this folder. The folder structure itself can't be imported. ")
 1799:                                                   .'</span></p>';
 1800:     }
 1801:     my %wishlist = &getWishlist();
 1802: 
 1803:     #FIXME Saved string containing all folders in wishlist.db-file (key 'folders') in first version of lonwishlist
 1804:     #After splitting lonwishlist into two modules, this is not necessary anymore. So, dependent from when the wishlist
 1805:     #was first called (i.e. when wishlist.db was created), there might be an entry 'folders' or not. Number of links in
 1806:     #wishlist.db depends on wether this entry exists or not...JW  
 1807:     my $fnum;
 1808:     if (defined $wishlist{'folders'}) {
 1809:         $fnum = (keys %wishlist)-2;
 1810:     }
 1811:     else {
 1812:         $fnum = (keys %wishlist)-1;
 1813:     }
 1814: 
 1815:     $inner .= '<form method="post" name="groupsort">'.
 1816:               '<input type="hidden" value="'.$fnum.'" name="fnum">'.
 1817:               '<input type="button" onclick="javascript:checkAll()" id="checkallbutton" value="'.&mt('Check All').'">'.
 1818:               '<input type="button" onclick="javascript:uncheckAll()" id="uncheckallbutton" value="'.&mt('Uncheck All').'">'.
 1819:               '<input type="button" value="'.&mt('Import Checked').'" onclick="finish_import();">'.    
 1820:               '<input type="button" value="'.&mt('Cancel').'" onclick="window.close();"><br/><br/>'; 
 1821: 
 1822:     
 1823:     # wishlist-table
 1824:     &wishlistImport(\@childrenRt);
 1825:     if ($wishlistHTMLimport ne '') {
 1826:         $inner .= '<table class="LC_data_table LC_tableOfContent">'.$wishlistHTMLimport.'</table>';
 1827:     }
 1828:     else {
 1829:         $inner .= '<span class="LC_info">'.&mt("Your wishlist ist currently empty.").'</span>';
 1830:     }
 1831:     $wishlistHTMLimport = '';
 1832: 
 1833:     $inner .= '</form>';
 1834: 
 1835:     # end_page 
 1836:     my $endPage =  &Apache::loncommon::end_page();
 1837: 
 1838:     # put all page-elements together
 1839:     my $page = $startPage.$js.$inner.$endPage;
 1840: 
 1841:     return $page;
 1842: }
 1843: 
 1844: 
 1845: # Returns the HTML-Markup for error-page
 1846: sub makeErrorPage {
 1847:     # breadcrumbs and start_page 
 1848:     &Apache::lonhtmlcommon::add_breadcrumb(
 1849:               { href => '/adm/wishlist',
 1850:                 text => 'Wishlist'});
 1851:     my $startPage = &Apache::loncommon::start_page('Wishlist');
 1852:     
 1853:     my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs(&mt('Wishlist').&Apache::loncommon::help_open_topic('Wishlist'));
 1854:     &Apache::lonhtmlcommon::clear_breadcrumbs();
 1855: 
 1856:     # error-message
 1857:     my $inner .= '<span class="LC_error">'.&mt('An error occurred! Please try again later.').'</span>';
 1858: 
 1859:     # end_page 
 1860:     my $endPage =  &Apache::loncommon::end_page();
 1861: 
 1862:     # put all page-elements together
 1863:     my $page = $startPage.$breadcrumbs.$inner.$endPage;
 1864: 
 1865:     return $page;
 1866: }
 1867: 
 1868: 
 1869: # ----------------------------------------------------- package Tree
 1870: # Extend CPAN-Module Tree by function like 'moveNode' or 'deleteNode'
 1871: package Apache::Tree;
 1872: 
 1873: =pod
 1874: 
 1875: =head2 Routines from package Tree
 1876: 
 1877: =over 4
 1878: 
 1879: =item * &getNodeByIndex(index, nodes)
 1880: 
 1881:      Searches for a node, specified by the index, in nodes (reference to array) and returns it. 
 1882:  
 1883: 
 1884: =item * &moveNode(node, at, newParent)
 1885: 
 1886:      Moves a given node to a new parent (if new parents is defined) or change the position from a node within its siblings (means sorting, at must be defined).
 1887: 
 1888: 
 1889: =item * &removeNode(node)
 1890: 
 1891:      Removes a node given by node from the tree.
 1892: 
 1893: 
 1894: =item * &TreeIndex(children)
 1895: 
 1896:      Sets an index for every node in the tree, beginning with 0.
 1897:      Recursive call starting with all children of the root of the tree (parameter children is reference to an array containing the nodes of the current level).     
 1898: 
 1899: 
 1900: =item * &setCountZero()
 1901: 
 1902:      Resets index counter.
 1903: 
 1904: 
 1905: =item * &RootToHash(childrenRt)
 1906: 
 1907:      Converts the root-node to a hash-entry: the key is root and values are just the indices of root's children.
 1908:    
 1909: 
 1910: =item * &TreeToHash(childrenRt)
 1911: 
 1912:      Converts all other nodes in the tree to hash. Each node is one hash-entry where the keys are the index of a node and the values are all other attributes (containing tile, path, note, date and indices for all direct children).
 1913:      Recursive call starting with all children of the root of the tree (parameter childrenRT is reference to an array containing the nodes of the current level).     
 1914: 
 1915: 
 1916: =item * &HashToTree()
 1917: 
 1918:      Converts the hash to a tree. Builds a tree-object for each entry in the hash. Afterwards call &buildTree(node, childrenIn, TreeNodes, TreeHash) to connect the tree-objects.
 1919: 
 1920: 
 1921: =item * &buildTree(node, childrenIn, TreeNodes, TreeHash)
 1922: 
 1923:      Joins the nodes to a tree.
 1924:      Recursive call starting with root and all children of root (parameter childrenIn is reference to an array containing the nodes indices of the current level).
 1925:    
 1926: 
 1927: =back
 1928: 
 1929: =cut
 1930: 
 1931: 
 1932: # returns the node with a given index from a list of nodes
 1933: sub getNodeByIndex {
 1934:     my $index = shift;
 1935:     my $nodes = shift;
 1936:     my $found;
 1937:     
 1938:     foreach my $n (@$nodes) {
 1939:         my $curIndex = $n->value()->nindex();
 1940:         if ($curIndex == $index) {
 1941:             $found = $n;
 1942:         }
 1943:     }
 1944:     return $found;
 1945: }
 1946: 
 1947: # moves a given node to a new parent or change the position from a node
 1948: # within its siblings (sorting)
 1949: sub moveNode {
 1950:     my $node = shift;
 1951:     my $at = shift;
 1952:     my $newParent = shift;
 1953: 
 1954: 
 1955:     if (!$newParent) {
 1956:         $newParent = $node->parent();
 1957:     }
 1958: 
 1959:     $node->parent()->remove_child($node);
 1960: 
 1961:     if (defined $at) {
 1962:         $newParent->add_child({at => $at},$node);
 1963:     }
 1964:     else {
 1965:         $newParent->add_child($node);
 1966:     }
 1967:     
 1968:     # updating root's children
 1969:     @childrenRt = $root->children();
 1970: }
 1971: 
 1972: # removes a given node
 1973: sub removeNode() {
 1974:     my $node = shift;
 1975:     my @children = $node->children();
 1976: 
 1977:     if ($#children >= 0) {
 1978:         foreach my $c (@children) {
 1979:             &removeNode($c);
 1980:         }
 1981:     }
 1982:     $node->parent()->remove_child($node);
 1983: 
 1984:     # updating root's children
 1985:     @childrenRt = $root->children();
 1986: }
 1987: 
 1988: 
 1989: # set an index for every node in the tree, beginning with 0
 1990: my $count = 0;
 1991: sub TreeIndex {
 1992:     my $children = shift;
 1993: 
 1994:     foreach my $n (@$children) {
 1995:         my @children = $n->children();
 1996:         $n->value()->nindex($count);$count++;
 1997: 
 1998:         if ($#children>=0) {
 1999:             &TreeIndex(\@children);
 2000:         }
 2001:     }
 2002: }
 2003: 
 2004: # reset index counter
 2005: sub setCountZero {
 2006:     $count = 0;
 2007: }
 2008: 
 2009: 
 2010: # convert the tree to a hash
 2011: # each node is one hash-entry
 2012: # keys are the indices, values are all other attributes
 2013: # (containing tile, path, note, date and indices for all direct children)
 2014: # except for root: the key is root and values are
 2015: # just the indices of root's children
 2016: sub RootToHash {
 2017:     my $childrenRt = shift;
 2018:     my @indexarr = ();
 2019: 
 2020:     foreach my $c (@$childrenRt) {
 2021:        push (@indexarr, $c->value()->nindex());
 2022:     }
 2023:     $TreeToHash{'root'} = [@indexarr];
 2024: }
 2025: 
 2026: sub TreeToHash {
 2027:     my $childrenRt = shift;
 2028: 
 2029:     foreach my $n (@$childrenRt) {
 2030:         my @arrtmp = ();
 2031:         $arrtmp[0] = $n->value()->title();
 2032:         $arrtmp[1] = $n->value()->path();
 2033:         $arrtmp[2] = $n->value()->note();
 2034:         $arrtmp[3] = $n->value()->date();
 2035:         my @childrenRt = $n->children();
 2036:         my $co = 4;
 2037:         foreach my $c (@childrenRt) {
 2038:             my $i = $c->value()->nindex();
 2039:             $arrtmp[$co] = $i;
 2040:             $co++;
 2041:         }
 2042:         $TreeToHash{$n->value()->nindex} = [ @arrtmp]; 
 2043:         if ($#childrenRt>=0) {
 2044:             &TreeToHash(\@childrenRt);
 2045:         }
 2046:     }
 2047: }
 2048: 
 2049: 
 2050: # convert the hash to a tree
 2051: # build a tree-object for each entry in the hash
 2052: # afterwards call &buildTree to connect the tree-objects
 2053: sub HashToTree {
 2054:     my $TreeHash = shift;
 2055:     my @TreeNodes = ();
 2056:     my $root;
 2057: 
 2058:     foreach my $key (keys %$TreeHash) {
 2059:         if ($key eq 'root') {
 2060:             $root = Tree->new("root");
 2061:         }
 2062:         elsif ($key ne 'folders') {
 2063:         my @attributes = @{ $$TreeHash{$key} };
 2064:         my $tmpNode;
 2065:             $tmpNode = Tree->new(Entry->new(title=>$attributes[0],
 2066:                                             path=>$attributes[1],
 2067:                                             note=>$attributes[2],
 2068:                                             date=>$attributes[3],
 2069:                                             nindex=>$key));
 2070:         push(@TreeNodes, $tmpNode);
 2071:         # shift all attributes except for
 2072:         # the indices representing the children of a node
 2073:         shift(@attributes);
 2074:         shift(@attributes);
 2075:         shift(@attributes);
 2076:         shift(@attributes);
 2077:         $$TreeHash{$key} = [ @attributes ];
 2078:         }
 2079:     }
 2080:     # if there are nodes, build up the tree-structure
 2081:     if (defined $$TreeHash{'root'} && $$TreeHash{'root'} ne '') {
 2082:         my @childrenRtIn = @{ $$TreeHash{'root'} };
 2083:         &buildTree(\$root, \@childrenRtIn,\@TreeNodes,$TreeHash);
 2084:     }
 2085:     return $root; 
 2086: }
 2087: 
 2088: 
 2089: # join the nodes to a tree
 2090: sub buildTree {
 2091:     my ($node, $childrenIn, $TreeNodes, $TreeHash) = @_;
 2092:     bless($node, 'Tree');
 2093:     foreach my $c (@$childrenIn) {
 2094:         my $tmpNode =  &getNodeByIndex($c,$TreeNodes);
 2095:         $$node->add_child($tmpNode);
 2096:         my @childrenIn = @{ $$TreeHash{$tmpNode->value()->nindex()} };
 2097:         &buildTree(\$tmpNode,\@childrenIn,$TreeNodes,$TreeHash);
 2098:     }
 2099: 
 2100: }
 2101: 
 2102: 
 2103: # ----------------------------------------------------- package Entry
 2104: # package that defines the entrys a wishlist could have
 2105: # i.e. folders and links
 2106: package Entry;
 2107: 
 2108: # constructor
 2109: sub new {
 2110:     my $invocant = shift;
 2111:     my $class = ref($invocant) || $invocant;
 2112:     my $self = { @_ }; #set attributes
 2113:     bless($self, $class);
 2114:     return $self;    
 2115: }
 2116: 
 2117: # getter and setter
 2118: sub title {
 2119:     my $self = shift;
 2120:     if ( @_ ) { $self->{title} = shift}
 2121:     return $self->{title};
 2122: }
 2123: 
 2124: sub date {
 2125:     my $self = shift;
 2126:     if ( @_ ) { $self->{date} = shift}
 2127:     return $self->{date};
 2128: }
 2129: 
 2130: sub note {
 2131:     my $self = shift;
 2132:     if ( @_ ) { $self->{note} = shift}
 2133:     return $self->{note};
 2134: }
 2135: 
 2136: sub path {
 2137:     my $self = shift;
 2138:     if ( @_ ) { $self->{path} = shift}
 2139:     return $self->{path};
 2140: }
 2141: 
 2142: sub nindex {
 2143:     my $self = shift;
 2144:     if ( @_ ) { $self->{nindex} = shift}
 2145:     return $self->{nindex};
 2146: }
 2147: 
 2148: 
 2149: 1;
 2150: __END__

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