![]() ![]() | ![]() |
- Wording change: Wishlist -> Stored Links.
1: # The LearningOnline Network with CAPA 2: # Utility-routines for wishlist 3: # 4: # $Id: lonwishlist.pm,v 1.16 2012/08/01 19:07:51 raeburn 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 qw(:DEFAULT :match); 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, numskipped) 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: Side effect: increments the scalar ref: numskipped with a count of items in 474: Stored Links unavailable for selection, (e.g., now marked obsolete or 475: inaccessible in Community context). 476: 477: =item * &makePage(mode, marked) 478: 479: 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". 480: Calls &wishlistView(nodes), &wishlistEdit(nodes) or &wishlistMove(nodes, marked). 481: 482: 483: =item * &makePopUpNewLink(title, path) 484: 485: 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. 486: 487: 488: =item * &makePopUpNewFolder() 489: 490: Returns the HTML-markup for the pop-up-window 'Add Folder'. 491: 492: 493: =item * &makePageSet() 494: 495: Returns the HTML-Markup for the page shown when a link was set by using the icon when viewing a resource. 496: 497: 498: =item * &makePageImport() 499: 500: Returns the HTML-Markup for the page shown when links should be imported into courses. 501: 502: 503: =item * &makeErrorPage () 504: 505: Returns the HTML-Markup for an error-page shown if the wishlist could not be loaded. 506: 507: 508: =back 509: 510: =cut 511: 512: 513: # Return a script-tag containing Javascript-function 514: # needed for wishlist actions like 'new link' ect. 515: sub JSforWishlist { 516: my $startPagePopup = &Apache::loncommon::start_page('Stored Links',undef, 517: {'only_body' => 1, 518: 'js_ready' => 1, 519: 'bgcolor' => '#FFFFFF',}); 520: my $endPagePopup = &Apache::loncommon::end_page({'js_ready' => 1}); 521: 522: @allFolders = (); 523: &getFoldersToArray(\@childrenRt); 524: &getFoldersForOption(\@childrenRt); 525: 526: # it is checked, wether a path links to a LON-CAPA-resource or an external website. links to course-contents are not allowed 527: # because they probably will return a kind of 'no access' (unless the user is already in the course, the path links to). 528: # also importing these kind of links into a course does not make much sense. 529: # to find out if a path (not starting with /res/...) links to course-contents, the same filter as in lonwrapper is used, 530: # that means that it is checked wether a path contains .problem, .quiz, .exam etc. 531: # this is good for most cases but crashes as soon as a real external website contains one of this pattern in its URL. 532: # so maybe there's a better way to find out wether a given URL belongs to a LON-CAPA-server or not ...? 533: my $warningLinkNotAllowed1 = 534: &mt('You can only insert links to LON-CAPA resources from the resource-pool'. 535: ' or to external websites.'. 536: ' Paths to LON-CAPA resources must be of the form /res/domain/user/...'. 537: ' Paths to external websites must contain the network protocol, e.g. http://...'); 538: my $warningLinkNotAllowed2 = &mt('The following link is not allowed:').' '; 539: my $warningLink = &mt('You must insert a title and a path!'); 540: my $warningFolder = &mt('You must insert a title!'); 541: my $warningDelete = &mt('Are you sure you want to delete the selected entries? Deleting a folder also deletes all entries within this folder!'); 542: 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.'); 543: my $warningMoveS = &mt('You must select at minimum one entry to move!'); 544: my $warningMoveD = &mt('You must select a destination folder!'); 545: $foldersOption = ''; 546: 547: my $js = &Apache::lonhtmlcommon::scripttag(<<JAVASCRIPT); 548: function newLink() { 549: newlinkWin=window.open('/adm/wishlist?mode=newLink','newlinkWin','width=580,height=350, scrollbars=yes'); 550: } 551: 552: function newFolder() { 553: newfolderWin=window.open('/adm/wishlist?mode=newFolder','newfolderWin','width=580,height=270, scrollbars=yes'); 554: } 555: 556: function setFormAction(action,mode) { 557: var r = true; 558: setAction(''); 559: if (action == 'delete') { 560: r = confirm("$warningDelete"); 561: setAction('delete'); 562: } 563: else if (action == 'save') { 564: var d = getDifferences(); 565: if (d) { 566: if (!confirm('$warningSave')) { 567: setAction('noSave'); 568: r = true; 569: } 570: else { 571: r = linksOK(); 572: } 573: } 574: } 575: else if (action == 'saveOK') { 576: r = linksOK(); 577: } 578: else if (action == 'move') { 579: r = selectDestinationFolder(mode); 580: } 581: document.getElementsByName('list')[0].setAttribute("action", "/adm/wishlist?mode="+mode); 582: if (r) { 583: document.getElementsByName('list')[0].submit(); 584: } 585: } 586: 587: function setAction(action) { 588: document.getElementById('action').value = action; 589: } 590: 591: function getDifferences() { 592: var newtitles = document.getElementsByName('newtitle'); 593: var i = 0; 594: for (i=0;i<newtitles.length;i++) { 595: var newt = newtitles[i].value; 596: var oldt = newtitles[i].alt; 597: if (newt != oldt) { 598: return true; 599: } 600: } 601: var newpath = document.getElementsByName('newpath'); 602: var i = 0; 603: for (i=0;i<newpath.length;i++) { 604: var newp = newpath[i].value; 605: var oldp = newpath[i].alt; 606: if (newp != oldp) { 607: return true; 608: } 609: } 610: var newnote = document.getElementsByName('newnote'); 611: var i = 0; 612: for (i=0;i<newnote.length;i++) { 613: var newn = newnote[i].value; 614: var oldn = newnote[i].innerHTML; 615: if (newn != oldn) { 616: return true; 617: } 618: } 619: return false; 620: } 621: 622: function linksOK() { 623: var newpath = document.getElementsByName('newpath'); 624: var i = 0; 625: for (i=0;i<newpath.length;i++) { 626: var path = newpath[i].value; 627: var linkOK = (path.match(/^http:\\/\\//) || path.match(/^https:\\/\\//)) 628: && !(path.match(/\\.problem/) || path.match(/\\.exam/) 629: || path.match(/\\.quiz/) || path.match(/\\.assess/) 630: || path.match(/\\.survey/) || path.match(/\\.form/) 631: || path.match(/\\.library/) || path.match(/\\.page/) 632: || path.match(/\\.sequence/)); 633: if (!path.match(/^(\\/res\\/)/) && !linkOK) { 634: alert("$warningLinkNotAllowed1 $warningLinkNotAllowed2"+path); 635: return false; 636: } 637: } 638: return true; 639: } 640: 641: function onLoadAction(mode) { 642: window.name = 'wishlist'; 643: if (mode == "edit") { 644: var deepestRows = getDeepestRows(); 645: setDisplaySelect(deepestRows, ''); 646: } 647: } 648: 649: function folderAction(rowid) { 650: var row = document.getElementById(rowid); 651: var indent = getIndent(row); 652: var displ; 653: var status; 654: if (getImage(row) == 'closed') { 655: displ = ''; 656: status = 'open'; 657: } 658: else { 659: displ = 'LC_hidden'; 660: status = 'closed'; 661: } 662: setImage(row,status); 663: if (getNextRow(row) != null) { 664: var nextIndent = getIndent(getNextRow(row)); 665: row = getNextRow(row); 666: while (nextIndent > indent) { 667: if (displ == '') { 668: row.className = (row.className).replace('LC_hidden',''); 669: } 670: else if (displ != '' && !((row.className).match('LC_hidden'))) { 671: var oldClass = row.className; 672: row.className = oldClass+' LC_hidden'; 673: setDisplayNote(row.id.replace('row','note'),'LC_hidden'); 674: } 675: if (status == 'open' && getImage(row).match('closed')) { 676: row = getNextRowWithIndent(row, getIndent(row)); 677: } 678: else { 679: row = getNextRow(row); 680: } 681: if (row != null) { 682: nextIndent = getIndent(row); 683: } 684: else { 685: nextIndent = indent; 686: } 687: } 688: } 689: setClasses(); 690: var newtitles = document.getElementsByName('newtitle'); 691: if (newtitles.length>0) { 692: var deepestRows = getDeepestRows(); 693: var otherRows = getOtherRows(deepestRows); 694: setDisplaySelect(deepestRows,''); 695: setDisplaySelect(otherRows,'LC_hidden'); 696: } 697: } 698: 699: function selectAction(rowid) { 700: var row = document.getElementById(rowid); 701: var indent = getIndent(row); 702: var checked = getChecked(row); 703: var previousFolderRows = new Array(); 704: if (indent != 0) { 705: previousFolderRows = getPreviousFolderRows(row); 706: } 707: if (getNextRow(row) != null) { 708: var nextIndent = getIndent(getNextRow(row)); 709: row = getNextRow(row); 710: while (nextIndent > indent) { 711: setChecked(row,checked); 712: if (status == 'open' && getImage(row).match('closed')) { 713: row = getNextRowWithIndent(row, getIndent(row)); 714: } 715: else { 716: row = getNextRow(row); 717: } 718: if (row != null) { 719: nextIndent = getIndent(row); 720: } 721: else { 722: nextIndent = indent; 723: } 724: } 725: } 726: if (!checked) { 727: var i = 0; 728: for (i=0;i<previousFolderRows.length;i++) { 729: setChecked(previousFolderRows[i], false); 730: } 731: } 732: } 733: 734: function getNextNote(row) { 735: var rowId = row.id; 736: var nextRowId = parseInt(rowId.substr(3,rowId.length))+1; 737: nextRowId = "note"+nextRowId; 738: var nextRow = document.getElementById(nextRowId); 739: return nextRow; 740: } 741: 742: function getNextRow(row) { 743: var rowId = row.id; 744: var nextRowId = parseInt(rowId.substr(3,rowId.length))+1; 745: nextRowId = "row"+nextRowId; 746: var nextRow = document.getElementById(nextRowId); 747: return nextRow; 748: } 749: 750: function getPreviousRow(row) { 751: var rowId = row.id; 752: var previousRowId = parseInt(rowId.substr(3,rowId.length))-1; 753: previousRowId = "row"+previousRowId; 754: var previousRow =document.getElementById(previousRowId); 755: return previousRow; 756: } 757: 758: function getIndent(row) { 759: var childPADD = document.getElementById(row.id.replace('row','padd')); 760: indent = childPADD.style.paddingLeft; 761: indent = parseInt(indent.substr(0,(indent.length-2))); 762: 763: if (getImage(row).match('link')) { 764: indent -= $indentConst; 765: } 766: return indent; 767: } 768: 769: function getNextRowWithIndent(row, indent) { 770: var nextRow = getNextRow(row); 771: if (nextRow != null) { 772: var nextIndent = getIndent(nextRow); 773: while (nextIndent >= indent) { 774: if (nextIndent == indent) { 775: return nextRow; 776: } 777: nextRow = getNextRow(nextRow); 778: if (nextRow == null) { 779: return null; 780: } 781: nextIndent = getIndent(nextRow); 782: } 783: } 784: return nextRow; 785: } 786: 787: function getImage(row) { 788: var childIMG = document.getElementById(row.id.replace('row','img')); 789: if ((childIMG.src).match('closed')) { 790: return 'closed'; 791: } 792: else if ((childIMG.src).match('open')) { 793: return 'open;' 794: } 795: else { 796: return 'link'; 797: } 798: } 799: 800: function setImage(row, status) { 801: var childIMG = document.getElementById(row.id.replace('row','img')); 802: var childIMGFolder = document.getElementById(row.id.replace('row','imgFolder')); 803: childIMG.src = "/adm/lonIcons/arrow."+status+".gif"; 804: childIMGFolder.src="/adm/lonIcons/navmap.folder."+status+".gif"; 805: } 806: 807: function getChecked(row) { 808: var childCHECK = document.getElementById(row.id.replace('row','check')); 809: var checked = childCHECK.checked; 810: return checked; 811: } 812: 813: function setChecked(row,checked) { 814: var childCHECK = document.getElementById(row.id.replace('row','check')); 815: if (!childCHECK.disabled) { 816: childCHECK.checked = checked; 817: } 818: } 819: 820: function getPreviousFolderRows(row) { 821: var previousRow = getPreviousRow(row); 822: var indent = getIndent(previousRow); 823: var kindOfEntry = getImage(previousRow); 824: var rows = new Array(); 825: if (kindOfEntry != 'link') { 826: rows.push(previousRow); 827: } 828: 829: while (indent >0) { 830: previousRow = getPreviousRow(previousRow); 831: if (previousRow != null) { 832: indent = getIndent(previousRow); 833: kindOfEntry = getImage(previousRow); 834: if (kindOfEntry != 'link') { 835: rows.push(previousRow); 836: } 837: } 838: else { 839: indent = 0; 840: } 841: } 842: return rows; 843: } 844: 845: function getDeepestRows() { 846: var row = document.getElementById('row0'); 847: var firstRow = row; 848: var indent = getIndent(row); 849: var maxIndent = indent; 850: while (getNextRow(row) != null) { 851: row = getNextRow(row); 852: indent = getIndent(row); 853: if (indent>maxIndent && !((row.className).match('LC_hidden'))) { 854: maxIndent = indent; 855: } 856: } 857: var deepestRows = new Array(); 858: row = firstRow; 859: var rowIndent; 860: while (getNextRow(row) != null) { 861: rowIndent = getIndent(row); 862: if (rowIndent == maxIndent) { 863: deepestRows.push(row); 864: } 865: row = getNextRow(row); 866: } 867: rowIndent = getIndent(row); 868: if (rowIndent == maxIndent) { 869: deepestRows.push(row); 870: } 871: return deepestRows; 872: } 873: 874: function getOtherRows(deepestRows) { 875: var row = document.getElementById('row0'); 876: var otherRows = new Array(); 877: var isIn = false; 878: while (getNextRow(row) != null) { 879: var i = 0; 880: for (i=0; i < deepestRows.length; i++) { 881: if (row.id == deepestRows[i].id) { 882: isIn = true; 883: } 884: } 885: if (!isIn) { 886: otherRows.push(row); 887: } 888: row = getNextRow(row); 889: isIn = false; 890: } 891: for (i=0; i < deepestRows.length; i++) { 892: if (row.id == deepestRows[i].id) { 893: isIn = true; 894: } 895: } 896: if (!isIn) { 897: otherRows.push(row); 898: } 899: return otherRows; 900: } 901: 902: function setDisplaySelect(deepestRows, displ) { 903: var i = 0; 904: for (i = 0; i < deepestRows.length; i++) { 905: var row = deepestRows[i]; 906: var childSEL = document.getElementById(row.id.replace('row','sel')); 907: childSEL.className = displ; 908: } 909: } 910: 911: function submitSelect() { 912: var list = document.getElementsByName('list')[0]; 913: list.setAttribute("action","/adm/wishlist?mode=edit"); 914: list.submit(); 915: } 916: 917: function setDisplayNote(rowid, displ) { 918: var row = document.getElementById(rowid); 919: if (!displ) { 920: if ((row.className).match('LC_hidden')) { 921: row.className = (row.className).replace('LC_hidden',''); 922: } 923: else { 924: var oldClass = row.className; 925: row.className = oldClass+' LC_hidden'; 926: } 927: } 928: else { 929: if (displ == '') { 930: row.className = (row.className).replace('LC_hidden',''); 931: } 932: else if (displ != '' && !((row.className).match('LC_hidden'))) { 933: var oldClass = row.className; 934: row.className = oldClass+' LC_hidden'; 935: } 936: } 937: var noteText = document.getElementById(rowid.replace('note','noteText')); 938: var noteImg = document.getElementById(rowid.replace('note','noteImg')); 939: if (noteText.value) { 940: noteImg.src = "/res/adm/pages/anot2.png"; 941: } 942: else { 943: noteImg.src = "/res/adm/pages/anot.png"; 944: } 945: 946: } 947: 948: function setClasses() { 949: var row = document.getElementById("row0"); 950: var note = document.getElementById("note0"); 951: var LC_class = 0; 952: if (getNextRow(row) != null) { 953: while (getNextRow(row) != null) { 954: if (!(row.className).match('LC_hidden')) { 955: note.className = (note.className).replace('LC_even_row',''); 956: note.className = (note.className).replace('LC_odd_row',''); 957: if (LC_class) { 958: row.className = 'LC_even_row'; 959: note.className = 'LC_even_row'+note.className; 960: } 961: else { 962: row.className = 'LC_odd_row'; 963: note.className = 'LC_odd_row'+note.className;; 964: } 965: LC_class = !LC_class; 966: } 967: note = getNextNote(row); 968: row = getNextRow(row); 969: } 970: } 971: if (!(row.className).match('LC_hidden')) { 972: note.className = (note.className).replace('LC_even_row',''); 973: note.className = (note.className).replace('LC_odd_row',''); 974: if (LC_class) { 975: row.className = 'LC_even_row'; 976: note.className = 'LC_even_row'+note.className; 977: } 978: else { 979: row.className = 'LC_odd_row'; 980: note.className = 'LC_odd_row'+note.className; 981: } 982: } 983: } 984: 985: function selectDestinationFolder(mode) { 986: var mark = document.getElementsByName('mark'); 987: var i = 0; 988: for (i = 0; i < mark.length; i++) { 989: if (mark[i].checked) { 990: document.getElementsByName('list')[0].submit(); 991: return true; 992: } 993: } 994: if (mode == 'move') { 995: alert('$warningMoveS'); 996: } 997: else { 998: alert('$warningMoveD'); 999: } 1000: return false; 1001: } 1002: 1003: function preview(url) { 1004: var newWin; 1005: if (!(url.match(/^http:\\/\\//) || url.match(/^https:\\/\\//))) { 1006: newWin = window.open(url+'?inhibitmenu=yes','preview','width=560,height=350,scrollbars=yes'); 1007: } 1008: else { 1009: newWin = window.open(url,'preview','width=560,height=350,scrollbars=yes'); 1010: } 1011: newWin.focus(); 1012: } 1013: 1014: function checkAll() { 1015: var checkboxes = document.getElementsByName('check'); 1016: for (var i = 0; i < checkboxes.length; i++) { 1017: if (!checkboxes[i].disabled) { 1018: checkboxes[i].checked = "checked"; 1019: } 1020: } 1021: } 1022: 1023: function uncheckAll() { 1024: var checkboxes = document.getElementsByName('check'); 1025: for (var i = 0; i < checkboxes.length; i++) { 1026: if (!checkboxes[i].disabled) { 1027: checkboxes[i].checked = ""; 1028: } 1029: } 1030: } 1031: 1032: JAVASCRIPT 1033: return $js; 1034: } 1035: 1036: sub JSforImport{ 1037: my $rat = shift; 1038: 1039: my $js; 1040: if ($rat eq 'simple' || $rat eq '') { 1041: $js = &Apache::lonhtmlcommon::scripttag(<<JAVASCRIPT); 1042: function finish_import() { 1043: opener.document.forms.simpleedit.importdetail.value=''; 1044: for (var num = 0; num < document.forms.groupsort.fnum.value; num++) { 1045: if (eval("document.forms.groupsort.check"+num+".checked") && eval("document.forms.groupsort.filelink"+num+".value") != '') { 1046: opener.document.forms.simpleedit.importdetail.value+='&'+ 1047: eval("document.forms.groupsort.title"+num+".value")+'='+ 1048: eval("document.forms.groupsort.filelink"+num+".value")+'='+ 1049: eval("document.forms.groupsort.id"+num+".value"); 1050: } 1051: } 1052: opener.document.forms.simpleedit.submit(); 1053: self.close(); 1054: } 1055: JAVASCRIPT 1056: } 1057: else { 1058: $js = &Apache::lonhtmlcommon::scripttag(<<JAVASCRIPT); 1059: function finish_import() { 1060: var linkflag=false; 1061: for (var num=0; num<document.forms.groupsort.fnum.value; num++) { 1062: if (eval("document.forms.groupsort.check"+num+".checked") && eval("document.forms.groupsort.filelink"+num+".value") != '') { 1063: insertRowInLastRow(); 1064: placeResourceInLastRow( 1065: eval("document.forms.groupsort.title"+num+".value"), 1066: eval("document.forms.groupsort.filelink"+num+".value"), 1067: eval("document.forms.groupsort.id"+num+".value"), 1068: linkflag 1069: ); 1070: linkflag=true; 1071: } 1072: } 1073: opener.editmode=0; 1074: opener.notclear=0; 1075: opener.linkmode=0; 1076: opener.draw(); 1077: self.close(); 1078: } 1079: 1080: function insertRowInLastRow() { 1081: opener.insertrow(opener.maxrow); 1082: opener.addobj(opener.maxrow,'e&2'); 1083: } 1084: 1085: function placeResourceInLastRow (title,url,id,linkflag) { 1086: opener.mostrecent=opener.newresource(opener.maxrow,2,opener.unescape(title), 1087: opener.unescape(url),'false','normal',id); 1088: opener.save(); 1089: if (linkflag) { 1090: opener.joinres(opener.linkmode,opener.mostrecent,0); 1091: } 1092: opener.linkmode=opener.mostrecent; 1093: } 1094: JAVASCRIPT 1095: } 1096: return $js; 1097: } 1098: 1099: # HTML-Markup for table if in view-mode 1100: my $wishlistHTMLview; 1101: my $indent = $indentConst; 1102: sub wishlistView { 1103: my $nodes = shift; 1104: 1105: foreach my $n (@$nodes) { 1106: my $index = $n->value()->nindex(); 1107: 1108: # 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. 1109: # only display the top level entries on load 1110: $wishlistHTMLview .= ($n->parent()->value() eq 'root')?&Apache::loncommon::start_data_table_row('','row'.$index) 1111: :&Apache::loncommon::continue_data_table_row('LC_hidden','row'.$index); 1112: 1113: 1114: # checkboxes 1115: $wishlistHTMLview .= '<td><input type="checkbox" name="mark" id="check'.$index.'" value="'.$index.'" '. 1116: 'onclick="selectAction('."'row".$index."'".')" /></td>'; 1117: 1118: # entry is a folder 1119: if ($n->value()->path() eq '') { 1120: $wishlistHTMLview .= '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<0?0:($indent-$indentConst)).'px; min-width: 220px;">'. 1121: '<a href="javascript:;" onclick="folderAction('."'row".$index."'".')" style="vertical-align:top">'. 1122: '<img src="/adm/lonIcons/arrow.closed.gif" id="img'.$index.'" alt = "" class="LC_icon"/>'. 1123: '<img src="/adm/lonIcons/navmap.folder.closed.gif" id="imgFolder'.$index.'" alt="folder"/>'. 1124: $n->value()->title().'</a></td>'; 1125: } 1126: # entry is a link 1127: else { 1128: $wishlistHTMLview .= '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<=0?$indentConst:$indent).'px; min-width: 220px;">'. 1129: '<a href="javascript:preview('."'".$n->value()->path()."'".');">'. 1130: '<img src="/res/adm/pages/wishlist-link.png" id="img'.$index.'" alt="link" />'. 1131: $n->value()->title().'</a></td>'; 1132: } 1133: 1134: # note-icon, different icons for an entries with note and those without 1135: my $noteIMG = 'anot.png'; 1136: 1137: if ($n->value()->note() ne '') { 1138: $noteIMG = 'anot2.png'; 1139: } 1140: 1141: $wishlistHTMLview .= '<td style="padding-left:10px;"><a href="javascript:;" onclick="setDisplayNote('."'note".$index."'".')">'. 1142: '<img id="noteImg'.$index.'" src="/res/adm/pages/'.$noteIMG.'" alt="'.&mt('Note').'" title="'.&mt('Note').'" '. 1143: ' class="LC_icon"/></a></td>'; 1144: 1145: $wishlistHTMLview .= &Apache::loncommon::end_data_table_row(); 1146: 1147: # start row containing the textarea for the note, do not display note on default 1148: $wishlistHTMLview .= &Apache::loncommon::continue_data_table_row('LC_hidden','note'.$index). 1149: '<td></td><td>'. 1150: '<textarea id="noteText'.$index.'" cols="25" rows="3" style="width:100%" '. 1151: 'name="newnote" >'. 1152: $n->value()->note().'</textarea></td><td></td>'; 1153: $wishlistHTMLview .= &Apache::loncommon::end_data_table_row(); 1154: 1155: # if the entry is a folder, it could have other entries as content. if it has, call wishlistView for those entries 1156: my @children = $n->children(); 1157: if ($#children >=0) { 1158: $indent += 20; 1159: &wishlistView(\@children); 1160: $indent -= 20; 1161: } 1162: } 1163: } 1164: 1165: 1166: # HTML-Markup for table if in edit-mode 1167: my $wishlistHTMLedit; 1168: my $indent = $indentConst; 1169: sub wishlistEdit { 1170: my $nodes = shift; 1171: my $curNode = 1; 1172: 1173: foreach my $n (@$nodes) { 1174: my $index = $n->value()->nindex(); 1175: 1176: # start row, use data_table routines to set class to LC_even or LC_odd automatically. 1177: # this rows contains a checkbox, a select-field for sorting entries, the title in an input-field and the note-icon. 1178: # only display the top level entries on load 1179: $wishlistHTMLedit .= ($n->parent()->value() eq 'root')?&Apache::loncommon::start_data_table_row('','row'.$index) 1180: :&Apache::loncommon::continue_data_table_row('LC_hidden','row'.$index); 1181: 1182: # checkboxes 1183: $wishlistHTMLedit .= '<td><input type="checkbox" name="mark" id="check'.$index.'" value="'.$index.'" '. 1184: 'onclick="selectAction('."'row".$index."'".')" /></td>'; 1185: 1186: # 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. 1187: # set the number for the current entry into brackets 1188: my $options; 1189: for (my $i = 1; $i < ((scalar @{$nodes})+1); $i++) { 1190: if ($i == $curNode) { 1191: $options .= '<option selected="selected" value="">('.$i.')</option>'; 1192: } 1193: else { 1194: $options .= '<option value="'.$i.'">'.$i.'</option>'; 1195: } 1196: } 1197: $curNode++; 1198: 1199: # entry is a folder 1200: if ($n->value()->path() eq '') { 1201: $wishlistHTMLedit .= '<td><select class="LC_hidden" name="sel" id="sel'.$index.'" onchange="submitSelect();">'. 1202: $options.'</select></td>'. 1203: '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<0?0:($indent-$indentConst)).'px;">'. 1204: '<a href="javascript:;" onclick="folderAction('."'row".$index."'".')" style="vertical-align:top" >'. 1205: '<img src="/adm/lonIcons/arrow.closed.gif" id="img'.$index.'" alt = "" class="LC_icon"/>'. 1206: '<img src="/adm/lonIcons/navmap.folder.closed.gif" id="imgFolder'.$index.'" alt="folder"/></a>'. 1207: '<input type="text" name="newtitle" value="'.$n->value()->title().'" alt = "'.$n->value()->title().'" />'. 1208: '</td><td></td>'; 1209: 1210: } 1211: # entry is a link 1212: else { 1213: $wishlistHTMLedit .= '<td><select class="LC_hidden" name="sel" id="sel'.$index.'" onchange="submitSelect();">'. 1214: $options.'</select></td>'. 1215: '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<=0?$indentConst:$indent).'px;">'. 1216: '<img src="/res/adm/pages/wishlist-link.png" id="img'.$index.'" alt="link"/>'. 1217: '<input type="text" name="newtitle" value="'.$n->value()->title().'" alt = "'.$n->value()->title().'" /></td>'. 1218: '<td><input type="text" name="newpath" value="'.$n->value()->path().'" alt = "'.$n->value()->path().'" /></td>'; 1219: } 1220: 1221: # note-icon, different icons for an entries with note and those without 1222: my $noteIMG = 'anot.png'; 1223: 1224: if ($n->value()->note() ne '') { 1225: $noteIMG = 'anot2.png'; 1226: } 1227: 1228: $wishlistHTMLedit .= '<td style="padding-left:10px;"><a href="javascript:;" onclick="setDisplayNote('."'note".$index."'".')">'. 1229: '<img id="noteImg'.$index.'" src="/res/adm/pages/'.$noteIMG.'" alt="'.&mt('Note').'" title="'.&mt('Note').'" '. 1230: ' class="LC_icon"/></a></td>'; 1231: 1232: $wishlistHTMLedit .= &Apache::loncommon::end_data_table_row(); 1233: 1234: # start row containing the textarea for the note 1235: $wishlistHTMLedit .= &Apache::loncommon::continue_data_table_row('LC_hidden','note'.$index). 1236: '<td></td><td></td><td colspan="2">'. 1237: '<textarea id="noteText'.$index.'" cols="25" rows="3" style="width:100%" '. 1238: 'name="newnote">'. 1239: $n->value()->note().'</textarea></td><td></td>'; 1240: $wishlistHTMLedit .= &Apache::loncommon::end_data_table_row(); 1241: 1242: # if the entry is a folder, it could have other entries as content. if it has, call wishlistEdit for those entries 1243: my @children = $n->children(); 1244: if ($#children >=0) { 1245: $indent += 20; 1246: &wishlistEdit(\@children); 1247: $indent -= 20; 1248: } 1249: } 1250: } 1251: 1252: 1253: 1254: # HTML-Markup for table if in move-mode 1255: my $wishlistHTMLmove ='<tr id="root" class="LC_odd_row"><td><input type="radio" name="mark" id="radioRoot" value="root" /></td>'. 1256: '<td>'.&mt('Top level').'</td><td></td></tr>'; 1257: my $indent = $indentConst; 1258: sub wishlistMove { 1259: my $nodes = shift; 1260: my $marked = shift; 1261: 1262: foreach my $n (@$nodes) { 1263: my $index = $n->value()->nindex(); 1264: 1265: #find out wether the current entry was marked to be moved. 1266: my $isIn = 0; 1267: foreach my $m (@$marked) { 1268: if ($index == $m) { 1269: $isIn = 1; 1270: } 1271: } 1272: # 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 1273: $wishlistHTMLmove .= &Apache::loncommon::start_data_table_row('','row'.$index); 1274: 1275: 1276: # entry is a folder 1277: if ($n->value()->path() eq '') { 1278: # display a radio-button, if the folder was not selected to be moved 1279: if (!$isIn) { 1280: $wishlistHTMLmove .= '<td><input type="radio" name="mark" id="radio'.$index.'" value="'.$index.'" /></td>'. 1281: '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<0?0:($indent-$indentConst)).'px; min-width: 220px;">'; 1282: } 1283: # highlight the title, if the folder was selected to be moved 1284: else { 1285: $wishlistHTMLmove .= '<td></td>'. 1286: '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<0?0:($indent-$indentConst)).'px; min-width: 220px;'. 1287: 'color:red;">'; 1288: } 1289: #arrow- and folder-image, all folders are open, and title 1290: $wishlistHTMLmove .= '<img src="/adm/lonIcons/arrow.open.gif" id="img'.$index.'" alt = "" />'. 1291: '<img src="/adm/lonIcons/navmap.folder.open.gif" id="imgFolder'.$index.'" alt="folder"/>'. 1292: $n->value()->title().'</td>'; 1293: } 1294: # entry is a link 1295: else { 1296: # higlight the title, if the link was selected to be moved 1297: my $highlight = ''; 1298: if ($isIn) { 1299: $highlight = 'style="color:red;"'; 1300: } 1301: # link-image and title 1302: $wishlistHTMLmove .= '<td></td>'. 1303: '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<=0?$indentConst:$indent).'px; min-width: 220px;">'. 1304: '<a href="javascript:preview('."'".$n->value()->path()."'".');" '.$highlight.'>'. 1305: '<img src="/res/adm/pages/wishlist-link.png" id="img'.$index.'" alt="link"/>'. 1306: $n->value()->title().'</a></td>'; 1307: } 1308: 1309: # note-icon, different icons for an entries with note and those without 1310: my $noteIMG = 'anot.png'; 1311: 1312: if ($n->value()->note() ne '') { 1313: $noteIMG = 'anot2.png'; 1314: } 1315: 1316: $wishlistHTMLmove .= '<td style="padding-left:10px;"><a href="javascript:;" onclick="setDisplayNote('."'note".$index."'".')">'. 1317: '<img id="noteImg'.$index.'" src="/res/adm/pages/'.$noteIMG.'" alt="'.&mt('Note').'" title="'.&mt('Note').'" '. 1318: ' class="LC_icon"/></a></td>'; 1319: 1320: $wishlistHTMLmove .= &Apache::loncommon::end_data_table_row(); 1321: 1322: # start row containing the textarea for the note, readonly in move-mode 1323: $wishlistHTMLmove .= &Apache::loncommon::continue_data_table_row('LC_hidden','note'.$index). 1324: '<td></td><td>'. 1325: '<textarea id="noteText'.$index.'" cols="25" rows="3" style="width:100%" '. 1326: 'name="newnote" readonly="readonly">'. 1327: $n->value()->note().'</textarea></td><td></td>'. 1328: &Apache::loncommon::end_data_table_row(); 1329: 1330: # if the entry is a folder, it could have other entries as content. if it has, call wishlistMove for those entries 1331: my @children = $n->children(); 1332: if ($#children >=0) { 1333: $indent += 20; 1334: &wishlistMove(\@children, $marked); 1335: $indent -= 20; 1336: } 1337: } 1338: } 1339: 1340: 1341: 1342: # HTML-Markup for table if in import-mode 1343: my $wishlistHTMLimport; 1344: my $indent = $indentConst; 1345: my $form = 1; 1346: sub wishlistImport { 1347: my ($nodes,$numskipped) = @_; 1348: 1349: my ($is_community,%nopick); 1350: if ($env{'request.course.id'}) { 1351: if (&Apache::loncommon::course_type() eq 'Community') { 1352: $is_community = 1; 1353: } 1354: } 1355: 1356: foreach my $n (@$nodes) { 1357: my $index = $n->value()->nindex(); 1358: if ($n->value()->path() =~ m{^(/res/$match_domain/$match_username/)}) { 1359: if ($is_community) { 1360: unless (&Apache::lonnet::allowed('bro',$n->value()->path())) { 1361: $nopick{$n->value()->path()} = $n->value()->title(); 1362: $$numskipped ++; 1363: } 1364: } else { 1365: unless (&Apache::lonnet::allowed('bre',$n->value()->path())) { 1366: $nopick{$n->value()->path()} = $n->value()->title(); 1367: $$numskipped ++; 1368: } 1369: } 1370: } 1371: 1372: # 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. 1373: # only display the top level entries on load 1374: $wishlistHTMLimport .= ($n->parent()->value() eq 'root')?&Apache::loncommon::start_data_table_row('','row'.$index) 1375: :&Apache::loncommon::continue_data_table_row('LC_hidden','row'.$index); 1376: 1377: 1378: # checkboxes 1379: $wishlistHTMLimport .= '<td>'; 1380: my ($disabled,$onclick,$image,$style); 1381: if ($nopick{$n->value()->path()}) { 1382: $disabled = ' disabled="disabled"'; 1383: $image = 'wishlist-link-lighter.png'; 1384: $style = 'style="color:#808080;"'; 1385: } else { 1386: $onclick = ' onclick="selectAction('."'row".$index."'".')"'; 1387: $image = 'wishlist-link.png'; 1388: } 1389: $wishlistHTMLimport .= '<input type="checkbox" name="check" id="check'.$index.'" value="'.$index.'" '. 1390: $disabled.$onclick.' />'. 1391: '<input type="hidden" name="title'.$index.'" value="'.&escape($n->value()->title()).'" />'. 1392: '<input type="hidden" name="filelink'.$index.'" value="'.&escape($n->value()->path()).'" />'. 1393: '<input type="hidden" name="id'.$index.'" />'; 1394: $wishlistHTMLimport .= '</td>'; 1395: 1396: # entry is a folder 1397: if ($n->value()->path() eq '') { 1398: $wishlistHTMLimport .= '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<0?0:($indent-$indentConst)).'px; min-width: 220px;">'. 1399: '<a href="javascript:;" onclick="folderAction('."'row".$index."'".')" style="vertical-align:top">'. 1400: '<img src="/adm/lonIcons/arrow.closed.gif" id="img'.$index.'" alt = "" class="LC_icon"/>'. 1401: '<img src="/adm/lonIcons/navmap.folder.closed.gif" id="imgFolder'.$index.'" alt="folder"/>'. 1402: $n->value()->title().'</a></td>'; 1403: } 1404: # entry is a link 1405: else { 1406: $wishlistHTMLimport .= '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<=0?$indentConst:$indent).'px; min-width: 220px;">'; 1407: unless ($nopick{$n->value()->path()}) { 1408: $wishlistHTMLimport .= '<a href="javascript:preview('."'".$n->value()->path()."'".');">'; 1409: } 1410: $wishlistHTMLimport .= '<img src="/res/adm/pages/'.$image.'" id="img'.$index.'" alt="link" />'. 1411: '<span '.$style.'>'.$n->value()->title().'</span></a></td>'; 1412: $form++; 1413: } 1414: 1415: # note-icon, different icons for an entries with note and those without 1416: my $noteIMG = 'anot.png'; 1417: 1418: if ($n->value()->note() ne '') { 1419: $noteIMG = 'anot2.png'; 1420: } 1421: 1422: $wishlistHTMLimport .= '<td style="padding-left:10px;"><a href="javascript:;" onclick="setDisplayNote('."'note".$index."'".')">'. 1423: '<img id="noteImg'.$index.'" src="/res/adm/pages/'.$noteIMG.'" alt="'.&mt('Note').'" title="'.&mt('Note').'" '. 1424: ' class="LC_icon"/></a></td>'; 1425: 1426: $wishlistHTMLimport .= &Apache::loncommon::end_data_table_row(); 1427: 1428: # start row containing the textarea for the note, do not display note on default, readonly in import-mode 1429: $wishlistHTMLimport .= &Apache::loncommon::continue_data_table_row('LC_hidden','note'.$index). 1430: '<td></td><td>'. 1431: '<textarea id="noteText'.$index.'" cols="25" rows="3" style="width:100%" '. 1432: 'name="newnote" readonly="readonly">'. 1433: $n->value()->note().'</textarea></td><td></td>'; 1434: $wishlistHTMLimport .= &Apache::loncommon::end_data_table_row(); 1435: 1436: # if the entry is a folder, it could have other entries as content. if it has, call wishlistImport for those entries 1437: my @children = $n->children(); 1438: if ($#children >=0) { 1439: $indent += 20; 1440: &wishlistImport(\@children,$numskipped); 1441: $indent -= 20; 1442: } 1443: } 1444: return; 1445: } 1446: 1447: # Returns the HTML-Markup for wishlist 1448: sub makePage { 1449: my $rootgiven = shift; 1450: my $mode = shift; 1451: my $marked = shift; 1452: 1453: $root = $rootgiven; 1454: @childrenRt = $root->children(); 1455: 1456: # breadcrumbs and start_page 1457: &Apache::lonhtmlcommon::clear_breadcrumbs(); 1458: &Apache::lonhtmlcommon::add_breadcrumb( 1459: { href => '/adm/wishlist?mode='.$mode, 1460: text => 'Stored Links'}); 1461: my $startPage = &Apache::loncommon::start_page('Stored Links',undef, 1462: {'add_entries' => { 1463: 'onload' => 'javascript:onLoadAction('."'".$mode."'".');', 1464: 'onunload' => 'javascript:window.name = '."'loncapaclient'"}}); 1465: 1466: my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs(&mt('Stored Links').&Apache::loncommon::help_open_topic('Wishlist')); 1467: 1468: # get javascript-code for wishlist-interactions 1469: my $js = &JSforWishlist(); 1470: 1471: # texthash for items in funtionlist 1472: my %lt = &Apache::lonlocal::texthash( 1473: 'ed' => 'Edit', 1474: 'vw' => 'View', 1475: 'al' => 'Add Link', 1476: 'af' => 'Add Folder', 1477: 'mv' => 'Move Selected', 1478: 'dl' => 'Delete Selected', 1479: 'sv' => 'Save'); 1480: 1481: # start functionlist 1482: my $functions = &Apache::lonhtmlcommon::start_funclist(); 1483: 1484: # icon for edit-mode, display when in view-mode 1485: if ($mode eq 'view') { 1486: $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '. 1487: 'onclick="setFormAction('."'save','edit'".');" class="LC_menubuttons_link">'. 1488: '<img src="/res/adm/pages/edit-mode-22x22.png" alt="'.$lt{'ed'}.'" '. 1489: 'title="'.$lt{'ed'}.'" class="LC_icon"/> '. 1490: '<span class="LC_menubuttons_inline_text">'.$lt{'ed'}.'</span></a>'); 1491: } 1492: # icon for view-mode, display when in edit-mode 1493: else { 1494: $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '. 1495: 'onclick="setFormAction('."'save','view'".');" class="LC_menubuttons_link">'. 1496: '<img src="/res/adm/pages/view-mode-22x22.png" alt="'.$lt{'vw'}.'" '. 1497: 'title="'.$lt{'vw'}.'" class="LC_icon"/> '. 1498: '<span class="LC_menubuttons_inline_text">'.$lt{'vw'}.'</span></a>'); 1499: } 1500: 1501: # icon for adding a new link 1502: $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '. 1503: 'onclick="newLink();" class="LC_menubuttons_link">'. 1504: '<img src="/res/adm/pages/link-new-22x22.png" alt="'.$lt{'al'}.'" '. 1505: 'title="'.$lt{'al'}.'" class="LC_icon"/>'. 1506: '<span class="LC_menubuttons_inline_text">'.$lt{'al'}.'</span></a>'); 1507: 1508: # icon for adding a new folder 1509: $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '. 1510: 'onclick="newFolder();" class="LC_menubuttons_link">'. 1511: '<img src="/res/adm/pages/folder-new-22x22.png" alt="'.$lt{'af'}.'" '. 1512: 'title="'.$lt{'af'}.'" class="LC_icon"/>'. 1513: '<span class="LC_menubuttons_inline_text">'.$lt{'af'}.'</span></a>'); 1514: 1515: # icon for moving entries 1516: $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '. 1517: 'onclick="setFormAction('."'move','move'".'); " class="LC_menubuttons_link">'. 1518: '<img src="/res/adm/pages/move-22x22.png" alt="'.$lt{'mv'}.'" '. 1519: 'title="'.$lt{'mv'}.'" class="LC_icon" />'. 1520: '<span class="LC_menubuttons_inline_text">'.$lt{'mv'}.'</span></a>'); 1521: 1522: # icon for deleting entries 1523: $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '. 1524: 'onclick="setFormAction('."'delete','".$mode."'".'); " class="LC_menubuttons_link">'. 1525: '<img src="/res/adm/pages/del.png" alt="'.$lt{'dl'}.'" '. 1526: 'title="'.$lt{'dl'}.'" class="LC_icon" />'. 1527: '<span class="LC_menubuttons_inline_text">'.$lt{'dl'}.'</span></a>'); 1528: 1529: # icon for saving changes 1530: $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '. 1531: 'onclick="setFormAction('."'saveOK','".$mode."'".'); " class="LC_menubuttons_link">'. 1532: '<img src="/res/adm/pages/save-22x22.png" alt="'.$lt{'sv'}.'" '. 1533: 'title="'.$lt{'sv'}.'" class="LC_icon" />'. 1534: '<span class="LC_menubuttons_inline_text">'.$lt{'sv'}.'</span></a>'); 1535: 1536: # end funtionlist and generate subbox 1537: $functions.= &Apache::lonhtmlcommon::end_funclist(); 1538: my $subbox = &Apache::loncommon::head_subbox($functions); 1539: 1540: # start form 1541: my $inner .= '<form name="list" action ="/adm/wishlist" method="post">'. 1542: '<input type="hidden" id="action" name="action" value="" />'; 1543: 1544: # only display subbox in view- or edit-mode 1545: if ($mode eq 'view' || $mode eq 'edit') { 1546: $inner .= $subbox; 1547: } 1548: 1549: # generate table-content depending on mode 1550: if ($mode eq 'edit') { 1551: &wishlistEdit(\@childrenRt); 1552: if ($wishlistHTMLedit ne '') { 1553: $inner .= &Apache::loncommon::start_data_table("LC_tableOfContent"); 1554: $inner .= $wishlistHTMLedit; 1555: $inner .= &Apache::loncommon::end_data_table(); 1556: } 1557: else { 1558: $inner .= '<span class="LC_info">'.&mt("Your Stored Links list is currently empty.").'</span>'; 1559: } 1560: $wishlistHTMLedit = ''; 1561: } 1562: elsif ($mode eq 'view') { 1563: &wishlistView(\@childrenRt); 1564: if ($wishlistHTMLview ne '') { 1565: $inner .= '<table class="LC_data_table LC_tableOfContent">'.$wishlistHTMLview.'</table>'; 1566: } 1567: else { 1568: $inner .= '<span class="LC_info">'.&mt("Your Stored Links list is currently empty.").'</span>'; 1569: } 1570: $wishlistHTMLview = ''; 1571: } 1572: else { 1573: my $markStr = ''; 1574: foreach my $m (@$marked) { 1575: $markStr .= $m.','; 1576: } 1577: if ($markStr) { 1578: $markStr = substr($markStr, 0, length($markStr)-1); 1579: $inner .= '<input type="hidden" value="'.$markStr.'" name="markedToMove" />'; 1580: $inner .= '<p><span class="LC_info">'.&mt('You have selected the red marked entries to be moved to another folder. '. 1581: 'Now choose the new destination folder.').'</span></p>'; 1582: &wishlistMove(\@childrenRt, $marked); 1583: $inner .= '<table class="LC_data_table LC_tableOfContent">'.$wishlistHTMLmove.'</table><br/><br/>'; 1584: $inner .= '<input type="button" value="'.&mt('Move').'" onclick="setFormAction('."'move','view'".');" />'. 1585: '<input type="button" value="'.&mt('Cancel').'" onclick="go('."'/adm/wishlist'".')" />'; 1586: 1587: $wishlistHTMLmove ='<tr id="root" class="LC_odd_row"><td><input type="radio" name="mark" id="radioRoot" value="root" /></td>'. 1588: '<td>'.&mt('Top level').'</td><td></td></tr>'; 1589: } 1590: else { 1591: $inner .= '<p><span class="LC_info">'.&mt("You haven't marked any entry to move.").'</span></p>'. 1592: '<input type="button" value="'.&mt('Back').'" onclick="go('."'/adm/wishlist'".')" />'; 1593: } 1594: } 1595: 1596: # end form 1597: $inner .= '</form>'; 1598: 1599: # end_page 1600: my $endPage = &Apache::loncommon::end_page(); 1601: 1602: # put all page-elements together 1603: my $page = $startPage.$breadcrumbs.$js.$inner.$endPage; 1604: 1605: return $page; 1606: } 1607: 1608: 1609: # Returns the HTML-Markup for the PopUp, shown when a new link should set, when NOT 1610: # beeing in the wishlist-interface (method is called in lonmenu and lonsearchcat) 1611: sub makePopUpNewLink { 1612: my ($title, $path) = @_; 1613: 1614: # Get all existing folders to offer posibility to set a new link 1615: # into a folder 1616: my %TreeHashLink = &Apache::lonwishlist::getWishlist(); 1617: my $rootLink = &Apache::Tree::HashToTree(\%TreeHashLink); 1618: my @childrenRtLink = $rootLink->children(); 1619: 1620: $foldersOption = ''; 1621: @allFolders = (); 1622: &getFoldersToArray(\@childrenRtLink); 1623: &getFoldersForOption(\@childrenRtLink); 1624: 1625: my $options = '<option value="" selected="selected">('.&mt('Top level').')</option>'.$foldersOption; 1626: $foldersOption = ''; 1627: @allFolders = (); 1628: 1629: # HTML-Markup for the Pop-Up-window 'Set a link for this resource to wishlist' 1630: my $startPageWishlistlink = 1631: &Apache::loncommon::start_page('Save to Stored Links',undef, 1632: {'only_body' => 1, 1633: 'bgcolor' => '#FFFFFF',}); 1634: 1635: my $warningLink = &mt('You must insert a title!'); 1636: my $warningLinkNotAllowed1 = 1637: &mt('You can only insert links to LON-CAPA resources from the resource-pool'. 1638: ' or to external websites.'. 1639: ' Paths to LON-CAPA resources must be of the form /res/domain/user/...'. 1640: ' Paths to external websites must contain the network protocol, e.g. http://...'); 1641: 1642: my $inPageWishlistlink1 = '<h1>'.&mt('Save to Stored Links').'</h1>'; 1643: # If no title is delivered, 'New Link' is called up from the wishlist-interface, so after 1644: # submitting the window should close instead of offering a link to wishlist (like it should do 1645: # if we call 'Set New Link' from within a browsed ressource) 1646: if (!$title) { 1647: $inPageWishlistlink1 .= '<form method="post" name="newlink" action="/adm/wishlist" target="wishlist"'. 1648: 'onsubmit="return newlinksubmit();" >'; 1649: } 1650: else { 1651: $inPageWishlistlink1 .= '<form method="post" name="newlink" action="/adm/wishlist?mode=set" '. 1652: 'onsubmit="return newlinksubmit();" >'; 1653: } 1654: $inPageWishlistlink1 .= &Apache::lonhtmlcommon::start_pick_box(). 1655: &Apache::lonhtmlcommon::row_title(&mt('Link Title')); 1656: 1657: my $inPageWishlistlink2 = &Apache::lonhtmlcommon::row_closure(). 1658: &Apache::lonhtmlcommon::row_title(&mt('Path')); 1659: 1660: my $inPageWishlistlink3 = &Apache::lonhtmlcommon::row_closure(). 1661: &Apache::lonhtmlcommon::row_title(&mt('Note')). 1662: '<textarea name="note" rows="3" cols="35" style="width:100%"></textarea>'. 1663: &Apache::lonhtmlcommon::row_closure(1). 1664: &Apache::lonhtmlcommon::end_pick_box(). 1665: '<br/><br/>'. 1666: '<input type="submit" value="'.&mt('Save in').'" />'. 1667: '<select name="folders">'. 1668: $options. 1669: '</select>'. 1670: '<input type="button" value="'.&mt('Cancel').'" onclick="javascript:window.close();" />'. 1671: '</form>'; 1672: $options = ''; 1673: 1674: my $endPageWishlistlink = &Apache::loncommon::end_page(); 1675: 1676: my $popUp = $startPageWishlistlink. 1677: $inPageWishlistlink1. 1678: '<input type="text" name="title" size="45" value="" />'. 1679: $inPageWishlistlink2. 1680: '<input type="text" name="path" size="45" value="" />'. 1681: $inPageWishlistlink3; 1682: 1683: # JavaScript-function to set title and path of ressource automatically 1684: # and show warning, if no title was set or path is invalid 1685: $popUp .= <<SCRIPT; 1686: <script type="text\/javascript"> 1687: document.getElementsByName("title")[0].value = '$title'; 1688: document.getElementsByName("path")[0].value = '$path'; 1689: var fromwishlist = false; 1690: var titleget = '$title'; 1691: if (!titleget) { 1692: fromwishlist = true; 1693: } 1694: function newlinksubmit(){ 1695: var title = document.getElementsByName("title")[0].value; 1696: var path = document.getElementsByName("path")[0].value; 1697: if (!title) { 1698: alert("$warningLink"); 1699: return false;} 1700: var linkOK = (path.match(/\^http:(\\\/\\\/)/) || path.match(/\^https:(\\\/\\\/)/)) 1701: && !(path.match(/\\.problem/) || path.match(/\\.exam/) 1702: || path.match(/\\.quiz/) || path.match(/\\.assess/) 1703: || path.match(/\\.survey/) || path.match(/\\.form/) 1704: || path.match(/\\.library/) || path.match(/\\.page/) 1705: || path.match(/\\.sequence/)); 1706: if (!path.match(/\^(\\\/res\\\/)/) && !linkOK) { 1707: alert("$warningLinkNotAllowed1"); 1708: return false;} 1709: if (fromwishlist) { 1710: window.close(); 1711: } 1712: return true;} 1713: <\/script> 1714: SCRIPT 1715: 1716: $popUp .= $endPageWishlistlink; 1717: 1718: return $popUp; 1719: } 1720: 1721: sub makePopUpNewFolder { 1722: # Get all existing folders to offer posibility to create a new folder 1723: # into an existing folder 1724: my %TreeHashLink = &Apache::lonwishlist::getWishlist(); 1725: my $rootLink = &Apache::Tree::HashToTree(\%TreeHashLink); 1726: my @childrenRtLink = $rootLink->children(); 1727: 1728: $foldersOption = ''; 1729: @allFolders = (); 1730: &getFoldersToArray(\@childrenRtLink); 1731: &getFoldersForOption(\@childrenRtLink); 1732: 1733: my $options = '<option value="" selected="selected">('.&mt('Top level').')</option>'.$foldersOption; 1734: $foldersOption = ''; 1735: @allFolders = (); 1736: 1737: # HTML-Markup for the Pop-Up-window 'New Folder' 1738: my $startPageWishlistfolder = 1739: &Apache::loncommon::start_page('New Folder',undef, 1740: {'only_body' => 1, 1741: 'bgcolor' => '#FFFFFF',}); 1742: 1743: my $warningFolder = &mt('You must insert a title!'); 1744: 1745: 1746: my $inPageNewFolder = '<h1>'.&mt('New Folder').'</h1>'. 1747: '<form method="post" name="newfolder" action="/adm/wishlist" target="wishlist" '. 1748: 'onsubmit="return newfoldersubmit();" >'. 1749: &Apache::lonhtmlcommon::start_pick_box(). 1750: &Apache::lonhtmlcommon::row_title(&mt('Folder title')). 1751: '<input type="text" name="title" size="45" value="" /><br />'. 1752: &Apache::lonhtmlcommon::row_closure(). 1753: &Apache::lonhtmlcommon::row_title(&mt('Note')). 1754: '<textarea name="note" rows="3" cols="35" style="width:100%"></textarea><br />'. 1755: &Apache::lonhtmlcommon::row_closure(1). 1756: &Apache::lonhtmlcommon::end_pick_box(). 1757: '<br/><br/>'. 1758: '<input type="submit" value="'.&mt('Save in').'" />'. 1759: '<select name="folders">'. 1760: $options. 1761: '</select>'. 1762: '<input type="button" value="'.&mt('Cancel').'" onclick="javascript:window.close();" />'. 1763: '</form>'; 1764: my $endPageWishlistfolder = &Apache::loncommon::end_page(); 1765: 1766: my $popUp = $startPageWishlistfolder. 1767: $inPageNewFolder; 1768: 1769: $popUp .= <<SCRIPT; 1770: <script type="text\/javascript"> 1771: function newfoldersubmit(){ 1772: var title = document.getElementsByName("title")[0].value; 1773: if (!title) { 1774: alert("$warningFolder"); 1775: return false;} 1776: else { 1777: window.close(); 1778: return true;}} 1779: <\/script> 1780: SCRIPT 1781: 1782: $popUp .= $endPageWishlistfolder; 1783: 1784: return $popUp; 1785: } 1786: 1787: # Returns the HTML-Markup for the page, shown when a link was set 1788: sub makePageSet { 1789: # start_page 1790: my $startPage = &Apache::loncommon::start_page('Stored Links',undef, 1791: {'only_body' => 1}); 1792: 1793: # confirm success and offer link to wishlist 1794: my $message = &Apache::lonhtmlcommon::confirm_success(&mt('Link successfully saved!')); 1795: $message = &Apache::loncommon::confirmwrapper($message); 1796: 1797: my $inner .= '<br>'.$message.'<br/><br/>'. 1798: '<a href="javascript:;" onclick="opener.open('."'/adm/wishlist'".');window.close();">'.&mt('Go to Stored Links').'</a>'.(' ' x 3). 1799: '<a href="javascript:;" onclick="window.close();">'.&mt('Close this window').'</a>'; 1800: 1801: # end_page 1802: my $endPage = &Apache::loncommon::end_page(); 1803: 1804: # put all page-elements together 1805: my $page = $startPage.$inner.$endPage; 1806: 1807: return $page; 1808: } 1809: 1810: 1811: # Returns the HTML-Markup for the page, shown when links should be imported into a course 1812: sub makePageImport { 1813: my $rootgiven = shift; 1814: my $rat = shift; 1815: 1816: $root = $rootgiven; 1817: @childrenRt = $root->children(); 1818: # start_page 1819: my $startPage = &Apache::loncommon::start_page('Stored Links',undef, 1820: {'only_body' => 1}); 1821: 1822: # get javascript-code for wishlist-interactions 1823: my $js = &JSforWishlist(); 1824: $js .= &JSforImport($rat); 1825: 1826: my $inner = '<h1>'.&mt('Import Resources from Stored Links').'</h1>'; 1827: if (!$rat) { 1828: $inner .= 1829: '<ul>'. 1830: '<li class="LC_info">'.&mt('Use the checkboxes corresponding to a folder to '. 1831: 'easily check all links within the folder.').'</li>'. 1832: '<li class="LC_info">'.&mt('The folder structure itself cannot be imported.').'</li>'. 1833: '<li class="LC_info">'.&mt('All checked links will be imported into the current folder of your course.').'</li>'. 1834: '</ul>'; 1835: } 1836: else { 1837: $inner .= 1838: '<ul>'. 1839: '<li class="LC_info">'.&mt('Use the checkboxes corresponding to a folder to '. 1840: 'easily check all links within this folder.').'</li>'. 1841: '<li class="LC_info">'.&mt('The folder structure itself cannot be imported.').'</li>'. 1842: '</ul>'; 1843: } 1844: my %wishlist = &getWishlist(); 1845: 1846: #FIXME Saved string containing all folders in wishlist.db-file (key 'folders') in first version of lonwishlist 1847: #After splitting lonwishlist into two modules, this is not necessary anymore. So, dependent from when the wishlist 1848: #was first called (i.e. when wishlist.db was created), there might be an entry 'folders' or not. Number of links in 1849: #wishlist.db depends on wether this entry exists or not...JW 1850: my $fnum; 1851: if (defined $wishlist{'folders'}) { 1852: $fnum = (keys %wishlist)-2; 1853: } 1854: else { 1855: $fnum = (keys %wishlist)-1; 1856: } 1857: 1858: $inner .= '<form method="post" name="groupsort" action="">'. 1859: '<input type="hidden" value="'.$fnum.'" name="fnum" />'. 1860: '<input type="button" onclick="javascript:checkAll()" id="checkallbutton" value="'.&mt('Check All').'" />'. 1861: '<input type="button" onclick="javascript:uncheckAll()" id="uncheckallbutton" value="'.&mt('Uncheck All').'" />'. 1862: '<input type="button" value="'.&mt('Import Checked').'" onclick="finish_import();" />'. 1863: '<input type="button" value="'.&mt('Cancel').'" onclick="window.close();" /><br/><br/>'; 1864: 1865: 1866: # wishlist-table 1867: my $numskipped = 0; 1868: &wishlistImport(\@childrenRt,\$numskipped); 1869: if ($wishlistHTMLimport ne '') { 1870: $inner .= '<table class="LC_data_table LC_tableOfContent">'.$wishlistHTMLimport.'</table>'; 1871: } 1872: else { 1873: $inner .= '<span class="LC_info">'.&mt("Your Stored Links list is currently empty.").'</span>'; 1874: } 1875: if ($numskipped > 0) { 1876: $inner .= '<p class="LC_info">'.&mt('Note: where a Stored Link is unavailable for import in the current context it is grayed out.').'</p>'; 1877: } 1878: $wishlistHTMLimport = ''; 1879: 1880: $inner .= '</form>'; 1881: 1882: # end_page 1883: my $endPage = &Apache::loncommon::end_page(); 1884: 1885: # put all page-elements together 1886: my $page = $startPage.$js.$inner.$endPage; 1887: 1888: return $page; 1889: } 1890: 1891: 1892: # Returns the HTML-Markup for error-page 1893: sub makeErrorPage { 1894: # breadcrumbs and start_page 1895: &Apache::lonhtmlcommon::add_breadcrumb( 1896: { href => '/adm/wishlist', 1897: text => 'Stored Links'}); 1898: my $startPage = &Apache::loncommon::start_page('Stored Links'); 1899: 1900: my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs(&mt('Stored Links').&Apache::loncommon::help_open_topic('Wishlist')); 1901: &Apache::lonhtmlcommon::clear_breadcrumbs(); 1902: 1903: # error-message 1904: my $inner .= '<span class="LC_error">'.&mt('An error occurred! Please try again later.').'</span>'; 1905: 1906: # end_page 1907: my $endPage = &Apache::loncommon::end_page(); 1908: 1909: # put all page-elements together 1910: my $page = $startPage.$breadcrumbs.$inner.$endPage; 1911: 1912: return $page; 1913: } 1914: 1915: 1916: # ----------------------------------------------------- package Tree 1917: # Extend CPAN-Module Tree by function like 'moveNode' or 'deleteNode' 1918: package Apache::Tree; 1919: 1920: =pod 1921: 1922: =head2 Routines from package Tree 1923: 1924: =over 4 1925: 1926: =item * &getNodeByIndex(index, nodes) 1927: 1928: Searches for a node, specified by the index, in nodes (reference to array) and returns it. 1929: 1930: 1931: =item * &moveNode(node, at, newParent) 1932: 1933: 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). 1934: 1935: 1936: =item * &removeNode(node) 1937: 1938: Removes a node given by node from the tree. 1939: 1940: 1941: =item * &TreeIndex(children) 1942: 1943: Sets an index for every node in the tree, beginning with 0. 1944: 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). 1945: 1946: 1947: =item * &setCountZero() 1948: 1949: Resets index counter. 1950: 1951: 1952: =item * &RootToHash(childrenRt) 1953: 1954: Converts the root-node to a hash-entry: the key is root and values are just the indices of root's children. 1955: 1956: 1957: =item * &TreeToHash(childrenRt) 1958: 1959: 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). 1960: 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). 1961: 1962: 1963: =item * &HashToTree() 1964: 1965: 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. 1966: 1967: 1968: =item * &buildTree(node, childrenIn, TreeNodes, TreeHash) 1969: 1970: Joins the nodes to a tree. 1971: 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). 1972: 1973: 1974: =back 1975: 1976: =cut 1977: 1978: 1979: # returns the node with a given index from a list of nodes 1980: sub getNodeByIndex { 1981: my $index = shift; 1982: my $nodes = shift; 1983: my $found; 1984: 1985: foreach my $n (@$nodes) { 1986: my $curIndex = $n->value()->nindex(); 1987: if ($curIndex == $index) { 1988: $found = $n; 1989: } 1990: } 1991: return $found; 1992: } 1993: 1994: # moves a given node to a new parent or change the position from a node 1995: # within its siblings (sorting) 1996: sub moveNode { 1997: my $node = shift; 1998: my $at = shift; 1999: my $newParent = shift; 2000: 2001: 2002: if (!$newParent) { 2003: $newParent = $node->parent(); 2004: } 2005: 2006: $node->parent()->remove_child($node); 2007: 2008: if (defined $at) { 2009: $newParent->add_child({at => $at},$node); 2010: } 2011: else { 2012: $newParent->add_child($node); 2013: } 2014: 2015: # updating root's children 2016: @childrenRt = $root->children(); 2017: } 2018: 2019: # removes a given node 2020: sub removeNode() { 2021: my $node = shift; 2022: my @children = $node->children(); 2023: 2024: if ($#children >= 0) { 2025: foreach my $c (@children) { 2026: &removeNode($c); 2027: } 2028: } 2029: $node->parent()->remove_child($node); 2030: 2031: # updating root's children 2032: @childrenRt = $root->children(); 2033: } 2034: 2035: 2036: # set an index for every node in the tree, beginning with 0 2037: my $count = 0; 2038: sub TreeIndex { 2039: my $children = shift; 2040: 2041: foreach my $n (@$children) { 2042: my @children = $n->children(); 2043: $n->value()->nindex($count);$count++; 2044: 2045: if ($#children>=0) { 2046: &TreeIndex(\@children); 2047: } 2048: } 2049: } 2050: 2051: # reset index counter 2052: sub setCountZero { 2053: $count = 0; 2054: } 2055: 2056: 2057: # convert the tree to a hash 2058: # each node is one hash-entry 2059: # keys are the indices, values are all other attributes 2060: # (containing tile, path, note, date and indices for all direct children) 2061: # except for root: the key is root and values are 2062: # just the indices of root's children 2063: sub RootToHash { 2064: my $childrenRt = shift; 2065: my @indexarr = (); 2066: 2067: foreach my $c (@$childrenRt) { 2068: push (@indexarr, $c->value()->nindex()); 2069: } 2070: $TreeToHash{'root'} = [@indexarr]; 2071: } 2072: 2073: sub TreeToHash { 2074: my $childrenRt = shift; 2075: 2076: foreach my $n (@$childrenRt) { 2077: my @arrtmp = (); 2078: $arrtmp[0] = $n->value()->title(); 2079: $arrtmp[1] = $n->value()->path(); 2080: $arrtmp[2] = $n->value()->note(); 2081: $arrtmp[3] = $n->value()->date(); 2082: my @childrenRt = $n->children(); 2083: my $co = 4; 2084: foreach my $c (@childrenRt) { 2085: my $i = $c->value()->nindex(); 2086: $arrtmp[$co] = $i; 2087: $co++; 2088: } 2089: $TreeToHash{$n->value()->nindex} = [ @arrtmp]; 2090: if ($#childrenRt>=0) { 2091: &TreeToHash(\@childrenRt); 2092: } 2093: } 2094: } 2095: 2096: 2097: # convert the hash to a tree 2098: # build a tree-object for each entry in the hash 2099: # afterwards call &buildTree to connect the tree-objects 2100: sub HashToTree { 2101: my $TreeHash = shift; 2102: my @TreeNodes = (); 2103: my $root; 2104: 2105: foreach my $key (keys %$TreeHash) { 2106: if ($key eq 'root') { 2107: $root = Tree->new("root"); 2108: } 2109: elsif ($key ne 'folders') { 2110: my @attributes = @{ $$TreeHash{$key} }; 2111: my $tmpNode; 2112: $tmpNode = Tree->new(Entry->new(title=>$attributes[0], 2113: path=>$attributes[1], 2114: note=>$attributes[2], 2115: date=>$attributes[3], 2116: nindex=>$key)); 2117: push(@TreeNodes, $tmpNode); 2118: # shift all attributes except for 2119: # the indices representing the children of a node 2120: shift(@attributes); 2121: shift(@attributes); 2122: shift(@attributes); 2123: shift(@attributes); 2124: $$TreeHash{$key} = [ @attributes ]; 2125: } 2126: } 2127: # if there are nodes, build up the tree-structure 2128: if (defined $$TreeHash{'root'} && $$TreeHash{'root'} ne '') { 2129: my @childrenRtIn = @{ $$TreeHash{'root'} }; 2130: &buildTree(\$root, \@childrenRtIn,\@TreeNodes,$TreeHash); 2131: } 2132: return $root; 2133: } 2134: 2135: 2136: # join the nodes to a tree 2137: sub buildTree { 2138: my ($node, $childrenIn, $TreeNodes, $TreeHash) = @_; 2139: bless($node, 'Tree'); 2140: foreach my $c (@$childrenIn) { 2141: my $tmpNode = &getNodeByIndex($c,$TreeNodes); 2142: $$node->add_child($tmpNode); 2143: my @childrenIn = @{ $$TreeHash{$tmpNode->value()->nindex()} }; 2144: &buildTree(\$tmpNode,\@childrenIn,$TreeNodes,$TreeHash); 2145: } 2146: 2147: } 2148: 2149: 2150: # ----------------------------------------------------- package Entry 2151: # package that defines the entrys a wishlist could have 2152: # i.e. folders and links 2153: package Entry; 2154: 2155: # constructor 2156: sub new { 2157: my $invocant = shift; 2158: my $class = ref($invocant) || $invocant; 2159: my $self = { @_ }; #set attributes 2160: bless($self, $class); 2161: return $self; 2162: } 2163: 2164: # getter and setter 2165: sub title { 2166: my $self = shift; 2167: if ( @_ ) { $self->{title} = shift} 2168: return $self->{title}; 2169: } 2170: 2171: sub date { 2172: my $self = shift; 2173: if ( @_ ) { $self->{date} = shift} 2174: return $self->{date}; 2175: } 2176: 2177: sub note { 2178: my $self = shift; 2179: if ( @_ ) { $self->{note} = shift} 2180: return $self->{note}; 2181: } 2182: 2183: sub path { 2184: my $self = shift; 2185: if ( @_ ) { $self->{path} = shift} 2186: return $self->{path}; 2187: } 2188: 2189: sub nindex { 2190: my $self = shift; 2191: if ( @_ ) { $self->{nindex} = shift} 2192: return $self->{nindex}; 2193: } 2194: 2195: 2196: 1; 2197: __END__