--- loncom/interface/lonwishlist.pm 2010/08/16 08:58:39 1.2 +++ 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.2 2010/08/16 08:58:39 wenzelju Exp $ +# $Id: lonwishlist.pm,v 1.9 2011/01/27 14:38:44 wenzelju Exp $ # # Copyright Michigan State University Board of Trustees # @@ -26,12 +26,26 @@ # http://www.lon-capa.org/ # +=pod +=head1 NAME + +Apache::lonwishlist - Wishlist-Module + +=head1 SYNOPSIS + +The wishlist offers a possibility to store links to resources from the resource-pool and external websites in a hierarchical list. +It is only available for user with access to the resource-pool. The list can be structured by folders. + +The wishlist-module uses the CPAN-module "Tree" for easily handling the directory-structure of the wishlist. Each node in the tree has an index to be referenced by. + +=back + +=cut package Apache::lonwishlist; use strict; -use Apache::Constants qw(:common); use Apache::lonnet; use Apache::loncommon(); use Apache::lonhtmlcommon; @@ -48,18 +62,48 @@ my %TreeToHash; my @allFolders; my @allNodes; my $indentConst = 20; +my $foldersOption; + +=pod + +=head2 Routines for getting and putting the wishlist data from and accordingly to users data. + +=over 4 + +=item * &getWishlist() + + Get the wishlist-data via lonnet::getkeys() and lonnet::get() and returns the got data in a hash. + + +=item * &putWishlist(wishlist) + + Parameter is a reference to a hash. Puts the wishlist-data contained in the given hash via lonnet::put() to user-data. + + +=item * &deleteWishlist() + + Deletes all entries from the user-data for wishlist. Do this before putting in new data. + + +=back + +=cut # Read wishlist from user-data 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); @@ -67,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!'); @@ -92,9 +136,64 @@ sub deleteWishlist { } +=pod + +=head2 Routines for changing the directory struture of the wishlist. + +=over 4 + +=item * &newEntry(title, path, note) + + Creates a new entry in the wishlist containing the given informations. Additionally saves the date of creation in the entry. + + +=item * &deleteEntries(marked) + + Parameter is a reference to an array containing the indices of all nodes that should be removed from the tree. + + +=item * &sortEntries(indexNode, at) + + Changes the position of a node given by indexNode within its siblings. New position is given by at. + + +=item * &moveEntries(indexNodesToMove, indexParent) + + Parameter is a reference to an array containing the indices of all nodes that should be moved. indexParent specifies the node that will become the new Parent for these nodes. + + +=item * &setNewTitle(nodeindex, newTitle) + + Sets the title for the node given by nodeindex to newTitle. + + +=item * &setNewPath(nodeindex, newPath) + + Sets the path for the node given by nodeindex to newPath. + + +=item * &setNewNote(nodeindex, newNote) + + Sets the note for the node given by nodeindex to newNote. + + +=item * &saveChanges() + + Prepares the wishlist-hash to save it via &putWishlist(wishlist). + + +=back + +=cut + + # Create a new entry 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); @@ -105,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); } @@ -167,55 +282,113 @@ 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 ($rootgiven, $nodeindex, $newPath) = @_; + + $root = $rootgiven; + @childrenRt = $root->children(); + + &getNodesToArray(\@childrenRt); + my $found = &Apache::Tree::getNodeByIndex($nodeindex, \@allNodes); + if ($found->value()->path()) { + $found->value()->path($newPath); + return &saveChanges(); + } + @allNodes = (); + return 0; } # 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; } +=pod + +=head2 Routines for handling the directory structure + +=over 4 + +=item * &getFoldersForOption(nodes) + + Return the titles for all exiting folders in an option-tag, used to offer the users a possibility to create a new link or folder in an existing folder. + Recursive call starting with all children of the root of the tree (parameter nodes is reference to an array containing the nodes of the current level). + + +=item * &getFoldersToArray(children) + + Puts all nodes that represent folders in the wishlist into an array. + Recursive call starting with all children of the root of the tree (parameter nodes is reference to an array containing the nodes of the current level). + + +=item * &getNodesToArray(children) + + Puts all existing nodes into an array (apart from the root node, because this one does not represent an entry in the wishlist). + Recursive call starting with all children of the root of the tree (parameter nodes is reference to an array containing the nodes of the current level). + + +=back + +=cut + + # Return the names for all exiting folders in option-tags, so # a new link or a new folder can be created in an existing folder my $indent = 0; -my $foldersOption; sub getFoldersForOption { my $nodes = shift; @@ -236,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; @@ -280,6 +437,67 @@ sub getNodesToArray { } +=pod + +=head2 Routines for the user-interface of the wishlist + +=over 4 + +=item * &JSforWishlist() + + Returns JavaScript-functions needed for wishlist actions like open and close folders. + + +=item * &wishlistView(nodes) + + Returns the table-HTML-markup for the wishlist in mode "view". + Recursive call starting with all children of the root of the tree (parameter nodes is reference to an array containing the nodes of the current level). + + +=item * &wishlistEdit(nodes) + + Returns the table-HTML-markup for the wishlist in mode "edit". + Recursive call starting with all children of the root of the tree (parameter nodes is reference to an array containing the nodes of the current level). + + +=item * &wishlistMove(nodes, marked) + + Returns the table-HTML-markup for the wishlist in mode "move". Highlights all entry "selected to move" contained in marked (reference to array). + Recursive call starting with all children of the root of the tree (parameter nodes is reference to an array containing the nodes of the current level). + + +=item * &wishlistImport(nodes) + + Returns the table-HTML-markup for the wishlist in mode "import". + Recursive call starting with all children of the root of the tree (parameter nodes is reference to an array containing the nodes of the current level). + + +=item * &makePage(mode, marked) + + Returns the HTML-markup for the whole wishlist depending on mode. If mode is "move" we need the marked entries to be highlighted a "selected to move". + Calls &wishlistView(nodes), &wishlistEdit(nodes) or &wishlistMove(nodes, marked). + + +=item * &makePageSet() + + Returns the HTML-Markup for the page shown when a link was set by using the icon when viewing a resource. + + +=item * &makePageImport() + + Returns the HTML-Markup for the page shown when links should be imported into courses. + + +=item * &makeErrorPage () + + Returns the HTML-Markup for an error-page shown if the wishlist could not be loaded. + + +=back + +=cut + + # Return a script-tag containing Javascript-function # needed for wishlist actions like 'new link' ect. sub JSforWishlist { @@ -352,11 +570,23 @@ sub JSforWishlist { $inPageNewLink =~ s/\n//g; $inPageNewFolder =~ s/\n//g; + # it is checked, wether a path links to a LON-CAPA-resource or an external website. links to course-contents are not allowed + # because they probably will return a kind of 'no access' (unless the user is already in the course, the path links to). + # also importing these kind of links into a course does not make much sense. + # to find out if a path (not starting with /res/...) links to course-contents, the same filter as in lonwrapper is used, + # that means that it is checked wether a path contains .problem, .quiz, .exam etc. + # this is good for most cases but crashes as soon as a real external website contains one of this pattern in its URL. + # so maybe there's a better way to find out wether a given URL belongs to a LON-CAPA-server or not ...? + my $warningLinkNotAllowed1 = &mt('You can only insert links to LON-CAPA resources from the resource-pool '. + 'or to external websites. Paths to LON-CAPA resources must be of the form /res/dom/usr... . '. + 'Paths to external websites must contain the network protocol (e.g. http://...).'); + my $warningLinkNotAllowed2 = &mt('The following link is not allowed: '); my $warningLink = &mt('You must insert a title and a path!'); my $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(<'. ''. ''. - 'folder'; + 'folder'. + ''. + ''; } # entry is a link @@ -953,12 +1290,11 @@ sub wishlistEdit { $wishlistHTMLedit .= ''. ''. - 'link'; + 'link'. + ''. + ''; } - - # input-field for title - $wishlistHTMLedit .= ''; - + # note-icon, different icons for an entries with note and those without my $noteIMG = 'anot.png'; @@ -974,7 +1310,7 @@ sub wishlistEdit { # start row containing the textarea for the note $wishlistHTMLedit .= &Apache::loncommon::continue_data_table_row('LC_hidden','note'.$index). - ''. + ''. ''; @@ -1155,9 +1491,13 @@ sub wishlistImport { # Returns the HTML-Markup for wishlist sub makePage { + my $rootgiven = shift; my $mode = shift; my $marked = shift; + $root = $rootgiven; + @childrenRt = $root->children(); + # breadcrumbs and start_page &Apache::lonhtmlcommon::clear_breadcrumbs(); &Apache::lonhtmlcommon::add_breadcrumb( @@ -1168,10 +1508,7 @@ sub makePage { 'onload' => 'javascript:onLoadAction('."'".$mode."'".');', 'onunload' => 'javascript:window.name = '."'loncapaclient'"}}); - my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs('Wishlist '. - ''. - ''.&mt('Help').''); + my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs(&mt('Wishlist').&Apache::loncommon::help_open_topic('Wishlist')); # get javascript-code for wishlist-interactions my $js = &JSforWishlist(); @@ -1192,7 +1529,7 @@ sub makePage { # icon for edit-mode, display when in view-mode if ($mode eq 'view') { $functions .= &Apache::lonhtmlcommon::add_item_funclist(''. + 'onclick="setFormAction('."'save','edit'".');" class="LC_menubuttons_link">'. ''.$lt{'ed'}.' '. ''.$lt{'ed'}.''); @@ -1200,7 +1537,7 @@ sub makePage { # icon for view-mode, display when in edit-mode else { $functions .= &Apache::lonhtmlcommon::add_item_funclist(''. + 'onclick="setFormAction('."'save','view'".');" class="LC_menubuttons_link">'. ''.$lt{'vw'}.' '. ''.$lt{'vw'}.''); @@ -1236,7 +1573,7 @@ sub makePage { # icon for saving changes $functions .= &Apache::lonhtmlcommon::add_item_funclist(''. + 'onclick="setFormAction('."'saveOK','".$mode."'".'); " class="LC_menubuttons_link">'. ''.$lt{'sv'}.''. ''.$lt{'sv'}.''); @@ -1289,7 +1626,7 @@ sub makePage { 'Now choose the new destination folder.').'

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


'; - $inner .= ''. + $inner .= ''. ''; $wishlistHTMLmove =''. @@ -1314,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 @@ -1340,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 .= '
'. ''. @@ -1393,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 @@ -1411,122 +1865,69 @@ 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; - - } +# ----------------------------------------------------- package Tree +# Extend CPAN-Module Tree by function like 'moveNode' or 'deleteNode' +package Apache::Tree; - # delete entries - if ($env{'form.action'} eq 'delete') { - &deleteEntries(\@marked); - } +=pod + +=head2 Routines from package Tree + +=over 4 + +=item * &getNodeByIndex(index, nodes) + + Searches for a node, specified by the index, in nodes (reference to array) and returns it. + + +=item * &moveNode(node, at, newParent) + + Moves a given node to a new parent (if new parents is defined) or change the position from a node within its siblings (means sorting, at must be defined). + + +=item * &removeNode(node) + + Removes a node given by node from the tree. + + +=item * &TreeIndex(children) + + Sets an index for every node in the tree, beginning with 0. + Recursive call starting with all children of the root of the tree (parameter children is reference to an array containing the nodes of the current level). + + +=item * &setCountZero() + + Resets index counter. + + +=item * &RootToHash(childrenRt) + + Converts the root-node to a hash-entry: the key is root and values are just the indices of root's children. - # get all titles and notes and save them - # only save, if user wants to save changes - # do not save, when current action is 'delete' or 'sort' or 'move' - my @newTitles = (); - my @newNotes = (); - if ((defined $env{'form.newtitle'} || defined $env{'form.newnote'}) && ($env{'form.action'} ne 'noSave') && ($env{'form.action'} ne 'delete') && !$changeOrder) { - @newTitles = &Apache::loncommon::get_env_multiple('form.newtitle'); - @newNotes = &Apache::loncommon::get_env_multiple('form.newnote'); - my $node = 0; - foreach my $t (@newTitles) { - &setNewTitle($node, $t); - $node++; - } - $node = 0; - foreach my $n (@newNotes) { - &setNewNote($node, $n); - $node++; - } - &saveChanges(); - } - - # Create HTML-markup - my $page; - if ($env{'form.mode'} eq 'edit') { - $page = &makePage("edit"); - } - elsif ($env{'form.mode'} eq 'move') { - $page = &makePage("move", \@marked); - } - elsif ($env{'form.mode'} eq 'import') { - $page = &makePageImport(); - } - elsif ($env{'form.mode'} eq 'set') { - $page = &makePageSet(); - } - else { - $page = &makePage("view"); - } - @marked = (); - $r->print($page); - } - # An error occured, print an error-page - else { - my $errorPage = &makeErrorPage(); - $r->print($errorPage); - } - return OK; -} +=item * &TreeToHash(childrenRt) + + Converts all other nodes in the tree to hash. Each node is one hash-entry where the keys are the index of a node and the values are all other attributes (containing tile, path, note, date and indices for all direct children). + Recursive call starting with all children of the root of the tree (parameter childrenRT is reference to an array containing the nodes of the current level). + + +=item * &HashToTree() + + Converts the hash to a tree. Builds a tree-object for each entry in the hash. Afterwards call &buildTree(node, childrenIn, TreeNodes, TreeHash) to connect the tree-objects. + + +=item * &buildTree(node, childrenIn, TreeNodes, TreeHash) + + Joins the nodes to a tree. + Recursive call starting with root and all children of root (parameter childrenIn is reference to an array containing the nodes indices of the current level). + + +=back + +=cut -# ----------------------------------------------------- package Tree -# Extend CPAN-Module Tree by function like 'moveNode' or 'deleteNode' -package Tree; # returns the node with a given index from a list of nodes sub getNodeByIndex { @@ -1534,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; } } @@ -1650,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], @@ -1672,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; }