--- loncom/interface/lonwishlist.pm 2010/08/16 15:14:37 1.4 +++ loncom/interface/lonwishlist.pm 2014/02/17 16:40:22 1.20 @@ -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.20 2014/02/17 16:40:22 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -39,17 +39,18 @@ 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; use Apache::lonlocal; -use LONCAPA; +use LONCAPA qw(:DEFAULT :match); use Tree; @@ -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; @@ -446,11 +466,13 @@ sub getNodesToArray { 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) +=item * &wishlistImport(nodes, numskipped) 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). - + 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). + Side effect: increments the scalar ref: numskipped with a count of items in + Stored Links unavailable for selection, (e.g., now marked obsolete or + inaccessible in Community context). =item * &makePage(mode, marked) @@ -458,6 +480,16 @@ sub getNodesToArray { Calls &wishlistView(nodes), &wishlistEdit(nodes) or &wishlistMove(nodes, marked). +=item * &makePopUpNewLink(title, path) + + Returns the HTML-markup for the pop-up-window 'Add Link'. If this is called up from a browsed resource, the input-fields titel and path are pre-filled with the resources' meta-data-title and it's path. + + +=item * &makePopUpNewFolder() + + Returns the HTML-markup for the pop-up-window 'Add Folder'. + + =item * &makePageSet() Returns the HTML-Markup for the page shown when a link was set by using the icon when viewing a resource. @@ -481,7 +513,7 @@ sub getNodesToArray { # Return a script-tag containing Javascript-function # needed for wishlist actions like 'new link' ect. sub JSforWishlist { - my $startPagePopup = &Apache::loncommon::start_page('Wishlist',undef, + my $startPagePopup = &Apache::loncommon::start_page('Stored Links',undef, {'only_body' => 1, 'js_ready' => 1, 'bgcolor' => '#FFFFFF',}); @@ -491,65 +523,6 @@ sub JSforWishlist { &getFoldersToArray(\@childrenRt); &getFoldersForOption(\@childrenRt); - # texthash - my %lt = &Apache::lonlocal::texthash( - 'nl' => 'New Link', - 'nf' => 'New Folder', - 'lt' => 'Link Title', - 'ft' => 'Folder Title', - 'pa' => 'Path', - 'nt' => 'Note', - 'si' => 'Save in', - 'cl' => 'Cancel'); - - - my $inPageNewLink = '

'.$lt{'nl'}.'

'. - '
'. - &Apache::lonhtmlcommon::start_pick_box(). - &Apache::lonhtmlcommon::row_title($lt{'lt'}). - ''. - &Apache::lonhtmlcommon::row_closure(). - &Apache::lonhtmlcommon::row_title($lt{'pa'}). - ''. - &Apache::lonhtmlcommon::row_closure(). - &Apache::lonhtmlcommon::row_title($lt{'nt'}). - ''. - &Apache::lonhtmlcommon::row_closure(1). - &Apache::lonhtmlcommon::end_pick_box(). - '

'. - ''. - ''. - ''. - '
'; - - my $inPageNewFolder = '

'.$lt{'nf'}.'

'. - '
'. - &Apache::lonhtmlcommon::start_pick_box(). - &Apache::lonhtmlcommon::row_title($lt{'ft'}). - '
'. - &Apache::lonhtmlcommon::row_closure(). - &Apache::lonhtmlcommon::row_title($lt{'nt'}). - '
'. - &Apache::lonhtmlcommon::row_closure(1). - &Apache::lonhtmlcommon::end_pick_box(). - '

'. - ''. - ''. - ''. - '
'; - - # Remove all \n for inserting on javascript document.write - $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. @@ -557,62 +530,27 @@ sub JSforWishlist { # 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 $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/domain/user/...'. + ' 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 $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 $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(<' - +'function newlinksubmit(){' - +'var path = document.getElementsByName("path")[0].value;' - +'var title = document.getElementsByName("title")[0].value;' - +'if (!path || !title) {' - +'alert("$warningLink");' - +'return false;}' - +'var linkOK = (path.match(/^http:(\\\\/\\\\/)/) || path.match(/^https:(\\\\/\\\\/)/))' - +'&& !(path.match(/\\.problem/) || path.match(/\\.exam/)' - +'|| path.match(/\\.quiz/) || path.match(/\\.assess/)' - +'|| path.match(/\\.survey/) || path.match(/\\.form/)' - +'|| path.match(/\\.library/) || path.match(/\\.page/)' - +'|| path.match(/\\.sequence/));' - +'if (!path.match(/^(\\\\/res\\\\/)/) && !linkOK) {' - +'alert("$warningLinkNotAllowed1");' - +'return false;}' - +'else {' - +'window.close();' - +'return true;}}' - +'<\/scr'+'ipt>' - +'$inPageNewLink' - +'$endPagePopup'); - newlinkWin.document.close(); + newlinkWin=window.open('/adm/wishlist?mode=newLink','newlinkWin','width=580,height=350, scrollbars=yes'); } function newFolder() { - newfolderWin=window.open('','newfolderWin','width=580,height=270, scrollbars=yes'); - newfolderWin.document.write('$startPagePopup' - +'