--- loncom/interface/lonwishlist.pm 2011/02/15 14:54:51 1.10 +++ loncom/interface/lonwishlist.pm 2015/06/09 21:22:57 1.25 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Utility-routines for wishlist # -# $Id: lonwishlist.pm,v 1.10 2011/02/15 14:54:51 wenzelju Exp $ +# $Id: lonwishlist.pm,v 1.25 2015/06/09 21:22:57 damieng Exp $ # # Copyright Michigan State University Board of Trustees # @@ -50,7 +50,7 @@ use Apache::lonnet; use Apache::loncommon(); use Apache::lonhtmlcommon; use Apache::lonlocal; -use LONCAPA; +use LONCAPA qw(:DEFAULT :match); use Tree; @@ -94,10 +94,10 @@ my $foldersOption; sub getWishlist { my @keys = &Apache::lonnet::getkeys('wishlist'); my %wishlist = &Apache::lonnet::get('wishlist',\@keys); - foreach my $i ( keys %wishlist) { + foreach my $i (keys(%wishlist)) { #File not found. This appears at the first time using the wishlist #Create file and put 'root' into it - if ($i =~m/^error:No such file/) { + if ($i =~m/^\Qerror:No such file\E/) { &Apache::lonnet::logthis($i.'! Create file by putting in the "root" of the directory tree.'); &Apache::lonnet::put('wishlist', {'root' => ''}); my $options = ''; @@ -113,7 +113,7 @@ sub getWishlist { # 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) { + if ((keys(%wishlist)) == 0) { &Apache::lonnet::logthis('ERROR while attempting to get wishlist: no keys retrieved!'); return 'error'; } @@ -197,7 +197,7 @@ sub newEntry() { my $date = gmtime(); # Create Entry-Object my $entry = Entry->new(title => $title, path => $path, note => $note, date => $date); - # Create Tree-Object, this correspones a node in the wishlist-tree + # Create Tree-Object, this corresponds a node in the wishlist-tree my $tree = Tree->new($entry); # Add this node to wishlist-tree my $folderIndex = $env{'form.folders'}; @@ -466,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) @@ -511,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',}); @@ -528,16 +530,22 @@ 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 $warningLink = &mt('You must insert a title and a path!'); - my $warningFolder = &mt('You must insert a title!'); + 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 $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 $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!'); + &js_escape(\$warningLinkNotAllowed1); + &js_escape(\$warningLinkNotAllowed2); + &js_escape(\$warningDelete); + &js_escape(\$warningSave); + &js_escape(\$warningMoveS); + &js_escape(\$warningMoveD); $foldersOption = ''; my $js = &Apache::lonhtmlcommon::scripttag(<'; + 'onclick="selectAction('."'row".$index."'".')" />'; # entry is a folder if ($n->value()->path() eq '') { - $wishlistHTMLview .= ''. + $wishlistHTMLview .= ''. ''. ''. 'folder'. @@ -1115,8 +1135,9 @@ sub wishlistView { } # entry is a link else { - $wishlistHTMLview .= ''. - 'value()->path()."'".');">'. + my $quotable_link = &Apache::loncommon::escape_single($n->value()->path()); + $wishlistHTMLview .= ''. + ''. 'link'. $n->value()->title().''; } @@ -1145,9 +1166,9 @@ sub wishlistView { # if the entry is a folder, it could have other entries as content. if it has, call wishlistView for those entries my @children = $n->children(); if ($#children >=0) { - $indent += 20; + $indent_view += 20; &wishlistView(\@children); - $indent -= 20; + $indent_view -= 20; } } } @@ -1155,7 +1176,7 @@ sub wishlistView { # HTML-Markup for table if in edit-mode my $wishlistHTMLedit; -my $indent = $indentConst; +my $indent_edit = $indentConst; sub wishlistEdit { my $nodes = shift; my $curNode = 1; @@ -1171,7 +1192,7 @@ sub wishlistEdit { # checkboxes $wishlistHTMLedit .= ''; + 'onclick="selectAction('."'row".$index."'".')" />'; # option-tags for sorting entries. we need the numbers from 1 to n with n being the number of entries on the same level as the current entry. # set the number for the current entry into brackets @@ -1190,11 +1211,11 @@ sub wishlistEdit { if ($n->value()->path() eq '') { $wishlistHTMLedit .= ''. - ''. + ''. ''. ''. 'folder'. - ''. + ''. ''; } @@ -1202,10 +1223,10 @@ sub wishlistEdit { else { $wishlistHTMLedit .= ''. - ''. + ''. 'link'. - ''. - ''; + ''. + ''; } # note-icon, different icons for an entries with note and those without @@ -1232,9 +1253,9 @@ sub wishlistEdit { # if the entry is a folder, it could have other entries as content. if it has, call wishlistEdit for those entries my @children = $n->children(); if ($#children >=0) { - $indent += 20; + $indent_edit += 20; &wishlistEdit(\@children); - $indent -= 20; + $indent_edit -= 20; } } } @@ -1244,7 +1265,7 @@ sub wishlistEdit { # HTML-Markup for table if in move-mode my $wishlistHTMLmove =''. ''.&mt('Top level').''; -my $indent = $indentConst; +my $indent_move = $indentConst; sub wishlistMove { my $nodes = shift; my $marked = shift; @@ -1268,12 +1289,12 @@ sub wishlistMove { # display a radio-button, if the folder was not selected to be moved if (!$isIn) { $wishlistHTMLmove .= ''. - ''; + ''; } - # higlight the title, if the folder was selected to be moved + # highlight the title, if the folder was selected to be moved else { $wishlistHTMLmove .= ''. - ''; } #arrow- and folder-image, all folders are open, and title @@ -1289,9 +1310,10 @@ sub wishlistMove { $highlight = 'style="color:red;"'; } # link-image and title + my $quotable_link = &Apache::loncommon::escape_single($n->value()->path()); $wishlistHTMLmove .= ''. - ''. - 'value()->path()."'".');" '.$highlight.'>'. + ''. + ''. 'link'. $n->value()->title().''; } @@ -1320,9 +1342,9 @@ sub wishlistMove { # if the entry is a folder, it could have other entries as content. if it has, call wishlistMove for those entries my @children = $n->children(); if ($#children >=0) { - $indent += 20; + $indent_move += 20; &wishlistMove(\@children, $marked); - $indent -= 20; + $indent_move -= 20; } } } @@ -1331,14 +1353,48 @@ sub wishlistMove { # HTML-Markup for table if in import-mode my $wishlistHTMLimport; -my $indent = $indentConst; +my $indent_imp = $indentConst; my $form = 1; sub wishlistImport { - my $nodes = shift; + my ($nodes,$numskipped) = @_; + + my ($is_community,%nopick); + if ($env{'request.course.id'}) { + if (&Apache::loncommon::course_type() eq 'Community') { + $is_community = 1; + } + } foreach my $n (@$nodes) { my $index = $n->value()->nindex(); + # + # Determine which resources in stored links may be imported into a course/community. + # (a) Import of directories in /res space is not supported. + # (b) Import of a resource into a community requires user has 'bro' privilege for resource + # (i.e., user has author or co-author role for corresponcding Authoring Space). + # (c) Import of a resource into a course requires user has 'be' privilege for resource. + # + + if ($n->value()->path() =~ m{^(/res/$match_domain/$match_username/)}) { + if ($n->value()->path() =~ m{/$}) { + $nopick{$n->value()->path()} = $n->value()->title(); + $$numskipped ++; + } else { + if ($is_community) { + unless (&Apache::lonnet::allowed('bro',$n->value()->path())) { + $nopick{$n->value()->path()} = $n->value()->title(); + $$numskipped ++; + } + } else { + unless (&Apache::lonnet::allowed('bre',$n->value()->path())) { + $nopick{$n->value()->path()} = $n->value()->title(); + $$numskipped ++; + } + } + } + } + # start row, use data_table routines to set class to LC_even or LC_odd automatically. this row contains a checkbox, the title and the note-icon. # only display the top level entries on load $wishlistHTMLimport .= ($n->parent()->value() eq 'root')?&Apache::loncommon::start_data_table_row('','row'.$index) @@ -1346,17 +1402,26 @@ sub wishlistImport { # checkboxes - $wishlistHTMLimport .= ''. - ''. - ''. - ''. - ''. - ''; + $wishlistHTMLimport .= ''; + my ($disabled,$onclick,$image,$style); + if ($nopick{$n->value()->path()}) { + $disabled = ' disabled="disabled"'; + $image = 'wishlist-link-lighter.png'; + $style = 'style="color:#808080;"'; + } else { + $onclick = ' onclick="selectAction('."'row".$index."'".')"'; + $image = 'wishlist-link.png'; + } + $wishlistHTMLimport .= ''. + ''. + ''. + ''; + $wishlistHTMLimport .= ''; # entry is a folder if ($n->value()->path() eq '') { - $wishlistHTMLimport .= ''. + $wishlistHTMLimport .= ''. ''. ''. 'folder'. @@ -1364,10 +1429,13 @@ sub wishlistImport { } # entry is a link else { - $wishlistHTMLimport .= ''. - 'value()->path()."'".');">'. - 'link'. - $n->value()->title().''; + $wishlistHTMLimport .= ''; + unless ($nopick{$n->value()->path()}) { + my $quotable_link = &Apache::loncommon::escape_single($n->value()->path()); + $wishlistHTMLimport .= ''; + } + $wishlistHTMLimport .= 'link'. + ''.$n->value()->title().''; $form++; } @@ -1395,11 +1463,12 @@ sub wishlistImport { # if the entry is a folder, it could have other entries as content. if it has, call wishlistImport for those entries my @children = $n->children(); if ($#children >=0) { - $indent += 20; - &wishlistImport(\@children); - $indent -= 20; + $indent_imp += 20; + &wishlistImport(\@children,$numskipped); + $indent_imp -= 20; } } + return; } # Returns the HTML-Markup for wishlist @@ -1415,13 +1484,13 @@ sub makePage { &Apache::lonhtmlcommon::clear_breadcrumbs(); &Apache::lonhtmlcommon::add_breadcrumb( { href => '/adm/wishlist?mode='.$mode, - text => 'Wishlist'}); - my $startPage = &Apache::loncommon::start_page('Wishlist',undef, + text => 'Stored Links'}); + my $startPage = &Apache::loncommon::start_page('Stored Links',undef, {'add_entries' => { 'onload' => 'javascript:onLoadAction('."'".$mode."'".');', 'onunload' => 'javascript:window.name = '."'loncapaclient'"}}); - my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs(&mt('Wishlist').&Apache::loncommon::help_open_topic('Wishlist')); + my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs(&mt('Stored Links'),'Wishlist'); # get javascript-code for wishlist-interactions my $js = &JSforWishlist(); @@ -1497,7 +1566,7 @@ sub makePage { # start form my $inner .= '
'. - ''; + ''; # only display subbox in view- or edit-mode if ($mode eq 'view' || $mode eq 'edit') { @@ -1513,7 +1582,7 @@ sub makePage { $inner .= &Apache::loncommon::end_data_table(); } else { - $inner .= ''.&mt("Your wishlist ist currently empty.").''; + $inner .= ''.&mt("Your Stored Links list is currently empty.").''; } $wishlistHTMLedit = ''; } @@ -1523,7 +1592,7 @@ sub makePage { $inner .= ''.$wishlistHTMLview.'
'; } else { - $inner .= ''.&mt("Your wishlist ist currently empty.").''; + $inner .= ''.&mt("Your Stored Links list is currently empty.").''; } $wishlistHTMLview = ''; } @@ -1534,20 +1603,20 @@ sub makePage { } if ($markStr) { $markStr = substr($markStr, 0, length($markStr)-1); - $inner .= ''; + $inner .= ''; $inner .= '

'.&mt('You have selected the red marked entries to be moved to another folder. '. 'Now choose the new destination folder.').'

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


'; - $inner .= ''. - ''; + $inner .= ''. + ''; $wishlistHTMLmove =''. ''.&mt('Top level').''; } else { $inner .= '

'.&mt("You haven't marked any entry to move.").'

'. - ''; + ''; } } @@ -1586,16 +1655,20 @@ sub makePopUpNewLink { # HTML-Markup for the Pop-Up-window 'Set a link for this resource to wishlist' my $startPageWishlistlink = - &Apache::loncommon::start_page('Set link to wishlist',undef, + &Apache::loncommon::start_page('Save to Stored Links',undef, {'only_body' => 1, 'bgcolor' => '#FFFFFF',}); my $warningLink = &mt('You must insert a title!'); - 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 $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://...'); + &js_escape(\$warningLink); + &js_escape(\$warningLinkNotAllowed1); - my $inPageWishlistlink1 = '

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

'; + my $inPageWishlistlink1 = '

'.&mt('Save to Stored Links').'

'; # If no title is delivered, 'New Link' is called up from the wishlist-interface, so after # submitting the window should close instead of offering a link to wishlist (like it should do # if we call 'Set New Link' from within a browsed ressource) @@ -1623,7 +1696,7 @@ sub makePopUpNewLink { ''. - ''. + ''. '
'; $options = ''; @@ -1631,9 +1704,9 @@ sub makePopUpNewLink { my $popUp = $startPageWishlistlink. $inPageWishlistlink1. - ''. + ''. $inPageWishlistlink2. - ''. + ''. $inPageWishlistlink3; # JavaScript-function to set title and path of ressource automatically @@ -1697,7 +1770,7 @@ sub makePopUpNewFolder { 'bgcolor' => '#FFFFFF',}); my $warningFolder = &mt('You must insert a title!'); - + &js_escape(\$warningFolder); my $inPageNewFolder = '

'.&mt('New Folder').'

'. '
1}); + my $title = 'Stored Links'; + + # start_page + my $output = + &Apache::loncommon::start_page($title,undef, + {'only_body' => 1}) + .'

'.&mt($title).'

'; # confirm success and offer link to wishlist - my $message = &Apache::lonhtmlcommon::confirm_success(&mt('Link successfully set!')); - $message = &Apache::loncommon::confirmwrapper($message); - - my $inner .= '
'.$message.'

'. - ''.&mt('Go to wishlist').''. - ' '.&mt('Close this window').''; + $output .= + &Apache::loncommon::confirmwrapper( + &Apache::lonhtmlcommon::confirm_success( + &mt('Link successfully saved!'))) + .&Apache::lonhtmlcommon::actionbox( + [''.&mt('Go to Stored Links').'', + ''.&mt('Close this window').'' + ]); # end_page - my $endPage = &Apache::loncommon::end_page(); + $output .= &Apache::loncommon::end_page(); - # put all page-elements together - my $page = $startPage.$inner.$endPage; - - return $page; + return $output; } @@ -1772,53 +1848,64 @@ sub makePageImport { $root = $rootgiven; @childrenRt = $root->children(); # start_page - my $startPage = &Apache::loncommon::start_page('Wishlist',undef, + my $startPage = &Apache::loncommon::start_page('Stored Links',undef, {'only_body' => 1}); # get javascript-code for wishlist-interactions my $js = &JSforWishlist(); $js .= &JSforImport($rat); - my $inner = '

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

'; + my $inner = '

'.&mt('Import Resources from Stored Links').'

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

'; + $inner .= + ''; } 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. ") - .'

'; + $inner .= + ''; } my %wishlist = &getWishlist(); #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 + #wishlist.db depends on whether this entry exists or not...JW my $fnum; if (defined $wishlist{'folders'}) { - $fnum = (keys %wishlist)-2; + $fnum = (keys(%wishlist))-2; } else { - $fnum = (keys %wishlist)-1; + $fnum = (keys(%wishlist))-1; } - $inner .= ''. - ''. - ''. - ''. - ''. - '

'; + $inner .= ''. + ''. + ''. + ''. + ''. + '

'; # wishlist-table - &wishlistImport(\@childrenRt); + my $numskipped = 0; + &wishlistImport(\@childrenRt,\$numskipped); if ($wishlistHTMLimport ne '') { $inner .= ''.$wishlistHTMLimport.'
'; } else { - $inner .= ''.&mt("Your wishlist ist currently empty.").''; + $inner .= ''.&mt("Your Stored Links list is currently empty.").''; + } + if ($numskipped > 0) { + $inner .= '

'.&mt('Note: where a Stored Link is unavailable for import in the current context it is grayed out.').'

'; } $wishlistHTMLimport = ''; @@ -1839,14 +1926,14 @@ sub makeErrorPage { # breadcrumbs and start_page &Apache::lonhtmlcommon::add_breadcrumb( { href => '/adm/wishlist', - text => 'Wishlist'}); - my $startPage = &Apache::loncommon::start_page('Wishlist'); + text => 'Stored Links'}); + my $startPage = &Apache::loncommon::start_page('Stored Links'); - my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs(&mt('Wishlist').&Apache::loncommon::help_open_topic('Wishlist')); + my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs(&mt('Stored Links'),'Wishlist'); &Apache::lonhtmlcommon::clear_breadcrumbs(); # error-message - my $inner .= ''.&mt('An error occurred! Please try again later.').''; + my $inner .= '

'.&mt('An error occurred! Please try again later.').'

'; # end_page my $endPage = &Apache::loncommon::end_page(); @@ -2047,7 +2134,7 @@ sub HashToTree { my @TreeNodes = (); my $root; - foreach my $key (keys %$TreeHash) { + foreach my $key (keys(%$TreeHash)) { if ($key eq 'root') { $root = Tree->new("root"); }