--- loncom/interface/lonwishlist.pm 2010/08/16 15:14:37 1.4 +++ loncom/interface/lonwishlist.pm 2011/01/27 14:38:44 1.9 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA -# Routines to control the wishlist +# Utility-routines for wishlist # -# $Id: lonwishlist.pm,v 1.4 2010/08/16 15:14:37 wenzelju Exp $ +# $Id: lonwishlist.pm,v 1.9 2011/01/27 14:38:44 wenzelju Exp $ # # Copyright Michigan State University Board of Trustees # @@ -39,12 +39,13 @@ It is only available for user with acces 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. +=back + =cut package Apache::lonwishlist; use strict; -use Apache::Constants qw(:common); use Apache::lonnet; use Apache::loncommon(); use Apache::lonhtmlcommon; @@ -61,6 +62,7 @@ my %TreeToHash; my @allFolders; my @allNodes; my $indentConst = 20; +my $foldersOption; =pod @@ -70,7 +72,7 @@ my $indentConst = 20; =item * &getWishlist() - Get the wishlist-data via lonnet::dump() and returns the got data in a hash. + Get the wishlist-data via lonnet::getkeys() and lonnet::get() and returns the got data in a hash. =item * &putWishlist(wishlist) @@ -90,14 +92,18 @@ my $indentConst = 20; # Read wishlist from user-data sub getWishlist { - my %wishlist = &Apache::lonnet::dump('wishlist'); + my @keys = &Apache::lonnet::getkeys('wishlist'); + my %wishlist = &Apache::lonnet::get('wishlist',\@keys); foreach my $i ( keys %wishlist) { #File not found. This appears at the first time using the wishlist #Create file and put 'root' into it if ($i =~m/^error:No such file/) { &Apache::lonnet::logthis($i.'! Create file by putting in the "root" of the directory tree.'); &Apache::lonnet::put('wishlist', {'root' => ''}); - %wishlist = &Apache::lonnet::dump('wishlist'); + my $options = ''; + &Apache::lonnet::put('wishlist', {'folders' => $options}); + @keys = &Apache::lonnet::getkeys('wishlist'); + %wishlist = &Apache::lonnet::get('wishlist',\@keys); } elsif ($i =~ /^(con_lost|error|no_such_host)/i) { &Apache::lonnet::logthis('ERROR while attempting to get wishlist: '.$i); @@ -105,7 +111,7 @@ sub getWishlist { } } - # if we got no keys in hash returned by dump(), return error. + # if we got no keys in hash returned by get(), return error. # wishlist will not be loaded, instead the user will be asked to try again later if ((keys %wishlist) == 0) { &Apache::lonnet::logthis('ERROR while attempting to get wishlist: no keys retrieved!'); @@ -183,7 +189,11 @@ sub deleteWishlist { # Create a new entry sub newEntry() { - my ($title, $path, $note) = @_; + my ($rootgiven, $title, $path, $note) = @_; + + $root = $rootgiven; + @childrenRt = $root->children(); + my $date = gmtime(); # Create Entry-Object my $entry = Entry->new(title => $title, path => $path, note => $note, date => $date); @@ -194,53 +204,69 @@ sub newEntry() { if ($folderIndex ne '') { @allFolders = (); &getFoldersToArray(\@childrenRt); - my $folderToInsertOn = &Tree::getNodeByIndex($folderIndex,\@allFolders); + my $folderToInsertOn = &Apache::Tree::getNodeByIndex($folderIndex,\@allFolders); $folderToInsertOn->add_child($tree); } else { $root->add_child($tree); } - &saveChanges(); + return &saveChanges(); } # Delete entries sub deleteEntries { + my $rootgiven = shift; my $marked = shift; - &getNodesToArray(\@childrenRt); + $root = $rootgiven; + @childrenRt = $root->children(); + + &getNodesToArray(\@childrenRt); foreach my $m (@$marked) { - my $found = &Tree::getNodeByIndex($m, \@allNodes); - &Tree::removeNode($found); + my $found = &Apache::Tree::getNodeByIndex($m, \@allNodes); + # be sure, that entry exists (may have been deleted before, e.g. in an other browsertab) + if (defined $found) { + &Apache::Tree::removeNode($found); + } } @allNodes = (); - &saveChanges(); + return &saveChanges(); } # Sort entries sub sortEntries { + my $rootgiven = shift; my $indexNode = shift; my $at = shift; + + $root = $rootgiven; + @childrenRt = $root->children(); &getNodesToArray(\@childrenRt); - my $foundNode = &Tree::getNodeByIndex($indexNode, \@allNodes); + my $foundNode = &Apache::Tree::getNodeByIndex($indexNode, \@allNodes); - &Tree::moveNode($foundNode,$at,undef); + &Apache::Tree::moveNode($foundNode,$at,undef); @allNodes = (); + return &saveChanges(); } # Move entries sub moveEntries { + my $rootgiven = shift; my $indexNodesToMove = shift; my $indexParent = shift; my @nodesToMove = (); + $root = $rootgiven; + @childrenRt = $root->children(); + # get all nodes that should be moved &getNodesToArray(\@childrenRt); foreach my $index (@$indexNodesToMove) { - my $foundNode = &Tree::getNodeByIndex($index, \@allNodes); + my $foundNode = &Apache::Tree::getNodeByIndex($index, \@allNodes); push(@nodesToMove, $foundNode); } @@ -256,36 +282,46 @@ sub moveEntries { } if (!$parentIsIn) { if ($indexParent ne "root") { - $foundParent = &Tree::getNodeByIndex($indexParent, \@allNodes); - &Tree::moveNode($node,undef,$foundParent); + $foundParent = &Apache::Tree::getNodeByIndex($indexParent, \@allNodes); + &Apache::Tree::moveNode($node,undef,$foundParent); } else { - &Tree::moveNode($node,undef,$root); + &Apache::Tree::moveNode($node,undef,$root); } } } @allNodes = (); + return &saveChanges(); } # Set a new title for an entry sub setNewTitle { - my ($nodeindex, $newTitle) = @_; + my ($rootgiven, $nodeindex, $newTitle) = @_; + + $root = $rootgiven; + @childrenRt = $root->children(); + &getNodesToArray(\@childrenRt); - my $found = &Tree::getNodeByIndex($nodeindex, \@allNodes); + my $found = &Apache::Tree::getNodeByIndex($nodeindex, \@allNodes); $found->value()->title($newTitle); @allNodes = (); + return &saveChanges(); } # Set a new path for an entry sub setNewPath { - my ($nodeindex, $newPath) = @_; + my ($rootgiven, $nodeindex, $newPath) = @_; + + $root = $rootgiven; + @childrenRt = $root->children(); + &getNodesToArray(\@childrenRt); - my $found = &Tree::getNodeByIndex($nodeindex, \@allNodes); + my $found = &Apache::Tree::getNodeByIndex($nodeindex, \@allNodes); if ($found->value()->path()) { $found->value()->path($newPath); - return 1; + return &saveChanges(); } @allNodes = (); return 0; @@ -294,23 +330,29 @@ sub setNewPath { # Set a new note for an entry sub setNewNote { - my ($nodeindex, $newNote) = @_; + my ($rootgiven, $nodeindex, $newNote) = @_; + + $root = $rootgiven; + @childrenRt = $root->children(); + &getNodesToArray(\@childrenRt); - my $found = &Tree::getNodeByIndex($nodeindex, \@allNodes); + my $found = &Apache::Tree::getNodeByIndex($nodeindex, \@allNodes); $found->value()->note($newNote); @allNodes = (); + return &saveChanges(); } # Save all changes sub saveChanges { @childrenRt = $root->children(); - &Tree::TreeIndex(\@childrenRt); - &Tree::setCountZero(); - &Tree::RootToHash(\@childrenRt); - &Tree::TreeToHash(\@childrenRt); + &Apache::Tree::TreeIndex(\@childrenRt); + &Apache::Tree::setCountZero(); + &Apache::Tree::RootToHash(\@childrenRt); + &Apache::Tree::TreeToHash(\@childrenRt); &deleteWishlist(); &putWishlist(\%TreeToHash); + return $root; } @@ -327,11 +369,6 @@ sub saveChanges { Recursive call starting with all children of the root of the tree (parameter nodes is reference to an array containing the nodes of the current level). -=item * &getfoldersOption() - - Returns the option-tag build by &getFoldersForOption(nodes). Use it to transfer this to other modules (e.g. lonmenu.pm). - - =item * &getFoldersToArray(children) Puts all nodes that represent folders in the wishlist into an array. @@ -344,7 +381,7 @@ sub saveChanges { Recursive call starting with all children of the root of the tree (parameter nodes is reference to an array containing the nodes of the current level). - =back +=back =cut @@ -352,7 +389,6 @@ sub saveChanges { # Return the names for all exiting folders in option-tags, so # a new link or a new folder can be created in an existing folder my $indent = 0; -my $foldersOption; sub getFoldersForOption { my $nodes = shift; @@ -373,22 +409,6 @@ sub getFoldersForOption { } -sub getfoldersOption { - if (&getWishlist ne 'error') { - %TreeHash = &getWishlist(); - $root = &Tree::HashToTree(); - @childrenRt = $root->children(); - &getFoldersForOption(\@childrenRt); - my $options = ''.$foldersOption; - $foldersOption = ''; - return $options; - } - else { - return ''; - } -} - - # Put all folder-nodes to an array sub getFoldersToArray { my $children = shift; @@ -565,7 +585,8 @@ sub JSforWishlist { my $warningFolder = &mt('You must insert a title!'); my $warningDelete = &mt('Are you sure you want to delete the selected entries? Deleting a folder also deletes all entries within this folder!'); my $warningSave = &mt('You have unsaved changes. You can either save these changes now by clicking "ok" or click "cancel" if you do not want to save your changes.'); - my $warningMove = &mt('You must select a destination folder!'); + my $warningMoveS = &mt('You must select at minimum one entry to move!'); + my $warningMoveD = &mt('You must select a destination folder!'); $foldersOption = ''; my $js = &Apache::lonhtmlcommon::scripttag(<children(); + # breadcrumbs and start_page &Apache::lonhtmlcommon::clear_breadcrumbs(); &Apache::lonhtmlcommon::add_breadcrumb( @@ -1427,7 +1508,7 @@ sub makePage { 'onload' => 'javascript:onLoadAction('."'".$mode."'".');', 'onunload' => 'javascript:window.name = '."'loncapaclient'"}}); - my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs('Wishlist '.&Apache::loncommon::help_open_topic('Wishlist')); + my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs(&mt('Wishlist').&Apache::loncommon::help_open_topic('Wishlist')); # get javascript-code for wishlist-interactions my $js = &JSforWishlist(); @@ -1545,7 +1626,7 @@ sub makePage { 'Now choose the new destination folder.').'

'; &wishlistMove(\@childrenRt, $marked); $inner .= ''.$wishlistHTMLmove.'


'; - $inner .= ''. + $inner .= ''. ''; $wishlistHTMLmove =''. @@ -1570,6 +1651,103 @@ sub makePage { } +# Returns the HTML-Markup for the PopUp, shown when a new link should set, when NOT +# beeing in the wishlist-interface (method is called in lonmenu and lonsearchcat) +sub makePopUpNewLink{ + + # Get all existing folders to offer posibility to set a new link + # into a folder + my %TreeHashLink = &Apache::lonwishlist::getWishlist(); + my $rootLink = &Apache::Tree::HashToTree(\%TreeHashLink); + my @childrenRtLink = $rootLink->children(); + + $foldersOption = ''; + @allFolders = (); + &getFoldersToArray(\@childrenRtLink); + &getFoldersForOption(\@childrenRtLink); + + my $options = ''.$foldersOption; + $foldersOption = ''; + @allFolders = (); + + # HTML-Markup for 'Set a link for this resource to wishlist' + # this is written via JavaScript document.write (function set_wishlistlink) + # it is split into 3 parts and the inputfields for title and path are left out + # these fields are inserted later to set the values for title and path + # automatically via JavaScript (document.title and location.pathname) + + my $start_page_wishlistlink = + &Apache::loncommon::start_page('Set link to wishlist',undef, + {'only_body' => 1, + 'js_ready' => 1, + 'bgcolor' => '#FFFFFF',}); + + my $warningLink = &mt('You must insert a title!'); + + my $in_page_wishlistlink1 = '

'.&mt('Set a link to wishlist').'

'. + '
'. + &Apache::lonhtmlcommon::start_pick_box(). + &Apache::lonhtmlcommon::row_title(&mt('Link Title')); + + my $in_page_wishlistlink2 = &Apache::lonhtmlcommon::row_closure(). + &Apache::lonhtmlcommon::row_title(&mt('Path')); + + my $in_page_wishlistlink3 = &Apache::lonhtmlcommon::row_closure(). + &Apache::lonhtmlcommon::row_title(&mt('Note')). + ''. + &Apache::lonhtmlcommon::row_closure(1). + &Apache::lonhtmlcommon::end_pick_box(). + '

'. + ''. + ''. + ''. + '
'; + $options = ''; + + # remove all \n for inserting on javascript document.write + $in_page_wishlistlink1 =~ s/\n//g; + $in_page_wishlistlink2 =~ s/\n//g; + $in_page_wishlistlink3 =~ s/\n//g; + + my $end_page_wishlistlink = + &Apache::loncommon::end_page({'js_ready' => 1}); + + # Add JavaScript-function to set link for a ressource to wishlist + my $js.=<' + +'function newlinksubmit(){' + +'var title = document.getElementsByName("title")[0].value;' + +'if (!title) {' + +'alert("$warningLink");' + +'return false;}' + +'return true;}' + +'<\/scr'+'ipt>' + +'$in_page_wishlistlink1' + +'' + +'$in_page_wishlistlink2' + +'' + +'$in_page_wishlistlink3' + +'$end_page_wishlistlink' ); + wishlistlink.document.close(); +SCRIPT + + return $js; +} + # Returns the HTML-Markup for the page, shown when a link was set sub makePageSet { # start_page @@ -1596,20 +1774,43 @@ sub makePageSet { # Returns the HTML-Markup for the page, shown when links should be imported into a course sub makePageImport { + my $rootgiven = shift; + my $rat = shift; + + $root = $rootgiven; + @childrenRt = $root->children(); # start_page my $startPage = &Apache::loncommon::start_page('Wishlist',undef, {'only_body' => 1}); # get javascript-code for wishlist-interactions my $js = &JSforWishlist(); + $js .= &JSforImport($rat); my $inner = '

'.&mt('Import Resources from Wishlist').'

'; - $inner .= '

'.&mt("Please note that you can use the checkboxes corresponding to a folder to ". - "easily check all links within this folder. The folder structure itself can't be imported. ". - "All checked links will be imported into the current folder of your course.").'

'; - + if (!$rat) { + $inner .= '

'.&mt("Please note that you can use the checkboxes corresponding to a folder to ". + "easily check all links within this folder. The folder structure itself can't be imported. ". + "All checked links will be imported into the current folder of your course.").'

'; + } + else { + $inner .= '

'.&mt("Please note that you can use the checkboxes corresponding to a folder to ". + "easily check all links within this folder. The folder structure itself can't be imported. ") + .'

'; + } my %wishlist = &getWishlist(); - my $fnum = (keys %wishlist)-1; + + #FIXME Saved string containing all folders in wishlist.db-file (key 'folders') in first version of lonwishlist + #After splitting lonwishlist into two modules, this is not necessary anymore. So, dependent from when the wishlist + #was first called (i.e. when wishlist.db was created), there might be an entry 'folders' or not. Number of links in + #wishlist.db depends on wether this entry exists or not...JW + my $fnum; + if (defined $wishlist{'folders'}) { + $fnum = (keys %wishlist)-2; + } + else { + $fnum = (keys %wishlist)-1; + } $inner .= '
'. ''. @@ -1649,10 +1850,7 @@ sub makeErrorPage { text => 'Wishlist'}); my $startPage = &Apache::loncommon::start_page('Wishlist'); - my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs('Wishlist '. - ''. - ''.&mt('Help').''); + my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs(&mt('Wishlist').&Apache::loncommon::help_open_topic('Wishlist')); &Apache::lonhtmlcommon::clear_breadcrumbs(); # error-message @@ -1667,133 +1865,10 @@ sub makeErrorPage { return $page; } -# ----------------------------------------------------- Main Handler, package lonwishlist -sub handler { - my ($r) = @_; - &Apache::loncommon::content_type($r,'text/html'); - $r->send_http_header; - - if (&getWishlist() ne 'error') { - # get wishlist entries from user-data db-file and build a tree out of these entries - %TreeHash = &getWishlist(); - $root = &Tree::HashToTree(); - @childrenRt = $root->children(); - - # greate a new entry - if ($env{'form.title'}) { - &newEntry($env{'form.title'}, $env{'form.path'}, $env{'form.note'}); - } - - # get unprocessed_cgi (i.e. marked entries, mode ...) - &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['action','mark','markedToMove','mode','newtitle','note']); - - # change the order of entries within a level, that means sorting the entries - my $changeOrder = 0; - if (defined $env{'form.sel'}) { - my @sel = &Apache::loncommon::get_env_multiple('form.sel'); - my $indexNode; - my $at; - for (my $s=0; $s<($#sel+1); $s++) { - if ($sel[$s] ne '') { - $indexNode = $s; - $at = $sel[$s]-1; - } - } - if ($at ne '') { - $changeOrder = 1; - &sortEntries($indexNode,$at); - &saveChanges(); - } - } - - # get all marked (checkboxes) entries - my @marked = (); - if (defined $env{'form.mark'}) { - @marked = &Apache::loncommon::get_env_multiple('form.mark'); - } - - # move entries from one folder to another - if (defined $env{'form.markedToMove'}) { - my $markedToMove = $env{'form.markedToMove'}; - my @ToMove = split(/\,/,$markedToMove); - my $moveTo = $env{'form.mark'}; - if (defined $moveTo){ - &moveEntries(\@ToMove,$moveTo); - &saveChanges(); - } - $changeOrder = 1; - - } - - # delete entries - if ($env{'form.action'} eq 'delete') { - &deleteEntries(\@marked); - } - - - # get all titles and notes and save them - # only save, if user wants to save changes - # do not save, when current action is 'delete' or 'sort' or 'move' - my @newTitles = (); - my @newPaths = (); - my @newNotes = (); - if ((defined $env{'form.newtitle'} || defined $env{'form.newpath'} || defined $env{'form.newnote'}) - && ($env{'form.action'} ne 'noSave') && ($env{'form.action'} ne 'delete') && !$changeOrder) { - @newTitles = &Apache::loncommon::get_env_multiple('form.newtitle'); - @newPaths = &Apache::loncommon::get_env_multiple('form.newpath'); - @newNotes = &Apache::loncommon::get_env_multiple('form.newnote'); - my $node = 0; - foreach my $t (@newTitles) { - &setNewTitle($node, $t); - $node++; - } - $node = 0; - my $path = 0; - for (my $i = 0; $i < ($#newTitles+1); $i++ ) { - if (&setNewPath($node, $newPaths[$path])) { - $path++; - } - $node++; - } - $node = 0; - foreach my $n (@newNotes) { - &setNewNote($node, $n); - $node++; - } - &saveChanges(); - } - - # Create HTML-markup - my $page; - if ($env{'form.mode'} eq 'edit') { - $page = &makePage("edit"); - } - elsif ($env{'form.mode'} eq 'move') { - $page = &makePage("move", \@marked); - } - elsif ($env{'form.mode'} eq 'import') { - $page = &makePageImport(); - } - elsif ($env{'form.mode'} eq 'set') { - $page = &makePageSet(); - } - else { - $page = &makePage("view"); - } - @marked = (); - $r->print($page); - } - # An error occured, print an error-page - else { - my $errorPage = &makeErrorPage(); - $r->print($errorPage); - } - return OK; -} # ----------------------------------------------------- package Tree # Extend CPAN-Module Tree by function like 'moveNode' or 'deleteNode' -package Tree; +package Apache::Tree; =pod @@ -1860,9 +1935,9 @@ sub getNodeByIndex { my $nodes = shift; my $found; - for my $n (@$nodes) { + foreach my $n (@$nodes) { my $curIndex = $n->value()->nindex(); - if ($n->value()->nindex() == $index) { + if ($curIndex == $index) { $found = $n; } } @@ -1976,15 +2051,16 @@ sub TreeToHash { # build a tree-object for each entry in the hash # afterwards call &buildTree to connect the tree-objects sub HashToTree { + my $TreeHash = shift; my @TreeNodes = (); my $root; - foreach my $key (keys %TreeHash) { + foreach my $key (keys %$TreeHash) { if ($key eq 'root') { $root = Tree->new("root"); } - else { - my @attributes = @{ $TreeHash{$key} }; + elsif ($key ne 'folders') { + my @attributes = @{ $$TreeHash{$key} }; my $tmpNode; $tmpNode = Tree->new(Entry->new(title=>$attributes[0], path=>$attributes[1], @@ -1998,13 +2074,13 @@ sub HashToTree { shift(@attributes); shift(@attributes); shift(@attributes); - $TreeHash{$key} = [ @attributes ]; + $$TreeHash{$key} = [ @attributes ]; } } # if there are nodes, build up the tree-structure - if (defined $TreeHash{'root'} && $TreeHash{'root'} ne '') { - my @childrenRtIn = @{ $TreeHash{'root'} }; - &buildTree(\$root, \@childrenRtIn,\@TreeNodes,\%TreeHash); + if (defined $$TreeHash{'root'} && $$TreeHash{'root'} ne '') { + my @childrenRtIn = @{ $$TreeHash{'root'} }; + &buildTree(\$root, \@childrenRtIn,\@TreeNodes,$TreeHash); } return $root; }