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