File:
[LON-CAPA] /
loncom /
interface /
lonwishlist.pm
Revision
1.1:
download - view:
text,
annotated -
select for diffs
Tue Aug 10 14:30:20 2010 UTC (13 years, 10 months ago) by
wenzelju
Branches:
MAIN
CVS tags:
HEAD
Add new module lonwishlist:
- This module will replace the bookmark-collection. Bookmark-collection is out of use because it's partly faulty and intricate to use.
- The wishlist will offer a possibility to link resources on a hierarchal list. The list can be modified by adding new links and folder, to structure the list. Existing entries can be renamed, moved, sorted and deleted. Additional information can be attached for each entry by adding a note. Linked resources from the wishlist can be imported into courses. The wishlist is only available for roles with access to the resource-pool.
Still under progress! Function for setting a link to wishlist, when viewing a resource, and import-functionality will follow!
1: # The LearningOnline Network with CAPA
2: # Routines to control the wishlist
3: #
4: # $Id: lonwishlist.pm,v 1.1 2010/08/10 14:30:20 wenzelju 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:
30:
31: package Apache::lonwishlist;
32:
33: use strict;
34: use Apache::Constants qw(:common);
35: use Apache::lonnet;
36: use Apache::loncommon();
37: use Apache::lonhtmlcommon;
38: use Apache::lonlocal;
39: use Tree;
40:
41:
42: # Global variables
43: my $root;
44: my @childrenRt;
45: my %TreeHash;
46: my %TreeToHash;
47: my @allFolders;
48: my @allNodes;
49: my $indentConst = 20;
50:
51:
52: # Read wishlist from user-data
53: sub getWishlist {
54: my @wishlistkeys = &Apache::lonnet::getkeys('wishlist');
55: my %wishlist = &Apache::lonnet::get('wishlist',\@wishlistkeys);
56: foreach my $i (%wishlist) {
57: #File not found. This appears at the first time using the wishlist
58: #Create file and put 'root' into it
59: if ($i =~m/^error:No such file/) {
60: &Apache::lonnet::logthis($i.'! Create file by putting in the "root" of the directory tree.');
61: &Apache::lonnet::put('wishlist', {'root' => ''});
62: @wishlistkeys = &Apache::lonnet::getkeys('wishlist');
63: %wishlist = &Apache::lonnet::get('wishlist',\@wishlistkeys);
64: }
65: elsif ($i =~ /^(con_lost|error|no_such_host)/i) {
66: &Apache::lonnet::logthis('ERROR while attempting to get wishlist: '.$i);
67: return 'error';
68: }
69: }
70:
71: #If hash is empty, put 'root' into it, so we got a node to start the tree
72: if ((keys %wishlist) == 0) {
73: &Apache::lonnet::logthis('ERROR while attempting to get wishlist: no keys retrieved!');
74: return 'error';
75: }
76:
77: return %wishlist;
78: }
79:
80:
81: # Write wishlist to user-data
82: sub putWishlist {
83: my $wishlist = shift;
84: &Apache::lonnet::put('wishlist',$wishlist);
85: }
86:
87:
88: # Removes all existing entrys for wishlist in user-data
89: sub deleteWishlist {
90: my @wishlistkeys = &Apache::lonnet::getkeys('wishlist');
91: my %wishlist = &Apache::lonnet::del('wishlist',\@wishlistkeys);
92: }
93:
94:
95: # Create a new entry
96: sub newEntry() {
97: my ($title, $path, $note) = @_;
98: my $date = gmtime();
99: # Create Entry-Object
100: my $entry = Entry->new(title => $title, path => $path, note => $note, date => $date);
101: # Create Tree-Object, this correspones a node in the wishlist-tree
102: my $tree = Tree->new($entry);
103: # Add this node to wishlist-tree
104: my $folderIndex = $env{'form.folders'};
105: if ($folderIndex ne '') {
106: @allFolders = ();
107: &getFoldersToArray(\@childrenRt);
108: my $folderToInsertOn = &Tree::getNodeByIndex($folderIndex,\@allFolders);
109: $folderToInsertOn->add_child($tree);
110: }
111: else {
112: $root->add_child($tree);
113: }
114: &saveChanges();
115: }
116:
117:
118: # Delete entries
119: sub deleteEntries {
120: my $marked = shift;
121: &getNodesToArray(\@childrenRt);
122:
123: foreach my $m (@$marked) {
124: my $found = &Tree::getNodeByIndex($m, \@allNodes);
125: &Tree::removeNode($found);
126: }
127: @allNodes = ();
128: &saveChanges();
129: }
130:
131:
132: # Sort entries
133: sub sortEntries {
134: my $indexNode = shift;
135: my $at = shift;
136:
137: &getNodesToArray(\@childrenRt);
138: my $foundNode = &Tree::getNodeByIndex($indexNode, \@allNodes);
139:
140: &Tree::moveNode($foundNode,$at,undef);
141: @allNodes = ();
142: }
143:
144:
145: # Move entries
146: sub moveEntries {
147: my $indexNodesToMove = shift;
148: my $indexParent = shift;
149: my @nodesToMove = ();
150:
151: &getNodesToArray(\@childrenRt);
152: foreach my $index (@$indexNodesToMove) {
153: my $foundNode = &Tree::getNodeByIndex($index, \@allNodes);
154: push(@nodesToMove, $foundNode);
155: }
156:
157: foreach my $node (@nodesToMove) {
158: my $foundParent;
159: my $parentIsIn = 0;
160: foreach my $n (@nodesToMove) {
161: if ($node->parent()->value() ne "root") {
162: if ($node->parent()->value()->nindex() == $n->value()->nindex()) {
163: $parentIsIn = 1;
164: }
165: }
166: }
167: if (!$parentIsIn) {
168: if ($indexParent ne "root") {
169: $foundParent = &Tree::getNodeByIndex($indexParent, \@allNodes);
170: &Tree::moveNode($node,undef,$foundParent);
171: }
172: else {
173: &Tree::moveNode($node,undef,$root);
174: }
175: }
176: }
177: @allNodes = ();
178: }
179:
180:
181: # Set a new title for an entry
182: sub setNewTitle {
183: my ($nodeindex, $newTitle) = @_;
184: &getNodesToArray(\@childrenRt);
185: my $found = &Tree::getNodeByIndex($nodeindex, \@allNodes);
186: $found->value()->title($newTitle);
187: @allNodes = ();
188: }
189:
190:
191: # Set a new note for an entry
192: sub setNewNote {
193: my ($nodeindex, $newNote) = @_;
194: &getNodesToArray(\@childrenRt);
195: my $found = &Tree::getNodeByIndex($nodeindex, \@allNodes);
196: $found->value()->note($newNote);
197: @allNodes = ();
198: }
199:
200:
201: # Save all changes
202: sub saveChanges {
203: @childrenRt = $root->children();
204: &Tree::TreeIndex(\@childrenRt);
205: &Tree::setCountZero();
206: &Tree::RootToHash(\@childrenRt);
207: &Tree::TreeToHash(\@childrenRt);
208: &deleteWishlist();
209: &putWishlist(\%TreeToHash);
210:
211: }
212:
213:
214: # Return the names for all exiting folders in option-tags, so
215: # a new link or a new folder can be created in an existing folder
216: my $indent = 0;
217: my $foldersOption;
218: sub getFoldersForOption {
219: my $nodes = shift;
220:
221: foreach my $n (@$nodes) {
222: if ($n->value()->path() eq '') {
223: $foldersOption .= '<option value="'.$n->value()->nindex().'" style="margin-left:'.$indent.'px">'.
224: $n->value()->title().
225: '</option>';
226:
227: my @children = $n->children();
228: if ($#children >=0) {
229: $indent += 10;
230: &getFoldersForOption(\@children);
231: $indent -= 10;
232: }
233: }
234: }
235: }
236:
237:
238: sub getfoldersOption {
239: if (&getWishlist ne 'error') {
240: %TreeHash = &getWishlist();
241: $root = &Tree::HashToTree();
242: @childrenRt = $root->children();
243: &getFoldersForOption(\@childrenRt);
244: my $options = '<option value="" selected="selected"></option>'.$foldersOption;
245: $foldersOption = '';
246: return $options;
247: }
248: else {
249: return '';
250: }
251: }
252:
253:
254: # Put all folder-nodes to an array
255: sub getFoldersToArray {
256: my $children = shift;
257: foreach my $c (@$children) {
258: if ($c->value()->path() eq '') {
259: push(@allFolders,$c);
260: }
261: my @newchildren = $c->children();
262: if ($#newchildren >= 0) {
263: &getFoldersToArray(\@newchildren);
264: }
265: }
266: }
267:
268:
269: # Put all nodes to an array
270: sub getNodesToArray {
271: my $children = shift;
272: foreach my $c (@$children) {
273: push(@allNodes,$c);
274: my @newchildren = $c->children();
275: if ($#newchildren >= 0) {
276: &getNodesToArray(\@newchildren);
277: }
278: }
279: }
280:
281:
282: # Return a script-tag containing Javascript-function
283: # needed for wishlist actions like 'new link' ect.
284: sub JSforWishlist {
285: my $startPagePopup = &Apache::loncommon::start_page('Wishlist',undef,
286: {'only_body' => 1,
287: 'js_ready' => 1,
288: 'bgcolor' => '#FFFFFF',});
289: my $endPagePopup = &Apache::loncommon::end_page({'js_ready' => 1});
290:
291: @allFolders = ();
292: &getFoldersToArray(\@childrenRt);
293: &getFoldersForOption(\@childrenRt);
294:
295: # texthash
296: my %lt = &Apache::lonlocal::texthash(
297: 'nl' => 'New Link',
298: 'nf' => 'New Folder',
299: 'lt' => 'Link Title',
300: 'ft' => 'Folder Title',
301: 'pa' => 'Path',
302: 'nt' => 'Note',
303: 'si' => 'Save in',
304: 'cl' => 'Cancel');
305:
306:
307: my $inPageNewLink = '<h1>'.$lt{'nl'}.'</h1>'.
308: '<form method="post" name="newlink" action="/adm/wishlist" target="wishlist" '.
309: 'onsubmit="return newlinksubmit();" >'.
310: &Apache::lonhtmlcommon::start_pick_box().
311: &Apache::lonhtmlcommon::row_title($lt{'lt'}).
312: '<input type="text" name="title" size="45" value="" />'.
313: &Apache::lonhtmlcommon::row_closure().
314: &Apache::lonhtmlcommon::row_title($lt{'pa'}).
315: '<input type="text" name="path" size="45" value="" />'.
316: &Apache::lonhtmlcommon::row_closure().
317: &Apache::lonhtmlcommon::row_title($lt{'nt'}).
318: '<textarea name="note" rows="3" cols="35" style="width:100%"></textarea>'.
319: &Apache::lonhtmlcommon::row_closure(1).
320: &Apache::lonhtmlcommon::end_pick_box().
321: '<br/><br/>'.
322: '<input type="submit" value="'.$lt{'si'}.'" />'.
323: '<select name="folders">'.
324: '<option value="" selected="selected"></option>'.
325: $foldersOption.
326: '</select>'.
327: '<input type="button" value="'.$lt{'cl'}.'" onclick="javascript:window.close();" />'.
328: '</form>';
329:
330: my $inPageNewFolder = '<h1>'.$lt{'nf'}.'</h1>'.
331: '<form method="post" name="newfolder" action="/adm/wishlist" target="wishlist" '.
332: 'onsubmit="return newfoldersubmit();" >'.
333: &Apache::lonhtmlcommon::start_pick_box().
334: &Apache::lonhtmlcommon::row_title($lt{'ft'}).
335: '<input type="text" name="title" size="45" value="" /><br />'.
336: &Apache::lonhtmlcommon::row_closure().
337: &Apache::lonhtmlcommon::row_title($lt{'nt'}).
338: '<textarea name="note" rows="3" cols="35" style="width:100%"></textarea><br />'.
339: &Apache::lonhtmlcommon::row_closure(1).
340: &Apache::lonhtmlcommon::end_pick_box().
341: '<br/><br/>'.
342: '<input type="submit" value="'.$lt{'si'}.'" />'.
343: '<select name="folders">'.
344: '<option value="" selected="selected"></option>'.
345: $foldersOption.
346: '</select>'.
347: '<input type="button" value="'.$lt{'cl'}.'" onclick="javascript:window.close();" />'.
348: '</form>';
349:
350: # Remove all \n for inserting on javascript document.write
351: $inPageNewLink =~ s/\n//g;
352: $inPageNewFolder =~ s/\n//g;
353:
354: my $warningLink = &mt('You must insert a title and a path!');
355: my $warningFolder = &mt('You must insert a title!');
356: my $warningDelete = &mt('Are you sure you want to delete the selected entries? Deleting a folder also deletes all entries within this folder!');
357: 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.');
358: my $warningMove = &mt('You must select a destination folder!');
359: $foldersOption = '';
360:
361: my $js = &Apache::lonhtmlcommon::scripttag(<<JAVASCRIPT);
362: function newLink() {
363: newlinkWin=window.open('','newlinkWin','width=580,height=320,scrollbars=yes');
364: newlinkWin.document.write('$startPagePopup'
365: +'<script type="text\/javascript">'
366: +'function newlinksubmit(){'
367: +'var path = document.getElementsByName("path")[0].value;'
368: +'var title = document.getElementsByName("title")[0].value;'
369: +'if (!path || !title) {'
370: +'alert("$warningLink");'
371: +'return false;}'
372: +'else {'
373: +'window.close();'
374: +'return true;}}'
375: +'<\/scr'+'ipt>'
376: +'$inPageNewLink'
377: +'$endPagePopup');
378: newlinkWin.document.close();
379: }
380:
381: function newFolder() {
382: newfolderWin=window.open('','newfolderWin','width=580,height=270, scrollbars=yes');
383: newfolderWin.document.write('$startPagePopup'
384: +'<script type="text\/javascript">'
385: +'function newfoldersubmit(){'
386: +'var title = document.getElementsByName("title")[0].value;'
387: +'if (!title) {'
388: +'alert("$warningFolder");'
389: +'return false;}'
390: +'else {'
391: +'window.close();'
392: +'return true;}}'
393: +'<\/scr'+'ipt>'
394: +'$inPageNewFolder'
395: +'$endPagePopup');
396: newfolderWin.document.close();
397: }
398:
399: function setFormAction(action,mode) {
400: var r = true;
401: setAction('');
402: if (action == 'delete') {
403: r = confirm("$warningDelete");
404: setAction('delete');
405: }
406: else if (action == 'save') {
407: var d = getDifferences();
408: if (d) {
409: if (!confirm('$warningSave')) {
410: setAction('noSave');
411: }
412: }
413: r = true;
414: }
415: document.getElementsByName('list')[0].setAttribute("action", "/adm/wishlist?mode="+mode);
416: if(r){
417: document.getElementsByName('list')[0].submit();
418: }
419: }
420:
421: function setAction(action) {
422: document.getElementById('action').value = action;
423: }
424:
425: function getDifferences() {
426: var newtitles = document.getElementsByName('newtitle');
427: var i = 0;
428: for (i=0;i<newtitles.length;i++) {
429: var newt = newtitles[i].value;
430: var oldt = newtitles[i].alt;
431: if (newt != oldt) {
432: return true;
433: }
434: }
435: var newnote = document.getElementsByName('newnote');
436: var i = 0;
437: for (i=0;i<newnote.length;i++) {
438: var newn = newnote[i].value;
439: var oldn = newnote[i].innerHTML;
440: if (newn != oldn) {
441: return true;
442: }
443: }
444: return false;
445: }
446:
447: function onLoadAction(mode) {
448: window.name = 'wishlist';
449: if (mode == "edit") {
450: var deepestRows = getDeepestRows();
451: setDisplaySelect(deepestRows, '');
452: }
453: }
454:
455: function folderAction(rowid) {
456: var row = document.getElementById(rowid);
457: var indent = getIndent(row);
458: var displ;
459: var status;
460: if (getImage(row) == 'closed') {
461: displ = '';
462: status = 'open';
463: }
464: else {
465: displ = 'LC_hidden';
466: status = 'closed';
467: }
468: setImage(row,status);
469: if (getNextRow(row) != null) {
470: var nextIndent = getIndent(getNextRow(row));
471: row = getNextRow(row);
472: while (nextIndent > indent) {
473: if (displ == '') {
474: row.className = (row.className).replace('LC_hidden','');
475: }
476: else if (displ != '' && !((row.className).match('LC_hidden'))) {
477: var oldClass = row.className;
478: row.className = oldClass+' LC_hidden';
479: setDisplayNote(row.id.replace('row','note'),'LC_hidden');
480: }
481: if (status == 'open' && getImage(row).match('closed')) {
482: row = getNextRowWithIndent(row, getIndent(row));
483: }
484: else {
485: row = getNextRow(row);
486: }
487: if (row != null) {
488: nextIndent = getIndent(row);
489: }
490: else {
491: nextIndent = indent;
492: }
493: }
494: }
495: setClasses();
496: var newtitles = document.getElementsByName('newtitle');
497: if (newtitles.length>0) {
498: var deepestRows = getDeepestRows();
499: var otherRows = getOtherRows(deepestRows);
500: setDisplaySelect(deepestRows,'');
501: setDisplaySelect(otherRows,'LC_hidden');
502: }
503: }
504:
505: function selectAction(rowid) {
506: var row = document.getElementById(rowid);
507: var indent = getIndent(row);
508: var checked = getChecked(row);
509: var previousFolderRows = new Array();
510: if (indent != 0) {
511: previousFolderRows = getPreviousFolderRows(row);
512: }
513: if (getNextRow(row) != null) {
514: var nextIndent = getIndent(getNextRow(row));
515: row = getNextRow(row);
516: while (nextIndent > indent) {
517: setChecked(row,checked);
518: if (status == 'open' && getImage(row).match('closed')) {
519: row = getNextRowWithIndent(row, getIndent(row));
520: }
521: else {
522: row = getNextRow(row);
523: }
524: if (row != null) {
525: nextIndent = getIndent(row);
526: }
527: else {
528: nextIndent = indent;
529: }
530: }
531: }
532: if (!checked) {
533: var i = 0;
534: for (i=0;i<previousFolderRows.length;i++) {
535: setChecked(previousFolderRows[i], false);
536: }
537: }
538: }
539:
540: function getNextNote(row) {
541: var rowId = row.id;
542: var nextRowId = parseInt(rowId.substr(3,rowId.length))+1;
543: nextRowId = "note"+nextRowId;
544: var nextRow = document.getElementById(nextRowId);
545: return nextRow;
546: }
547:
548: function getNextRow(row) {
549: var rowId = row.id;
550: var nextRowId = parseInt(rowId.substr(3,rowId.length))+1;
551: nextRowId = "row"+nextRowId;
552: var nextRow = document.getElementById(nextRowId);
553: return nextRow;
554: }
555:
556: function getPreviousRow(row) {
557: var rowId = row.id;
558: var previousRowId = parseInt(rowId.substr(3,rowId.length))-1;
559: previousRowId = "row"+previousRowId;
560: var previousRow =document.getElementById(previousRowId);
561: return previousRow;
562: }
563:
564: function getIndent(row) {
565: var childPADD = document.getElementById(row.id.replace('row','padd'));
566: indent = childPADD.style.paddingLeft;
567: indent = parseInt(indent.substr(0,(indent.length-2)));
568:
569: if (getImage(row).match('link')) {
570: indent -= $indentConst;
571: }
572: return indent;
573: }
574:
575: function getNextRowWithIndent(row, indent) {
576: var nextRow = getNextRow(row);
577: if (nextRow != null) {
578: var nextIndent = getIndent(nextRow);
579: while (nextIndent >= indent) {
580: if (nextIndent == indent) {
581: return nextRow;
582: }
583: nextRow = getNextRow(nextRow);
584: if (nextRow == null) {
585: return null;
586: }
587: nextIndent = getIndent(nextRow);
588: }
589: }
590: return nextRow;
591: }
592:
593: function getImage(row) {
594: var childIMG = document.getElementById(row.id.replace('row','img'));
595: if ((childIMG.src).match('closed')) {
596: return 'closed';
597: }
598: else if ((childIMG.src).match('open')) {
599: return 'open;'
600: }
601: else {
602: return 'link';
603: }
604: }
605:
606: function setImage(row, status) {
607: var childIMG = document.getElementById(row.id.replace('row','img'));
608: var childIMGFolder = document.getElementById(row.id.replace('row','imgFolder'));
609: childIMG.src = "/adm/lonIcons/arrow."+status+".gif";
610: childIMGFolder.src="/adm/lonIcons/navmap.folder."+status+".gif";
611: }
612:
613: function getChecked(row) {
614: var childCHECK = document.getElementById(row.id.replace('row','check'));
615: var checked = childCHECK.checked;
616: return checked;
617: }
618:
619: function setChecked(row,checked) {
620: var childCHECK = document.getElementById(row.id.replace('row','check'));
621: childCHECK.checked = checked;
622: }
623:
624: function getPreviousFolderRows(row) {
625: var previousRow = getPreviousRow(row);
626: var indent = getIndent(previousRow);
627: var kindOfEntry = getImage(previousRow);
628: var rows = new Array();
629: if (kindOfEntry != 'link') {
630: rows.push(previousRow);
631: }
632:
633: while (indent >0) {
634: previousRow = getPreviousRow(previousRow);
635: if (previousRow != null) {
636: indent = getIndent(previousRow);
637: kindOfEntry = getImage(previousRow);
638: if (kindOfEntry != 'link') {
639: rows.push(previousRow);
640: }
641: }
642: else {
643: indent = 0;
644: }
645: }
646: return rows;
647: }
648:
649: function getDeepestRows() {
650: var row = document.getElementById('row0');
651: var firstRow = row;
652: var indent = getIndent(row);
653: var maxIndent = indent;
654: while (getNextRow(row) != null) {
655: row = getNextRow(row);
656: indent = getIndent(row);
657: if (indent>maxIndent && !((row.className).match('LC_hidden'))) {
658: maxIndent = indent;
659: }
660: }
661: var deepestRows = new Array();
662: row = firstRow;
663: var rowIndent;
664: while (getNextRow(row) != null) {
665: rowIndent = getIndent(row);
666: if (rowIndent == maxIndent) {
667: deepestRows.push(row);
668: }
669: row = getNextRow(row);
670: }
671: rowIndent = getIndent(row);
672: if (rowIndent == maxIndent) {
673: deepestRows.push(row);
674: }
675: return deepestRows;
676: }
677:
678: function getOtherRows(deepestRows) {
679: var row = document.getElementById('row0');
680: var otherRows = new Array();
681: var isIn = false;
682: while (getNextRow(row) != null) {
683: var i = 0;
684: for (i=0; i < deepestRows.length; i++) {
685: if (row.id == deepestRows[i].id) {
686: isIn = true;
687: }
688: }
689: if (!isIn) {
690: otherRows.push(row);
691: }
692: row = getNextRow(row);
693: isIn = false;
694: }
695: for (i=0; i < deepestRows.length; i++) {
696: if (row.id == deepestRows[i].id) {
697: isIn = true;
698: }
699: }
700: if (!isIn) {
701: otherRows.push(row);
702: }
703: return otherRows;
704: }
705:
706: function setDisplaySelect(deepestRows, displ) {
707: var i = 0;
708: for (i = 0; i < deepestRows.length; i++) {
709: var row = deepestRows[i];
710: var childSEL = document.getElementById(row.id.replace('row','sel'));
711: childSEL.className = displ;
712: }
713: }
714:
715: function submitSelect() {
716: var list = document.getElementsByName('list')[0];
717: list.setAttribute("action","/adm/wishlist?mode=edit");
718: list.submit();
719: }
720:
721: function setDisplayNote(rowid, displ) {
722: var row = document.getElementById(rowid);
723: if (!displ) {
724: if ((row.className).match('LC_hidden')) {
725: row.className = (row.className).replace('LC_hidden','');
726: }
727: else {
728: var oldClass = row.className;
729: row.className = oldClass+' LC_hidden';
730: }
731: }
732: else {
733: if (displ == '') {
734: row.className = (row.className).replace('LC_hidden','');
735: }
736: else if (displ != '' && !((row.className).match('LC_hidden'))) {
737: var oldClass = row.className;
738: row.className = oldClass+' LC_hidden';
739: }
740: }
741: var noteText = document.getElementById(rowid.replace('note','noteText'));
742: var noteImg = document.getElementById(rowid.replace('note','noteImg'));
743: if (noteText.value) {
744: noteImg.src = "/res/adm/pages/anot2.png";
745: }
746: else {
747: noteImg.src = "/res/adm/pages/anot.png";
748: }
749:
750: }
751:
752: function setClasses() {
753: var row = document.getElementById("row0");
754: var note = document.getElementById("note0");
755: var LC_class = 0;
756: if (getNextRow(row) != null) {
757: while (getNextRow(row) != null) {
758: if (!(row.className).match('LC_hidden')) {
759: note.className = (note.className).replace('LC_even_row','');
760: note.className = (note.className).replace('LC_odd_row','');
761: if (LC_class) {
762: row.className = 'LC_even_row';
763: note.className = 'LC_even_row'+note.className;
764: }
765: else {
766: row.className = 'LC_odd_row';
767: note.className = 'LC_odd_row'+note.className;;
768: }
769: LC_class = !LC_class;
770: }
771: note = getNextNote(row);
772: row = getNextRow(row);
773: }
774: }
775: if (!(row.className).match('LC_hidden')) {
776: note.className = (note.className).replace('LC_even_row','');
777: note.className = (note.className).replace('LC_odd_row','');
778: if (LC_class) {
779: row.className = 'LC_even_row';
780: note.className = 'LC_even_row'+note.className;
781: }
782: else {
783: row.className = 'LC_odd_row';
784: note.className = 'LC_odd_row'+note.className;
785: }
786: }
787: }
788:
789: function selectDestinationFolder() {
790: var mark = document.getElementsByName('mark');
791: var i = 0;
792: for (i = 0; i < mark.length; i++) {
793: if (mark[i].checked) {
794: document.getElementsByName('list')[0].submit();
795: return true;
796: }
797: }
798: alert('$warningMove');
799: return false;
800: }
801:
802: JAVASCRIPT
803: return $js;
804: }
805:
806:
807: # HTML-Markup for table if in view-mode
808: my $wishlistHTMLview;
809: my $indent = $indentConst;
810: sub wishlistView {
811: my $nodes = shift;
812:
813: foreach my $n (@$nodes) {
814: my $index = $n->value()->nindex();
815:
816: # 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.
817: # only display the top level entries on load
818: $wishlistHTMLview .= ($n->parent()->value() eq 'root')?&Apache::loncommon::start_data_table_row('','row'.$index)
819: :&Apache::loncommon::continue_data_table_row('LC_hidden','row'.$index);
820:
821:
822: # checkboxes
823: $wishlistHTMLview .= '<td><input type="checkbox" name="mark" id="check'.$index.'" value="'.$index.'" '.
824: 'onclick="selectAction('."'row".$index."'".')"/></td>';
825:
826: # entry is a folder
827: if ($n->value()->path() eq '') {
828: $wishlistHTMLview .= '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<0?0:($indent-$indentConst)).'px; min-width: 220px;">'.
829: '<a href="javascript:;" onclick="folderAction('."'row".$index."'".')" style="vertical-align:top">'.
830: '<img src="/adm/lonIcons/arrow.closed.gif" id="img'.$index.'" alt = "" class="LC_icon"/>'.
831: '<img src="/adm/lonIcons/navmap.folder.closed.gif" id="imgFolder'.$index.'" alt="folder"/>'.
832: $n->value()->title().'</a></td>';
833: }
834: # entry is a link
835: else {
836: $wishlistHTMLview .= '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<=0?$indentConst:$indent).'px; min-width: 220px;">'.
837: '<img src="/res/adm/pages/wishlist-link.png" id="img'.$index.'" alt="link" />'.
838: '<a href="'.$n->value()->path().'">'.$n->value()->title().'</a></td>';
839: }
840:
841: # note-icon, different icons for an entries with note and those without
842: my $noteIMG = 'anot.png';
843:
844: if ($n->value()->note() ne '') {
845: $noteIMG = 'anot2.png';
846: }
847:
848: $wishlistHTMLview .= '<td style="padding-left:10px;"><a href="javascript:;" onclick="setDisplayNote('."'note".$index."'".')">'.
849: '<img id="noteImg'.$index.'" src="/res/adm/pages/'.$noteIMG.'" alt="'.&mt('Note').'" title="'.&mt('Note').'" '.
850: ' class="LC_icon"/></a></td>';
851:
852: $wishlistHTMLview .= &Apache::loncommon::end_data_table_row();
853:
854: # start row containing the textarea for the note, do not display note on default
855: $wishlistHTMLview .= &Apache::loncommon::continue_data_table_row('LC_hidden','note'.$index).
856: '<td></td><td>'.
857: '<textarea id="noteText'.$index.'" cols="25" rows="3" style="width:100%" '.
858: 'name="newnote" >'.
859: $n->value()->note().'</textarea></td><td></td>';
860: $wishlistHTMLview .= &Apache::loncommon::end_data_table_row();
861:
862: # if the entry is a folder, it could have other entries as content. if it has, call wishlistView for those entries
863: my @children = $n->children();
864: if ($#children >=0) {
865: $indent += 20;
866: &wishlistView(\@children);
867: $indent -= 20;
868: }
869: }
870: }
871:
872:
873: # HTML-Markup for table if in edit-mode
874: my $wishlistHTMLedit;
875: my $indent = $indentConst;
876: sub wishlistEdit {
877: my $nodes = shift;
878: my $curNode = 1;
879:
880: foreach my $n (@$nodes) {
881: my $index = $n->value()->nindex();
882:
883: # start row, use data_table routines to set class to LC_even or LC_odd automatically.
884: # this rows contains a checkbox, a select-field for sorting entries, the title in an input-field and the note-icon.
885: # only display the top level entries on load
886: $wishlistHTMLedit .= ($n->parent()->value() eq 'root')?&Apache::loncommon::start_data_table_row('','row'.$index)
887: :&Apache::loncommon::continue_data_table_row('LC_hidden','row'.$index);
888:
889: # checkboxes
890: $wishlistHTMLedit .= '<td><input type="checkbox" name="mark" id="check'.$index.'" value="'.$index.'" '.
891: 'onclick="selectAction('."'row".$index."'".')"/></td>';
892:
893: # 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.
894: # set the number for the current entry into brackets
895: my $options;
896: for (my $i = 1; $i < ((scalar @{$nodes})+1); $i++) {
897: if ($i == $curNode) {
898: $options .= '<option selected="selected" value="">('.$i.')</option>';
899: }
900: else {
901: $options .= '<option value="'.$i.'">'.$i.'</option>';
902: }
903: }
904: $curNode++;
905:
906: # entry is a folder
907: if ($n->value()->path() eq '') {
908: $wishlistHTMLedit .= '<td><select class="LC_hidden" name="sel" id="sel'.$index.'" onchange="submitSelect();">'.
909: $options.'</select></td>'.
910: '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<0?0:($indent-$indentConst)).'px;">'.
911: '<a href="javascript:;" onclick="folderAction('."'row".$index."'".')" style="vertical-align:top" >'.
912: '<img src="/adm/lonIcons/arrow.closed.gif" id="img'.$index.'" alt = "" class="LC_icon"/>'.
913: '<img src="/adm/lonIcons/navmap.folder.closed.gif" id="imgFolder'.$index.'" alt="folder"/></a>';
914:
915: }
916: # entry is a link
917: else {
918: $wishlistHTMLedit .= '<td><select class="LC_hidden" name="sel" id="sel'.$index.'" onchange="submitSelect();">'.
919: $options.'</select></td>'.
920: '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<=0?$indentConst:$indent).'px;">'.
921: '<img src="/res/adm/pages/wishlist-link.png" id="img'.$index.'" alt="link"/>';
922: }
923:
924: # input-field for title
925: $wishlistHTMLedit .= '<input type="text" name="newtitle" value="'.$n->value()->title().'" alt = "'.$n->value()->title().'"/></td>';
926:
927: # note-icon, different icons for an entries with note and those without
928: my $noteIMG = 'anot.png';
929:
930: if ($n->value()->note() ne '') {
931: $noteIMG = 'anot2.png';
932: }
933:
934: $wishlistHTMLedit .= '<td style="padding-left:10px;"><a href="javascript:;" onclick="setDisplayNote('."'note".$index."'".')">'.
935: '<img id="noteImg'.$index.'" src="/res/adm/pages/'.$noteIMG.'" alt="'.&mt('Note').'" title="'.&mt('Note').'" '.
936: ' class="LC_icon"/></a></td>';
937:
938: $wishlistHTMLedit .= &Apache::loncommon::end_data_table_row();
939:
940: # start row containing the textarea for the note
941: $wishlistHTMLedit .= &Apache::loncommon::continue_data_table_row('LC_hidden','note'.$index).
942: '<td></td><td></td><td>'.
943: '<textarea id="noteText'.$index.'" cols="25" rows="3" style="width:100%" '.
944: 'name="newnote">'.
945: $n->value()->note().'</textarea></td><td></td>';
946: $wishlistHTMLedit .= &Apache::loncommon::end_data_table_row();
947:
948: # if the entry is a folder, it could have other entries as content. if it has, call wishlistEdit for those entries
949: my @children = $n->children();
950: if ($#children >=0) {
951: $indent += 20;
952: &wishlistEdit(\@children);
953: $indent -= 20;
954: }
955: }
956: }
957:
958:
959:
960: # HTML-Markup for table if in move-mode
961: my $wishlistHTMLmove ='<tr id="root" class="LC_odd_row"><td><input type="radio" name="mark" id="radioRoot" value="root" /></td>'.
962: '<td>'.&mt('Top level').'</td><td></td></tr>';
963: my $indent = $indentConst;
964: sub wishlistMove {
965: my $nodes = shift;
966: my $marked = shift;
967:
968: foreach my $n (@$nodes) {
969: my $index = $n->value()->nindex();
970:
971: #find out wether the current entry was marked to be moved.
972: my $isIn = 0;
973: foreach my $m (@$marked) {
974: if ($index == $m) {
975: $isIn = 1;
976: }
977: }
978: # 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
979: $wishlistHTMLmove .= &Apache::loncommon::start_data_table_row('','row'.$index);
980:
981:
982: # entry is a folder
983: if ($n->value()->path() eq '') {
984: # display a radio-button, if the folder was not selected to be moved
985: if (!$isIn) {
986: $wishlistHTMLmove .= '<td><input type="radio" name="mark" id="radio'.$index.'" value="'.$index.'" /></td>'.
987: '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<0?0:($indent-$indentConst)).'px; min-width: 220px;">';
988: }
989: # higlight the title, if the folder was selected to be moved
990: else {
991: $wishlistHTMLmove .= '<td></td>'.
992: '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<0?0:($indent-$indentConst)).'px; min-width: 220px;'.
993: 'color:red;">';
994: }
995: #arrow- and folder-image, all folders are open, and title
996: $wishlistHTMLmove .= '<img src="/adm/lonIcons/arrow.open.gif" id="img'.$index.'" alt = "" />'.
997: '<img src="/adm/lonIcons/navmap.folder.open.gif" id="imgFolder'.$index.'" alt="folder"/>'.
998: $n->value()->title().'</td>';
999: }
1000: # entry is a link
1001: else {
1002: if (!$isIn) {
1003: $wishlistHTMLmove .= '<td></td>'.
1004: '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<=0?$indentConst:$indent).'px; min-width: 220px;">';
1005: }
1006: # higlight the title, if the link was selected to be moved
1007: else {
1008: $wishlistHTMLmove .= '<td></td>'.
1009: '<td id="padd'.$index.'" style="padding-left:'.(($indent-$indentConst)<=0?$indentConst:$indent).'px; min-width: 220px;'.
1010: 'color:red;">';
1011: }
1012: # link-image and title
1013: $wishlistHTMLmove .= '<img src="/res/adm/pages/wishlist-link.png" id="img'.$index.'" alt="link"/>'.
1014: $n->value()->title().'</td>';
1015: }
1016:
1017: # note-icon, different icons for an entries with note and those without
1018: my $noteIMG = 'anot.png';
1019:
1020: if ($n->value()->note() ne '') {
1021: $noteIMG = 'anot2.png';
1022: }
1023:
1024: $wishlistHTMLmove .= '<td style="padding-left:10px;"><a href="javascript:;" onclick="setDisplayNote('."'note".$index."'".')">'.
1025: '<img id="noteImg'.$index.'" src="/res/adm/pages/'.$noteIMG.'" alt="'.&mt('Note').'" title="'.&mt('Note').'" '.
1026: ' class="LC_icon"/></a></td>';
1027:
1028: $wishlistHTMLmove .= &Apache::loncommon::end_data_table_row();
1029:
1030: # start row containing the textarea for the note, readonly in move-mode
1031: $wishlistHTMLmove .= &Apache::loncommon::continue_data_table_row('LC_hidden','note'.$index).
1032: '<td></td><td>'.
1033: '<textarea id="noteText'.$index.'" cols="25" rows="3" style="width:100%" '.
1034: 'name="newnote" readonly="readonly">'.
1035: $n->value()->note().'</textarea></td><td></td>'.
1036: &Apache::loncommon::end_data_table_row();
1037:
1038: # if the entry is a folder, it could have other entries as content. if it has, call wishlistMove for those entries
1039: my @children = $n->children();
1040: if ($#children >=0) {
1041: $indent += 20;
1042: &wishlistMove(\@children, $marked);
1043: $indent -= 20;
1044: }
1045: }
1046: }
1047:
1048: # Returns the HTML-Markup for wishlist
1049: sub makePage {
1050: my $mode = shift;
1051: my $marked = shift;
1052:
1053: # breadcrumbs and start_page
1054: &Apache::lonhtmlcommon::clear_breadcrumbs();
1055: &Apache::lonhtmlcommon::add_breadcrumb(
1056: { href => '/adm/wishlist?mode='.$mode,
1057: text => 'Wishlist'});
1058: my $startPage = &Apache::loncommon::start_page('Wishlist',undef,
1059: {'add_entries' => {
1060: 'onload' => 'javascript:onLoadAction('."'".$mode."'".');',
1061: 'onunload' => 'javascript:window.name = '."'loncapaclient'"}});
1062:
1063: my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs('Wishlist '.
1064: '<a title="Online-Hilfe" href="/adm/help/Wishlist.hlp" target="_top">'.
1065: '<img src="/adm/help/help.png" alt="'.&mt('Help').'" '.
1066: 'title="'.&mt('Help').'" class="LC_icon" /></a>');
1067:
1068: # get javascript-code for wishlist-interactions
1069: my $js = &JSforWishlist();
1070:
1071: # texthash for items in funtionlist
1072: my %lt = &Apache::lonlocal::texthash(
1073: 'ed' => 'Edit',
1074: 'vw' => 'View',
1075: 'al' => 'Add Link',
1076: 'af' => 'Add Folder',
1077: 'mv' => 'Move Selected',
1078: 'dl' => 'Delete Selected',
1079: 'sv' => 'Save');
1080:
1081: # start functionlist
1082: my $functions = &Apache::lonhtmlcommon::start_funclist();
1083:
1084: # icon for edit-mode, display when in view-mode
1085: if ($mode eq 'view') {
1086: $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
1087: 'onclick="setFormAction('."'save','edit'".'); list.submit();" class="LC_menubuttons_link">'.
1088: '<img src="/res/adm/pages/edit-mode-22x22.png" alt="'.$lt{'ed'}.'" '.
1089: 'title="'.$lt{'ed'}.'" class="LC_icon"/> '.
1090: '<span class="LC_menubuttons_inline_text">'.$lt{'ed'}.'</span></a>');
1091: }
1092: # icon for view-mode, display when in edit-mode
1093: else {
1094: $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
1095: 'onclick="setFormAction('."'save','view'".'); list.submit();" class="LC_menubuttons_link">'.
1096: '<img src="/res/adm/pages/view-mode-22x22.png" alt="'.$lt{'vw'}.'" '.
1097: 'title="'.$lt{'vw'}.'" class="LC_icon"/> '.
1098: '<span class="LC_menubuttons_inline_text">'.$lt{'vw'}.'</span></a>');
1099: }
1100:
1101: # icon for adding a new link
1102: $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
1103: 'onclick="newLink();" class="LC_menubuttons_link">'.
1104: '<img src="/res/adm/pages/link-new-22x22.png" alt="'.$lt{'al'}.'" '.
1105: 'title="'.$lt{'al'}.'" class="LC_icon"/>'.
1106: '<span class="LC_menubuttons_inline_text">'.$lt{'al'}.'</span></a>');
1107:
1108: # icon for adding a new folder
1109: $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
1110: 'onclick="newFolder();" class="LC_menubuttons_link">'.
1111: '<img src="/res/adm/pages/folder-new-22x22.png" alt="'.$lt{'af'}.'" '.
1112: 'title="'.$lt{'af'}.'" class="LC_icon"/>'.
1113: '<span class="LC_menubuttons_inline_text">'.$lt{'af'}.'</span></a>');
1114:
1115: # icon for moving entries
1116: $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
1117: 'onclick="setFormAction('."'move','move'".'); " class="LC_menubuttons_link">'.
1118: '<img src="/res/adm/pages/move-22x22.png" alt="'.$lt{'mv'}.'" '.
1119: 'title="'.$lt{'mv'}.'" class="LC_icon" />'.
1120: '<span class="LC_menubuttons_inline_text">'.$lt{'mv'}.'</span></a>');
1121:
1122: # icon for deleting entries
1123: $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
1124: 'onclick="setFormAction('."'delete','".$mode."'".'); " class="LC_menubuttons_link">'.
1125: '<img src="/res/adm/pages/del.png" alt="'.$lt{'dl'}.'" '.
1126: 'title="'.$lt{'dl'}.'" class="LC_icon" />'.
1127: '<span class="LC_menubuttons_inline_text">'.$lt{'dl'}.'</span></a>');
1128:
1129: # icon for saving changes
1130: $functions .= &Apache::lonhtmlcommon::add_item_funclist('<a href="javascript:;" '.
1131: 'onclick="setFormAction('."'','".$mode."'".'); " class="LC_menubuttons_link">'.
1132: '<img src="/res/adm/pages/save-22x22.png" alt="'.$lt{'sv'}.'" '.
1133: 'title="'.$lt{'sv'}.'" class="LC_icon" />'.
1134: '<span class="LC_menubuttons_inline_text">'.$lt{'sv'}.'</span></a>');
1135:
1136: # end funtionlist and generate subbox
1137: $functions.= &Apache::lonhtmlcommon::end_funclist();
1138: my $subbox = &Apache::loncommon::head_subbox($functions);
1139:
1140: # start form
1141: my $inner .= '<form name="list" action ="/adm/wishlist" method="post">'.
1142: '<input type="hidden" id="action" name="action" value=""/>';
1143:
1144: # only display subbox in view- or edit-mode
1145: if ($mode ne 'move') {
1146: $inner .= $subbox;
1147: }
1148:
1149: # generate table-content depending on mode
1150: if ($mode eq 'edit') {
1151: &wishlistEdit(\@childrenRt);
1152: if ($wishlistHTMLedit ne '') {
1153: $inner .= &Apache::loncommon::start_data_table("LC_tableOfContent");
1154: $inner .= $wishlistHTMLedit;
1155: $inner .= &Apache::loncommon::end_data_table();
1156: }
1157: else {
1158: $inner .= '<span class="LC_info">'.&mt("Your wihlist ist currently empty.").'</span>';
1159: }
1160: $wishlistHTMLedit = '';
1161: }
1162: elsif ($mode eq 'view') {
1163: &wishlistView(\@childrenRt);
1164: if ($wishlistHTMLview ne '') {
1165: $inner .= '<table class="LC_data_table LC_tableOfContent">'.$wishlistHTMLview.'</table>';
1166: }
1167: else {
1168: $inner .= '<span class="LC_info">'.&mt("Your wihlist ist currently empty.").'</span>';
1169: }
1170: $wishlistHTMLview = '';
1171: }
1172: else {
1173: my $markStr = '';
1174: foreach my $m (@$marked) {
1175: $markStr .= $m.',';
1176: }
1177: if ($markStr) {
1178: $markStr = substr($markStr, 0, length($markStr)-1);
1179: $inner .= '<input type="hidden" value="'.$markStr.'" name="markedToMove"/>';
1180: $inner .= '<span class="LC_info">'.&mt('You have selected the red marked entries to be moved to another folder. '.
1181: 'Now choose the new destination folder.').'</span><br/><br/>';
1182: &wishlistMove(\@childrenRt, $marked);
1183: $inner .= '<table class="LC_data_table LC_tableOfContent">'.$wishlistHTMLmove.'</table><br/><br/>';
1184: $inner .= '<input type="button" value="'.&mt('Move').'" onclick="setFormAction('."'','view'".'); selectDestinationFolder()"/>'.
1185: '<input type="button" value="'.&mt('Cancel').'" onclick="go('."'/adm/wishlist'".')"/>';
1186:
1187: $wishlistHTMLmove ='<tr id="root" class="LC_odd_row"><td><input type="radio" name="mark" id="radioRoot" value="root" /></td>'.
1188: '<td>'.&mt('Top level').'</td><td></td></tr>';
1189: }
1190: else {
1191: $inner .= '<span class="LC_info">'.&mt("You haven't marked any entry to move.").'</span><br/>'.
1192: '<input type="button" value="'.&mt('Back').'" onclick="go('."'/adm/wishlist'".')"/>';
1193: }
1194: }
1195:
1196: # end form
1197: $inner .= '</form>';
1198:
1199: # end_page
1200: my $endPage = &Apache::loncommon::end_page();
1201:
1202: # put all page-elements togther
1203: my $page = $startPage.$breadcrumbs.$js.$inner.$endPage;
1204:
1205: return $page;
1206: }
1207:
1208:
1209: # Returns the HTML-Markup for the page, shown when a link was set
1210: sub makePageSet {
1211: # start_page
1212: my $startPage = &Apache::loncommon::start_page('Wishlist',undef,
1213: {'only_body' => 1});
1214:
1215: # confirm success and offer link to wishlist
1216: my $message = &Apache::lonhtmlcommon::confirm_success(&mt('Link successfully set!'));
1217: $message = &Apache::loncommon::confirmwrapper($message);
1218:
1219: my $inner .= '<br>'.$message.'<br/><br/>'.
1220: '<a href="javascript:;" onclick="opener.open('."'/adm/wishlist'".');window.close();">'.&mt('Go to wishlist').'</a>'.
1221: ' <a href="javascript:;" onclick="window.close();">'.&mt('Close this window').'</a>';
1222:
1223: # end_page
1224: my $endPage = &Apache::loncommon::end_page();
1225:
1226: # put all page-elements togther
1227: my $page = $startPage.$inner.$endPage;
1228:
1229: return $page;
1230: }
1231:
1232:
1233: # Returns the HTML-Markup for error-page
1234: sub makeErrorPage {
1235: # breadcrumbs and start_page
1236: &Apache::lonhtmlcommon::add_breadcrumb(
1237: { href => '/adm/wishlist',
1238: text => 'Wishlist'});
1239: my $startPage = &Apache::loncommon::start_page('Wishlist');
1240:
1241: my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs('Wishlist '.
1242: '<a title="Online-Hilfe" href="/adm/help/Wishlist.hlp" target="_top">'.
1243: '<img src="/adm/help/help.png" alt="'.&mt('Help').'" '.
1244: 'title="'.&mt('Help').'" class="LC_icon" /></a>');
1245: &Apache::lonhtmlcommon::clear_breadcrumbs();
1246:
1247: # error-message
1248: my $inner .= '<span class="LC_error">'.&mt('An error occurred! Please try again later.').'</span>';
1249:
1250: # end_page
1251: my $endPage = &Apache::loncommon::end_page();
1252:
1253: # put all page-elements togther
1254: my $page = $startPage.$breadcrumbs.$inner.$endPage;
1255:
1256: return $page;
1257: }
1258:
1259: # ----------------------------------------------------- Main Handler, package lonwishlist
1260: sub handler {
1261: my ($r) = @_;
1262: &Apache::loncommon::content_type($r,'text/html');
1263: $r->send_http_header;
1264:
1265: if (&getWishlist() ne 'error') {
1266: # get wishlist entries from user-data db-file and build a tree out of these entries
1267: %TreeHash = &getWishlist();
1268: $root = &Tree::HashToTree();
1269: @childrenRt = $root->children();
1270:
1271: # greate a new entry
1272: if ($env{'form.title'}) {
1273: &newEntry($env{'form.title'}, $env{'form.path'}, $env{'form.note'});
1274: }
1275:
1276: # get unprocessed_cgi (i.e. marked entries, mode ...)
1277: &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['action','mark','markedToMove','mode','newtitle','note']);
1278:
1279: # change the order of entries within a level, that means sorting the entries
1280: my $changeOrder = 0;
1281: if (defined $env{'form.sel'}) {
1282: my @sel = &Apache::loncommon::get_env_multiple('form.sel');
1283: my $indexNode;
1284: my $at;
1285: for (my $s=0; $s<($#sel+1); $s++) {
1286: if ($sel[$s] ne '') {
1287: $indexNode = $s;
1288: $at = $sel[$s]-1;
1289: }
1290: }
1291: if ($at ne '') {
1292: $changeOrder = 1;
1293: &sortEntries($indexNode,$at);
1294: &saveChanges();
1295: }
1296: }
1297:
1298: # get all marked (checkboxes) entries
1299: my @marked = ();
1300: if (defined $env{'form.mark'}) {
1301: @marked = &Apache::loncommon::get_env_multiple('form.mark');
1302: }
1303:
1304: # move entries from one folder to another
1305: if (defined $env{'form.markedToMove'}) {
1306: my $markedToMove = $env{'form.markedToMove'};
1307: my @ToMove = split(/\,/,$markedToMove);
1308: my $moveTo = $env{'form.mark'};
1309: if (defined $moveTo){
1310: &moveEntries(\@ToMove,$moveTo);
1311: &saveChanges();
1312: }
1313: $changeOrder = 1;
1314:
1315: }
1316:
1317: # delete entries
1318: if ($env{'form.action'} eq 'delete') {
1319: &deleteEntries(\@marked);
1320: }
1321:
1322:
1323: # get all titles and notes and save them
1324: # only save, if user wants to save changes
1325: # do not save, when current action is 'delete' or 'sort' or 'move'
1326: my @newTitles = ();
1327: my @newNotes = ();
1328: if ((defined $env{'form.newtitle'} || defined $env{'form.newnote'}) && ($env{'form.action'} ne 'noSave') && ($env{'form.action'} ne 'delete') && !$changeOrder) {
1329: @newTitles = &Apache::loncommon::get_env_multiple('form.newtitle');
1330: @newNotes = &Apache::loncommon::get_env_multiple('form.newnote');
1331: my $node = 0;
1332: foreach my $t (@newTitles) {
1333: &setNewTitle($node, $t);
1334: $node++;
1335: }
1336: $node = 0;
1337: foreach my $n (@newNotes) {
1338: &setNewNote($node, $n);
1339: $node++;
1340: }
1341: &saveChanges();
1342: }
1343:
1344: # Create HTML-markup
1345: my $page;
1346: if ($env{'form.mode'} eq 'edit') {
1347: $page = &makePage("edit");
1348: }
1349: elsif ($env{'form.mode'} eq 'move') {
1350: $page = &makePage("move", \@marked);
1351: }
1352: elsif ($env{'form.mode'} eq 'set') {
1353: $page = &makePageSet();
1354: }
1355: else {
1356: $page = &makePage("view");
1357: }
1358: @marked = ();
1359: $r->print($page);
1360: }
1361: # An error occured, print an error-page
1362: else {
1363: my $errorPage = &makeErrorPage();
1364: $r->print($errorPage);
1365: }
1366: return OK;
1367: }
1368:
1369: # ----------------------------------------------------- package Tree
1370: # Extend CPAN-Module Tree by function like 'moveNode' or 'deleteNode'
1371: package Tree;
1372:
1373: # returns the node with a given index from a list of nodes
1374: sub getNodeByIndex {
1375: my $index = shift;
1376: my $nodes = shift;
1377: my $found;
1378:
1379: for my $n (@$nodes) {
1380: my $curIndex = $n->value()->nindex();
1381: if ($n->value()->nindex() == $index) {
1382: $found = $n;
1383: }
1384: }
1385: return $found;
1386: }
1387:
1388: # moves a given node to a new parent or change the position from a node
1389: # within its siblings (sorting)
1390: sub moveNode {
1391: my $node = shift;
1392: my $at = shift;
1393: my $newParent = shift;
1394:
1395:
1396: if (!$newParent) {
1397: $newParent = $node->parent();
1398: }
1399:
1400: $node->parent()->remove_child($node);
1401:
1402: if (defined $at) {
1403: $newParent->add_child({at => $at},$node);
1404: }
1405: else {
1406: $newParent->add_child($node);
1407: }
1408:
1409: # updating root's children
1410: @childrenRt = $root->children();
1411: }
1412:
1413: # removes a given node
1414: sub removeNode() {
1415: my $node = shift;
1416: my @children = $node->children();
1417:
1418: if ($#children >= 0) {
1419: foreach my $c (@children) {
1420: &removeNode($c);
1421: }
1422: }
1423: $node->parent()->remove_child($node);
1424:
1425: # updating root's children
1426: @childrenRt = $root->children();
1427: }
1428:
1429:
1430: # set an index for every node in the tree, beginning with 0
1431: my $count = 0;
1432: sub TreeIndex {
1433: my $children = shift;
1434:
1435: foreach my $n (@$children) {
1436: my @children = $n->children();
1437: $n->value()->nindex($count);$count++;
1438:
1439: if ($#children>=0) {
1440: &TreeIndex(\@children);
1441: }
1442: }
1443: }
1444:
1445: # reset index counter
1446: sub setCountZero {
1447: $count = 0;
1448: }
1449:
1450:
1451: # convert the tree to a hash
1452: # each node is one hash-entry
1453: # keys are the indices, values are all other attributes
1454: # (containing tile, path, note, date and indices for all direct children)
1455: # except for root: the key is root and values are
1456: # just the indices of root's children
1457: sub RootToHash {
1458: my $childrenRt = shift;
1459: my @indexarr = ();
1460:
1461: foreach my $c (@$childrenRt) {
1462: push (@indexarr, $c->value()->nindex());
1463: }
1464: $TreeToHash{'root'} = [@indexarr];
1465: }
1466:
1467: sub TreeToHash {
1468: my $childrenRt = shift;
1469:
1470: foreach my $n (@$childrenRt) {
1471: my @arrtmp = ();
1472: $arrtmp[0] = $n->value()->title();
1473: $arrtmp[1] = $n->value()->path();
1474: $arrtmp[2] = $n->value()->note();
1475: $arrtmp[3] = $n->value()->date();
1476: my @childrenRt = $n->children();
1477: my $co = 4;
1478: foreach my $c (@childrenRt) {
1479: my $i = $c->value()->nindex();
1480: $arrtmp[$co] = $i;
1481: $co++;
1482: }
1483: $TreeToHash{$n->value()->nindex} = [ @arrtmp];
1484: if ($#childrenRt>=0) {
1485: &TreeToHash(\@childrenRt);
1486: }
1487: }
1488: }
1489:
1490:
1491: # convert the hash to a tree
1492: # build a tree-object for each entry in the hash
1493: # afterwards call &buildTree to connect the tree-objects
1494: sub HashToTree {
1495: my @TreeNodes = ();
1496: my $root;
1497:
1498: foreach my $key (keys %TreeHash) {
1499: if ($key eq 'root') {
1500: $root = Tree->new("root");
1501: }
1502: else {
1503: my @attributes = @{ $TreeHash{$key} };
1504: my $tmpNode;
1505: $tmpNode = Tree->new(Entry->new(title=>$attributes[0],
1506: path=>$attributes[1],
1507: note=>$attributes[2],
1508: date=>$attributes[3],
1509: nindex=>$key));
1510: push(@TreeNodes, $tmpNode);
1511: # shift all attributes except for
1512: # the indices representing the children of a node
1513: shift(@attributes);
1514: shift(@attributes);
1515: shift(@attributes);
1516: shift(@attributes);
1517: $TreeHash{$key} = [ @attributes ];
1518: }
1519: }
1520: # if there are nodes, build up the tree-structure
1521: if (defined $TreeHash{'root'}) {
1522: my @childrenRtIn = @{ $TreeHash{'root'} };
1523: &buildTree(\$root, \@childrenRtIn,\@TreeNodes,\%TreeHash);
1524: }
1525: return $root;
1526: }
1527:
1528:
1529: # join the nodes to a tree
1530: sub buildTree {
1531: my ($node, $childrenIn, $TreeNodes, $TreeHash) = @_;
1532: bless($node, 'Tree');
1533: foreach my $c (@$childrenIn) {
1534: my $tmpNode = &getNodeByIndex($c,$TreeNodes);
1535: $$node->add_child($tmpNode);
1536: my @childrenIn = @{ $$TreeHash{$tmpNode->value()->nindex()} };
1537: &buildTree(\$tmpNode,\@childrenIn,$TreeNodes,$TreeHash);
1538: }
1539:
1540: }
1541:
1542:
1543: # ----------------------------------------------------- package Entry
1544: # package that defines the entrys a wishlist could have
1545: # i.e. folders and links
1546: package Entry;
1547:
1548: # constructor
1549: sub new {
1550: my $invocant = shift;
1551: my $class = ref($invocant) || $invocant;
1552: my $self = { @_ }; #set attributes
1553: bless($self, $class);
1554: return $self;
1555: }
1556:
1557: # getter and setter
1558: sub title {
1559: my $self = shift;
1560: if ( @_ ) { $self->{title} = shift}
1561: return $self->{title};
1562: }
1563:
1564: sub date {
1565: my $self = shift;
1566: if ( @_ ) { $self->{date} = shift}
1567: return $self->{date};
1568: }
1569:
1570: sub note {
1571: my $self = shift;
1572: if ( @_ ) { $self->{note} = shift}
1573: return $self->{note};
1574: }
1575:
1576: sub path {
1577: my $self = shift;
1578: if ( @_ ) { $self->{path} = shift}
1579: return $self->{path};
1580: }
1581:
1582: sub nindex {
1583: my $self = shift;
1584: if ( @_ ) { $self->{nindex} = shift}
1585: return $self->{nindex};
1586: }
1587:
1588:
1589: 1;
1590: __END__
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>