1: # The LearningOnline Network with CAPA
2: # Utility-routines for wishlist
3: #
4: # $Id: lonwishlist.pm,v 1.20 2014/02/17 16:40:22 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: try {
1046: eval("document.forms.groupsort.filelink"+num+".value");
1047: }
1048: catch(err) {
1049: continue;
1050: }
1051: if (eval("document.forms.groupsort.check"+num+".checked") && eval("document.forms.groupsort.filelink"+num+".value") != '') {
1052: opener.document.forms.simpleedit.importdetail.value+='&'+
1053: eval("document.forms.groupsort.title"+num+".value")+'='+
1054: eval("document.forms.groupsort.filelink"+num+".value")+'='+
1055: eval("document.forms.groupsort.id"+num+".value");
1056: }
1057: }
1058: opener.document.forms.simpleedit.submit();
1059: self.close();
1060: }
1061: JAVASCRIPT
1062: }
1063: else {
1064: $js = &Apache::lonhtmlcommon::scripttag(<<JAVASCRIPT);
1065: function finish_import() {
1066: var linkflag=false;
1067: for (var num=0; num<document.forms.groupsort.fnum.value; num++) {
1068: if (eval("document.forms.groupsort.check"+num+".checked") && eval("document.forms.groupsort.filelink"+num+".value") != '') {
1069: insertRowInLastRow();
1070: placeResourceInLastRow(
1071: eval("document.forms.groupsort.title"+num+".value"),
1072: eval("document.forms.groupsort.filelink"+num+".value"),
1073: eval("document.forms.groupsort.id"+num+".value"),
1074: linkflag
1075: );
1076: linkflag=true;
1077: }
1078: }
1079: opener.editmode=0;
1080: opener.notclear=0;
1081: opener.linkmode=0;
1082: opener.draw();
1083: self.close();
1084: }
1085:
1086: function insertRowInLastRow() {
1087: opener.insertrow(opener.maxrow);
1088: opener.addobj(opener.maxrow,'e&2');
1089: }
1090:
1091: function placeResourceInLastRow (title,url,id,linkflag) {
1092: opener.mostrecent=opener.newresource(opener.maxrow,2,opener.unescape(title),
1093: opener.unescape(url),'false','normal',id);
1094: opener.save();
1095: if (linkflag) {
1096: opener.joinres(opener.linkmode,opener.mostrecent,0);
1097: }
1098: opener.linkmode=opener.mostrecent;
1099: }
1100: JAVASCRIPT
1101: }
1102: return $js;
1103: }
1104:
1105: # HTML-Markup for table if in view-mode
1106: my $wishlistHTMLview;
1107: my $indent_view = $indentConst;
1108: sub wishlistView {
1109: my $nodes = shift;
1110:
1111: foreach my $n (@$nodes) {
1112: my $index = $n->value()->nindex();
1113:
1114: # 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.
1115: # only display the top level entries on load
1116: $wishlistHTMLview .= ($n->parent()->value() eq 'root')?&Apache::loncommon::start_data_table_row('','row'.$index)
1117: :&Apache::loncommon::continue_data_table_row('LC_hidden','row'.$index);
1118:
1119:
1120: # checkboxes
1121: $wishlistHTMLview .= '<td><input type="checkbox" name="mark" id="check'.$index.'" value="'.$index.'" '.
1122: 'onclick="selectAction('."'row".$index."'".')" /></td>';
1123:
1124: # entry is a folder
1125: if ($n->value()->path() eq '') {
1126: $wishlistHTMLview .= '<td id="padd'.$index.'" style="padding-left:'.(($indent_view-$indentConst)<0?0:($indent_view-$indentConst)).'px; min-width: 220px;">'.
1127: '<a href="javascript:;" onclick="folderAction('."'row".$index."'".')" style="vertical-align:top">'.
1128: '<img src="/adm/lonIcons/arrow.closed.gif" id="img'.$index.'" alt = "" class="LC_icon"/>'.
1129: '<img src="/adm/lonIcons/navmap.folder.closed.gif" id="imgFolder'.$index.'" alt="folder"/>'.
1130: $n->value()->title().'</a></td>';
1131: }
1132: # entry is a link
1133: else {
1134: $wishlistHTMLview .= '<td id="padd'.$index.'" style="padding-left:'.(($indent_view-$indentConst)<=0?$indentConst:$indent_view).'px; min-width: 220px;">'.
1135: '<a href="javascript:preview('."'".$n->value()->path()."'".');">'.
1136: '<img src="/res/adm/pages/wishlist-link.png" id="img'.$index.'" alt="link" />'.
1137: $n->value()->title().'</a></td>';
1138: }
1139:
1140: # note-icon, different icons for an entries with note and those without
1141: my $noteIMG = 'anot.png';
1142:
1143: if ($n->value()->note() ne '') {
1144: $noteIMG = 'anot2.png';
1145: }
1146:
1147: $wishlistHTMLview .= '<td style="padding-left:10px;"><a href="javascript:;" onclick="setDisplayNote('."'note".$index."'".')">'.
1148: '<img id="noteImg'.$index.'" src="/res/adm/pages/'.$noteIMG.'" alt="'.&mt('Note').'" title="'.&mt('Note').'" '.
1149: ' class="LC_icon"/></a></td>';
1150:
1151: $wishlistHTMLview .= &Apache::loncommon::end_data_table_row();
1152:
1153: # start row containing the textarea for the note, do not display note on default
1154: $wishlistHTMLview .= &Apache::loncommon::continue_data_table_row('LC_hidden','note'.$index).
1155: '<td></td><td>'.
1156: '<textarea id="noteText'.$index.'" cols="25" rows="3" style="width:100%" '.
1157: 'name="newnote" >'.
1158: $n->value()->note().'</textarea></td><td></td>';
1159: $wishlistHTMLview .= &Apache::loncommon::end_data_table_row();
1160:
1161: # if the entry is a folder, it could have other entries as content. if it has, call wishlistView for those entries
1162: my @children = $n->children();
1163: if ($#children >=0) {
1164: $indent_view += 20;
1165: &wishlistView(\@children);
1166: $indent_view -= 20;
1167: }
1168: }
1169: }
1170:
1171:
1172: # HTML-Markup for table if in edit-mode
1173: my $wishlistHTMLedit;
1174: my $indent_edit = $indentConst;
1175: sub wishlistEdit {
1176: my $nodes = shift;
1177: my $curNode = 1;
1178:
1179: foreach my $n (@$nodes) {
1180: my $index = $n->value()->nindex();
1181:
1182: # start row, use data_table routines to set class to LC_even or LC_odd automatically.
1183: # this rows contains a checkbox, a select-field for sorting entries, the title in an input-field and the note-icon.
1184: # only display the top level entries on load
1185: $wishlistHTMLedit .= ($n->parent()->value() eq 'root')?&Apache::loncommon::start_data_table_row('','row'.$index)
1186: :&Apache::loncommon::continue_data_table_row('LC_hidden','row'.$index);
1187:
1188: # checkboxes
1189: $wishlistHTMLedit .= '<td><input type="checkbox" name="mark" id="check'.$index.'" value="'.$index.'" '.
1190: 'onclick="selectAction('."'row".$index."'".')" /></td>';
1191:
1192: # 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.
1193: # set the number for the current entry into brackets
1194: my $options;
1195: for (my $i = 1; $i < ((scalar @{$nodes})+1); $i++) {
1196: if ($i == $curNode) {
1197: $options .= '<option selected="selected" value="">('.$i.')</option>';
1198: }
1199: else {
1200: $options .= '<option value="'.$i.'">'.$i.'</option>';
1201: }
1202: }
1203: $curNode++;
1204:
1205: # entry is a folder
1206: if ($n->value()->path() eq '') {
1207: $wishlistHTMLedit .= '<td><select class="LC_hidden" name="sel" id="sel'.$index.'" onchange="submitSelect();">'.
1208: $options.'</select></td>'.
1209: '<td id="padd'.$index.'" style="padding-left:'.(($indent_edit-$indentConst)<0?0:($indent_edit-$indentConst)).'px;">'.
1210: '<a href="javascript:;" onclick="folderAction('."'row".$index."'".')" style="vertical-align:top" >'.
1211: '<img src="/adm/lonIcons/arrow.closed.gif" id="img'.$index.'" alt = "" class="LC_icon"/>'.
1212: '<img src="/adm/lonIcons/navmap.folder.closed.gif" id="imgFolder'.$index.'" alt="folder"/></a>'.
1213: '<input type="text" name="newtitle" value="'.$n->value()->title().'" alt = "'.$n->value()->title().'" />'.
1214: '</td><td></td>';
1215:
1216: }
1217: # entry is a link
1218: else {
1219: $wishlistHTMLedit .= '<td><select class="LC_hidden" name="sel" id="sel'.$index.'" onchange="submitSelect();">'.
1220: $options.'</select></td>'.
1221: '<td id="padd'.$index.'" style="padding-left:'.(($indent_edit-$indentConst)<=0?$indentConst:$indent_edit).'px;">'.
1222: '<img src="/res/adm/pages/wishlist-link.png" id="img'.$index.'" alt="link"/>'.
1223: '<input type="text" name="newtitle" value="'.$n->value()->title().'" alt = "'.$n->value()->title().'" /></td>'.
1224: '<td><input type="text" name="newpath" value="'.$n->value()->path().'" alt = "'.$n->value()->path().'" /></td>';
1225: }
1226:
1227: # note-icon, different icons for an entries with note and those without
1228: my $noteIMG = 'anot.png';
1229:
1230: if ($n->value()->note() ne '') {
1231: $noteIMG = 'anot2.png';
1232: }
1233:
1234: $wishlistHTMLedit .= '<td style="padding-left:10px;"><a href="javascript:;" onclick="setDisplayNote('."'note".$index."'".')">'.
1235: '<img id="noteImg'.$index.'" src="/res/adm/pages/'.$noteIMG.'" alt="'.&mt('Note').'" title="'.&mt('Note').'" '.
1236: ' class="LC_icon"/></a></td>';
1237:
1238: $wishlistHTMLedit .= &Apache::loncommon::end_data_table_row();
1239:
1240: # start row containing the textarea for the note
1241: $wishlistHTMLedit .= &Apache::loncommon::continue_data_table_row('LC_hidden','note'.$index).
1242: '<td></td><td></td><td colspan="2">'.
1243: '<textarea id="noteText'.$index.'" cols="25" rows="3" style="width:100%" '.
1244: 'name="newnote">'.
1245: $n->value()->note().'</textarea></td><td></td>';
1246: $wishlistHTMLedit .= &Apache::loncommon::end_data_table_row();
1247:
1248: # if the entry is a folder, it could have other entries as content. if it has, call wishlistEdit for those entries
1249: my @children = $n->children();
1250: if ($#children >=0) {
1251: $indent_edit += 20;
1252: &wishlistEdit(\@children);
1253: $indent_edit -= 20;
1254: }
1255: }
1256: }
1257:
1258:
1259:
1260: # HTML-Markup for table if in move-mode
1261: my $wishlistHTMLmove ='<tr id="root" class="LC_odd_row"><td><input type="radio" name="mark" id="radioRoot" value="root" /></td>'.
1262: '<td>'.&mt('Top level').'</td><td></td></tr>';
1263: my $indent_move = $indentConst;
1264: sub wishlistMove {
1265: my $nodes = shift;
1266: my $marked = shift;
1267:
1268: foreach my $n (@$nodes) {
1269: my $index = $n->value()->nindex();
1270:
1271: #find out wether the current entry was marked to be moved.
1272: my $isIn = 0;
1273: foreach my $m (@$marked) {
1274: if ($index == $m) {
1275: $isIn = 1;
1276: }
1277: }
1278: # 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
1279: $wishlistHTMLmove .= &Apache::loncommon::start_data_table_row('','row'.$index);
1280:
1281:
1282: # entry is a folder
1283: if ($n->value()->path() eq '') {
1284: # display a radio-button, if the folder was not selected to be moved
1285: if (!$isIn) {
1286: $wishlistHTMLmove .= '<td><input type="radio" name="mark" id="radio'.$index.'" value="'.$index.'" /></td>'.
1287: '<td id="padd'.$index.'" style="padding-left:'.(($indent_move-$indentConst)<0?0:($indent_move-$indentConst)).'px; min-width: 220px;">';
1288: }
1289: # highlight the title, if the folder was selected to be moved
1290: else {
1291: $wishlistHTMLmove .= '<td></td>'.
1292: '<td id="padd'.$index.'" style="padding-left:'.(($indent_move-$indentConst)<0?0:($indent_move-$indentConst)).'px; min-width: 220px;'.
1293: 'color:red;">';
1294: }
1295: #arrow- and folder-image, all folders are open, and title
1296: $wishlistHTMLmove .= '<img src="/adm/lonIcons/arrow.open.gif" id="img'.$index.'" alt = "" />'.
1297: '<img src="/adm/lonIcons/navmap.folder.open.gif" id="imgFolder'.$index.'" alt="folder"/>'.
1298: $n->value()->title().'</td>';
1299: }
1300: # entry is a link
1301: else {
1302: # higlight the title, if the link was selected to be moved
1303: my $highlight = '';
1304: if ($isIn) {
1305: $highlight = 'style="color:red;"';
1306: }
1307: # link-image and title
1308: $wishlistHTMLmove .= '<td></td>'.
1309: '<td id="padd'.$index.'" style="padding-left:'.(($indent_move-$indentConst)<=0?$indentConst:$indent_move).'px; min-width: 220px;">'.
1310: '<a href="javascript:preview('."'".$n->value()->path()."'".');" '.$highlight.'>'.
1311: '<img src="/res/adm/pages/wishlist-link.png" id="img'.$index.'" alt="link"/>'.
1312: $n->value()->title().'</a></td>';
1313: }
1314:
1315: # note-icon, different icons for an entries with note and those without
1316: my $noteIMG = 'anot.png';
1317:
1318: if ($n->value()->note() ne '') {
1319: $noteIMG = 'anot2.png';
1320: }
1321:
1322: $wishlistHTMLmove .= '<td style="padding-left:10px;"><a href="javascript:;" onclick="setDisplayNote('."'note".$index."'".')">'.
1323: '<img id="noteImg'.$index.'" src="/res/adm/pages/'.$noteIMG.'" alt="'.&mt('Note').'" title="'.&mt('Note').'" '.
1324: ' class="LC_icon"/></a></td>';
1325:
1326: $wishlistHTMLmove .= &Apache::loncommon::end_data_table_row();
1327:
1328: # start row containing the textarea for the note, readonly in move-mode
1329: $wishlistHTMLmove .= &Apache::loncommon::continue_data_table_row('LC_hidden','note'.$index).
1330: '<td></td><td>'.
1331: '<textarea id="noteText'.$index.'" cols="25" rows="3" style="width:100%" '.
1332: 'name="newnote" readonly="readonly">'.
1333: $n->value()->note().'</textarea></td><td></td>'.
1334: &Apache::loncommon::end_data_table_row();
1335:
1336: # if the entry is a folder, it could have other entries as content. if it has, call wishlistMove for those entries
1337: my @children = $n->children();
1338: if ($#children >=0) {
1339: $indent_move += 20;
1340: &wishlistMove(\@children, $marked);
1341: $indent_move -= 20;
1342: }
1343: }
1344: }
1345:
1346:
1347:
1348: # HTML-Markup for table if in import-mode
1349: my $wishlistHTMLimport;
1350: my $indent_imp = $indentConst;
1351: my $form = 1;
1352: sub wishlistImport {
1353: my ($nodes,$numskipped) = @_;
1354:
1355: my ($is_community,%nopick);
1356: if ($env{'request.course.id'}) {
1357: if (&Apache::loncommon::course_type() eq 'Community') {
1358: $is_community = 1;
1359: }
1360: }
1361:
1362: foreach my $n (@$nodes) {
1363: my $index = $n->value()->nindex();
1364: if ($n->value()->path() =~ m{^(/res/$match_domain/$match_username/)}) {
1365: if ($is_community) {
1366: unless (&Apache::lonnet::allowed('bro',$n->value()->path())) {
1367: $nopick{$n->value()->path()} = $n->value()->title();
1368: $$numskipped ++;
1369: }
1370: } else {
1371: unless (&Apache::lonnet::allowed('bre',$n->value()->path())) {
1372: $nopick{$n->value()->path()} = $n->value()->title();
1373: $$numskipped ++;
1374: }
1375: }
1376: }
1377:
1378: # 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.
1379: # only display the top level entries on load
1380: $wishlistHTMLimport .= ($n->parent()->value() eq 'root')?&Apache::loncommon::start_data_table_row('','row'.$index)
1381: :&Apache::loncommon::continue_data_table_row('LC_hidden','row'.$index);
1382:
1383:
1384: # checkboxes
1385: $wishlistHTMLimport .= '<td>';
1386: my ($disabled,$onclick,$image,$style);
1387: if ($nopick{$n->value()->path()}) {
1388: $disabled = ' disabled="disabled"';
1389: $image = 'wishlist-link-lighter.png';
1390: $style = 'style="color:#808080;"';
1391: } else {
1392: $onclick = ' onclick="selectAction('."'row".$index."'".')"';
1393: $image = 'wishlist-link.png';
1394: }
1395: $wishlistHTMLimport .= '<input type="checkbox" name="check" id="check'.$index.'" value="'.$index.'" '.
1396: $disabled.$onclick.' />'.
1397: '<input type="hidden" name="title'.$index.'" value="'.&escape($n->value()->title()).'" />'.
1398: '<input type="hidden" name="filelink'.$index.'" value="'.&escape($n->value()->path()).'" />'.
1399: '<input type="hidden" name="id'.$index.'" />';
1400: $wishlistHTMLimport .= '</td>';
1401:
1402: # entry is a folder
1403: if ($n->value()->path() eq '') {
1404: $wishlistHTMLimport .= '<td id="padd'.$index.'" style="padding-left:'.(($indent_imp-$indentConst)<0?0:($indent_imp-$indentConst)).'px; min-width: 220px;">'.
1405: '<a href="javascript:;" onclick="folderAction('."'row".$index."'".')" style="vertical-align:top">'.
1406: '<img src="/adm/lonIcons/arrow.closed.gif" id="img'.$index.'" alt = "" class="LC_icon"/>'.
1407: '<img src="/adm/lonIcons/navmap.folder.closed.gif" id="imgFolder'.$index.'" alt="folder"/>'.
1408: $n->value()->title().'</a></td>';
1409: }
1410: # entry is a link
1411: else {
1412: $wishlistHTMLimport .= '<td id="padd'.$index.'" style="padding-left:'.(($indent_imp-$indentConst)<=0?$indentConst:$indent_imp).'px; min-width: 220px;">';
1413: unless ($nopick{$n->value()->path()}) {
1414: $wishlistHTMLimport .= '<a href="javascript:preview('."'".$n->value()->path()."'".');">';
1415: }
1416: $wishlistHTMLimport .= '<img src="/res/adm/pages/'.$image.'" id="img'.$index.'" alt="link" />'.
1417: '<span '.$style.'>'.$n->value()->title().'</span></a></td>';
1418: $form++;
1419: }
1420:
1421: # note-icon, different icons for an entries with note and those without
1422: my $noteIMG = 'anot.png';
1423:
1424: if ($n->value()->note() ne '') {
1425: $noteIMG = 'anot2.png';
1426: }
1427:
1428: $wishlistHTMLimport .= '<td style="padding-left:10px;"><a href="javascript:;" onclick="setDisplayNote('."'note".$index."'".')">'.
1429: '<img id="noteImg'.$index.'" src="/res/adm/pages/'.$noteIMG.'" alt="'.&mt('Note').'" title="'.&mt('Note').'" '.
1430: ' class="LC_icon"/></a></td>';
1431:
1432: $wishlistHTMLimport .= &Apache::loncommon::end_data_table_row();
1433:
1434: # start row containing the textarea for the note, do not display note on default, readonly in import-mode
1435: $wishlistHTMLimport .= &Apache::loncommon::continue_data_table_row('LC_hidden','note'.$index).
1436: '<td></td><td>'.
1437: '<textarea id="noteText'.$index.'" cols="25" rows="3" style="width:100%" '.
1438: 'name="newnote" readonly="readonly">'.
1439: $n->value()->note().'</textarea></td><td></td>';
1440: $wishlistHTMLimport .= &Apache::loncommon::end_data_table_row();
1441:
1442: # if the entry is a folder, it could have other entries as content. if it has, call wishlistImport for those entries
1443: my @children = $n->children();
1444: if ($#children >=0) {
1445: $indent_imp += 20;
1446: &wishlistImport(\@children,$numskipped);
1447: $indent_imp -= 20;
1448: }
1449: }
1450: return;
1451: }
1452:
1453: # Returns the HTML-Markup for wishlist
1454: sub makePage {
1455: my $rootgiven = shift;
1456: my $mode = shift;
1457: my $marked = shift;
1458:
1459: $root = $rootgiven;
1460: @childrenRt = $root->children();
1461:
1462: # breadcrumbs and start_page
1463: &Apache::lonhtmlcommon::clear_breadcrumbs();
1464: &Apache::lonhtmlcommon::add_breadcrumb(
1465: { href => '/adm/wishlist?mode='.$mode,
1466: text => 'Stored Links'});
1467: my $startPage = &Apache::loncommon::start_page('Stored Links',undef,
1468: {'add_entries' => {
1469: 'onload' => 'javascript:onLoadAction('."'".$mode."'".');',
1470: 'onunload' => 'javascript:window.name = '."'loncapaclient'"}});
1471:
1472: my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs(&mt('Stored Links'),'Wishlist');
1473:
1474: # get javascript-code for wishlist-interactions
1475: my $js = &JSforWishlist();
1476:
1477: # texthash for items in funtionlist
1478: my %lt = &Apache::lonlocal::texthash(
1479: 'ed' => 'Edit',
1480: 'vw' => 'View',
1481: 'al' => 'Add Link',
1482: 'af' => 'Add Folder',
1483: 'mv' => 'Move Selected',
1484: 'dl' => 'Delete Selected',
1485: 'sv' => 'Save');
1486:
1487: # start functionlist
1488: my $functions = &Apache::lonhtmlcommon::start_funclist();
1489:
1490: # icon for edit-mode, display when in view-mode
1491: if ($mode eq 'view') {
1492: $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
1493: 'onclick="setFormAction('."'save','edit'".');" class="LC_menubuttons_link">'.
1494: '<img src="/res/adm/pages/edit-mode-22x22.png" alt="'.$lt{'ed'}.'" '.
1495: 'title="'.$lt{'ed'}.'" class="LC_icon"/> '.
1496: '<span class="LC_menubuttons_inline_text">'.$lt{'ed'}.'</span></a>');
1497: }
1498: # icon for view-mode, display when in edit-mode
1499: else {
1500: $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
1501: 'onclick="setFormAction('."'save','view'".');" class="LC_menubuttons_link">'.
1502: '<img src="/res/adm/pages/view-mode-22x22.png" alt="'.$lt{'vw'}.'" '.
1503: 'title="'.$lt{'vw'}.'" class="LC_icon"/> '.
1504: '<span class="LC_menubuttons_inline_text">'.$lt{'vw'}.'</span></a>');
1505: }
1506:
1507: # icon for adding a new link
1508: $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
1509: 'onclick="newLink();" class="LC_menubuttons_link">'.
1510: '<img src="/res/adm/pages/link-new-22x22.png" alt="'.$lt{'al'}.'" '.
1511: 'title="'.$lt{'al'}.'" class="LC_icon"/>'.
1512: '<span class="LC_menubuttons_inline_text">'.$lt{'al'}.'</span></a>');
1513:
1514: # icon for adding a new folder
1515: $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
1516: 'onclick="newFolder();" class="LC_menubuttons_link">'.
1517: '<img src="/res/adm/pages/folder-new-22x22.png" alt="'.$lt{'af'}.'" '.
1518: 'title="'.$lt{'af'}.'" class="LC_icon"/>'.
1519: '<span class="LC_menubuttons_inline_text">'.$lt{'af'}.'</span></a>');
1520:
1521: # icon for moving entries
1522: $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
1523: 'onclick="setFormAction('."'move','move'".'); " class="LC_menubuttons_link">'.
1524: '<img src="/res/adm/pages/move-22x22.png" alt="'.$lt{'mv'}.'" '.
1525: 'title="'.$lt{'mv'}.'" class="LC_icon" />'.
1526: '<span class="LC_menubuttons_inline_text">'.$lt{'mv'}.'</span></a>');
1527:
1528: # icon for deleting entries
1529: $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
1530: 'onclick="setFormAction('."'delete','".$mode."'".'); " class="LC_menubuttons_link">'.
1531: '<img src="/res/adm/pages/del.png" alt="'.$lt{'dl'}.'" '.
1532: 'title="'.$lt{'dl'}.'" class="LC_icon" />'.
1533: '<span class="LC_menubuttons_inline_text">'.$lt{'dl'}.'</span></a>');
1534:
1535: # icon for saving changes
1536: $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
1537: 'onclick="setFormAction('."'saveOK','".$mode."'".'); " class="LC_menubuttons_link">'.
1538: '<img src="/res/adm/pages/save-22x22.png" alt="'.$lt{'sv'}.'" '.
1539: 'title="'.$lt{'sv'}.'" class="LC_icon" />'.
1540: '<span class="LC_menubuttons_inline_text">'.$lt{'sv'}.'</span></a>');
1541:
1542: # end funtionlist and generate subbox
1543: $functions.= &Apache::lonhtmlcommon::end_funclist();
1544: my $subbox = &Apache::loncommon::head_subbox($functions);
1545:
1546: # start form
1547: my $inner .= '<form name="list" action ="/adm/wishlist" method="post">'.
1548: '<input type="hidden" id="action" name="action" value="" />';
1549:
1550: # only display subbox in view- or edit-mode
1551: if ($mode eq 'view' || $mode eq 'edit') {
1552: $inner .= $subbox;
1553: }
1554:
1555: # generate table-content depending on mode
1556: if ($mode eq 'edit') {
1557: &wishlistEdit(\@childrenRt);
1558: if ($wishlistHTMLedit ne '') {
1559: $inner .= &Apache::loncommon::start_data_table("LC_tableOfContent");
1560: $inner .= $wishlistHTMLedit;
1561: $inner .= &Apache::loncommon::end_data_table();
1562: }
1563: else {
1564: $inner .= '<span class="LC_info">'.&mt("Your Stored Links list is currently empty.").'</span>';
1565: }
1566: $wishlistHTMLedit = '';
1567: }
1568: elsif ($mode eq 'view') {
1569: &wishlistView(\@childrenRt);
1570: if ($wishlistHTMLview ne '') {
1571: $inner .= '<table class="LC_data_table LC_tableOfContent">'.$wishlistHTMLview.'</table>';
1572: }
1573: else {
1574: $inner .= '<span class="LC_info">'.&mt("Your Stored Links list is currently empty.").'</span>';
1575: }
1576: $wishlistHTMLview = '';
1577: }
1578: else {
1579: my $markStr = '';
1580: foreach my $m (@$marked) {
1581: $markStr .= $m.',';
1582: }
1583: if ($markStr) {
1584: $markStr = substr($markStr, 0, length($markStr)-1);
1585: $inner .= '<input type="hidden" value="'.$markStr.'" name="markedToMove" />';
1586: $inner .= '<p><span class="LC_info">'.&mt('You have selected the red marked entries to be moved to another folder. '.
1587: 'Now choose the new destination folder.').'</span></p>';
1588: &wishlistMove(\@childrenRt, $marked);
1589: $inner .= '<table class="LC_data_table LC_tableOfContent">'.$wishlistHTMLmove.'</table><br/><br/>';
1590: $inner .= '<input type="button" value="'.&mt('Move').'" onclick="setFormAction('."'move','view'".');" />'.
1591: '<input type="button" value="'.&mt('Cancel').'" onclick="go('."'/adm/wishlist'".')" />';
1592:
1593: $wishlistHTMLmove ='<tr id="root" class="LC_odd_row"><td><input type="radio" name="mark" id="radioRoot" value="root" /></td>'.
1594: '<td>'.&mt('Top level').'</td><td></td></tr>';
1595: }
1596: else {
1597: $inner .= '<p><span class="LC_info">'.&mt("You haven't marked any entry to move.").'</span></p>'.
1598: '<input type="button" value="'.&mt('Back').'" onclick="go('."'/adm/wishlist'".')" />';
1599: }
1600: }
1601:
1602: # end form
1603: $inner .= '</form>';
1604:
1605: # end_page
1606: my $endPage = &Apache::loncommon::end_page();
1607:
1608: # put all page-elements together
1609: my $page = $startPage.$breadcrumbs.$js.$inner.$endPage;
1610:
1611: return $page;
1612: }
1613:
1614:
1615: # Returns the HTML-Markup for the PopUp, shown when a new link should set, when NOT
1616: # beeing in the wishlist-interface (method is called in lonmenu and lonsearchcat)
1617: sub makePopUpNewLink {
1618: my ($title, $path) = @_;
1619:
1620: # Get all existing folders to offer posibility to set a new link
1621: # into a folder
1622: my %TreeHashLink = &Apache::lonwishlist::getWishlist();
1623: my $rootLink = &Apache::Tree::HashToTree(\%TreeHashLink);
1624: my @childrenRtLink = $rootLink->children();
1625:
1626: $foldersOption = '';
1627: @allFolders = ();
1628: &getFoldersToArray(\@childrenRtLink);
1629: &getFoldersForOption(\@childrenRtLink);
1630:
1631: my $options = '<option value="" selected="selected">('.&mt('Top level').')</option>'.$foldersOption;
1632: $foldersOption = '';
1633: @allFolders = ();
1634:
1635: # HTML-Markup for the Pop-Up-window 'Set a link for this resource to wishlist'
1636: my $startPageWishlistlink =
1637: &Apache::loncommon::start_page('Save to Stored Links',undef,
1638: {'only_body' => 1,
1639: 'bgcolor' => '#FFFFFF',});
1640:
1641: my $warningLink = &mt('You must insert a title!');
1642: my $warningLinkNotAllowed1 =
1643: &mt('You can only insert links to LON-CAPA resources from the resource-pool'.
1644: ' or to external websites.'.
1645: ' Paths to LON-CAPA resources must be of the form /res/domain/user/...'.
1646: ' Paths to external websites must contain the network protocol, e.g. http://...');
1647:
1648: my $inPageWishlistlink1 = '<h1>'.&mt('Save to Stored Links').'</h1>';
1649: # If no title is delivered, 'New Link' is called up from the wishlist-interface, so after
1650: # submitting the window should close instead of offering a link to wishlist (like it should do
1651: # if we call 'Set New Link' from within a browsed ressource)
1652: if (!$title) {
1653: $inPageWishlistlink1 .= '<form method="post" name="newlink" action="/adm/wishlist" target="wishlist"'.
1654: 'onsubmit="return newlinksubmit();" >';
1655: }
1656: else {
1657: $inPageWishlistlink1 .= '<form method="post" name="newlink" action="/adm/wishlist?mode=set" '.
1658: 'onsubmit="return newlinksubmit();" >';
1659: }
1660: $inPageWishlistlink1 .= &Apache::lonhtmlcommon::start_pick_box().
1661: &Apache::lonhtmlcommon::row_title(&mt('Link Title'));
1662:
1663: my $inPageWishlistlink2 = &Apache::lonhtmlcommon::row_closure().
1664: &Apache::lonhtmlcommon::row_title(&mt('Path'));
1665:
1666: my $inPageWishlistlink3 = &Apache::lonhtmlcommon::row_closure().
1667: &Apache::lonhtmlcommon::row_title(&mt('Note')).
1668: '<textarea name="note" rows="3" cols="35" style="width:100%"></textarea>'.
1669: &Apache::lonhtmlcommon::row_closure(1).
1670: &Apache::lonhtmlcommon::end_pick_box().
1671: '<br/><br/>'.
1672: '<input type="submit" value="'.&mt('Save in').'" />'.
1673: '<select name="folders">'.
1674: $options.
1675: '</select>'.
1676: '<input type="button" value="'.&mt('Cancel').'" onclick="javascript:window.close();" />'.
1677: '</form>';
1678: $options = '';
1679:
1680: my $endPageWishlistlink = &Apache::loncommon::end_page();
1681:
1682: my $popUp = $startPageWishlistlink.
1683: $inPageWishlistlink1.
1684: '<input type="text" name="title" size="45" value="" />'.
1685: $inPageWishlistlink2.
1686: '<input type="text" name="path" size="45" value="" />'.
1687: $inPageWishlistlink3;
1688:
1689: # JavaScript-function to set title and path of ressource automatically
1690: # and show warning, if no title was set or path is invalid
1691: $popUp .= <<SCRIPT;
1692: <script type="text\/javascript">
1693: document.getElementsByName("title")[0].value = '$title';
1694: document.getElementsByName("path")[0].value = '$path';
1695: var fromwishlist = false;
1696: var titleget = '$title';
1697: if (!titleget) {
1698: fromwishlist = true;
1699: }
1700: function newlinksubmit(){
1701: var title = document.getElementsByName("title")[0].value;
1702: var path = document.getElementsByName("path")[0].value;
1703: if (!title) {
1704: alert("$warningLink");
1705: return false;}
1706: var linkOK = (path.match(/\^http:(\\\/\\\/)/) || path.match(/\^https:(\\\/\\\/)/))
1707: && !(path.match(/\\.problem/) || path.match(/\\.exam/)
1708: || path.match(/\\.quiz/) || path.match(/\\.assess/)
1709: || path.match(/\\.survey/) || path.match(/\\.form/)
1710: || path.match(/\\.library/) || path.match(/\\.page/)
1711: || path.match(/\\.sequence/));
1712: if (!path.match(/\^(\\\/res\\\/)/) && !linkOK) {
1713: alert("$warningLinkNotAllowed1");
1714: return false;}
1715: if (fromwishlist) {
1716: window.close();
1717: }
1718: return true;}
1719: <\/script>
1720: SCRIPT
1721:
1722: $popUp .= $endPageWishlistlink;
1723:
1724: return $popUp;
1725: }
1726:
1727: sub makePopUpNewFolder {
1728: # Get all existing folders to offer posibility to create a new folder
1729: # into an existing folder
1730: my %TreeHashLink = &Apache::lonwishlist::getWishlist();
1731: my $rootLink = &Apache::Tree::HashToTree(\%TreeHashLink);
1732: my @childrenRtLink = $rootLink->children();
1733:
1734: $foldersOption = '';
1735: @allFolders = ();
1736: &getFoldersToArray(\@childrenRtLink);
1737: &getFoldersForOption(\@childrenRtLink);
1738:
1739: my $options = '<option value="" selected="selected">('.&mt('Top level').')</option>'.$foldersOption;
1740: $foldersOption = '';
1741: @allFolders = ();
1742:
1743: # HTML-Markup for the Pop-Up-window 'New Folder'
1744: my $startPageWishlistfolder =
1745: &Apache::loncommon::start_page('New Folder',undef,
1746: {'only_body' => 1,
1747: 'bgcolor' => '#FFFFFF',});
1748:
1749: my $warningFolder = &mt('You must insert a title!');
1750:
1751:
1752: my $inPageNewFolder = '<h1>'.&mt('New Folder').'</h1>'.
1753: '<form method="post" name="newfolder" action="/adm/wishlist" target="wishlist" '.
1754: 'onsubmit="return newfoldersubmit();" >'.
1755: &Apache::lonhtmlcommon::start_pick_box().
1756: &Apache::lonhtmlcommon::row_title(&mt('Folder title')).
1757: '<input type="text" name="title" size="45" value="" /><br />'.
1758: &Apache::lonhtmlcommon::row_closure().
1759: &Apache::lonhtmlcommon::row_title(&mt('Note')).
1760: '<textarea name="note" rows="3" cols="35" style="width:100%"></textarea><br />'.
1761: &Apache::lonhtmlcommon::row_closure(1).
1762: &Apache::lonhtmlcommon::end_pick_box().
1763: '<br/><br/>'.
1764: '<input type="submit" value="'.&mt('Save in').'" />'.
1765: '<select name="folders">'.
1766: $options.
1767: '</select>'.
1768: '<input type="button" value="'.&mt('Cancel').'" onclick="javascript:window.close();" />'.
1769: '</form>';
1770: my $endPageWishlistfolder = &Apache::loncommon::end_page();
1771:
1772: my $popUp = $startPageWishlistfolder.
1773: $inPageNewFolder;
1774:
1775: $popUp .= <<SCRIPT;
1776: <script type="text\/javascript">
1777: function newfoldersubmit(){
1778: var title = document.getElementsByName("title")[0].value;
1779: if (!title) {
1780: alert("$warningFolder");
1781: return false;}
1782: else {
1783: window.close();
1784: return true;}}
1785: <\/script>
1786: SCRIPT
1787:
1788: $popUp .= $endPageWishlistfolder;
1789:
1790: return $popUp;
1791: }
1792:
1793: # Returns the HTML-Markup for the page, shown when a link was set
1794: sub makePageSet {
1795: my $title = 'Stored Links';
1796:
1797: # start_page
1798: my $output =
1799: &Apache::loncommon::start_page($title,undef,
1800: {'only_body' => 1})
1801: .'<h1>'.&mt($title).'</h1>';
1802:
1803: # confirm success and offer link to wishlist
1804: $output .=
1805: &Apache::loncommon::confirmwrapper(
1806: &Apache::lonhtmlcommon::confirm_success(
1807: &mt('Link successfully saved!')))
1808: .&Apache::lonhtmlcommon::actionbox(
1809: ['<a href="javascript:;" onclick="opener.open('."'/adm/wishlist'".');window.close();">'.&mt('Go to Stored Links').'</a>',
1810: '<a href="javascript:;" onclick="window.close();">'.&mt('Close this window').'</a>'
1811: ]);
1812:
1813: # end_page
1814: $output .= &Apache::loncommon::end_page();
1815:
1816: return $output;
1817: }
1818:
1819:
1820: # Returns the HTML-Markup for the page, shown when links should be imported into a course
1821: sub makePageImport {
1822: my $rootgiven = shift;
1823: my $rat = shift;
1824:
1825: $root = $rootgiven;
1826: @childrenRt = $root->children();
1827: # start_page
1828: my $startPage = &Apache::loncommon::start_page('Stored Links',undef,
1829: {'only_body' => 1});
1830:
1831: # get javascript-code for wishlist-interactions
1832: my $js = &JSforWishlist();
1833: $js .= &JSforImport($rat);
1834:
1835: my $inner = '<h1>'.&mt('Import Resources from Stored Links').'</h1>';
1836: if (!$rat) {
1837: $inner .=
1838: '<ul>'.
1839: '<li class="LC_info">'.&mt('Use the checkboxes corresponding to a folder to '.
1840: 'easily check all links within the folder.').'</li>'.
1841: '<li class="LC_info">'.&mt('The folder structure itself cannot be imported.').'</li>'.
1842: '<li class="LC_info">'.&mt('All checked links will be imported into the current folder of your course.').'</li>'.
1843: '</ul>';
1844: }
1845: else {
1846: $inner .=
1847: '<ul>'.
1848: '<li class="LC_info">'.&mt('Use the checkboxes corresponding to a folder to '.
1849: 'easily check all links within this folder.').'</li>'.
1850: '<li class="LC_info">'.&mt('The folder structure itself cannot be imported.').'</li>'.
1851: '</ul>';
1852: }
1853: my %wishlist = &getWishlist();
1854:
1855: #FIXME Saved string containing all folders in wishlist.db-file (key 'folders') in first version of lonwishlist
1856: #After splitting lonwishlist into two modules, this is not necessary anymore. So, dependent from when the wishlist
1857: #was first called (i.e. when wishlist.db was created), there might be an entry 'folders' or not. Number of links in
1858: #wishlist.db depends on wether this entry exists or not...JW
1859: my $fnum;
1860: if (defined $wishlist{'folders'}) {
1861: $fnum = (keys %wishlist)-2;
1862: }
1863: else {
1864: $fnum = (keys %wishlist)-1;
1865: }
1866:
1867: $inner .= '<form method="post" name="groupsort" action="">'.
1868: '<input type="hidden" value="'.$fnum.'" name="fnum" />'.
1869: '<input type="button" onclick="javascript:checkAll()" id="checkallbutton" value="'.&mt('Check All').'" />'.
1870: '<input type="button" onclick="javascript:uncheckAll()" id="uncheckallbutton" value="'.&mt('Uncheck All').'" />'.
1871: '<input type="button" value="'.&mt('Import Checked').'" onclick="finish_import();" />'.
1872: '<input type="button" value="'.&mt('Cancel').'" onclick="window.close();" /><br/><br/>';
1873:
1874:
1875: # wishlist-table
1876: my $numskipped = 0;
1877: &wishlistImport(\@childrenRt,\$numskipped);
1878: if ($wishlistHTMLimport ne '') {
1879: $inner .= '<table class="LC_data_table LC_tableOfContent">'.$wishlistHTMLimport.'</table>';
1880: }
1881: else {
1882: $inner .= '<span class="LC_info">'.&mt("Your Stored Links list is currently empty.").'</span>';
1883: }
1884: if ($numskipped > 0) {
1885: $inner .= '<p class="LC_info">'.&mt('Note: where a Stored Link is unavailable for import in the current context it is grayed out.').'</p>';
1886: }
1887: $wishlistHTMLimport = '';
1888:
1889: $inner .= '</form>';
1890:
1891: # end_page
1892: my $endPage = &Apache::loncommon::end_page();
1893:
1894: # put all page-elements together
1895: my $page = $startPage.$js.$inner.$endPage;
1896:
1897: return $page;
1898: }
1899:
1900:
1901: # Returns the HTML-Markup for error-page
1902: sub makeErrorPage {
1903: # breadcrumbs and start_page
1904: &Apache::lonhtmlcommon::add_breadcrumb(
1905: { href => '/adm/wishlist',
1906: text => 'Stored Links'});
1907: my $startPage = &Apache::loncommon::start_page('Stored Links');
1908:
1909: my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs(&mt('Stored Links'),'Wishlist');
1910: &Apache::lonhtmlcommon::clear_breadcrumbs();
1911:
1912: # error-message
1913: my $inner .= '<p class="LC_error">'.&mt('An error occurred! Please try again later.').'</p>';
1914:
1915: # end_page
1916: my $endPage = &Apache::loncommon::end_page();
1917:
1918: # put all page-elements together
1919: my $page = $startPage.$breadcrumbs.$inner.$endPage;
1920:
1921: return $page;
1922: }
1923:
1924:
1925: # ----------------------------------------------------- package Tree
1926: # Extend CPAN-Module Tree by function like 'moveNode' or 'deleteNode'
1927: package Apache::Tree;
1928:
1929: =pod
1930:
1931: =head2 Routines from package Tree
1932:
1933: =over 4
1934:
1935: =item * &getNodeByIndex(index, nodes)
1936:
1937: Searches for a node, specified by the index, in nodes (reference to array) and returns it.
1938:
1939:
1940: =item * &moveNode(node, at, newParent)
1941:
1942: 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).
1943:
1944:
1945: =item * &removeNode(node)
1946:
1947: Removes a node given by node from the tree.
1948:
1949:
1950: =item * &TreeIndex(children)
1951:
1952: Sets an index for every node in the tree, beginning with 0.
1953: 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).
1954:
1955:
1956: =item * &setCountZero()
1957:
1958: Resets index counter.
1959:
1960:
1961: =item * &RootToHash(childrenRt)
1962:
1963: Converts the root-node to a hash-entry: the key is root and values are just the indices of root's children.
1964:
1965:
1966: =item * &TreeToHash(childrenRt)
1967:
1968: 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).
1969: 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).
1970:
1971:
1972: =item * &HashToTree()
1973:
1974: 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.
1975:
1976:
1977: =item * &buildTree(node, childrenIn, TreeNodes, TreeHash)
1978:
1979: Joins the nodes to a tree.
1980: 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).
1981:
1982:
1983: =back
1984:
1985: =cut
1986:
1987:
1988: # returns the node with a given index from a list of nodes
1989: sub getNodeByIndex {
1990: my $index = shift;
1991: my $nodes = shift;
1992: my $found;
1993:
1994: foreach my $n (@$nodes) {
1995: my $curIndex = $n->value()->nindex();
1996: if ($curIndex == $index) {
1997: $found = $n;
1998: }
1999: }
2000: return $found;
2001: }
2002:
2003: # moves a given node to a new parent or change the position from a node
2004: # within its siblings (sorting)
2005: sub moveNode {
2006: my $node = shift;
2007: my $at = shift;
2008: my $newParent = shift;
2009:
2010:
2011: if (!$newParent) {
2012: $newParent = $node->parent();
2013: }
2014:
2015: $node->parent()->remove_child($node);
2016:
2017: if (defined $at) {
2018: $newParent->add_child({at => $at},$node);
2019: }
2020: else {
2021: $newParent->add_child($node);
2022: }
2023:
2024: # updating root's children
2025: @childrenRt = $root->children();
2026: }
2027:
2028: # removes a given node
2029: sub removeNode() {
2030: my $node = shift;
2031: my @children = $node->children();
2032:
2033: if ($#children >= 0) {
2034: foreach my $c (@children) {
2035: &removeNode($c);
2036: }
2037: }
2038: $node->parent()->remove_child($node);
2039:
2040: # updating root's children
2041: @childrenRt = $root->children();
2042: }
2043:
2044:
2045: # set an index for every node in the tree, beginning with 0
2046: my $count = 0;
2047: sub TreeIndex {
2048: my $children = shift;
2049:
2050: foreach my $n (@$children) {
2051: my @children = $n->children();
2052: $n->value()->nindex($count);$count++;
2053:
2054: if ($#children>=0) {
2055: &TreeIndex(\@children);
2056: }
2057: }
2058: }
2059:
2060: # reset index counter
2061: sub setCountZero {
2062: $count = 0;
2063: }
2064:
2065:
2066: # convert the tree to a hash
2067: # each node is one hash-entry
2068: # keys are the indices, values are all other attributes
2069: # (containing tile, path, note, date and indices for all direct children)
2070: # except for root: the key is root and values are
2071: # just the indices of root's children
2072: sub RootToHash {
2073: my $childrenRt = shift;
2074: my @indexarr = ();
2075:
2076: foreach my $c (@$childrenRt) {
2077: push (@indexarr, $c->value()->nindex());
2078: }
2079: $TreeToHash{'root'} = [@indexarr];
2080: }
2081:
2082: sub TreeToHash {
2083: my $childrenRt = shift;
2084:
2085: foreach my $n (@$childrenRt) {
2086: my @arrtmp = ();
2087: $arrtmp[0] = $n->value()->title();
2088: $arrtmp[1] = $n->value()->path();
2089: $arrtmp[2] = $n->value()->note();
2090: $arrtmp[3] = $n->value()->date();
2091: my @childrenRt = $n->children();
2092: my $co = 4;
2093: foreach my $c (@childrenRt) {
2094: my $i = $c->value()->nindex();
2095: $arrtmp[$co] = $i;
2096: $co++;
2097: }
2098: $TreeToHash{$n->value()->nindex} = [ @arrtmp];
2099: if ($#childrenRt>=0) {
2100: &TreeToHash(\@childrenRt);
2101: }
2102: }
2103: }
2104:
2105:
2106: # convert the hash to a tree
2107: # build a tree-object for each entry in the hash
2108: # afterwards call &buildTree to connect the tree-objects
2109: sub HashToTree {
2110: my $TreeHash = shift;
2111: my @TreeNodes = ();
2112: my $root;
2113:
2114: foreach my $key (keys %$TreeHash) {
2115: if ($key eq 'root') {
2116: $root = Tree->new("root");
2117: }
2118: elsif ($key ne 'folders') {
2119: my @attributes = @{ $$TreeHash{$key} };
2120: my $tmpNode;
2121: $tmpNode = Tree->new(Entry->new(title=>$attributes[0],
2122: path=>$attributes[1],
2123: note=>$attributes[2],
2124: date=>$attributes[3],
2125: nindex=>$key));
2126: push(@TreeNodes, $tmpNode);
2127: # shift all attributes except for
2128: # the indices representing the children of a node
2129: shift(@attributes);
2130: shift(@attributes);
2131: shift(@attributes);
2132: shift(@attributes);
2133: $$TreeHash{$key} = [ @attributes ];
2134: }
2135: }
2136: # if there are nodes, build up the tree-structure
2137: if (defined $$TreeHash{'root'} && $$TreeHash{'root'} ne '') {
2138: my @childrenRtIn = @{ $$TreeHash{'root'} };
2139: &buildTree(\$root, \@childrenRtIn,\@TreeNodes,$TreeHash);
2140: }
2141: return $root;
2142: }
2143:
2144:
2145: # join the nodes to a tree
2146: sub buildTree {
2147: my ($node, $childrenIn, $TreeNodes, $TreeHash) = @_;
2148: bless($node, 'Tree');
2149: foreach my $c (@$childrenIn) {
2150: my $tmpNode = &getNodeByIndex($c,$TreeNodes);
2151: $$node->add_child($tmpNode);
2152: my @childrenIn = @{ $$TreeHash{$tmpNode->value()->nindex()} };
2153: &buildTree(\$tmpNode,\@childrenIn,$TreeNodes,$TreeHash);
2154: }
2155:
2156: }
2157:
2158:
2159: # ----------------------------------------------------- package Entry
2160: # package that defines the entrys a wishlist could have
2161: # i.e. folders and links
2162: package Entry;
2163:
2164: # constructor
2165: sub new {
2166: my $invocant = shift;
2167: my $class = ref($invocant) || $invocant;
2168: my $self = { @_ }; #set attributes
2169: bless($self, $class);
2170: return $self;
2171: }
2172:
2173: # getter and setter
2174: sub title {
2175: my $self = shift;
2176: if ( @_ ) { $self->{title} = shift}
2177: return $self->{title};
2178: }
2179:
2180: sub date {
2181: my $self = shift;
2182: if ( @_ ) { $self->{date} = shift}
2183: return $self->{date};
2184: }
2185:
2186: sub note {
2187: my $self = shift;
2188: if ( @_ ) { $self->{note} = shift}
2189: return $self->{note};
2190: }
2191:
2192: sub path {
2193: my $self = shift;
2194: if ( @_ ) { $self->{path} = shift}
2195: return $self->{path};
2196: }
2197:
2198: sub nindex {
2199: my $self = shift;
2200: if ( @_ ) { $self->{nindex} = shift}
2201: return $self->{nindex};
2202: }
2203:
2204:
2205: 1;
2206: __END__
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>