File:
[LON-CAPA] /
loncom /
interface /
lonfeedback.pm
Revision
1.389:
download - view:
text,
annotated -
select for diffs
Tue Jan 18 17:33:13 2022 UTC (2 years, 8 months ago) by
raeburn
Branches:
MAIN
CVS tags:
HEAD
Bug 6955
- If IP-based blocking set in domain config is in effect for user's
current IP address, and 'Messaging 'is among blocked functions:
(a) 'Send Feedback' (in course context), for resource(s) which allow it
is only messaging mechanism available to users with out 'evb' priv.
(b) Subject and Content for messages sent to instructor(s) via 'Send Feedback'
are replaced in user's Sent Folder with: "Not shown due to IP block".
1: # The LearningOnline Network
2: # Feedback
3: #
4: # $Id: lonfeedback.pm,v 1.389 2022/01/18 17:33:13 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:
30: package Apache::lonfeedback;
31:
32: use strict;
33: use Apache::Constants qw(:common);
34: use Apache::lonmsg();
35: use Apache::loncommon();
36: use Apache::lontexconvert();
37: use Apache::lonlocal; # must not have ()
38: use Apache::lonnet;
39: use Apache::lonhtmlcommon();
40: use Apache::lonnavmaps;
41: use Apache::lonenc();
42: use Apache::lonrss();
43: use HTML::LCParser();
44: #use HTML::Tidy::libXML;
45: use Apache::lonspeller();
46: use Apache::longroup;
47: use Archive::Zip qw( :ERROR_CODES );
48: use LONCAPA qw(:DEFAULT :match);
49:
50: sub discussion_open {
51: my ($status,$symb)=@_;
52: # Advanced roles can always discuss
53: if ($env{'request.role.adv'}) { return 1; }
54: # Get discussion closing date
55: my $close=&Apache::lonnet::EXT('resource.0.discussend',$symb);
56: # If it is defined and in the future, the instructor wants this discussion to be open
57: if (defined($close) && $close ne '' && $close > time) {
58: return 1;
59: }
60: # It was not explicitly open, check if the problem is available.
61: # If the problem is not available, close the discussion
62: if (defined($status) &&
63: !($status eq 'CAN_ANSWER' || $status eq 'CANNOT_ANSWER'
64: || $status eq 'OPEN')) {
65: return 0;
66: }
67: # The problem is available, but check if the instructor explictly closed discussion
68: if (defined($close) && $close ne '' && $close < time) {
69: return 0;
70: }
71: return 1;
72: }
73:
74: sub discussion_visible {
75: my ($status)=@_;
76: if (not &discussion_open($status)) {
77: my $hidden=&Apache::lonnet::EXT('resource.0.discusshide');
78: if (lc($hidden) eq 'yes' or $hidden eq '' or !defined($hidden)) {
79: if (!$env{'request.role.adv'}) { return 0; }
80: }
81: }
82: return 1;
83: }
84:
85: sub discussion_vote_available {
86: my ($status,$symb)=@_;
87: my $canvote=&Apache::lonnet::EXT('resource.0.discussvote',$symb);
88: if ((lc($canvote) eq 'yes') ||
89: ((lc($canvote) eq 'notended') && (&discussion_open($status,$symb)))) {
90: return 1;
91: }
92: }
93:
94: sub get_realsymb {
95: my ($symb) = @_;
96: my $realsymb = $symb;
97: if ($symb=~/^bulletin___/) {
98: my $filename=(&Apache::lonnet::decode_symb($symb))[2];
99: $filename=~s{^adm/wrapper/}{};
100: $realsymb=&Apache::lonnet::symbread($filename);
101: }
102: return $realsymb;
103: }
104:
105: sub list_discussion {
106: my ($mode,$status,$ressymb,$imsextras,$group)=@_;
107: unless ($ressymb) { $ressymb=&Apache::lonnet::symbread(); }
108: unless ($ressymb) { return ''; }
109: $ressymb=&wrap_symb($ressymb);
110: my $outputtarget=$env{'form.grade_target'};
111: if (defined($env{'form.export'})) {
112: if($env{'form.export'}) {
113: $outputtarget = 'export';
114: }
115: }
116: if (defined($imsextras)) {
117: if ($$imsextras{'caller'} eq 'imsexport') {
118: $outputtarget = 'export';
119: }
120: }
121: my ($nofooter,$nodisclink,$nofdbklink);
122: if (not &discussion_visible($status)) {
123: if ($mode ne 'board') {
124: ($nofooter,$nodisclink,$nofdbklink) = &check_menucoll();
125: if ($nofooter || $nofdbklink) {
126: return '<br />';
127: } else {
128: &Apache::lonenc::check_encrypt(\$ressymb);
129: return '<br /><div class="LC_feedback_link">'.&send_message_link($ressymb)."</div>";
130: }
131: }
132: }
133: if ($group ne '' && $mode eq 'board') {
134: if (&check_group_priv($group,'vgb') ne 'ok') {
135: return '';
136: }
137: }
138:
139: unless ($outputtarget eq 'export') {
140: ($nofooter,$nodisclink,$nofdbklink) = &check_menucoll();
141: }
142:
143: unless ($nofooter) {
144: my ($blocked,$blocktext) =
145: &Apache::loncommon::blocking_status('boards');
146: if ($blocked) {
147: my $footer = '<br /><div class="LC_feedback_link">';
148: unless ($nodisclink) {
149: $footer .= '<span class="LC_feedback_link">'.$blocktext.'</span>';
150: }
151: &Apache::lonenc::check_encrypt(\$ressymb);
152: if ($mode ne 'board') {
153: unless ($nofdbklink) {
154: $footer.=&send_message_link($ressymb);
155: }
156: }
157: $footer.='</div>';
158: return $footer;
159: }
160: }
161:
162: my @bgcols = ("LC_disc_old_item","LC_disc_new_item");
163: my $discussiononly=0;
164: if ($mode eq 'board') { $discussiononly=1; }
165: unless ($env{'request.course.id'}) { return ''; }
166: my $crs='/'.$env{'request.course.id'};
167: my $cid=$env{'request.course.id'};
168: if ($env{'request.course.sec'}) {
169: $crs.='_'.$env{'request.course.sec'};
170: }
171: $crs=~s/\_/\//g;
172: my $encsymb=&Apache::lonenc::check_encrypt($ressymb);
173: my $viewgrades=(&Apache::lonnet::allowed('vgr',$crs)
174: && ($ressymb=~/$LONCAPA::assess_re/));
175:
176: my %usernamesort = ();
177: my %namesort =();
178: my %subjectsort = ();
179:
180: # Get discussion display settings for this discussion
181: my $lastkey = $ressymb.'_lastread';
182: my $showkey = $ressymb.'_showonlyunread';
183: my $markkey = $ressymb.'_showonlyunmark',
184: my $visitkey = $ressymb.'_visit';
185: my $ondispkey = $ressymb.'_markondisp';
186: my $userpickkey = $ressymb.'_userpick';
187: my $toggkey = $ressymb.'_readtoggle';
188: my $readkey = $ressymb.'_read';
189: $ressymb=$encsymb;
190: my %dischash = &Apache::lonnet::get('nohist_'.$cid.'_discuss',[$lastkey,$showkey,$markkey,$visitkey,$ondispkey,$userpickkey,$toggkey,$readkey],$env{'user.domain'},$env{'user.name'});
191: my %discinfo = ();
192: my $showonlyunread = 0;
193: my $showunmark = 0;
194: my $markondisp = 0;
195: my $prevread = 0;
196: my $previous = 0;
197: my $visit = 0;
198: my $newpostsflag = 0;
199: my @posters = split(/\&/,$dischash{$userpickkey});
200:
201: # Retain identification of "NEW" posts identified in last display, if continuing 'previous' browsing of posts.
202: &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['previous','sortposts','rolefilter','statusfilter','sectionpick','grouppick','totposters']);
203: my $sortposts = $env{'form.sortposts'};
204: my $statusfilter = $env{'form.statusfilter'};
205: my @sectionpick = split(/,/,$env{'form.sectionpick'});
206: my @grouppick = split(/,/,$env{'form.grouppick'});
207: my @rolefilter = split(/,/,$env{'form.rolefilter'});
208:
209: my $totposters = $env{'form.totposters'};
210: $previous = $env{'form.previous'};
211: if ($previous > 0) {
212: $prevread = $previous;
213: } elsif (defined($dischash{$lastkey})) {
214: unless ($dischash{$lastkey} eq '') {
215: $prevread = $dischash{$lastkey};
216: }
217: }
218:
219: my $cdom = $env{'course.'.$cid.'.domain'};
220: my $cnum = $env{'course.'.$cid.'.num'};
221: my $crstype = &Apache::loncommon::course_type();
222:
223: # Get information about students and non-students in course for filtering display of posts
224: my %roleshash = ();
225: my %roleinfo = ();
226: my ($classgroups,$studentgroups);
227: if ($env{'form.rolefilter'}) {
228: %roleshash = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
229: foreach my $rolekey (keys(%roleshash)) {
230: my ($role,$uname,$udom,$sec) = split(/:/,$rolekey);
231: if ($role =~ /^cr/) {
232: $role = 'cr';
233: }
234: my ($end,$start) = split(/:/,$roleshash{$rolekey});
235: my $now = time;
236: my $status = 'Active';
237: if (($now < $start) || ($end > 0 && $now > $end)) {
238: $status = 'Expired';
239: }
240: if ($uname && $udom) {
241: push(@{$roleinfo{$uname.':'.$udom}}, $role.':'.$sec.':'.$status);
242: }
243: }
244: my ($classlist,$keylist) =
245: &Apache::loncoursedata::get_classlist($cdom,$cnum);
246: my $sec_index = &Apache::loncoursedata::CL_SECTION();
247: my $status_index = &Apache::loncoursedata::CL_STATUS();
248: while (my ($student,$data) = each %$classlist) {
249: my ($section,$status) = ($data->[$sec_index],
250: $data->[$status_index]);
251: push(@{$roleinfo{$student}}, 'st:'.$section.':'.$status);
252: }
253: ($classgroups,$studentgroups) =
254: &Apache::loncoursedata::get_group_memberships($classlist,$keylist,
255: $cdom,$cnum);
256: }
257:
258: # Get discussion display default settings for user
259: if ($env{'environment.discdisplay'} eq 'unread') {
260: $showonlyunread = 1;
261: }
262: if ($env{'environment.discmarkread'} eq 'ondisp') {
263: $markondisp = 1;
264: }
265:
266: # Override user's default if user specified display setting for this discussion
267: if (defined($dischash{$ondispkey})) {
268: unless ($dischash{$ondispkey} eq '') {
269: $markondisp = $dischash{$ondispkey};
270: }
271: }
272: if ($markondisp) {
273: $discinfo{$lastkey} = time;
274: }
275:
276: if (defined($dischash{$showkey})) {
277: unless ($dischash{$showkey} eq '') {
278: $showonlyunread = $dischash{$showkey};
279: }
280: }
281:
282: if (defined($dischash{$markkey})) {
283: unless ($dischash{$markkey} eq '') {
284: $showunmark = $dischash{$markkey};
285: }
286: }
287:
288: if (defined($dischash{$visitkey})) {
289: unless ($dischash{$visitkey} eq '') {
290: $visit = $dischash{$visitkey};
291: }
292: }
293: $visit ++;
294:
295: my $seeid;
296: if (&Apache::lonnet::allowed('rin',$env{'request.course.id'}.($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:''))) {
297: $seeid = 1;
298: }
299: my $seehidden = &can_see_hidden($mode,$ressymb,undef,$group,$cdom,$cnum,$crs);
300:
301: # Is voting on discussions available
302: my $realsymb = &get_realsymb($ressymb);
303: my $canvote = &discussion_vote_available($status,$realsymb);
304:
305: my @discussionitems=();
306: my %shown = ();
307: my @posteridentity=();
308:
309: my $current=0;
310: my $visible=0;
311: my @depth=();
312: my @replies = ();
313: my %alldiscussion=();
314: my %imsitems=();
315: my %imsfiles=();
316: my %notshown = ();
317: my %newitem = ();
318: my $maxdepth=0;
319: my %anonhash=();
320: my $anoncnt=0;
321:
322: my $now = time;
323: $discinfo{$visitkey} = $visit;
324:
325: &Apache::lonnet::put('nohist_'.$cid.'_discuss',\%discinfo,$env{'user.domain'},$env{'user.name'});
326: &build_posting_display(\%usernamesort,\%subjectsort,\%namesort,\%notshown,\%newitem,\%dischash,\%shown,\%alldiscussion,\%imsitems,\%imsfiles,\%roleinfo,\@discussionitems,\@replies,\@depth,\@posters,\$maxdepth,\$visible,\$newpostsflag,\$current,$status,$viewgrades,$seeid,$seehidden,$canvote,$prevread,$sortposts,$encsymb,$readkey,$showunmark,$showonlyunread,$totposters,\@rolefilter,\@sectionpick,\@grouppick,$classgroups,$statusfilter,$toggkey,$outputtarget,\%anonhash,$anoncnt,$group);
327:
328: my $discussion='';
329: my $manifestfile;
330: my $manifestok=0;
331: my $tempexport;
332: my $imsresources;
333: my $copyresult;
334:
335: my $function = &Apache::loncommon::get_users_function();
336: my %lt = &Apache::lonlocal::texthash(
337: 'cuse' => 'My settings for this discussion',
338: 'allposts' => 'All posts',
339: 'unread' => 'New posts only',
340: 'unmark' => 'Unread only',
341: 'ondisp' => 'Once displayed',
342: 'onmark' => 'Once marked not NEW',
343: 'toggoff' => 'Off',
344: 'toggon' => 'On',
345: 'disa' => 'Posts to be displayed',
346: 'npce' => 'Posts cease to be marked "NEW"',
347: 'epcb' => 'Each post can be toggled read/unread',
348: 'chgt' => 'Change',
349: 'disp' => 'Display',
350: 'nolo' => 'Not new',
351: 'togg' => 'Toggle read/unread',
352: 'aner' => 'An error occurred opening the manifest file.',
353: 'difo' => 'Discussion for',
354: 'aerr' => 'An error occurred opening the export file for posting',
355: 'discussions' => 'DISCUSSIONS'
356: );
357: my %js_lt = &Apache::lonlocal::texthash(
358: 'aysu' => 'Are you sure you want to delete this post?',
359: 'dpwn' => 'Deleted posts will no longer be visible to you and other students',
360: 'bwco' => 'but will continue to be visible to your instructor',
361: 'depo' => 'Deleted posts will no longer be visible to you or anyone else.',
362: );
363: &js_escape(\%js_lt);
364:
365: my $currdisp = $lt{'allposts'};
366: my $currmark = $lt{'onmark'};
367: my $currtogg = $lt{'toggoff'};
368: my $dispchange = $lt{'unread'};
369: my $markchange = $lt{'ondisp'};
370: my $toggchange = $lt{'toggon'};
371: my $chglink = '/adm/feedback?modifydisp='.$ressymb;
372: my $displinkA = 'onlyunread';
373: my $displinkB = 'onlyunmark';
374: my $marklink = 'markondisp';
375: my $togglink = 'toggon';
376:
377: if ($markondisp) {
378: $currmark = $lt{'ondisp'};
379: $markchange = $lt{'onmark'};
380: $marklink = 'markonread';
381: }
382:
383: if ($showonlyunread) {
384: $currdisp = $lt{'unread'};
385: $dispchange = $lt{'allposts'};
386: $displinkA = 'allposts';
387: }
388:
389: if ($showunmark) {
390: $currdisp = $lt{'unmark'};
391: $dispchange = $lt{'unmark'};
392: $displinkA='allposts';
393: $displinkB='onlyunread';
394: $showonlyunread = 0;
395: }
396:
397: if ($dischash{$toggkey}) {
398: $currtogg = $lt{'toggon'};
399: $toggchange = $lt{'toggoff'};
400: $togglink = 'toggoff';
401: }
402:
403: $chglink .= '&changes='.$displinkA.'_'.$displinkB.'_'.$marklink.'_'.$togglink;
404:
405: if ($newpostsflag) {
406: $chglink .= '&previous='.$prevread;
407: }
408: $chglink.=&group_args($group);
409:
410: if ($visible) {
411: # Print the discusssion
412: if ($outputtarget eq 'tex') {
413: $discussion.='<tex>{\tiny \vskip 0 mm\noindent\makebox[2 cm][b]{\hrulefill}'.
414: '\textbf{'.$lt{'discussions'}.'}\makebox[2 cm][b]{\hrulefill}\vskip 0 mm'.
415: '\noindent\textbf{'.$lt{'disa'}.'}: \textit{'.$currdisp.'}\vskip 0 mm'.
416: '\noindent\textbf{'.$lt{'npce'}.'}: \textit{'.$currmark.'}}</tex>';
417: } elsif ($outputtarget eq 'export') {
418: # Create temporary directory if this is an export
419: my $now = time;
420: if ((defined($imsextras)) && ($$imsextras{'caller'} eq 'imsexport')) {
421: $tempexport = $$imsextras{'tempexport'};
422: if (!-e $tempexport) {
423: mkdir($tempexport,0700);
424: }
425: $tempexport .= '/'.$$imsextras{'count'};
426: if (!-e $tempexport) {
427: mkdir($tempexport,0700);
428: }
429: } else {
430: $tempexport = $Apache::lonnet::perlvar{'lonDaemons'}.'/tmp/ims_exports';
431: if (!-e $tempexport) {
432: mkdir($tempexport,0700);
433: }
434: $tempexport .= '/'.$now;
435: if (!-e $tempexport) {
436: mkdir($tempexport,0700);
437: }
438: $tempexport .= '/'.$env{'user.domain'}.'_'.$env{'user.name'};
439: }
440: if (!-e $tempexport) {
441: mkdir($tempexport,0700);
442: }
443: # open manifest file
444: my $manifest = '/imsmanifest.xml';
445: my $manifestfilename = $tempexport.$manifest;
446: if ($manifestfile = Apache::File->new('>'.$manifestfilename)) {
447: $manifestok=1;
448: print $manifestfile qq|<?xml version="1.0" encoding="UTF-8"?>
449: <manifest xmlns="http://www.imsglobal.org/xsd/imscp_v1p1" xmlns:imsmd="http://www.imsglobal.org/xsd/imsmd_v1p2"
450: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
451: identifier="MANIFEST-$ressymb" xsi:schemaLocation="http://www.imsglobal.org/xsd/imscp_v1p1
452: imscp_v1p1.xsd http://www.imsglobal.org/xsd/imsmd_v1p2 imsmd_v1p2p2.xsd">
453: <organizations default="$ressymb">
454: <organization identifier="$ressymb">
455: <title>$lt{'difo'} $ressymb</title>\n|;
456: } else {
457: $discussion .= $lt{'aner'}.'<br />';
458: }
459: } else {
460: my $colspan=$maxdepth+1;
461: $discussion.= &Apache::lonhtmlcommon::scripttag(qq|
462: function verifydelete (caller,symb,idx,newflag,previous,groupparm) {
463: var symbparm = symb+':::'+idx
464: var prevparm = ""
465: if (newflag == 1) {
466: prevparm = "&previous="+previous
467: }
468: if (caller == 'studentdelete') {
469: if (confirm("$js_lt{'aysu'}\\n$js_lt{'dpwn'},\\n$js_lt{'bwco'}")) {
470: document.location.href = "/adm/feedback?hide="+symbparm+prevparm+groupparm
471: }
472: } else {
473: if (caller == 'seeiddelete') {
474: if (confirm("$js_lt{'aysu'}\\n$js_lt{'depo'}")) {
475: document.location.href = "/adm/feedback?deldisc="+symbparm+prevparm+groupparm
476: }
477: }
478: }
479: }
480: |);
481: $discussion.='<form name="readchoices" method="post" action="/adm/feedback?chgreads='.$ressymb.'" >'.
482: "\n".'<table width="100%" class="LC_discussion">';
483: $discussion .= &action_links_bar($colspan,$ressymb,$visible,
484: $newpostsflag,$group,
485: $prevread,$markondisp,$seehidden);
486: my $escsymb=&escape($ressymb);
487: my $numhidden = keys(%notshown);
488: if ($numhidden > 0) {
489: my $colspan = $maxdepth+1;
490: $discussion.="\n".'<tr><td bgcolor="#CCCCCC" colspan="'.$colspan.'">';
491: my $href = '/adm/feedback?allposts=1&symb='.$escsymb;
492: if ($newpostsflag) {
493: $href .= '&previous='.$prevread;
494: }
495: $href .= &group_args($group);
496: if ($showunmark) {
497: $discussion .= &mt('[_1]Show all posts[_2] to display [quant,_3,post] previously marked read',
498: '<a href="'.$href.'">','</a>',$numhidden);
499: } else {
500: $discussion .= &mt('[_1]Show all posts[_2] to display [quant,_3,post] previously viewed',
501: '<a href="'.$href.'">','</a>',$numhidden);
502: }
503: $discussion .= '<br/></td></tr>';
504: }
505: }
506:
507: # Choose sort mechanism
508: my @showposts = ();
509: if ($sortposts eq 'descdate') {
510: @showposts = (sort { $b <=> $a } keys(%alldiscussion));
511: } elsif ($sortposts eq 'thread') {
512: @showposts = (sort { $a <=> $b } keys(%alldiscussion));
513: } elsif ($sortposts eq 'subject') {
514: foreach my $key (sort(keys(%subjectsort))) {
515: push(@showposts, @{$subjectsort{$key}});
516: }
517: } elsif ($sortposts eq 'username') {
518: foreach my $domain (sort(keys(%usernamesort))) {
519: foreach my $key (sort(keys(%{$usernamesort{$domain}}))) {
520: push(@showposts, @{$usernamesort{$domain}{$key}});
521: }
522: }
523: } elsif ($sortposts eq 'lastfirst') {
524: foreach my $last (sort(keys(%namesort))) {
525: foreach my $key (sort(keys(%{$namesort{$last}}))) {
526: push(@showposts, @{$namesort{$last}{$key}});
527: }
528: }
529: } else {
530: @showposts = (sort { $a <=> $b } keys(%alldiscussion));
531: }
532: my $currdepth = 0;
533: my $firstidx = $alldiscussion{$showposts[0]};
534: foreach my $post (@showposts) {
535: unless (($sortposts eq 'thread') ||
536: (($sortposts eq '') && (!$env{'environment.unthreadeddiscussion'})) ||
537: ($outputtarget eq 'export')) {
538: $alldiscussion{$post} = $post;
539: }
540: unless ( ($notshown{$alldiscussion{$post}} eq '1') || ($shown{$alldiscussion{$post}} == 0) ) {
541: if ($outputtarget ne 'tex' && $outputtarget ne 'export') {
542: $discussion.="\n<tr>";
543: }
544: my $thisdepth=$depth[$alldiscussion{$post}];
545: if ($outputtarget ne 'tex' && $outputtarget ne 'export') {
546: for (1..$thisdepth) {
547: $discussion.='<td> </td>';
548: }
549: }
550: my $colspan=$maxdepth-$thisdepth+1;
551: if ($outputtarget eq 'tex') {
552: #cleanup block
553: $discussionitems[$alldiscussion{$post}]=~s/<table([^>]*)>/<table TeXwidth="90 mm">/;
554: $discussionitems[$alldiscussion{$post}]=~s/<tr([^>]*)><td([^>]*)>/<tr><td TeXwidth="20 mm" align="left">/;
555: my $threadinsert='';
556: if ($thisdepth > 0) {
557: $threadinsert='<br /><strong>Reply: '.$thisdepth.'</strong>';
558: }
559: $discussionitems[$alldiscussion{$post}]=~s/<\/td><td([^>]*)>/$threadinsert<\/td><td TeXwidth="65 mm" align="left">/;
560: $discussionitems[$alldiscussion{$post}]=~s/(<b>|<\/b>|<\/a>|<a([^>]+)>)//g;
561:
562: $discussionitems[$alldiscussion{$post}]='<tex>\vskip 0 mm\noindent\makebox[2 cm][b]{\hrulefill}</tex>'.$discussionitems[$alldiscussion{$post}];
563: $discussion.=$discussionitems[$alldiscussion{$post}];
564: } elsif ($outputtarget eq 'export') {
565: my $postfilename = $alldiscussion{$post}.'-'.$imsitems{$alldiscussion{$post}}{'timestamp'}.'.html';
566: if ($manifestok) {
567: if (($depth[$alldiscussion{$post}] <= $currdepth) && ($alldiscussion{$post} != $firstidx)) {
568: print $manifestfile ' </item>'."\n";
569: }
570: $currdepth = $depth[$alldiscussion{$post}];
571: print $manifestfile "\n".
572: '<item identifier="ITEM-'.$ressymb.'-'.$alldiscussion{$post}.'" isvisible="'.
573: $imsitems{$alldiscussion{$post}}{'isvisible'}.'" identifieref="RES-'.$ressymb.'-'.$alldiscussion{$post}.'">'.
574: '<title>'.$imsitems{$alldiscussion{$post}}{'title'}.'</title></item>';
575: $imsresources .= "\n".
576: '<resource identifier="RES-'.$ressymb.'-'.$alldiscussion{$post}.'" type="webcontent" href="'.$postfilename.'">'."\n".
577: '<file href="'.$postfilename.'">'."\n".
578: $imsfiles{$alldiscussion{$post}}{$imsitems{$alldiscussion{$post}}{'currversion'}}.'</file>'."\n".
579: '</resource>';
580: }
581: my $postingfile;
582: my $postingfilename = $tempexport.'/'.$postfilename;
583: if ($postingfile = Apache::File->new('>'.$postingfilename)) {
584: print $postingfile '<html><head><title>'.&mt('Discussion Post').'</title></head><body>'.
585: $imsitems{$alldiscussion{$post}}{'title'}.' '.
586: $imsitems{$alldiscussion{$post}}{'sender'}.
587: $imsitems{$alldiscussion{$post}}{'timestamp'}.'<br /><br />'.
588: $imsitems{$alldiscussion{$post}}{'message'}.'<br />'.
589: $imsitems{$alldiscussion{$post}}{'attach'}.'</body></html>'."\n";
590: close($postingfile);
591: } else {
592: $discussion .= $lt{'aerr'}.' '.$alldiscussion{$post}.'<br />';
593: }
594: $copyresult.=&replicate_attachments($imsitems{$alldiscussion{$post}}{'allattachments'},$tempexport);
595: } else {
596: $discussion.='<td class="'.$bgcols[$newitem{$alldiscussion{$post}}].
597: '" colspan="'.$colspan.'">'. $discussionitems[$alldiscussion{$post}].
598: '</td></tr>';
599: }
600: }
601: }
602: unless ($outputtarget eq 'tex' || $outputtarget eq 'export') {
603: my $colspan=$maxdepth+1;
604: $discussion .= <<END;
605: <tr>
606: <td colspan="$colspan">
607: <table width="100%">
608: <tr>
609: <td class="LC_disc_action_left">
610: <font size="-1"><b>$lt{'cuse'}</b>:
611: END
612: if ($newpostsflag) {
613: $discussion .=
614: '1. '.$lt{'disp'}.' - <i>'.$currdisp.'</i> 2. '.$lt{'nolo'}.' - <i>'.$currmark.'</i>';
615: if ($dischash{$toggkey}) {
616: $discussion .= ' 3. '.$lt{'togg'}.' - <i>'.$currtogg.'</i>';
617: }
618: } else {
619: if ($dischash{$toggkey}) {
620: $discussion .= '1. '.$lt{'disp'}.' - <i>'.$currdisp.'</i> 2. '.$lt{'togg'}.' - <i>'.$currtogg.'</i>';
621: } else {
622: $discussion .=
623: $lt{'disp'}.' - <i>'.$currdisp.'</i>';
624: }
625: }
626: $discussion .= <<END;
627: <b><a href="$chglink">$lt{'chgt'}</a></b></font>
628: </td>
629: END
630: if ($sortposts) {
631: my %sort_types = ();
632: my %role_types = ();
633: my %status_types = ();
634: &sort_filter_names(\%sort_types,\%role_types,\%status_types,$crstype);
635:
636: $discussion .= '<td class="LC_disc_action_right"><font size="-1"><b>'.&mt('Sorted by').'</b>: '.$sort_types{$sortposts}.'<br />';
637: if (defined($env{'form.totposters'})) {
638: $discussion .= &mt('Posts by').':';
639: if ($totposters > 0) {
640: foreach my $poster (@posters) {
641: $discussion .= ' '.$poster.',';
642: }
643: $discussion =~ s/,$//;
644: } else {
645: $discussion .= &mt('None selected');
646: }
647: } else {
648: my $filterchoice ='';
649: if (@sectionpick > 0) {
650: $filterchoice = '<i>'.&mt('sections').'</i>- '.$env{'form.sectionpick'};
651: $filterchoice .= ' ';
652: }
653: if (@grouppick > 0) {
654: $filterchoice = '<i>'.&mt('groups').'</i>- '.$env{'form.grouppick'};
655: $filterchoice .= ' ';
656: }
657: if (@rolefilter > 0) {
658: $filterchoice .= '<i>'.&mt('roles').'</i>-';
659: foreach my $role (@rolefilter) {
660: $filterchoice .= ' '.$role_types{$role}.',';
661: }
662: $filterchoice =~ s/,$//;
663: $filterchoice .= '<br />'.(' ' x8);
664: }
665: if ($statusfilter) {
666: $filterchoice .= '<i>'.&mt('status').'</i>- '.$status_types{$statusfilter};
667: }
668: if ($filterchoice) {
669: $discussion .= '<b>'.&mt('Filters').'</b>: '.$filterchoice;
670: }
671: }
672: $discussion .= '</font></td>';
673:
674: }
675: if ($dischash{$toggkey}) {
676: my $storebutton = &mt('Save read/unread changes');
677: $discussion.='<td align="right">'.
678: '<input type="hidden" name="discsymb" value="'.$ressymb.'" />'."\n".
679: '<input type="button" name="readoptions" value="'.$storebutton.'"'.
680: ' onclick="this.form.submit();" />'."\n".
681: '</td>';
682: }
683: $discussion .= (<<END);
684: </tr>
685: </table>
686: </td>
687: </tr>
688: END
689: $discussion .= &action_links_bar($colspan,$ressymb,$visible,
690: $newpostsflag,$group,
691: $prevread,$markondisp,$seehidden);
692: $discussion .= "</table></form>\n";
693: }
694: if ($outputtarget eq 'export') {
695: if ($manifestok) {
696: while ($currdepth > 0) {
697: print $manifestfile " </item>\n";
698: $currdepth --;
699: }
700: print $manifestfile qq|
701: </organization>
702: </organizations>
703: <resources>
704: $imsresources
705: </resources>
706: </manifest>
707: |;
708: close($manifestfile);
709: if ((defined($imsextras)) && ($$imsextras{'caller'} eq 'imsexport')) {
710: $discussion = $copyresult;
711: } else {
712:
713: #Create zip file in prtspool
714:
715: if (($env{'user.name'} =~ /^$match_username$/)
716: && ($env{'user.domain'} =~ /^$match_domain$/)) {
717: my $now = time();
718: my $imszipfile = '/prtspool/'.
719: join('_',$env{'user.name'},$env{'user.domain'},$now).
720: '_'.rand(1000000000).'.zip';
721: my $zip = Archive::Zip->new();
722: $zip->addTree($tempexport);
723: my $imszip = '/home/httpd/'.$imszipfile;
724: if ($zip->writeToFileNamed($imszip) == AZ_OK) {
725: $discussion .= &mt('Download the zip file from [_1]Discussion Posting Archive[_2]',
726: '<a href="'.$imszipfile.'">','</a>').'<br />';
727: } else {
728: $discussion .= &mt('Failed to create zip file').'<br />';
729: }
730: if ($copyresult) {
731: $discussion .= '<span class="LC_error">'.
732: &mt('The following errors occurred during export:').
733: '</span><br />'.$copyresult;
734: }
735: } else {
736: $discussion .= '<p class="LC_error">'.
737: &mt('Unfortunately you will not be able to retrieve an archive of the discussion posts at this time, because there was a problem creating the zip file.').'</p>';
738: }
739: }
740: } else {
741: $discussion .= '<p class="LC_error">'.
742: &mt('Unfortunately you will not be able to retrieve an archive of the discussion posts at this time, because there was a problem creating a manifest file.').'</p>';
743: }
744: return $discussion;
745: }
746: }
747: if ($discussiononly) {
748: my $now = time;
749: my $attachnum = 0;
750: my $currnewattach = [];
751: my $currdelold = [];
752: my $comment = '';
753: my $subject = '';
754: if ($env{'form.origpage'}) {
755: &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['addnewattach','deloldattach','delnewattach','timestamp','idx','subject','comment']);
756: $subject = &unescape($env{'form.subject'});
757: $comment = &unescape($env{'form.comment'});
758: my @keepold = ();
759: &process_attachments($currnewattach,$currdelold,\@keepold);
760: if (@{$currnewattach} > 0) {
761: $attachnum += @{$currnewattach};
762: }
763: }
764: if ((&discussion_open($status)) && ($outputtarget ne 'tex')) {
765: if (($group ne '') && ($mode eq 'board')) {
766: if ((&check_group_priv($group,'pgd') eq 'ok') &&
767: ($ressymb =~ m{^bulletin___\d+___adm/wrapper/adm/\Q$cdom\E/\Q$cnum\E/\d+/bulletinboard$})) {
768: $discussion .=
769: &postingform_display($mode,$ressymb,$now,$subject,
770: $comment,$outputtarget,$attachnum,
771: $currnewattach,$currdelold,
772: $group,$crstype);
773: }
774: } else {
775: if (&Apache::lonnet::allowed('pch',$env{'request.course.id'}.
776: ($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:''))) {
777:
778: $discussion.=
779: &postingform_display($mode,$ressymb,$now,$subject,
780: $comment,$outputtarget,$attachnum,
781: $currnewattach,$currdelold,'',$crstype);
782: } else {
783: $discussion.= '<span class="LC_feedback_link">'.
784: &mt('This discussion is closed.').'</span>';
785: }
786: }
787: }
788: if (!(&discussion_open($status)) && ($outputtarget ne 'tex')) {
789: $discussion.= '<span class="LC_feedback_link">'.
790: &mt('This discussion is closed.').'</span>';
791: }
792: } elsif ($outputtarget ne 'tex') {
793: unless ($nofooter) {
794: $discussion.='<div class="LC_feedback_link">';
795: unless ($nodisclink) {
796: if (&discussion_open($status) &&
797: &Apache::lonnet::allowed('pch',
798: $env{'request.course.id'}.
799: ($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:''))) {
800: $discussion.= &send_feedback_link($ressymb);
801: if ($env{'request.role.adv'}) {
802: my $close = &Apache::lonnet::EXT('resource.0.discussend',$ressymb);
803: my $canvote = &Apache::lonnet::EXT('resource.0.discussvote',$ressymb);
804: if (defined($close) && $close ne '' && $close < time) {
805: if ($canvote eq 'notended') {
806: $discussion .= ' '.&mt('(Posting and voting closed for [_1] roles)',
807: &Apache::lonnet::plaintext('st',$crstype));
808: } else {
809: $discussion .= ' '.&mt('(Closed for [_1] roles)',
810: &Apache::lonnet::plaintext('st',$crstype));
811: }
812: }
813: }
814: } else {
815: $discussion.= '<span class="LC_feedback_link">'.&mt('This discussion is closed.').'</span>';
816: }
817: }
818: unless ($nofdbklink) {
819: $discussion.= &send_message_link($ressymb);
820: }
821: $discussion.='</div>';
822: }
823: }
824: return $discussion;
825: }
826:
827: sub check_menucoll {
828: my ($nofooter,$nodisclink,$nofdbklink);
829: my ($menucoll,$deeplinkmenu,$menuref) = &Apache::loncommon::menucoll_in_effect();
830: if ($menucoll) {
831: if (ref($menuref) eq 'HASH') {
832: if ($menuref->{'foot'} eq 'n') {
833: $nofooter = 1;
834: } else {
835: unless ($menuref->{'disc'}) {
836: $nodisclink = 1;
837: }
838: unless ($menuref->{'fdbk'}) {
839: $nofdbklink = 1;
840: }
841: }
842: }
843: }
844: return ($nofooter,$nodisclink,$nofdbklink);
845: }
846:
847: sub can_see_hidden {
848: my ($mode,$ressymb,$feedurl,$group,$cdom,$cnum,$crs) = @_;
849: my $seehidden;
850: if ($env{'request.course.id'}) {
851: unless ($cdom ne '' && $cnum ne '') {
852: $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
853: $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
854: }
855: if ($crs eq '') {
856: $crs = '/'.$env{'request.course.id'};
857: if ($env{'request.course.sec'}) {
858: $crs.='_'.$env{'request.course.sec'};
859: }
860: $crs=~s{_}{/}g;
861: }
862: if ($mode eq '') {
863: $mode='board';
864: if ($feedurl =~ /$LONCAPA::assess_re/) {
865: $mode='problem';
866: }
867: }
868: if (($group ne '') && ($mode eq 'board') &&
869: ($ressymb =~ m{^bulletin___\d+\Q___adm/wrapper/adm/$cdom/$cnum/\E\d+/bulletinboard$})) {
870: if (&check_group_priv($group,'dgp') eq 'ok') {
871: $seehidden = 1;
872: }
873: } else {
874: $seehidden=&Apache::lonnet::allowed('rin',$crs);
875: }
876: }
877: return $seehidden;
878: }
879:
880: sub discussion_link {
881: my ($ressymb,$linktext,$cmd,$item,$flag,$prev,$adds,$title)=@_;
882: my $link='/adm/feedback?inhibitmenu=yes&modal=yes&'.$cmd.'='.&escape($ressymb).':::'.$item;
883: if ($flag) { $link .= '&previous='.$prev; }
884: if ($adds) { $link .= $adds; }
885: my $width=600;
886: my $height=600;
887: if (($cmd eq 'hide') || ($cmd eq 'unhide') || ($cmd eq 'like') || ($cmd eq 'unlike')) {
888: $width=300;
889: $height=200;
890: }
891: return &Apache::loncommon::modal_link($link,$linktext,$width,$height,undef,undef,$title);
892: }
893:
894:
895: sub send_feedback_link {
896: my ($ressymb) = @_;
897: return '<span class="LC_feedback_link">'.
898: &discussion_link($ressymb,
899: '<img alt="" class="LC_noBorder" src="'.
900: &Apache::loncommon::lonhttpdurl('/adm/lonMisc/chat.gif').
901: '" /><span class="LC_menubuttons_inline_text">'.&mt('Post Discussion').'</span>',
902: 'replydisc').
903: '</span>';
904: }
905:
906: sub send_message_link {
907: my ($ressymb) = @_;
908: my $output = '<span class="LC_message_link">'.
909: &discussion_link($ressymb,
910: '<img alt="" class="LC_noBorder" src="'.
911: &Apache::loncommon::lonhttpdurl('/res/adm/pages/feedback.png').
912: '" /><span class="LC_menubuttons_inline_text">'.&mt('Send Feedback').'</span>',
913: 'sendmessageonly').
914: '</span>';
915: return $output;
916: }
917:
918: sub action_links_bar {
919: my ($colspan,$ressymb,$visible,$newpostsflag,$group,$prevread,$markondisp,
920: $seehidden) = @_;
921: my $discussion = '<tr><td colspan="'.$colspan.'">'.
922: '<table width="100%"><tr>'.
923: '<td class="LC_disc_action_left">';
924: my $escsymb=&escape($ressymb);
925: if ($visible) {
926: $discussion .= '<a href="/adm/feedback?cmd=threadedon&symb='.$escsymb;
927: if ($newpostsflag) {
928: $discussion .= '&previous='.$prevread;
929: }
930: $discussion .= &group_args($group);
931: $discussion .='">'.&mt('Threaded View').'</a> '.
932: '<a href="/adm/feedback?cmd=threadedoff&symb='.$escsymb;
933: if ($newpostsflag) {
934: $discussion .= '&previous='.$prevread;
935: }
936: $discussion .= &group_args($group);
937: $discussion .='">'.&mt('Chronological View').'</a> ';
938:
939: my $otherviewurl='/adm/feedback?cmd=sortfilter&symb='.$escsymb.'&inhibitmenu=yes&modal=yes';
940: if ($newpostsflag) {
941: $otherviewurl .= '&previous='.$prevread;
942: }
943: $otherviewurl .= &group_args($group);
944: $discussion .= &Apache::loncommon::modal_link($otherviewurl,&mt('Other Views ...'),800,340);
945: $discussion .= '<br />';
946: }
947: $discussion .='<a href="/adm/feedback?export='.$escsymb;
948: if ($newpostsflag) {
949: $discussion .= '&previous='.$prevread;
950: }
951: $discussion .= &group_args($group);
952: $discussion .= '">'.&mt('Export').'</a>';
953: if ($seehidden) {
954: $discussion .= ' ';
955: $discussion .='<a href="/adm/feedback?undeleteall='.$escsymb;
956: if ($newpostsflag) {
957: $discussion .= '&previous='.$prevread;
958: }
959: $discussion .= &group_args($group);
960: $discussion .= '">'.&mt('Undelete all deleted entries').'</a>';
961: }
962: $discussion.='</td>';
963: if ($newpostsflag) {
964: if (!$markondisp) {
965: $discussion .='<td class="LC_disc_action_right"><a href="/adm/preferences?action=changediscussions';
966: $discussion .= &group_args($group);
967: $discussion .= '">'.
968: &mt('My general preferences on what is marked as NEW').
969: '</a><br /><a href="/adm/feedback?markread=1&symb='.$escsymb;
970: $discussion .= &group_args($group);
971: $discussion .= '">'.&mt('Mark NEW posts no longer new').'</a></td>';
972: } else {
973: $discussion .= '<td> </td>';
974: }
975: } else {
976: $discussion .= '<td> </td>';
977: }
978: $discussion .= '</tr></table></td></tr>';
979: return $discussion;
980: }
981:
982: sub postingform_display {
983: my ($mode,$ressymb,$now,$subject,$comment,$outputtarget,$attachnum,
984: $currnewattach,$currdelold,$group,$crstype) = @_;
985: my $newattachmsg;
986: my %lt = &Apache::lonlocal::texthash(
987: 'note' => 'Note: in anonymous discussion, your name is visible only to course faculty',
988: 'title' => 'Title',
989: 'podi' => 'Post Discussion',
990: 'poan' => 'Post Anonymous Discussion',
991: 'newa' => 'New attachments',
992: );
993: if ($crstype eq 'Community') {
994: $lt{'note'} = &mt('Note: in anonymous discussion, your name is visible only to community facilitators');
995: }
996: my ($postingform,$textareaclass);
997: if (&Apache::lonhtmlcommon::htmlareabrowser()) {
998: $postingform = &Apache::lonhtmlcommon::htmlareaselectactive();
999: $textareaclass = 'class="LC_richDefaultOff"';
1000: if ($env{'request.course.id'}) {
1001: unless (($env{'course.'.$env{'request.course.id'}.'.allow_limited_html_in_feedback'} =~ /^\s*yes\s*$/i) || ($env{'form.sendmessageonly'})) {
1002: undef($textareaclass);
1003: }
1004: }
1005: }
1006: my $postanon;
1007: if (&Apache::lonnet::allowed('pac',$env{'request.course.id'}.
1008: ($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:''))) {
1009: $postanon = '<input type="submit" name="anondiscuss" value="'.$lt{'poan'}.'" />';
1010: }
1011: $postingform .= (<<ENDDISCUSS);
1012: <form action="/adm/feedback" method="post" name="mailform" enctype="multipart/form-data"> <input type="submit" name="discuss" value="$lt{'podi'}" />
1013: $postanon<input type="hidden" name="symb" value="$ressymb" />
1014: <input type="hidden" name="sendit" value="true" />
1015: <input type="hidden" name="timestamp" value="$now" />
1016: <a name="newpost"></a>
1017: <font size="1">$lt{'note'}</font><br />
1018: <b>$lt{'title'}:</b> <input type="text" name="subject" value="$subject" size="30" /><br />
1019: <textarea name="comment" cols="80" rows="14" id="comment" $textareaclass>$comment</textarea>
1020: ENDDISCUSS
1021: if ($env{'form.origpage'}) {
1022: $postingform .= '<input type="hidden" name="origpage" value="'.
1023: $env{'form.origpage'}.'" />'."\n";
1024: foreach my $att (@{$currnewattach}) {
1025: $postingform .= '<input type="hidden" name="currnewattach" '.
1026: 'value="'.$att.'" />'."\n";
1027: }
1028: }
1029: if (exists($env{'form.ref'})) {
1030: $postingform .= '<input type="hidden" name="ref" value="'.
1031: $env{'form.ref'}.'" />';
1032: }
1033: if ($group ne '') {
1034: $postingform .='<input type="hidden" name="group" value="'.$group.'" />';
1035: }
1036: my $blockblog = &Apache::loncommon::blocking_status('blogs');
1037: if (!$blockblog) {
1038: $postingform .= &add_blog_checkbox($crstype);
1039: }
1040: $postingform .= "</form>\n";
1041: $postingform .= &generate_attachments_button('',$attachnum,$ressymb,
1042: $now,$currnewattach,
1043: $currdelold,'',$mode,
1044: $blockblog);
1045: if ((ref($currnewattach) eq 'ARRAY') && (@{$currnewattach} > 0)) {
1046: $newattachmsg = '<br /><b>'.$lt{'newa'}.'</b><br />';
1047: if (@{$currnewattach} > 1) {
1048: $newattachmsg .= '<ol>';
1049: foreach my $item (@{$currnewattach}) {
1050: $item =~ m#.*/([^/]+)$#;
1051: $newattachmsg .= '<li><a href="'.$item.'">'.$1.'</a></li>'."\n";
1052: }
1053: $newattachmsg .= '</ol>'."\n";
1054: } else {
1055: $$currnewattach[0] =~ m#.*/([^/]+)$#;
1056: $newattachmsg .= '<a href="'.$$currnewattach[0].'">'.$1.'</a><br />'."\n";
1057: }
1058: }
1059: $postingform .= $newattachmsg;
1060: $postingform .= &generate_preview_button();
1061: return $postingform;
1062: }
1063:
1064: sub build_posting_display {
1065: my ($usernamesort,$subjectsort,$namesort,$notshown,$newitem,$dischash,$shown,$alldiscussion,$imsitems,$imsfiles,$roleinfo,$discussionitems,$replies,$depth,$posters,$maxdepth,$visible,$newpostsflag,$current,$status,$viewgrades,$seeid,$seehidden,$canvote,$prevread,$sortposts,$ressymb,$readkey,$showunmark,$showonlyunread,$totposters,$rolefilter,$sectionpick,$grouppick,$classgroups,$statusfilter,$toggkey,$outputtarget,$anonhash,$anoncnt,$group) = @_;
1066: my @original=();
1067: my @index=();
1068: my $skip_group_check = 0;
1069: my $symb=&Apache::lonenc::check_decrypt($ressymb);
1070: my $escsymb=&escape($ressymb);
1071: # These are the discussion contributions
1072: my %contrib=&Apache::lonnet::restore($symb,$env{'request.course.id'},
1073: $env{'course.'.$env{'request.course.id'}.'.domain'},
1074: $env{'course.'.$env{'request.course.id'}.'.num'});
1075: my (%likes,%userlikes,%userunlikes,@theselikes,$oneplus,$twoplus,$oneminus,$twominus);
1076: my $thisuser=$env{'user.name'}.':'.$env{'user.domain'};
1077: if ($seeid || $canvote) {
1078: # And these are the likes/unlikes
1079: %likes=&Apache::lonnet::dump('disclikes',
1080: $env{'course.'.$env{'request.course.id'}.'.domain'},
1081: $env{'course.'.$env{'request.course.id'}.'.num'},
1082: '^'.$symb.':');
1083: # Array with likes to figure out averages, etc.
1084: @theselikes=();
1085: # Hashes containing likes and unlikes for this user.
1086: %userlikes=();
1087: %userunlikes=();
1088: }
1089: # Is the user allowed to see the real name behind anonymous postings?
1090: my $see_anonymous =
1091: &Apache::lonnet::allowed('rin',$env{'request.course.id'}.($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:''));
1092:
1093: if ((@{$grouppick} == 0) || (grep(/^all$/,@{$grouppick}))) {
1094: $skip_group_check = 1;
1095: }
1096: # Deletions and hiddens are just lists. Split them up into a hash for quicker lookup
1097: my (%deletions,%hiddens);
1098: if ($contrib{'deleted'}) {
1099: my $deleted = $contrib{'deleted'};
1100: $deleted =~ s/^\.//;
1101: $deleted =~ s/\.$//;
1102: %deletions = map { $_ => 1 } (split(/\.\./,$deleted));
1103: }
1104: if ($contrib{'hidden'}) {
1105: my $hidden = $contrib{'hidden'};
1106: $hidden =~ s/^\.//;
1107: $hidden =~ s/\.$//;
1108: %hiddens = map { $_ => 1 } (split(/\.\./,$hidden));
1109: }
1110: # Versions if store/restore are used to actually store the messages.
1111: if ($contrib{'version'}) {
1112: my $oldest = $contrib{'1:timestamp'};
1113: if ($prevread eq '0') {
1114: $prevread = $oldest-1;
1115: }
1116: my ($skiptest,$rolematch,$roleregexp,$secregexp,$statusregexp);
1117: if ($sortposts) {
1118: ($skiptest,$roleregexp,$secregexp,$statusregexp) =
1119: &filter_regexp($rolefilter,$sectionpick,$statusfilter);
1120: $rolematch = $roleregexp.':'.$secregexp.':'.$statusregexp;
1121: }
1122: my %votestyle;
1123: if ($seeid || $canvote) {
1124: # We need to go through this twice, first to get the likes/dislikes, then to actually build the display
1125: for (my $id=1;$id<=$contrib{'version'};$id++) {
1126: my $idx=$id;
1127: next if ($contrib{$idx.':deleted'});
1128: next if ($contrib{$idx.':hidden'});
1129: unless ((($hiddens{$idx}) && (!$seehidden)) || ($deletions{$idx}) || (!$contrib{$idx.':message'})) {
1130: push(@theselikes,$likes{$symb.':'.$idx.':likes'});
1131: if ($likes{$symb.':'.$idx.':likes'} ne '') {
1132: if (ref($likes{$symb.':'.$idx.':likers'}) eq 'HASH') {
1133: if (exists($likes{$symb.':'.$idx.':likers'}{$thisuser})) {
1134: $userlikes{$idx} = 1;
1135: }
1136: }
1137: if (ref($likes{$symb.':'.$idx.':unlikers'}) eq 'HASH') {
1138: if (exists($likes{$symb.':'.$idx.':unlikers'}{$thisuser})) {
1139: $userunlikes{$idx} = 1;
1140: }
1141: }
1142: }
1143: }
1144: }
1145: # Figure out average likes and standard deviation if there are enough
1146: # discussions to warrant that
1147: my $ave=0;
1148: my $stddev=10000;
1149: if ($#theselikes>1) {
1150: my $sum=0;
1151: my $num=$#theselikes+1;
1152: foreach my $thislike (@theselikes) {
1153: $sum+=$thislike;
1154: }
1155: $ave=$sum/$num;
1156: my $sumsq=0;
1157: foreach my $thislike (@theselikes) {
1158: $sumsq+=($thislike-$ave)*($thislike-$ave);
1159: }
1160: $stddev=sqrt($sumsq/$num);
1161: }
1162: # Now we know the average likes $ave and the standard deviation $stddev
1163: # Get the boundaries for markup
1164: $oneplus=$ave+$stddev;
1165: $twoplus=$ave+2.*$stddev;
1166: $oneminus=$ave-$stddev;
1167: $twominus=$ave-2.*$stddev;
1168: if ($#theselikes>1) {
1169: foreach my $class ('twoplus','oneplus','zero','oneminus','twominus') {
1170: my $fontstyle = $env{'course.'.$env{'request.course.id'}.'.discussion_post_fonts_'.$class};
1171: if ($fontstyle ne '') {
1172: my ($size,$weight,$style,$other) = split(/,/,$fontstyle);
1173: if ($size ne '') {
1174: $votestyle{$class} .= 'font-size: '.$size.';';
1175: }
1176: if ($weight ne '') {
1177: $votestyle{$class} .= 'font-weight: '.$weight.';';
1178: }
1179: if ($style ne '') {
1180: $votestyle{$class} .= 'font-style: '.$style.';';
1181: }
1182: if ($other ne '') {
1183: $votestyle{$class} .= $other;
1184: }
1185: if ($votestyle{$class} ne '') {
1186: $votestyle{$class} = 'style="'.$votestyle{$class}.'"';
1187: }
1188: }
1189: }
1190: }
1191: }
1192: #
1193: # This is now the real loop. Go through all entries, pick up what we need
1194: #
1195: for (my $id=1;$id<=$contrib{'version'};$id++) {
1196: my $idx=$id;
1197: next if ($contrib{$idx.':deleted'});
1198: next if ($contrib{$idx.':hidden'});
1199: # If we get here, we are actually going to display the message - we don't know where and we don't know if we display
1200: # previous edits, but it counts as one entry
1201: my $posttime = $contrib{$idx.':timestamp'};
1202: if ($prevread <= $posttime) {
1203: $$newpostsflag = 1;
1204: }
1205: my $studenthidden=($contrib{'studenthidden'}=~/\.$idx\./);
1206: my $origindex='0.';
1207: my $numoldver=0;
1208: if ($contrib{$idx.':replyto'}) {
1209: if ( ((!$env{'environment.unthreadeddiscussion'}) && ($sortposts eq '')) ||
1210: ($sortposts eq 'thread') || ($outputtarget eq 'export')) {
1211: # this is a follow-up message
1212: $original[$idx]=$original[$contrib{$idx.':replyto'}];
1213: $$depth[$idx]=$$depth[$contrib{$idx.':replyto'}]+1;
1214: $origindex=$index[$contrib{$idx.':replyto'}];
1215: if ($$depth[$idx]>$$maxdepth) { $$maxdepth=$$depth[$idx]; }
1216: } else {
1217: $original[$idx]=0;
1218: $$depth[$idx]=0;
1219: }
1220: } else {
1221: # this is an original message
1222: $original[$idx]=0;
1223: $$depth[$idx]=0;
1224: }
1225: if ($$replies[$$depth[$idx]]) {
1226: $$replies[$$depth[$idx]]++;
1227: } else {
1228: $$replies[$$depth[$idx]]=1;
1229: }
1230: unless ((($hiddens{$idx}) && (!$seehidden)) || ($deletions{$idx})) {
1231: $$visible++;
1232: if ($contrib{$idx.':history'}) {
1233: if ($contrib{$idx.':history'} =~ /:/) {
1234: my @oldversions = split(/:/,$contrib{$idx.':history'});
1235: $numoldver = @oldversions;
1236: } else {
1237: $numoldver = 1;
1238: }
1239: }
1240: $$current = $numoldver;
1241: my %messages = ();
1242: my %subjects = ();
1243: my %attachtxt = ();
1244: my %allattachments = ();
1245: my ($screenname,$plainname,$showaboutme);
1246: my $sender = &mt('Anonymous');
1247: # Anonymous users getting number within a discussion
1248: # Since idx is in static order, this should give the same sequence every time.
1249: my $key=$contrib{$idx.':sendername'}.'@'.$contrib{$idx.':senderdomain'};
1250: unless ($$anonhash{$key}) {
1251: $anoncnt++;
1252: $$anonhash{$key}=&mt('Anonymous').' '.$anoncnt;
1253: }
1254: my ($message,$subject,$vgrlink,$ctlink);
1255: &get_post_contents(\%contrib,$idx,$seeid,$seehidden,$outputtarget,\%messages,\%subjects,\%allattachments,\%attachtxt,$imsfiles,\$screenname,\$plainname,\$showaboutme,$numoldver);
1256:
1257:
1258: # Set up for sorting by subject
1259: unless ($outputtarget eq 'export') {
1260: $message=$messages{$numoldver};
1261: $message.=$attachtxt{$numoldver};
1262: $subject=$subjects{$numoldver};
1263: if ($message) {
1264: if ($hiddens{$idx}) {
1265: $message='<font color="#888888">'.$message.'</font>';
1266: if ($studenthidden) {
1267: $message .='<br /><br />Deleted by poster (student).';
1268: }
1269: }
1270:
1271: if ($subject eq '') {
1272: if (defined($$subjectsort{'__No subject'})) {
1273: push(@{$$subjectsort{'__No subject'}}, $idx);
1274: } else {
1275: @{$$subjectsort{'__No subject'}} = ("$idx");
1276: }
1277: } else {
1278: if (defined($$subjectsort{$subject})) {
1279: push(@{$$subjectsort{$subject}}, $idx);
1280: } else {
1281: @{$$subjectsort{$subject}} = ("$idx");
1282: }
1283: }
1284: if (!$contrib{$idx.':anonymous'} || $see_anonymous) {
1285: if ($showaboutme) {
1286: $sender = &Apache::loncommon::aboutmewrapper(
1287: $plainname,
1288: $contrib{$idx.':sendername'},
1289: $contrib{$idx.':senderdomain'});
1290: } else {
1291: $sender = $plainname;
1292: }
1293: if ($see_anonymous) {
1294: $sender .= ' ('.$contrib{$idx.':sendername'}.':'.
1295: $contrib{$idx.':senderdomain'}.')';
1296: }
1297: $sender = '<b>'.$sender.'</b>';
1298: if ($contrib{$idx.':anonymous'}) {
1299: $sender.=' <font color="red"><b>['.$$anonhash{$key}.']</b></font> '.
1300: $screenname;
1301: }
1302: if ($see_anonymous) {
1303: $sender.=&Apache::loncommon::student_image_tag($contrib{$idx.':senderdomain'},$contrib{$idx.':sendername'});
1304: }
1305: # Set up for sorting by domain, then username
1306: unless (defined($$usernamesort{$contrib{$idx.':senderdomain'}})) {
1307: %{$$usernamesort{$contrib{$idx.':senderdomain'}}} = ();
1308: }
1309: if (defined($$usernamesort{$contrib{$idx.':senderdomain'}}{$contrib{$idx.':sendername'}})) {
1310: push(@{$$usernamesort{$contrib{$idx.':senderdomain'}}{$contrib{$idx.':sendername'}}}, $idx);
1311: } else {
1312: @{$$usernamesort{$contrib{$idx.':senderdomain'}}{$contrib{$idx.':sendername'}}} = ("$idx");
1313: }
1314: # Set up for sorting by last name, then first name
1315: my %names = &Apache::lonnet::get('environment',
1316: ['firstname','lastname'],$contrib{$idx.':senderdomain'},
1317: ,$contrib{$idx.':sendername'});
1318: my $lastname = $names{'lastname'};
1319: my $firstname = $names{'firstname'};
1320: if ($lastname eq '') {
1321: $lastname = '_';
1322: }
1323: if ($firstname eq '') {
1324: $firstname = '_';
1325: }
1326: unless (defined($$namesort{$lastname})) {
1327: %{$$namesort{$lastname}} = ();
1328: }
1329: if (defined($$namesort{$lastname}{$firstname})) {
1330: push(@{$$namesort{$lastname}{$firstname}}, $idx);
1331: } else {
1332: @{$$namesort{$lastname}{$firstname}} = ("$idx");
1333: }
1334: if ($outputtarget ne 'tex') {
1335: # Add karma stars
1336: my $karma=&userkarma($contrib{$idx.':sendername'},$contrib{$idx.':senderdomain'});
1337: for (my $i=1;$i<=$karma;$i++) {
1338: $sender.='<img border="0" src="/res/adm/pages/star.gif" alt="'.&mt('Contributor Kudos').'" />';
1339: }
1340: # Can people edit this?
1341: if (&editing_allowed($escsymb.':::'.$idx,$group)) {
1342: if (($env{'user.domain'} eq $contrib{$idx.':senderdomain'}) && ($env{'user.name'} eq $contrib{$idx.':sendername'})) {
1343: $sender.=' '.
1344: &discussion_link($ressymb,&mt('Edit'),'editdisc',$idx,$$newpostsflag,$prevread,&group_args($group));
1345: unless ($seehidden) {
1346: my $grpargs = &group_args($group);
1347: $sender.=" <a href=\"javascript:verifydelete('studentdelete','$escsymb','$idx','$$newpostsflag','$prevread','$grpargs')";
1348: $sender .= '">'.&mt('Delete').'</a>';
1349: }
1350: }
1351: }
1352: if ($seehidden) {
1353: if ($hiddens{$idx}) {
1354: unless ($studenthidden) {
1355: $sender.=' '.
1356: &discussion_link($ressymb,&mt('Make Visible'),'unhide',$idx,$$newpostsflag,$prevread,&group_args($group));
1357: }
1358: } else {
1359: $sender.=' '.
1360: &discussion_link($ressymb,&mt('Hide'),'hide',$idx,$$newpostsflag,$prevread,&group_args($group));
1361: }
1362: my $grpargs = &group_args($group);
1363: $sender.=
1364: " <a href=\"javascript:verifydelete('seeiddelete','$escsymb','$idx','$$newpostsflag','$prevread','$grpargs')\">";
1365: $sender .= &mt('Delete').'</a>';
1366: }
1367: }
1368: } else {
1369: if ($screenname) {
1370: $sender='<i>'.$screenname.'</i>';
1371: } else {
1372: $sender='<i>'.$$anonhash{$key}.'</i>';
1373: }
1374: $sender = '<b>'.$sender.'</b>';
1375: # Set up for sorting by domain, then username for anonymous
1376: unless (defined($$usernamesort{'__anon'})) {
1377: %{$$usernamesort{'__anon'}} = ();
1378: }
1379: if (defined($$usernamesort{'__anon'}{'__anon'})) {
1380: push(@{$$usernamesort{'__anon'}{'__anon'}}, $idx);
1381: } else {
1382: @{$$usernamesort{'__anon'}{'__anon'}} = ("$idx");
1383: }
1384: # Set up for sorting by last name, then first name for anonymous
1385: unless (defined($$namesort{'__anon'})) {
1386: %{$$namesort{'__anon'}} = ();
1387: }
1388: if (defined($$namesort{'__anon'}{'__anon'})) {
1389: push(@{$$namesort{'__anon'}{'__anon'}}, $idx);
1390: } else {
1391: @{$$namesort{'__anon'}{'__anon'}} = ("$idx");
1392: }
1393: }
1394: if ($outputtarget ne 'tex') {
1395: if (&discussion_open($status)) {
1396: if (($group ne '') &&
1397: (&check_group_priv($group,'pgd') eq 'ok')) {
1398: $sender.=' '.
1399: &discussion_link($ressymb,&mt('Reply'),'replydisc',$idx,$$newpostsflag,$prevread,&group_args($group));
1400: } elsif (&Apache::lonnet::allowed('pch',
1401: $env{'request.course.id'}.
1402: ($env{'request.course.sec'}?'/'.
1403: $env{'request.course.sec'}:''))) {
1404: $sender.=' '.
1405: &discussion_link($ressymb,&mt('Reply'),'replydisc',$idx,$$newpostsflag,$prevread);
1406: }
1407: }
1408: if ($viewgrades) {
1409: $vgrlink=&Apache::loncommon::submlink(&mt('Submissions'),
1410: $contrib{$idx.':sendername'},$contrib{$idx.':senderdomain'},$ressymb);
1411: }
1412: if ($$dischash{$readkey}=~/\.$idx\./) {
1413: $ctlink = '<label><b>'.&mt('Mark unread').'?</b> <input type="checkbox" name="postunread_'.$idx.'" /></label>';
1414: } else {
1415: $ctlink = '<label><b>'.&mt('Mark read').'?</b> <input type="checkbox" name="postread_'.$idx.'" /></label>';
1416: }
1417: }
1418: }
1419: #figure out at what position this needs to print
1420: }
1421: if ($outputtarget eq 'export' || $message) {
1422: my $thisindex=$idx;
1423: if ( ((!$env{'environment.unthreadeddiscussion'}) && ($sortposts eq '')) ||
1424: ($sortposts eq 'thread') || ($outputtarget eq 'export')) {
1425: $thisindex=$origindex.substr('00'.$$replies[$$depth[$idx]],-2,2);
1426: }
1427: $$alldiscussion{$thisindex}=$idx;
1428: $$shown{$idx} = 0;
1429: $index[$idx]=$thisindex;
1430: }
1431: if ($outputtarget eq 'export') {
1432: %{$$imsitems{$idx}} = ();
1433: $$imsitems{$idx}{'isvisible'}='true';
1434: if ($hiddens{$idx}) {
1435: $$imsitems{$idx}{'isvisible'}='false';
1436: }
1437: $$imsitems{$idx}{'title'}=$subjects{$numoldver};
1438: $$imsitems{$idx}{'message'}=$messages{$numoldver};
1439: $$imsitems{$idx}{'attach'}=$attachtxt{$numoldver};
1440: $$imsitems{$idx}{'timestamp'}=$contrib{$idx.':timestamp'};
1441: $$imsitems{$idx}{'sender'}=$plainname.' ('.
1442: $contrib{$idx.':sendername'}.':'.
1443: $contrib{$idx.':senderdomain'}.')';
1444: $$imsitems{$idx}{'isanonymous'}='false';
1445: if ($contrib{$idx.':anonymous'}) {
1446: $$imsitems{$idx}{'isanonymous'}='true';
1447: }
1448: $$imsitems{$idx}{'currversion'}=$numoldver;
1449: %{$$imsitems{$idx}{'allattachments'}}=%allattachments;
1450: unless ($messages{$numoldver} eq '' && $attachtxt{$numoldver} eq '') {
1451: $$shown{$idx} = 1;
1452: }
1453: } else {
1454: if ($message) {
1455: my $spansize = 2;
1456: my ($uname,$udom);
1457: if ($showonlyunread && $prevread > $posttime) {
1458: $$notshown{$idx} = 1;
1459: } elsif ($showunmark && $$dischash{$readkey}=~/\.$idx\./) {
1460: $$notshown{$idx} = 1;
1461: } else {
1462: # apply filters
1463: $uname = $contrib{$idx.':sendername'};
1464: $udom = $contrib{$idx.':senderdomain'};
1465: my $poster = $uname.':'.$udom;
1466: if ($env{'form.totposters'} ne '') {
1467: if ($totposters == 0) {
1468: $$shown{$idx} = 0;
1469: } elsif ($totposters > 0) {
1470: if (grep/^$poster$/,@{$posters}) {
1471: $$shown{$idx} = 1;
1472: }
1473: }
1474: } elsif ($sortposts) {
1475: if ($skiptest) {
1476: $$shown{$idx} = 1;
1477: } else {
1478: foreach my $role (@{$$roleinfo{$poster}}) {
1479: if ($role =~ /^cc:/) {
1480: my $cc_regexp = $roleregexp.':[^:]*:'.$statusregexp;
1481: if ($role =~ /$cc_regexp/) {
1482: $$shown{$idx} = 1;
1483: last;
1484: }
1485: } elsif ($role =~ /^$rolematch$/) {
1486: $$shown{$idx} = 1;
1487: last;
1488: }
1489: }
1490: }
1491: if ($$shown{$idx} && !$skip_group_check) {
1492: my $showflag = 0;
1493: if (ref($$classgroups{$poster}{active}) eq 'HASH') {
1494: foreach my $grp (@{$grouppick}) {
1495: if (grep/^\Q$grp\E$/,
1496: keys(%{$$classgroups{$poster}{active}})) {
1497: $showflag = 1;
1498: last;
1499: }
1500: }
1501: }
1502: if ($showflag) {
1503: $$shown{$idx} = 1;
1504: } else {
1505: $$shown{$idx} = 0;
1506: }
1507: }
1508: } else {
1509: $$shown{$idx} = 1;
1510: }
1511: }
1512: unless ($$notshown{$idx} == 1) {
1513: if ($prevread > 0 && $prevread <= $posttime) {
1514: $$newitem{$idx} = 1;
1515: $$discussionitems[$idx] .= '<font color="#FF0000"><b>'.&mt('NEW').' </b></font>';
1516: } else {
1517: $$newitem{$idx} = 0;
1518: }
1519: $$discussionitems[$idx] .= '<b>'.$subject.'</b> '.
1520: $sender.' '.$vgrlink.' ('.
1521: &Apache::lonlocal::locallocaltime($posttime).')';
1522: if ($$dischash{$toggkey}) {
1523: $$discussionitems[$idx].=' '.$ctlink;
1524: }
1525: my $thislikes=$likes{$symb.':'.$idx.':likes'};
1526: my $likestyle;
1527: if ($seeid || $canvote) {
1528: # Figure out size based on likes
1529: my $class = 'zero';
1530: my $thislikes=$likes{$symb.':'.$idx.':likes'};
1531: if ($thislikes>$twoplus) {
1532: $class = 'twoplus';
1533: } elsif ($thislikes>$oneplus) {
1534: $class = 'oneplus';
1535: }
1536: if ($thislikes<$twominus) {
1537: $class = 'twominus';
1538: } elsif ($thislikes<$oneminus) {
1539: $class = 'oneminus';
1540: }
1541: $likestyle = $votestyle{$class};
1542: }
1543: # Actually glue in the message itself
1544: $$discussionitems[$idx].= '<br /><blockquote>'.
1545: "<div $likestyle>".
1546: $message.
1547: '</div></blockquote>';
1548: if ($canvote) {
1549: my $ownpost;
1550: if (($uname eq $env{'user.name'}) &&
1551: ($udom eq $env{'user.domain'})) {
1552: $ownpost = 1;
1553: }
1554: # Put in the like and unlike buttons
1555: if ($ownpost || (($hiddens{$idx}) && ($seehidden))) {
1556: my $novote;
1557: if ($ownpost) {
1558: $novote = &mt('No voting for your own posts.');
1559: } else {
1560: $novote = &mt('No voting for hidden posts.');
1561: }
1562: &html_escape(\$novote);
1563: $$discussionitems[$idx].=
1564: '<a href="javascript:alert('."'$novote'".');" style="text-decoration: none;">'.
1565: '<img border="0" src="/res/adm/pages/thumbsup_novote.png" alt="'.$novote.'" /> '.
1566: '<img border="0" src="/res/adm/pages/thumbsdown_novote.png" alt="'.$novote.'" /></a>';
1567:
1568: } else {
1569: if ($userlikes{$idx}) {
1570: $$discussionitems[$idx].='<img border="0" src="/res/adm/pages/thumbsup_gray.png" alt="'.&mt('You like this posting').'" />';
1571: } else {
1572: $$discussionitems[$idx].=' '.&discussion_link($ressymb,'<img border="0" src="/res/adm/pages/thumbsup.png" alt="'.&mt('Like').'" />','like',$idx,$$newpostsflag,$prevread,&group_args($group),&mt("Like this posting"));
1573: }
1574: if ($userunlikes{$idx}) {
1575: $$discussionitems[$idx].='<img border="0" src="/res/adm/pages/thumbsdown_gray.png" alt="'.&mt('You unlike this posting').'" />';
1576: } else {
1577: $$discussionitems[$idx].=' '.&discussion_link($ressymb,'<img border="0" src="/res/adm/pages/thumbsdown.png" alt="'.&mt('Unlike').'" />','unlike',$idx,$$newpostsflag,$prevread,&group_args($group),&mt("Unlike this posting"));
1578: }
1579: }
1580: }
1581: if ($seeid || $canvote) {
1582: my $thislikes=$likes{$symb.':'.$idx.':likes'};
1583: if ($thislikes>0) {
1584: $$discussionitems[$idx].=' ('.&mt("[_1] likes",$thislikes).')';
1585: } elsif ($thislikes<0) {
1586: $$discussionitems[$idx].=' ('.&mt("[_1] unlikes",abs($thislikes)).')';
1587: }
1588: }
1589: # If there is any history to this post, inform the reader
1590: if ($contrib{$idx.':history'}) {
1591: my @postversions = ();
1592: $$discussionitems[$idx] .= ' '.&mt('This post has been edited by the author.');
1593: if ($seehidden) {
1594: $$discussionitems[$idx] .= ' '.
1595: &discussion_link($ressymb,&mt('Display all versions'),'allversions',$idx,$$newpostsflag,$prevread,&group_args($group));
1596: }
1597: $$discussionitems[$idx].='<br/>'.&mt('Earlier version(s) were posted on: ');
1598: if ($contrib{$idx.':history'} =~ m/:/) {
1599: @postversions = split(/:/,$contrib{$idx.':history'});
1600: } else {
1601: @postversions = ("$contrib{$idx.':history'}");
1602: }
1603: for (my $i=0; $i<@postversions; $i++) {
1604: my $version = $i+1;
1605: $$discussionitems[$idx] .= '<b>'.$version.'.</b> - '.&Apache::lonlocal::locallocaltime($postversions[$i]).' ';
1606: }
1607: }
1608: # end of unless ($$notshown ...)
1609: }
1610: # end of if ($message) ...
1611: }
1612: # end of the else-branch of target being export
1613: }
1614: # end of unless hidden or deleted
1615: }
1616: # end of the loop over all discussion entries
1617: }
1618: # end of "if there actually are any discussions
1619: }
1620: # end of subroutine "build_posting_display"
1621: }
1622:
1623: sub filter_regexp {
1624: my ($rolefilter,$sectionpick,$statusfilter) = @_;
1625: my ($roleregexp,$secregexp,$statusregexp);
1626: my $skiptest = 1;
1627: if (@{$rolefilter} > 0) {
1628: my @okrolefilter = ();
1629: foreach my $role (@{$rolefilter}) {
1630: unless ($role eq '') {
1631: push(@okrolefilter, $role);
1632: }
1633: }
1634: if (@okrolefilter > 0) {
1635: if (grep/^all$/,@okrolefilter) {
1636: $roleregexp='[^:]+';
1637: } else {
1638: if (@okrolefilter == 1) {
1639: $roleregexp=$okrolefilter[0];
1640: } else {
1641: $roleregexp='('.join('|',@okrolefilter).')';
1642: }
1643: $skiptest = 0;
1644: }
1645: }
1646: }
1647: if (@{$sectionpick} > 0) {
1648: my @oksectionpick = ();
1649: foreach my $sec (@{$sectionpick}) {
1650: unless ($sec eq '') {
1651: push(@oksectionpick, $sec);
1652: }
1653: }
1654: if ((@oksectionpick > 0) && (!grep/^all$/,@oksectionpick)) {
1655: if (@oksectionpick == 1) {
1656: $secregexp = $oksectionpick[0];
1657: } else {
1658: $secregexp .= '('.join('|',@oksectionpick).')';
1659: }
1660: $skiptest = 0;
1661: } else {
1662: $secregexp .= '[^:]*';
1663: }
1664: }
1665:
1666: if (defined($statusfilter) && $statusfilter ne '') {
1667: if ($statusfilter eq 'all') {
1668: $statusregexp = '[^:]+';
1669: } else {
1670: $statusregexp = $statusfilter;
1671: $skiptest = 0;
1672: }
1673: }
1674: return ($skiptest,$roleregexp,$secregexp,$statusregexp);
1675: }
1676:
1677:
1678: sub get_post_contents {
1679: my ($contrib,$idx,$seeid,$seehidden,$type,$messages,$subjects,$allattachments,$attachtxt,$imsfiles,$screenname,$plainname,$showaboutme,$numver) = @_;
1680: my $discussion = '';
1681: my $start=$numver;
1682: my $end=$numver + 1;
1683: %{$$imsfiles{$idx}}=();
1684: if ($type eq 'allversions') {
1685: unless($seehidden) {
1686: $discussion=&mt('You do not have privileges to view all versions of posts.').' '.&mt('Please select a different role.');
1687: return $discussion;
1688: }
1689: }
1690: # $$screenname=&Apache::loncommon::screenname(
1691: # $$contrib{$idx.':sendername'},
1692: # $$contrib{$idx.':senderdomain'});
1693: $$plainname=&Apache::loncommon::nickname(
1694: $$contrib{$idx.':sendername'},
1695: $$contrib{$idx.':senderdomain'});
1696: $$screenname=$$contrib{$idx.':screenname'};
1697: $$showaboutme = &Apache::lonnet::usertools_access($$contrib{$idx.':sendername'},
1698: $$contrib{$idx.':senderdomain'},
1699: 'aboutme');
1700: my $sender = $$plainname;
1701: if ($$showaboutme) {
1702: $sender = &Apache::loncommon::aboutmewrapper(
1703: $$plainname,
1704: $$contrib{$idx.':sendername'},
1705: $$contrib{$idx.':senderdomain'});
1706: }
1707: if ($seeid) {
1708: $sender .= ' ('.$$contrib{$idx.':sendername'}.':'.
1709: $$contrib{$idx.':senderdomain'}.')';
1710: }
1711: my $attachmenturls = $$contrib{$idx.':attachmenturl'};
1712: my @postversions = ();
1713: if ($type eq 'allversions' || $type eq 'export') {
1714: $start = 0;
1715: if ($$contrib{$idx.':history'}) {
1716: @postversions = split(/:/,$$contrib{$idx.':history'});
1717: }
1718: &get_post_versions($messages,$$contrib{$idx.':message'},1);
1719: &get_post_versions($subjects,$$contrib{$idx.':subject'},1);
1720: push(@postversions,$$contrib{$idx.':timestamp'});
1721: $end = @postversions;
1722: } else {
1723: &get_post_versions($messages,$$contrib{$idx.':message'},1,$numver);
1724: &get_post_versions($subjects,$$contrib{$idx.':subject'},1,$numver);
1725: }
1726:
1727: if ($$contrib{$idx.':anonymous'}) {
1728: $sender.=' ['.&mt('anonymous').'] '.$$screenname;
1729: }
1730: if ($type eq 'allversions') {
1731: $discussion=('<b>'.$sender.'</b><br /><ul>');
1732: }
1733: for (my $i=$start; $i<$end; $i++) {
1734: my ($timesent,$attachmsg);
1735: my %currattach = ();
1736: $timesent = &Apache::lonlocal::locallocaltime($postversions[$i]);
1737: unless (&contains_block_html($messages->{$i})) {
1738: &newline_to_br(\$messages->{$i});
1739: }
1740: $$messages{$i}=&Apache::lontexconvert::msgtexconverted($$messages{$i});
1741: $$subjects{$i}=~s/\n/\<br \/\>/g;
1742: $$subjects{$i}=&Apache::lontexconvert::msgtexconverted($$subjects{$i});
1743: if ($attachmenturls) {
1744: &extract_attachments($attachmenturls,$idx,$i,\$attachmsg,$allattachments,\%currattach);
1745: }
1746: if ($type eq 'export') {
1747: $$imsfiles{$idx}{$i} = '';
1748: if ($attachmsg) {
1749: $$attachtxt{$i} = '<br />'.&mt('Attachments').':<br />';
1750: foreach my $key (sort(keys(%currattach))) {
1751: if ($$allattachments{$key}{'filename'} =~ m-^/uploaded/([^/]+/[^/]+)(/feedback)?(/?\d*)/([^/]+)$-) {
1752: my $fname = $1.$3.'/'.$4;
1753: $$imsfiles{$idx}{$i} .= '<file href="'.$fname.'" />'."\n";
1754: $$attachtxt{$i}.= '<a href="'.$fname.'">'.$4.'</a><br />';
1755: }
1756: }
1757: }
1758: } else {
1759: if ($attachmsg) {
1760: $$attachtxt{$i} = '<br />'.&mt('Attachments').':'.$attachmsg.'<br />';
1761: } else {
1762: $$attachtxt{$i} = '';
1763: }
1764: }
1765: if ($type eq 'allversions') {
1766: $discussion.= <<"END";
1767: <li><b>$$subjects{$i}</b>, $timesent<br />
1768: $$messages{$i}<br />
1769: $$attachtxt{$i}</li>
1770: END
1771: }
1772: }
1773: if ($type eq 'allversions') {
1774: $discussion.='</ul>';
1775: return $discussion;
1776: } else {
1777: return;
1778: }
1779: }
1780:
1781: sub replicate_attachments {
1782: my ($attachrefs,$tempexport) = @_;
1783: my $response;
1784: foreach my $id (keys(%{$attachrefs})) {
1785: if ($$attachrefs{$id}{'filename'} =~ m-^/uploaded/([^/]+)/([^/]+)(/feedback)?(/?\d*)/([^/]+)$-) {
1786: my $path = $tempexport;
1787: my $tail = $1.'/'.$2.$4;
1788: my @extras = split(/\//,$tail);
1789: my $destination = $tempexport.'/'.$1.'/'.$2.$4.'/'.$5;
1790: if (!-e $destination) {
1791: my $i= 0;
1792: while ($i<@extras) {
1793: $path .= '/'.$extras[$i];
1794: if (!-e $path) {
1795: mkdir($path,0700);
1796: }
1797: $i ++;
1798: }
1799: my ($content,$rtncode);
1800: my $uploadreply = &Apache::lonnet::getuploaded('GET',$$attachrefs{$id}{'filename'},$1,$2,$content,$rtncode);
1801: if ($uploadreply eq 'ok') {
1802: my $attachcopy;
1803: if ($attachcopy = Apache::File->new('>'.$destination)) {
1804: print $attachcopy $content;
1805: close($attachcopy);
1806: } else {
1807: $response .= &mt('Error copying file attachment - [_1] to IMS package',$5).': '.$!.'<br />'."\n";
1808: }
1809: } else {
1810: &Apache::lonnet::logthis("Replication of attachment failed when building IMS export of discussion posts - domain: $1, course: $2, file: $$attachrefs{$id}{'filename'} -error: $rtncode");
1811: $response .= &mt('Error copying file attachment - [_1] to IMS package: ',$5).$rtncode.'<br />'."\n";
1812: }
1813: }
1814: }
1815: }
1816: return $response;
1817: }
1818:
1819: sub mail_screen {
1820: my ($r,$feedurl,$options,$caller_symb,$attachmaxtext) = @_;
1821: if (exists($env{'form.origpage'})) {
1822: &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['subject','comment','currnewattach','addnewattach','deloldattach','delnewattach','timestamp','idx','anondiscuss','discuss','blog','group','ref']);
1823: }
1824:
1825: my %lt = &Apache::lonlocal::texthash(
1826: 'myqu' => 'Question/comment/feedback:',
1827: 'reta' => 'Retained attachments',
1828: 'atta' => 'Attachment',
1829: );
1830: if ($env{'form.editdisc'} || $env{'form.replydisc'}){
1831: $lt{'myqu'} = &mt('Post Discussion');
1832: }
1833: my $restitle = &get_resource_title($caller_symb,$feedurl);
1834: my $quote='';
1835: my $subject = '';
1836: my $comment = '';
1837: my $prevtag = '';
1838: my $parentmsg = '';
1839: my ($symb,$idx,$attachmenturls);
1840: my $numoldver = 0;
1841: my $attachmsg = '';
1842: my $newattachmsg = '';
1843: my @currnewattach = ();
1844: my @currdelold = ();
1845: my @keepold = ();
1846: my %attachments = ();
1847: my %currattach = ();
1848: my $attachnum = 0;
1849: my $anonchk = (<<END);
1850: function anonchk() {
1851: for (var i=0; i < document.mailform.discuss.length; i++) {
1852: if (document.mailform.discuss[i].checked) {
1853: document.attachment.discuss.value =
1854: document.mailform.discuss[i].value;
1855: }
1856: }
1857: if (document.mailform.blog.checked) {
1858: document.attachment.blog.value = 1;
1859: }
1860: return
1861: }
1862: END
1863: my $anonscript;
1864: if (exists($env{'form.origpage'})) {
1865: $anonscript = (<<END);
1866: function setposttype() {
1867: var disc = "$env{'form.discuss'}";
1868: for (var i=0; i < document.mailform.discuss.length; i++) {
1869: if (disc == document.mailform.discuss[i].value) {
1870: document.mailform.discuss[i].checked = 1;
1871: }
1872: }
1873: var blog = "$env{'form.blog'}";
1874: if (blog == 1) {
1875: document.mailform.blog.checked=1;
1876: }
1877: return
1878: }
1879: END
1880: } else {
1881: $anonscript = (<<END);
1882: function setposttype() {
1883: return
1884: }
1885: END
1886: }
1887: if (($env{'form.replydisc'}) || ($env{'form.editdisc'})) {
1888: if ($env{'form.replydisc'}) {
1889: ($symb,$idx)=split(/\:\:\:/,$env{'form.replydisc'});
1890: } else {
1891: ($symb,$idx)=split(/\:\:\:/,$env{'form.editdisc'});
1892: }
1893: my %contrib=&Apache::lonnet::restore($symb,$env{'request.course.id'},
1894: $env{'course.'.$env{'request.course.id'}.'.domain'},
1895: $env{'course.'.$env{'request.course.id'}.'.num'});
1896: unless (($contrib{'hidden'}=~/\.$idx\./) || ($contrib{'deleted'}=~/\.$idx\./)) {
1897: my $numoldver = 0;
1898: if ($contrib{$idx.':history'}) {
1899: if ($contrib{$idx.':history'} =~ /:/) {
1900: my @oldversions = split(/:/,$contrib{$idx.':history'});
1901: $numoldver = @oldversions;
1902: } else {
1903: $numoldver = 1;
1904: }
1905: }
1906: if ($idx > 0) {
1907: my (%msgversions,%subversions,$htmldecode);
1908: $htmldecode = 0;
1909: if ($env{'form.replydisc'}) {
1910: $htmldecode = 1;
1911: }
1912: &get_post_versions(\%msgversions,$contrib{$idx.':message'},0,$numoldver);
1913: &get_post_versions(\%subversions,$contrib{$idx.':subject'},$htmldecode,
1914: $numoldver);
1915: $subject = $subversions{$numoldver};
1916: if ($env{'form.replydisc'}) {
1917: $quote = $msgversions{$numoldver};
1918: $subject = &HTML::Entities::encode(&mt('Re: ').$subject,'<>&"');
1919: } else {
1920: $comment = $msgversions{$numoldver};
1921: }
1922: }
1923: if ($env{'form.editdisc'}) {
1924: $attachmenturls = $contrib{$idx.':attachmenturl'};
1925: if (defined($contrib{$idx.':replyto'})) {
1926: $parentmsg = $contrib{$idx.':replyto'};
1927: }
1928: unless (exists($env{'form.origpage'})) {
1929: my $anonflag = 'nonanon';
1930: if ($contrib{$idx.':anonymous'}) {
1931: $anonflag = 'anon';
1932: }
1933: $anonscript = (<<END);
1934: function setposttype () {
1935: var currtype = "$anonflag";
1936: for (var i=0; i<document.mailform.discuss.length; i++) {
1937: if (document.mailform.elements.discuss[i].value == currtype ) {
1938: document.mailform.elements.discuss[i].checked=1;
1939: }
1940: }
1941: return
1942: }
1943: END
1944: }
1945: }
1946: }
1947: if ($env{'form.previous'}) {
1948: $prevtag = '<input type="hidden" name="previous" value="'.$env{'form.previous'}.'" />';
1949: }
1950: }
1951:
1952: if ($env{'form.origpage'}) {
1953: $subject = &unescape($env{'form.subject'});
1954: $comment = &unescape($env{'form.comment'});
1955: &process_attachments(\@currnewattach,\@currdelold,\@keepold);
1956: }
1957: my $latexHelp=&Apache::loncommon::helpLatexCheatsheet(undef,undef,1,($env{'form.modal'}?'popup':0));
1958: my $send=&mt('Send');
1959: my $alert = &mt('Please select a feedback type.');
1960: &js_escape(\$alert);
1961: my $js= <<END;
1962: <script type="text/javascript">
1963: //<!--
1964: function gosubmit() {
1965: var rec=0;
1966: if (typeof(document.mailform.elements.discuss)!="undefined") {
1967: if (typeof(document.mailform.elements.discuss.length) == "undefined") {
1968: if (document.mailform.elements.discuss.checked ) {
1969: rec=1;
1970: }
1971: } else {
1972: for (var i=0; i<document.mailform.elements.discuss.length; i++) {
1973: if (document.mailform.elements.discuss[i].checked ) {
1974: rec=1;
1975: }
1976: }
1977: }
1978: }
1979: if (typeof(document.mailform.elements.blog)!="undefined") {
1980: if (document.mailform.elements.blog.checked) {
1981: rec=1;
1982: }
1983: }
1984:
1985: if (rec) {
1986: if (typeof(document.mailform.onsubmit)=='function') {
1987: document.mailform.onsubmit();
1988: }
1989: document.mailform.submit();
1990: } else {
1991: alert('$alert');
1992: }
1993: }
1994: $anonchk
1995: $anonscript
1996: //-->
1997: </script>
1998: <script type="text/javascript" src="/res/adm/includes/file_upload.js"></script>
1999: END
2000:
2001: my ($textareaheader,$textareaclass);
2002: if (&Apache::lonhtmlcommon::htmlareabrowser()) {
2003: $textareaheader = &Apache::lonhtmlcommon::htmlareaselectactive();
2004: $textareaclass = 'class="LC_richDefaultOff"';
2005: if ($env{'request.course.id'}) {
2006: unless (($env{'course.'.$env{'request.course.id'}.'.allow_limited_html_in_feedback'} =~ /^\s*yes\s*$/i) || ($env{'form.sendmessageonly'})) {
2007: undef($textareaclass);
2008: }
2009: }
2010: }
2011:
2012: # Breadcrumbs
2013: my $brcrum = [{'href' => '',
2014: 'text' => 'Resource Feedback and Discussion'}];
2015:
2016: my %onload = ('onload' => 'window.focus();setposttype();');
2017: my %parms=('add_entries' => \%onload);
2018: if ($env{'form.modal'} ne 'yes') { $parms{'bread_crumbs'} = $brcrum; }
2019: my $start_page=
2020: &Apache::loncommon::start_page('Resource Feedback and Discussion',$js,\%parms);
2021:
2022: if ($quote ne '') {
2023: $quote = &HTML::Entities::decode($quote);
2024: unless (&contains_block_html($quote)) {
2025: &newline_to_br(\$quote);
2026: }
2027: $quote=&Apache::lonhtmlcommon::start_pick_box().
2028: &Apache::lonhtmlcommon::row_title(&mt('Quote')).
2029: &Apache::lontexconvert::msgtexconverted($quote).
2030: &Apache::lonhtmlcommon::row_closure(1).
2031: &Apache::lonhtmlcommon::end_pick_box();
2032: }
2033: my $header='';
2034: unless ($env{'form.modal'}) {
2035: $header="<h2><tt>$restitle</tt></h2>";
2036: }
2037: $r->print(<<END);
2038: $start_page
2039: <h1>$lt{'myqu'}</h1>
2040: $header
2041: <form action="/adm/feedback" method="post" name="mailform"
2042: enctype="multipart/form-data">
2043: $prevtag
2044: <input type="hidden" name="postdata" value="$feedurl" />
2045: <input type="hidden" name="modal" value="$env{'form.modal'}" />
2046: END
2047: if ($env{'form.replydisc'}) {
2048: $r->print(<<END);
2049: <input type="hidden" name="replydisc" value="$env{'form.replydisc'}" />
2050: END
2051: } elsif ($env{'form.editdisc'}) {
2052: $r->print(<<END);
2053: <input type="hidden" name="editdisc" value="$env{'form.editdisc'}" />
2054: <input type="hidden" name="parentmsg" value="$parentmsg" />
2055: END
2056: }
2057: $r->print(<<END);
2058: $options
2059: <br />
2060: END
2061: $r->print(&Apache::lonhtmlcommon::start_pick_box());
2062: $r->print(<<END);
2063: $quote
2064: <p>
2065: $textareaheader
2066: </p>
2067: <p>
2068: $latexHelp
2069: </p>
2070: <p>
2071: END
2072:
2073:
2074: $r->print(&Apache::lonhtmlcommon::row_title(&mt('Subject')));
2075: $r->print('<input type="text" name="subject" size="30" value="'.
2076: $subject.'" /></p>');
2077: $r->print(&Apache::lonhtmlcommon::row_closure());
2078: $r->print(&Apache::lonhtmlcommon::row_title(&mt('Message')));
2079: $r->print('<textarea name="comment" id="comment" cols="55" rows="10" '.
2080: $textareaclass.'>'.$comment.
2081: '</textarea>');
2082: $r->print(&Apache::lonhtmlcommon::row_closure(1));
2083: $r->print(&Apache::lonhtmlcommon::end_pick_box());
2084:
2085: if ( ($env{'form.editdisc'}) || ($env{'form.replydisc'}) ) {
2086: if ($env{'form.origpage'}) {
2087: foreach my $attach (@currnewattach) {
2088: $r->print('<input type="hidden" name="currnewattach" value="'.$attach.'" />'."\n");
2089: }
2090: foreach my $oldatt (@currdelold) {
2091: $r->print('<input type="hidden" name="deloldattach" value="'.$oldatt.'" />'."\n");
2092: }
2093: }
2094: if ($env{'form.editdisc'}) {
2095: if ($attachmenturls) {
2096: &extract_attachments($attachmenturls,$idx,$numoldver,\$attachmsg,\%attachments,\%currattach,\@currdelold);
2097: $attachnum = scalar(keys(%currattach));
2098: foreach my $key (keys(%currattach)) {
2099: $r->print('<input type="hidden" name="keepold" value="'.$key.'" />'."\n");
2100: }
2101: }
2102: }
2103: } else {
2104: $r->print(<<END);
2105: <p>
2106: $lt{'atta'} $attachmaxtext: <input type="file" name="attachment" class="LC_flUpload" />
2107: <input type="hidden" id="LC_free_space" value="131072" />
2108: </p>
2109: END
2110: }
2111: if (exists($env{'form.group'})) {
2112: $r->print('<input type="hidden" name="group" value="'.$env{'form.group'}.'" />');
2113: }
2114: if (exists($env{'form.ref'})) {
2115: $r->print('<input type="hidden" name="ref" value="'.$env{'form.ref'}.'" />');
2116: }
2117: $r->print(<<END);
2118: <input type="hidden" name="sendit" value="1" /><br /><br />
2119: <input type="button" value="$send" onclick='gosubmit();' />
2120: </form>
2121: END
2122: if ($env{'form.editdisc'} || $env{'form.replydisc'}) {
2123: my $now = time;
2124: my $ressymb = $symb;
2125: &Apache::lonenc::check_encrypt(\$ressymb);
2126: my $postidx = '';
2127: if ($env{'form.editdisc'}) {
2128: $postidx = $idx;
2129: }
2130: if (@currnewattach > 0) {
2131: $attachnum += @currnewattach;
2132: }
2133: my $blockblog = &Apache::loncommon::blocking_status('blogs');
2134: if ($attachnum > 0) {
2135: if (@currnewattach > 0) {
2136: $newattachmsg .= '<br /><b>'.&mt('New attachments').'</b><br />';
2137: if (@currnewattach > 1) {
2138: $newattachmsg .= '<ol>';
2139: foreach my $item (@currnewattach) {
2140: $item =~ m#.*/([^/]+)$#;
2141: $newattachmsg .= '<li><a href="'.$item.'">'.$1.'</a></li>'."\n";
2142: }
2143: $newattachmsg .= '</ol>'."\n";
2144: } else {
2145: $currnewattach[0] =~ m#.*/([^/]+)$#;
2146: $newattachmsg .= '<a href="'.$currnewattach[0].'">'.$1.'</a><br />'."\n";
2147: }
2148: }
2149: if ($attachmsg) {
2150: $r->print("<br /><b>$lt{'reta'}</b>:$attachmsg<br />\n");
2151: }
2152: if ($newattachmsg) {
2153: $r->print("$newattachmsg");
2154: }
2155: }
2156: $r->print(&generate_attachments_button($postidx,$attachnum,$ressymb,$now,\@currnewattach,\@currdelold,$numoldver,'',$blockblog));
2157: }
2158: $r->print(&generate_preview_button().
2159: &Apache::loncommon::end_page());
2160:
2161: }
2162:
2163: sub print_display_options {
2164: my ($r,$symb,$previous,$dispchgA,$dispchgB,$markchg,$toggchg,$feedurl) = @_;
2165: &Apache::loncommon::content_type($r,'text/html');
2166: $r->send_http_header;
2167:
2168: my $function = &Apache::loncommon::get_users_function();
2169: my $tabcolor = &Apache::loncommon::designparm($function.'.tabbg',
2170: $env{'user.domain'});
2171:
2172: my %lt = &Apache::lonlocal::texthash(
2173: 'pref' => 'Display Preference',
2174: 'curr' => 'Current setting ',
2175: 'actn' => 'Action',
2176: 'deff' => 'Default for all discussions',
2177: 'prca' => 'Preferences can be set for this discussion that determine ....',
2178: 'whpo' => 'Which posts are displayed when you display this discussion board or resource, and',
2179: 'unwh' => 'Under what circumstances posts are identified as "NEW", and',
2180: 'wipa' => 'Whether individual posts can be marked as read/unread',
2181: 'allposts' => 'All posts',
2182: 'unread' => 'New posts only',
2183: 'unmark' => 'Posts not marked read',
2184: 'ondisp' => 'Once displayed',
2185: 'onmark' => 'Once marked not NEW',
2186: 'toggon' => 'Shown',
2187: 'toggoff' => 'Not shown',
2188: 'disa' => 'Posts displayed?',
2189: 'npmr' => 'New posts cease to be identified as "NEW"?',
2190: 'dotm' => 'Option to mark each post as read/unread?',
2191: 'chgt' => 'Change to ',
2192: 'mkdf' => 'Set to ',
2193: 'yhni' => 'You have not indicated that you wish to change any of the discussion settings',
2194: 'ywbr' => 'You will be returned to the previous page if you click OK.'
2195: );
2196: my %js_lt = &Apache::lonlocal::texthash(
2197: 'yhni' => 'You have not indicated that you wish to change any of the discussion settings',
2198: 'ywbr' => 'You will be returned to the previous page if you click OK.'
2199: );
2200: &js_escape(\%js_lt);
2201:
2202: my $dispchangeA = $lt{'unread'};
2203: my $dispchangeB = $lt{'unmark'};
2204: my $markchange = $lt{'ondisp'};
2205: my $toggchange = $lt{'toggon'};
2206: my $currdisp = $lt{'allposts'};
2207: my $currmark = $lt{'onmark'};
2208: my $discdisp = 'allposts';
2209: my $discmark = 'onmark';
2210: my $currtogg = $lt{'toggoff'};
2211: my $disctogg = 'toggoff';
2212:
2213: if ($dispchgA eq 'allposts') {
2214: $dispchangeA = $lt{'allposts'};
2215: $currdisp = $lt{'unread'};
2216: $discdisp = 'unread';
2217: }
2218:
2219: if ($markchg eq 'markonread') {
2220: $markchange = $lt{'onmark'};
2221: $currmark = $lt{'ondisp'};
2222: $discmark = 'ondisp';
2223: }
2224:
2225: if ($dispchgB eq 'onlyunread') {
2226: $dispchangeB = $lt{'unread'};
2227: $currdisp = $lt{'unmark'};
2228: $discdisp = 'unmark';
2229: }
2230: if ($toggchg eq 'toggoff') {
2231: $toggchange = $lt{'toggoff'};
2232: $currtogg = $lt{'toggon'};
2233: $disctogg = 'toggon';
2234: }
2235:
2236: my $js = <<END;
2237: <script type="text/javascript">
2238: function discdispChk(caller) {
2239: var disctogg = '$toggchg'
2240: if (caller == 0) {
2241: if (document.modifydisp.discdisp[0].checked == true) {
2242: if (document.modifydisp.discdisp[1].checked == true) {
2243: document.modifydisp.discdisp[1].checked = false
2244: }
2245: }
2246: }
2247: if (caller == 1) {
2248: if (document.modifydisp.discdisp[1].checked == true) {
2249: if (document.modifydisp.discdisp[0].checked == true) {
2250: document.modifydisp.discdisp[0].checked = false
2251: }
2252: if (disctogg == 'toggon') {
2253: document.modifydisp.disctogg.checked = true
2254: }
2255: if (disctogg == 'toggoff') {
2256: document.modifydisp.disctogg.checked = false
2257: }
2258: }
2259: }
2260: if (caller == 2) {
2261: var dispchgB = '$dispchgB'
2262: if (disctogg == 'toggoff') {
2263: if (document.modifydisp.disctogg.checked == true) {
2264: if (dispchgB == 'onlyunmark') {
2265: document.modifydisp.discdisp[1].checked = false
2266: }
2267: }
2268: }
2269: }
2270: }
2271:
2272: function setDisp() {
2273: var prev = "$previous"
2274: var chktotal = 0
2275: if (document.modifydisp.discdisp[0].checked == true) {
2276: document.modifydisp.$dispchgA.value = "$symb"
2277: chktotal ++
2278: }
2279: if (document.modifydisp.discdisp[1].checked == true) {
2280: document.modifydisp.$dispchgB.value = "$symb"
2281: chktotal ++
2282: }
2283: if (document.modifydisp.discmark.checked == true) {
2284: document.modifydisp.$markchg.value = "$symb"
2285: chktotal ++
2286: }
2287: if (document.modifydisp.disctogg.checked == true) {
2288: document.modifydisp.$toggchg.value = "$symb"
2289: chktotal ++
2290: }
2291: if (chktotal > 0) {
2292: document.modifydisp.submit()
2293: } else {
2294: if(confirm("$js_lt{'yhni'}. \\n$js_lt{'ywbr'}")) {
2295: if (prev > 0) {
2296: location.href = "$feedurl?previous=$previous"
2297: } else {
2298: location.href = "$feedurl"
2299: }
2300: }
2301: }
2302: }
2303: </script>
2304: END
2305:
2306:
2307: my $start_page =
2308: &Apache::loncommon::start_page('Discussion display options',$js);
2309: my $end_page =
2310: &Apache::loncommon::end_page();
2311: $r->print(<<END);
2312: $start_page
2313: <form name="modifydisp" method="post" action="/adm/feedback">
2314: $lt{'sdpf'}<br/> $lt{'prca'} <ol><li>$lt{'whpo'}</li><li>$lt{'unwh'}</li><li>$lt{'wipa'}</li></ol>
2315: <br />
2316: END
2317: $r->print(&Apache::loncommon::start_data_table());
2318: $r->print(<<END);
2319: <tr>
2320: <th>$lt{'pref'}</td>
2321: <th>$lt{'curr'}</td>
2322: <th>$lt{'actn'}?</td>
2323: </tr>
2324: END
2325: $r->print(&Apache::loncommon::start_data_table_row());
2326: $r->print(<<END);
2327: <td>$lt{'disa'}</td>
2328: <td>$lt{$discdisp}</td>
2329: <td><label><input type="checkbox" name="discdisp" onclick="discdispChk('0')" /> $lt{'chgt'} "$dispchangeA"</label>
2330: <br />
2331: <label><input type="checkbox" name="discdisp" onclick="discdispChk('1')" /> $lt{'chgt'} "$dispchangeB"</label>
2332: </td>
2333: END
2334: $r->print(&Apache::loncommon::end_data_table_row());
2335: $r->print(&Apache::loncommon::start_data_table_row());
2336: $r->print(<<END);
2337: <td>$lt{'npmr'}</td>
2338: <td>$lt{$discmark}</td>
2339: <td><label><input type="checkbox" name="discmark" />$lt{'chgt'} "$markchange"</label></td>
2340: END
2341: $r->print(&Apache::loncommon::end_data_table_row());
2342: $r->print(&Apache::loncommon::start_data_table_row());
2343: $r->print(<<END);
2344: <td>$lt{'dotm'}</td>
2345: <td>$lt{$disctogg}</td>
2346: <td><label><input type="checkbox" name="disctogg" onclick="discdispChk('2')" />$lt{'chgt'} "$toggchange"</label></td>
2347: END
2348: my $save = &mt('Save');
2349: $r->print(&Apache::loncommon::end_data_table_row());
2350: $r->print(&Apache::loncommon::end_data_table());
2351: $r->print(<<END);
2352: <br />
2353: <br />
2354: <input type="hidden" name="symb" value="$symb" />
2355: <input type="hidden" name="previous" value="$previous" />
2356: <input type="hidden" name="$dispchgA" value="" />
2357: <input type="hidden" name="$dispchgB" value="" />
2358: <input type="hidden" name="$markchg" value="" />
2359: <input type="hidden" name="$toggchg" value="" />
2360: <input type="button" name="sub" value="$save" onclick="javascript:setDisp()" />
2361: END
2362: if (exists($env{'form.group'})) {
2363: $r->print('<input type="hidden" name="group" value="'.$env{'form.group'}.'" />');
2364: }
2365: if (exists($env{'form.ref'})) {
2366: $r->print('<input type="hidden" name="ref" value="'.$env{'form.ref'}.'" />');
2367: }
2368: $r->print("
2369: <br />
2370: <br />
2371: </form>
2372: $end_page
2373: ");
2374: return;
2375: }
2376:
2377: sub print_sortfilter_options {
2378: my ($r,$symb,$previous,$feedurl) = @_;
2379:
2380: &Apache::loncommon::content_type($r,'text/html');
2381: $r->send_http_header;
2382:
2383: &Apache::lonenc::check_encrypt(\$symb);
2384: my @sections;
2385: my $section_sel = '';
2386: my $numvisible = 5;
2387: my @groups;
2388: my $group_sel = '';
2389: my $numgroupvis = 5;
2390: my %sectioncount = &Apache::loncommon::get_sections();
2391: my @courseroles = qw(st ad ep ta in);
2392: my $crstype = &Apache::loncommon::course_type();
2393: my $ccrole = 'cc';
2394: if ($crstype eq 'Community') {
2395: $ccrole = 'co';
2396: }
2397: push(@courseroles,$ccrole);
2398:
2399: if ($env{'request.course.sec'} !~ /^\s*$/) { #Restrict section choice to current section
2400: @sections = ('all',$env{'request.course.sec'});
2401: $numvisible = 2;
2402: } else {
2403: @sections = sort {$a cmp $b} keys(%sectioncount);
2404: if (scalar(@sections) < 4) {
2405: $numvisible = scalar(@sections) + 1;
2406: }
2407: unshift(@sections,'all'); # Put 'all' at the front of the list
2408:
2409: }
2410: foreach my $sec (@sections) {
2411: $section_sel .= " <option value=\"$sec\">$sec</option>\n";
2412: }
2413:
2414: if (&check_group_priv() eq 'ok') {
2415: my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
2416: my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
2417: my %curr_groups = &Apache::longroup::coursegroups($cdom,$cnum);
2418: @groups = sort {$a cmp $b} keys(%curr_groups);
2419: if (scalar(@groups) < 4) {
2420: $numgroupvis = scalar(@groups) + 1;
2421: }
2422: unshift(@groups,'all'); # Put 'all' at the front of the list
2423: } else {
2424: my @coursegroups = split(/:/,$env{'request.course.groups'});
2425: if (@coursegroups > 0) {
2426: @coursegroups = sort {$a cmp $b} @coursegroups;
2427: @groups = ('all',@coursegroups);
2428: if (scalar(@groups) < 4) {
2429: $numgroupvis = scalar(@groups) + 1;
2430: }
2431: } else {
2432: @groups = ('all');
2433: $numgroupvis = 1;
2434: }
2435: }
2436: foreach my $group (@groups) {
2437: $group_sel .= " <option value=\"$group\">$group</option>\n";
2438: }
2439:
2440: my $function = &Apache::loncommon::get_users_function();
2441: my $tabcolor = &Apache::loncommon::designparm($function.'.tabbg',
2442: $env{'user.domain'});
2443: my %lt = &Apache::lonlocal::texthash(
2444: 'diop' => 'Display Options',
2445: 'curr' => 'Current setting ',
2446: 'actn' => 'Action',
2447: 'prca' => 'Set options that control the sort order of posts, and/or which posts are displayed.',
2448: 'soor' => 'Sort order',
2449: 'spur' => 'Specific user roles',
2450: 'sprs' => 'Specific role status',
2451: 'spse' => 'Specific sections',
2452: 'spgr' => 'Specific groups',
2453: 'psub' => 'Pick specific users (by name)',
2454: 'shal' => 'Show a list of current posters',
2455: 'stor' => 'Save changes',
2456: );
2457:
2458: my %sort_types = ();
2459: my %role_types = ();
2460: my %status_types = ();
2461: &sort_filter_names(\%sort_types,\%role_types,\%status_types,$crstype);
2462:
2463: my $js = <<END;
2464: <script type="text/javascript">
2465: function verifyFilter() {
2466: var rolenum = 0
2467: for (var i=0; i<document.modifyshown.rolefilter.length; i++) {
2468: if (document.modifyshown.rolefilter.options[i].selected == true) {
2469: rolenum ++
2470: }
2471: }
2472: if (rolenum == 0) {
2473: document.modifyshown.rolefilter.options[0].selected = true
2474: }
2475:
2476: var secnum = 0
2477: for (var i=0; i<document.modifyshown.sectionpick.length; i++) {
2478: if (document.modifyshown.sectionpick.options[i].selected == true) {
2479: secnum ++
2480: }
2481: }
2482: if (secnum == 0) {
2483: document.modifyshown.sectionpick.options[0].selected = true
2484: }
2485:
2486: var grpnum = 0
2487: for (var i=0; i<document.modifyshown.grouppick.length; i++) {
2488: if (document.modifyshown.grouppick.options[i].selected == true) {
2489: grpnum ++
2490: }
2491: }
2492: if (grpnum == 0) {
2493: document.modifyshown.grouppick.options[0].selected = true
2494: }
2495:
2496: document.modifyshown.submit();
2497: }
2498: </script>
2499: END
2500:
2501: my $start_page=
2502: &Apache::loncommon::start_page('Discussion options',$js);
2503: my $end_page=
2504: &Apache::loncommon::end_page();
2505:
2506: $r->print(<<END);
2507: $start_page
2508: <form name="modifyshown" method="post" action="/adm/feedback">
2509: <b>$lt{'diso'}</b><br/> $lt{'prca'}
2510: <br />
2511: <table border="0">
2512: <tr>
2513: <th>$lt{'soor'}</th>
2514: <th> </th>
2515: <th>$lt{'sprs'}</th>
2516: <th> </th>
2517: <th>$lt{'spur'}</th>
2518: <th> </th>
2519: <th>$lt{'spse'}</th>
2520: <th> </th>
2521: <th>$lt{'spgr'}</th>
2522: <th> </th>
2523: <th>$lt{'psub'}</th>
2524: </tr>
2525: <tr>
2526: <td align="center" valign="top">
2527: <select name="sortposts">
2528: <option value="ascdate" selected="selected">$sort_types{'ascdate'}</option>
2529: <option value="descdate">$sort_types{'descdate'}</option>
2530: <option value="thread">$sort_types{'thread'}</option>
2531: <option value="subject">$sort_types{'subject'}</option>
2532: <option value="username">$sort_types{'username'}</option>
2533: <option value="lastfirst">$sort_types{'lastfirst'}</option>
2534: </select>
2535: </td>
2536: <td> </td>
2537: <td align="center" valign="top">
2538: <select name="statusfilter">
2539: <option value="all" selected="selected">$status_types{'all'}</option>
2540: <option value="Active">$status_types{'Active'}</option>
2541: <option value="Expired">$status_types{'Expired'}</option>
2542: <option value="Future">$status_types{'Future'}</option>
2543: </select>
2544: </td>
2545: <td> </td>
2546: <td align="center" valign="top">
2547: <select name="rolefilter" multiple="multiple" size="5">
2548: <option value="all">$role_types{'all'}</option>
2549: <option value="st">$role_types{'st'}</option>
2550: <option value="$ccrole">$role_types{$ccrole}</option>
2551: <option value="in">$role_types{'in'}</option>
2552: <option value="ta">$role_types{'ta'}</option>
2553: <option value="ep">$role_types{'ep'}</option>
2554: <option value="ad">$role_types{'ad'}</option>
2555: <option value="cr">$role_types{'cr'}</option>
2556: </select>
2557: </td>
2558: <td> </td>
2559: <td align="center" valign="top">
2560: <select name="sectionpick" multiple="multiple" size="$numvisible">
2561: $section_sel
2562: </select>
2563: </td>
2564: <td> </td>
2565: <td align="center" valign="top">
2566: <select name="grouppick" multiple="multiple" size="$numvisible">
2567: $group_sel
2568: </select>
2569: </td>
2570: <td> </td>
2571: <td valign="top"><label><input type="checkbox" name="posterlist" value="$symb" />$lt{'shal'}</label></td>
2572: </tr>
2573: </table>
2574: <br />
2575: <input type="hidden" name="inhibitmenu" value="$env{'form.inhibitmenu'}" />
2576: <input type="hidden" name="modal" value="$env{'form.modal'}" />
2577: <input type="hidden" name="previous" value="$previous" />
2578: <input type="hidden" name="applysort" value="$symb" />
2579: <input type="button" name="sub" value="$lt{'stor'}" onclick="verifyFilter()" />
2580: END
2581: if (exists($env{'form.group'})) {
2582: $r->print('<input type="hidden" name="group" value="'.$env{'form.group'}.'" />');
2583: }
2584: if (exists($env{'form.ref'})) {
2585: $r->print('<input type="hidden" name="ref" value="'.$env{'form.ref'}.'" />');
2586: }
2587: $r->print("
2588: <br />
2589: <br />
2590: </form>
2591: $end_page
2592: ");
2593: }
2594:
2595: sub print_showposters {
2596: my ($r,$symb,$previous,$feedurl,$sortposts) = @_;
2597:
2598: &Apache::loncommon::content_type($r,'text/html');
2599: $r->send_http_header;
2600:
2601: &Apache::lonenc::check_encrypt(\$symb);
2602: my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
2603: my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
2604: my $group = $env{'form.group'};
2605: my $ressymb = &wrap_symb($symb);
2606: my $seehidden = &can_see_hidden('',$ressymb,$feedurl,$group,$cdom,$cnum);
2607: my %contrib=&Apache::lonnet::restore($symb,$env{'request.course.id'},
2608: $cdom,$cnum);
2609: my %namesort = ();
2610: my %postcounts = ();
2611:
2612: my %lt = &Apache::lonlocal::texthash(
2613: sele => 'Select',
2614: full => 'Fullname',
2615: usdo => 'Username:domain',
2616: post => 'Posts',
2617: );
2618: if ($contrib{'version'}) {
2619: for (my $idx=1;$idx<=$contrib{'version'};$idx++) {
2620: my $hidden=($contrib{'hidden'}=~/\.$idx\./);
2621: my $deleted=($contrib{'deleted'}=~/\.$idx\./);
2622: unless ((($hidden) && (!$seehidden)) || ($deleted)) {
2623: if ((!$contrib{$idx.':anonymous'}) || (&Apache::lonnet::allowed('rin',$env{'request.course.id'}.($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:'')))) {
2624: my %names = &Apache::lonnet::get('environment',['firstname','lastname'],$contrib{$idx.':senderdomain'},$contrib{$idx.':sendername'});
2625: my $lastname = $names{'lastname'};
2626: my $firstname = $names{'firstname'};
2627: if ($lastname eq '') {
2628: $lastname = '_';
2629: }
2630: if ($firstname eq '') {
2631: $firstname = '_';
2632: }
2633: unless (defined($namesort{$lastname})) {
2634: %{$namesort{$lastname}} = ();
2635: }
2636: my $poster = $contrib{$idx.':sendername'}.':'.$contrib{$idx.':senderdomain'};
2637: $postcounts{$poster} ++;
2638: if (defined($namesort{$lastname}{$firstname})) {
2639: if (!grep/^$poster$/,@{$namesort{$lastname}{$firstname}}) {
2640: push(@{$namesort{$lastname}{$firstname}}, $poster);
2641: }
2642: } else {
2643: @{$namesort{$lastname}{$firstname}} = ("$poster");
2644: }
2645: }
2646: }
2647: }
2648: }
2649:
2650: my $start_page = &Apache::loncommon::start_page('Discussion options');
2651: my $table_start =&Apache::loncommon::start_data_table();
2652: $r->print(<<END);
2653: $start_page
2654: <form name="pickpostersform" method="post" action="">
2655: <br />
2656: $table_start
2657: <tr>
2658: <th>#</th>
2659: <th>$lt{'sele'}</th>
2660: <th>$lt{'full'} <font color="#999999">($lt{'usdo'})</font></th>
2661: <th>$lt{'post'}</th>
2662: </tr>
2663: END
2664: my $count = 0;
2665: foreach my $last (sort(keys(%namesort))) {
2666: foreach my $first (sort(keys(%{$namesort{$last}}))) {
2667: foreach my $user (sort(@{$namesort{$last}{$first}})) {
2668: my ($uname,$udom) = split(/:/,$user);
2669: if (!$uname || !$udom) {
2670: next;
2671: } else {
2672: $count ++;
2673: $r->print(&Apache::loncommon::start_data_table_row().
2674: '<td align="right">'.$count.'</td>
2675: <td align="center"><label><input name="stuinfo" type="checkbox" value="'.$user.'" /></td>
2676: <td>'.$last.', '.$first.' ('.$uname.':'.$udom.')</label></td>
2677: <td>'.$postcounts{$user}.'</td>'.
2678: &Apache::loncommon::end_data_table_row());
2679: }
2680: }
2681: }
2682: }
2683: $r->print(&Apache::loncommon::end_data_table());
2684: my $end_page = &Apache::loncommon::end_page();
2685: $r->print(<<END);
2686: <br />
2687: <input type="hidden" name="sortposts" value="$sortposts" />
2688: <input type="hidden" name="modal" value="$env{'form.modal'}" />
2689: <input type="hidden" name="userpick" value="$symb" />
2690: <input type="button" name="store" value="Display posts" onclick="javascript:document.pickpostersform.submit()" />
2691: </form>
2692: $end_page
2693: END
2694: }
2695:
2696: sub get_post_versions {
2697: my ($versions,$incoming,$htmldecode,$numver) = @_;
2698: if ($incoming =~ /^<version num="0">/) {
2699: my $p = HTML::LCParser->new(\$incoming);
2700: my $done = 0;
2701:
2702: while ( (my $token = $p->get_tag("version")) && (!$done)) {
2703: my $num = $token->[1]{num};
2704: my $text = $p->get_text("/version");
2705: if (defined($numver)) {
2706: if ($num == $numver) {
2707: if ($htmldecode) {
2708: $text = &HTML::Entities::decode($text);
2709: }
2710: $$versions{$numver}=$text;
2711: $done = 1;
2712: }
2713: } else {
2714: if ($htmldecode) {
2715: $text = &HTML::Entities::decode($text);
2716: }
2717: $$versions{$num}=$text;
2718: }
2719: }
2720: } else {
2721: if (!defined($numver)) {
2722: $numver = 0;
2723: }
2724: if ($htmldecode) {
2725: $$versions{$numver} = $incoming;
2726: } else {
2727: $$versions{$numver} = &HTML::Entities::encode($incoming,'<>&"');
2728: }
2729: }
2730: return;
2731: }
2732:
2733: sub get_post_attachments {
2734: my ($attachments,$attachmenturls) = @_;
2735: my $num;
2736: if ($attachmenturls =~ m/^<attachment id="0">/) {
2737: my $p = HTML::LCParser->new(\$attachmenturls);
2738: while (my $token = $p->get_tag("attachment","filename","post")) {
2739: if ($token->[0] eq "attachment") {
2740: $num = $token->[1]{id};
2741: %{$$attachments{$num}} =();
2742: } elsif ($token->[0] eq "filename") {
2743: $$attachments{$num}{'filename'} = $p->get_text("/filename");
2744: } elsif ($token->[0] eq "post") {
2745: my $id = $token->[1]{id};
2746: $$attachments{$num}{$id} = $p->get_text("/post");
2747: }
2748: }
2749: } else {
2750: %{$$attachments{'0'}} = ();
2751: $$attachments{'0'}{'filename'} = $attachmenturls;
2752: $$attachments{'0'}{'0'} = 'n';
2753: }
2754: return;
2755: }
2756:
2757: sub fail_redirect {
2758: my ($r,$feedurl,$delay) = @_;
2759: if ($feedurl=~/^\/adm\//) { $feedurl.='?register=1' };
2760: my %lt = &Apache::lonlocal::texthash(
2761: 'sorr' => 'Sorry, no recipients ...',
2762: );
2763: my $logo=&Apache::loncommon::lonhttpdurl('/adm/lonIcons/lonlogos.gif');
2764: my %parms=('only_body' => 1);
2765: if ($delay !~ /^\d+(|\.\d+)$/) {
2766: $delay = 0;
2767: }
2768: if ($env{'form.modal'}) {
2769: my $onload = 'document.forms.reldt.submit()';
2770: if ($delay) {
2771: my $js_delay = int(1000 * $delay);
2772: $onload = "setTimeout(function(){
2773: document.forms.reldt.submit();
2774: },$js_delay);";
2775: }
2776: $parms{'add_entries'}={'onload' => $onload};
2777: } else {
2778: $parms{'redirect'}=[$delay,$feedurl];
2779: }
2780: $r->print(&Apache::loncommon::start_page('Feedback not sent',undef,\%parms));
2781: my $windowname = 'loncapaclient';
2782: if ($env{'request.lti.login'}) {
2783: $windowname .= 'lti';
2784: }
2785: $r->print(<<ENDFAILREDIR);
2786: <img align="right" src="$logo" />
2787: <p class="LC_warning">$lt{'sorr'}</p>
2788: <form name="reldt" action="$feedurl" target="$windowname">
2789: </form>
2790: ENDFAILREDIR
2791: $r->print(&Apache::loncommon::end_page());
2792: }
2793:
2794: sub redirect_back {
2795: my ($r,$feedurl,$typestyle,$sendsomething,$sendposts,$blog,$status,$previous,$sort,
2796: $rolefilter,$statusfilter,$sectionpick,$grouppick,$numpicks,$group,$toolarge,$delay) = @_;
2797: my $sorttag = '';
2798: my $roletag = '';
2799: my $statustag = '';
2800: my $sectag = '';
2801: my $grptag = '';
2802: my $userpicktag = '';
2803: my $qrystr = '';
2804: my $prevtag = '';
2805:
2806: &Apache::loncommon::content_type($r,'text/html');
2807: $r->send_http_header;
2808: &dewrapper(\$feedurl);
2809: if ($feedurl=~/^\/adm\//) { $feedurl.='?register=1' };
2810: if ($previous > 0) {
2811: $qrystr = 'previous='.$previous;
2812: if ($feedurl =~ /\?register=1/) {
2813: $feedurl .= '&'.$qrystr;
2814: } else {
2815: $feedurl .= '?'.$qrystr;
2816: }
2817: $prevtag = '<input type="hidden" name="previous" value="'.$previous.'" />';
2818: }
2819: if (defined($sort)) {
2820: my $sortqry = 'sortposts='.$sort;
2821: if (($feedurl =~ /\?register=1/) || ($feedurl =~ /\?previous=/)) {
2822: $feedurl .= '&'.$sortqry;
2823: } else {
2824: $feedurl .= '?'.$sortqry;
2825: }
2826: $sorttag = '<input type="hidden" name="sortposts" value="'.$sort.'" />';
2827: if (defined($numpicks)) {
2828: my $userpickqry = 'totposters='.$numpicks;
2829: $feedurl .= '&'.$userpickqry;
2830: $userpicktag = '<input type="hidden" name="totposters" value="'.$numpicks.'" />';
2831: } else {
2832: if (ref($sectionpick) eq 'ARRAY') {
2833: $feedurl .= '&sectionpick=';
2834: $sectag .= '<input type="hidden" name="sectionpick" value="';
2835: foreach my $sec (@{$sectionpick}) {
2836: $feedurl .= $sec.',';
2837: $sectag .= $sec.',';
2838: }
2839: $feedurl =~ s/,$//;
2840: $sectag =~ s/,$//;
2841: $sectag .= '" />';
2842: } else {
2843: $feedurl .= '&sectionpick='.$sectionpick;
2844: $sectag = '<input type="hidden" name="sectionpick" value="'.$sectionpick.'" />';
2845: }
2846: if (ref($grouppick) eq 'ARRAY') {
2847: $feedurl .= '&grouppick=';
2848: $sectag .= '<input type="hidden" name="grouppick" value="';
2849: foreach my $grp (@{$grouppick}) {
2850: $feedurl .= $grp.',';
2851: $grptag .= $grp.',';
2852: }
2853: $feedurl =~ s/,$//;
2854: $grptag =~ s/,$//;
2855: $grptag .= '" />';
2856: } else {
2857: $feedurl .= '&grouppick='.$grouppick;
2858: $grptag = '<input type="hidden" name="grouppick" value="'.$grouppick.'" />';
2859: }
2860: if (ref($rolefilter) eq 'ARRAY') {
2861: $feedurl .= '&rolefilter=';
2862: $roletag .= '<input type="hidden" name="rolefilter" value="';
2863: foreach my $role (@{$rolefilter}) {
2864: $feedurl .= $role.',';
2865: $roletag .= $role.',';
2866: }
2867: $feedurl =~ s/,$//;
2868: $roletag =~ s/,$//;
2869: $roletag .= '" />';
2870: } else {
2871: $feedurl .= '&rolefilter='.$rolefilter;
2872: $roletag = '<input type="hidden" name="rolefilter" value="'.$rolefilter.'" />';
2873: }
2874: $feedurl .= '&statusfilter='.$statusfilter;
2875: $statustag ='<input type="hidden" name="statusfilter" value="'.$statusfilter.'" />';
2876: }
2877: }
2878: my $grouptag;
2879: if ($group ne '') {
2880: $grouptag = '<input type="hidden" name="group" value="'.$group.'" />';
2881: my $refarg;
2882: if (exists($env{'form.ref'})) {
2883: $refarg = '&ref='.$env{'form.ref'};
2884: $grouptag .= '<input type="hidden" name="ref" value="'.$env{'form.ref'}.'" />';
2885: }
2886: if ($feedurl =~ /\?/) {
2887: $feedurl .= '&group='.$group.$refarg;
2888: } else {
2889: $feedurl .= '?group='.$group.$refarg;
2890: }
2891: }
2892: &Apache::lonenc::check_encrypt(\$feedurl);
2893: my $logo=&Apache::loncommon::lonhttpdurl('/adm/lonIcons/lonlogos.gif');
2894: my %parms=('only_body' => 1);
2895: if ($delay !~ /^\d+(|\.\d+)$/) {
2896: $delay = 0;
2897: }
2898: if ($env{'form.modal'}) {
2899: my $onload = 'document.forms.reldt.submit()';
2900: if ($delay) {
2901: my $js_delay = int(1000 * $delay);
2902: $onload = "setTimeout(function(){
2903: document.forms.reldt.submit();
2904: },$js_delay);";
2905: }
2906: $parms{'add_entries'}={'onload' => $onload};
2907: } else {
2908: $parms{'redirect'}=[$delay,$feedurl];
2909: }
2910: my $start_page=
2911: &Apache::loncommon::start_page('Feedback sent',undef,\%parms);
2912: my $end_page = &Apache::loncommon::end_page();
2913: my $windowname = 'loncapaclient';
2914: if ($env{'request.lti.login'}) {
2915: $windowname .= 'lti';
2916: }
2917: $r->print(<<ENDREDIR);
2918: $start_page
2919: <img align="right" src="$logo" />
2920: $typestyle
2921: <b>Sent $sendsomething message(s), and $sendposts post(s).</b>
2922: $blog
2923: $toolarge
2924: <font color="red">$status</font>
2925: <form name="reldt" action="$feedurl" target="$windowname">
2926: $prevtag
2927: $sorttag
2928: $statustag
2929: $roletag
2930: $sectag
2931: $grptag
2932: $userpicktag
2933: $grouptag
2934: </form>
2935: $end_page
2936: ENDREDIR
2937: }
2938:
2939: sub no_redirect_back {
2940: my ($r,$feedurl,$delay) = @_;
2941: my $nofeed=&mt('Sorry, no feedback possible on this resource ...');
2942: my $form_for_modal;
2943: my %parms=('only_body' => 1,
2944: 'bgcolor' => '#FFFFFF',);
2945: if ($delay !~ /^\d+(|\.\d+)$/) {
2946: $delay = 0;
2947: }
2948: if ($env{'form.modal'}) {
2949: if (($feedurl !~ m{^/adm/feedback}) && ($feedurl ne '')) {
2950: my $onload = 'document.forms.reldt.submit()';
2951: if ($delay) {
2952: my $js_delay = int(1000 * $delay);
2953: $onload = "setTimeout(function(){
2954: document.forms.reldt.submit();
2955: },$js_delay);";
2956: }
2957: $parms{'add_entries'}={'onload' => $onload};
2958: my $windowname = 'loncapaclient';
2959: if ($env{'request.lti.login'}) {
2960: $windowname .= 'lti';
2961: }
2962: $form_for_modal = <<ENDFORM;
2963: <form name="reldt" action="$feedurl" target="$windowname">
2964: </form>
2965: ENDFORM
2966: }
2967: } else {
2968: if (($feedurl !~ m{^/adm/feedback}) && ($feedurl ne '')) {
2969: $parms{'redirect'}=[$delay,$feedurl];
2970: }
2971: }
2972:
2973: my $start_page=
2974: &Apache::loncommon::start_page('Feedback not sent',undef,
2975: \%parms);
2976:
2977: my $end_page = &Apache::loncommon::end_page();
2978:
2979: my $logo=&Apache::loncommon::lonhttpdurl('/adm/lonIcons/lonlogos.gif');
2980: $r->print (<<ENDNOREDIRTWO);
2981: $start_page
2982: <img align="right" src="$logo" />
2983: <b>$nofeed</b>
2984: <br />
2985: $form_for_modal
2986: $end_page
2987: ENDNOREDIRTWO
2988: }
2989:
2990: sub screen_header {
2991: my ($feedurl,$symb,$group) = @_;
2992: my %default = &Apache::lonlocal::texthash (
2993: question => 'Question about resource content',
2994: comment => 'Question/Comment/Feedback about course content',
2995: policy => 'Question/Comment/Feedback about course policy',
2996: );
2997: my $contribdisc = &mt('Contribution to course discussion of resource');
2998: my $anoncontrib = &mt('Anonymous contribution to course discussion of resource');
2999: my $namevis = &mt('name only visible to course faculty');
3000: my $crstype;
3001: if ($env{'request.course.id'}) {
3002: $crstype = &Apache::loncommon::course_type();
3003: if ($crstype eq 'Community') {
3004: $default{'comment'} = &mt('Question/Comment/Feedback about community content');
3005: $default{'policy'} = &mt('Question/Comment/Feedback about community policy');
3006: $contribdisc = &mt('Contribution to community discussion of resource');
3007: $anoncontrib = &mt('Anonymous contribution to community discussion of resource');
3008: $namevis = &mt('name only visible to community facilitators');
3009: }
3010: }
3011: my $msgoptions='';
3012: my $discussoptions='';
3013: my $checkradio = '';
3014: my $blockblog;
3015: my (%fdbkoptions,%discoptions);
3016: unless (($env{'form.replydisc'}) || ($env{'form.editdisc'})) {
3017: if (($feedurl=~/^\/res\//) && ($feedurl!~/^\/res\/adm/) && ($env{'user.adv'})) {
3018: $fdbkoptions{'author'} = 1;
3019: }
3020: if (&feedback_available(1)) {
3021: $fdbkoptions{'question'} = 1;
3022: }
3023: if (&feedback_available(0,1)) {
3024: $fdbkoptions{'course'} = 1;
3025: }
3026: if (&feedback_available(0,0,1)) {
3027: $fdbkoptions{'policy'} = 1;
3028: }
3029: }
3030: if (($env{'request.course.id'}) && (!$env{'form.sendmessageonly'})) {
3031: my ($blocked,$blocktext) = &Apache::loncommon::blocking_status('boards');
3032: my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
3033: my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
3034: my $realsymb = &get_realsymb($symb);
3035: if (!$blocked && &discussion_open(undef,$realsymb) &&
3036: (&Apache::lonnet::allowed('pch',
3037: $env{'request.course.id'}.
3038: ($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:'')) ||
3039: (($group ne '') && ($symb =~ m{^bulletin___\d+___adm/wrapper/adm/\Q$cdom\E/\Q$cnum\E/\d+/bulletinboard$}) && (&check_group_priv($group,'pgd') eq 'ok')))) {
3040: $discoptions{'nonanon'} = 1;
3041: if (&Apache::lonnet::allowed('pac',$env{'request.course.id'}.
3042: ($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:''))) {
3043: $discoptions{'anon'} = 1;
3044: }
3045: $blockblog = &Apache::loncommon::blocking_status('blogs');
3046: }
3047: }
3048: my $total = scalar(keys(%fdbkoptions)) + scalar(keys(%discoptions));
3049: return if (!$total);
3050: if ($total == 1) {
3051: $checkradio = ' checked="checked"';
3052: }
3053: if (keys(%fdbkoptions)) {
3054: if ($fdbkoptions{'author'}) {
3055: $msgoptions =
3056: '<label><input type="radio" name="discuss" value="author"'.$checkradio.' /> '.
3057: &mt('Feedback to resource author').'</label><br />';
3058: }
3059: foreach my $item ('question','comment','policy') {
3060: my $type = $item;
3061: if ($item eq 'comment') {
3062: $type = 'course';
3063: }
3064: my $optionhash=$env{'course.'.$env{'request.course.id'}.'.'.$item.'.email.text'};
3065: if ($fdbkoptions{$type}) {
3066: $msgoptions .=
3067: '<label><input type="radio" name="discuss" value="'.$type.'"'.$checkradio.' />'.
3068: ($optionhash?$optionhash:$default{$item}).'</label><br />';
3069: }
3070: }
3071: }
3072: if (keys(%discoptions)) {
3073: if ($discoptions{'nonanon'}) {
3074: $discussoptions='<label><input type="radio" name="discuss" value="nonanon" checked="checked" /> '.
3075: $contribdisc.
3076: '</label>';
3077: }
3078: if ($discoptions{'anon'}) {
3079: $discussoptions .= '<br /><label><input type="radio" name="discuss" value="anon"'.$checkradio.' /> '.
3080: $anoncontrib.
3081: ' <i>('.$namevis.')</i></label> '.
3082: '<a href="/adm/preferences?action=changescreenname">'.&mt('Change Screenname').'</a>';
3083: }
3084: if (!$blockblog) {
3085: $discussoptions.= &add_blog_checkbox($crstype);
3086: }
3087: }
3088: if ($msgoptions) {
3089: $msgoptions='<tr><th><img src="'.&Apache::loncommon::lonhttpdurl('/res/adm/pages/feedback.png').'" />'
3090: .'<br />'.&mt('Send Feedback').'<br />'.&Apache::lonhtmlcommon::coursepreflink(&mt('Feedback Settings'),'feedback').'</th>'.
3091: '<td>'.$msgoptions.'</td></tr>';
3092: }
3093: if ($discussoptions) {
3094: $discussoptions='<tr><th><img src="'.&Apache::loncommon::lonhttpdurl('/adm/lonMisc/chat.gif').'" alt="" />'
3095: .'<br />'.&mt('Discussion Contributions').'<br />'.&Apache::lonhtmlcommon::coursepreflink(&mt('Discussion Settings'),'discussion').'</th>'.
3096: '<td>'.$discussoptions.'</td></tr>';
3097: }
3098: return &Apache::loncommon::start_data_table().$msgoptions.$discussoptions.&Apache::loncommon::end_data_table();
3099: }
3100:
3101: sub resource_output {
3102: my ($feedurl) = @_;
3103: my $usersaw=&Apache::lonnet::ssi_body($feedurl);
3104: $usersaw=~s/\<body[^\>]*\>//gi;
3105: $usersaw=~s/\<\/body\>//gi;
3106: $usersaw=~s/\<html\>//gi;
3107: $usersaw=~s/\<\/html\>//gi;
3108: $usersaw=~s/\<head\>//gi;
3109: $usersaw=~s/\<\/head\>//gi;
3110: $usersaw=~s/action\s*\=/would_be_action\=/gi;
3111: return $usersaw;
3112: }
3113:
3114: sub clear_out_html {
3115: my ($message,$override)=@_;
3116: # Always allow the <m>-tag
3117: my %html=(M=>1);
3118: # Check if more is allowed
3119: my $cid=$env{'request.course.id'};
3120: if (($env{"course.$cid.allow_limited_html_in_feedback"} =~ m/yes/i) ||
3121: ($override)) {
3122: # allows <B> <I> <P> <A> <LI> <OL> <UL> <EM> <BR> <TT> <STRONG>
3123: # <BLOCKQUOTE> <DIV .*> <DIV> <IMG> <M> <SPAN> <H1> <H2> <H3> <H4> <SUB>
3124: # <SUP> <TABLE> <TR> <TD> <TH> <TBODY>
3125: %html=(B=>1, I=>1, P=>1, A=>1, LI=>1, OL=>1, UL=>1, EM=>1,
3126: BR=>1, TT=>1, STRONG=>1, BLOCKQUOTE=>1, PRE=>1, DIV=>1, IMG=>1,
3127: M=>1, CHEM=>1, ALGEBRA=>1, SUB=>1, SUP=>1, SPAN=>1,
3128: H1=>1, H2=>1, H3=>1, H4=>1, H5=>1, H6=>1,
3129: TABLE=>1, TR=>1, TD=>1, TH=>1, TBODY=>1);
3130: }
3131: # Do the substitution of everything that is not explicitly allowed
3132: $message =~ s/\<(\/?\s*(\w+)[^\>\<]*)/
3133: {($html{uc($2)}&&(length($1)<1000))?"\<$1":"\<$1"}/ge;
3134: $message =~ s/(\<?\s*(\w+)[^\<\>]*)\>/
3135: {($html{uc($2)}&&(length($1)<1000))?"$1\>":"$1\>"}/ge;
3136: return $message;
3137: }
3138:
3139: sub assemble_email {
3140: my ($message,$prevattempts,$usersaw,$useranswer)=@_;
3141: my %lt = &Apache::lonlocal::texthash(
3142: 'prev' => 'Previous attempts of student (if applicable)',
3143: 'orig' => 'Original screen output (if applicable)',
3144: 'corr' => 'Correct Answer(s) (if applicable)',
3145: );
3146: if (&Apache::loncommon::course_type() eq 'Community') {
3147: $lt{'prev'} = &mt('Previous attempts of member (if applicable)');
3148: }
3149: my $email=<<"ENDEMAIL";
3150: $message
3151: ENDEMAIL
3152: my $citations=<<"ENDCITE";
3153: <h2>$lt{'prev'}</h2>
3154: $prevattempts
3155: <br /><hr />
3156: <h2>$lt{'orig'}</h2>
3157: $usersaw
3158: <h2>$lt{'corr'}</h2>
3159: $useranswer
3160: ENDCITE
3161: return ($email,$citations);
3162: }
3163:
3164:
3165: sub feedback_available {
3166: my ($question,$course,$policy)=@_;
3167: my ($typestyle,%to)=&Apache::lonmsg::decide_receiver('',0,$question,
3168: $course,$policy);
3169: return scalar(%to);
3170: }
3171:
3172: sub send_msg {
3173: my ($title,$feedurl,$email,$citations,$attachmenturl,$symb,$clientip,%to)=@_;
3174: my $status='';
3175: my $sendsomething=0;
3176: my $delay;
3177: my $senthide;
3178: my %setters;
3179: my ($startblock,$endblock,$triggerblock,$by_ip,$blockdom) =
3180: &Apache::loncommon::blockcheck(\%setters,'com',$clientip);
3181: if ($by_ip) {
3182: $senthide = 1;
3183: }
3184: my $restitle = &get_resource_title($symb,$feedurl);
3185: if ($title=~/^Error/) { $title=&mt('Feedback').': '.$title; }
3186: unless ($title=~/\w/) { $title=&mt('Feedback'); }
3187: foreach my $key (keys(%to)) {
3188: if ($key) {
3189: my ($user,$domain) = split(/\:/,$key,2);
3190: if (!defined($user)) {
3191: $status.='<br />'.&mt('Error sending message to [_1], no user specified.',$key);
3192: } elsif (!defined($domain)) {
3193: $status.='<br />'.&mt('Error sending message to [_1], no domain specified.',$key);
3194: } else {
3195: unless (&Apache::lonmsg::user_normal_msg($user,$domain,
3196: $title.' ['.$restitle.']',$email,$citations,$feedurl,
3197: $attachmenturl,undef,undef,$symb,$restitle,undef,
3198: undef,undef,undef,$senthide)=~/ok/) {
3199: $status.='<br />'.&mt('Error sending message to').' '.$key.'<br />';
3200: } else {
3201: $sendsomething++;
3202: }
3203: }
3204: }
3205: }
3206: if ($sendsomething && $senthide) {
3207: if ($by_ip) {
3208: my $showdom = &Apache::lonnet::domain($blockdom);
3209: if ($showdom eq '') {
3210: $showdom = $blockdom;
3211: }
3212: $delay = 4;
3213: $status.='<br />'.&mt("Message content of feedback you send to instructor(s) from your current IP address: [_1] will be unavailable in your 'Sent' folder.",$clientip).
3214: '<ul><li>'.
3215: &mt('This does not affect delivery of feedback to your instructor(s).').
3216: '</li><li>'.
3217: &mt('Note: some types of communication functionality are blocked for certain IP address(es).').
3218: '</li><li>'.
3219: &mt('This restriction was set by an administrator in the [_1] LON-CAPA domain.',
3220: $showdom).
3221: '</li></ul><br />';
3222: }
3223: }
3224:
3225: # Records of number of feedback messages are kept under the "symb" called "_feedback"
3226: # There are two entries within the framework of a course:
3227: # - the URLs for which feedback was provided
3228: # - the total number of contributions
3229: if ($sendsomething) {
3230: my %record=&getfeedbackrecords();
3231: my ($temp)=keys(%record);
3232: unless ($temp=~/^error\:/) {
3233: my %newrecord=();
3234: $newrecord{'resource'}=$feedurl;
3235: $newrecord{'subnumber'}=$record{'subnumber'}+1;
3236: unless (&Apache::lonnet::cstore(\%newrecord,'_feedback') eq 'ok') {
3237: $status.='<br />'.&mt('Not registered').'<br />';
3238: }
3239: }
3240: }
3241: return ($status,$sendsomething,$delay);
3242: }
3243:
3244: # Routine to get the complete feedback records
3245:
3246: sub getfeedbackrecords {
3247: my ($uname,$udom,$course)=@_;
3248: unless ($uname) { $uname=$env{'user.name'}; }
3249: unless ($udom) { $udom=$env{'user.domain'}; }
3250: unless ($course) { $course=$env{'request.course.id'}; }
3251: my %record=&Apache::lonnet::restore('_feedback',$course,$udom,$uname);
3252: return %record;
3253: }
3254:
3255: # Routine to get feedback statistics
3256:
3257: sub getfeedbackstats {
3258: my %record=&getfeedbackrecords(@_);
3259: return ($record{'subnumber'},$record{'points'},$record{'totallikes'});
3260: }
3261:
3262: # Store feedback credit
3263:
3264: sub storefeedbackpoints {
3265: my ($points,$uname,$udom,$course)=@_;
3266: unless ($points) { $points=0; }
3267: unless ($uname) { $uname=$env{'user.name'}; }
3268: unless ($udom) { $udom=$env{'user.domain'}; }
3269: unless ($course) { $course=$env{'request.course.id'}; }
3270: my %record=('grader_user' => $env{'user.name'},
3271: 'grader_domain' => $env{'user.domain'},
3272: 'points' => $points);
3273: return &Apache::lonnet::cstore(\%record,'_feedback',$course,$udom,$uname);
3274: }
3275:
3276: # Store feedback "likes"
3277:
3278: sub storefeedbacklikes {
3279: my ($likes,$uname,$udom,$course)=@_;
3280: unless ($likes) { $likes=0; }
3281: if ($likes>0) { $likes=1; }
3282: if ($likes<0) { $likes=-1; }
3283: unless ($uname) { $uname=$env{'user.name'}; }
3284: unless ($udom) { $udom=$env{'user.domain'}; }
3285: unless ($course) { $course=$env{'request.course.id'}; }
3286: my %record=&getfeedbackrecords($uname,$udom,$course);
3287: my $totallikes=$record{'totallikes'};
3288: $totallikes+=$likes;
3289: my %newrecord=('likes_user' => $env{'user.name'},
3290: 'likes_domain' => $env{'user.domain'},
3291: 'likes' => $likes,
3292: 'totallikes' => $totallikes);
3293: return &Apache::lonnet::cstore(\%newrecord,'_feedback',$course,$udom,$uname);
3294: }
3295:
3296:
3297: sub adddiscuss {
3298: my ($symb,$email,$anon,$attachmenturl,$subject,$group)=@_;
3299: my $status='';
3300: my $realsymb = &get_realsymb($symb);
3301: my ($cnum,$cdom);
3302: if ($env{'request.course.id'}) {
3303: $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
3304: $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
3305: }
3306: if (&discussion_open(undef,$realsymb) &&
3307: (&Apache::lonnet::allowed('pch',$env{'request.course.id'}.
3308: ($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:'')) ||
3309: (($group ne '') && (&check_group_priv($group,'pgd') eq 'ok') &&
3310: ($symb =~ m{^bulletin___\d+___adm/wrapper/adm/\Q$cdom\E/\Q$cnum\E/\d+/bulletinboard$})))) {
3311:
3312: my %contrib=('message' => $email,
3313: 'sendername' => $env{'user.name'},
3314: 'senderdomain' => $env{'user.domain'},
3315: 'screenname' => $env{'environment.screenname'},
3316: 'plainname' => $env{'environment.firstname'}.' '.
3317: $env{'environment.middlename'}.' '.
3318: $env{'environment.lastname'}.' '.
3319: $env{'enrironment.generation'},
3320: 'attachmenturl'=> $attachmenturl,
3321: 'subject' => $subject);
3322: if ($env{'form.replydisc'}) {
3323: $contrib{'replyto'}=(split(/\:\:\:/,$env{'form.replydisc'}))[1];
3324: }
3325: if ($anon) {
3326: $contrib{'anonymous'}='true';
3327: }
3328: if (($symb) && ($email)) {
3329: my $now = time;
3330: if ($env{'form.editdisc'}) {
3331: $contrib{'ip'}=&Apache::lonnet::get_requestor_ip();
3332: $contrib{'host'}=$Apache::lonnet::perlvar{'lonHostID'};
3333: $contrib{'timestamp'} = $now;
3334: $contrib{'history'} = '';
3335: my $numoldver = 0;
3336: my ($oldsymb,$oldidx)=split(/\:\:\:/,$env{'form.editdisc'});
3337: &Apache::lonenc::check_decrypt(\$oldsymb);
3338: $oldsymb=~s|(bulletin___\d+___)adm/wrapper/|$1|;
3339: # get timestamp for last post and history
3340: my %oldcontrib=&Apache::lonnet::restore($oldsymb,$env{'request.course.id'},
3341: $env{'course.'.$env{'request.course.id'}.'.domain'},
3342: $env{'course.'.$env{'request.course.id'}.'.num'});
3343: if (defined($oldcontrib{$oldidx.':replyto'})) {
3344: $contrib{'replyto'} = $oldcontrib{$oldidx.':replyto'};
3345: }
3346: if (defined($oldcontrib{$oldidx.':history'})) {
3347: if ($oldcontrib{$oldidx.':history'} =~ /:/) {
3348: my @oldversions = split(/:/,$oldcontrib{$oldidx.':history'});
3349: $numoldver = @oldversions;
3350: } else {
3351: $numoldver = 1;
3352: }
3353: $contrib{'history'} = $oldcontrib{$oldidx.':history'}.':';
3354: }
3355: my $numnewver = $numoldver + 1;
3356: if (defined($oldcontrib{$oldidx.':subject'})) {
3357: if ($oldcontrib{$oldidx.':subject'} =~ /^<version num="0">/) {
3358: $contrib{'subject'} = '<version num="'.$numnewver.'">'.&HTML::Entities::encode($contrib{'subject'},'<>&"').'</version>';
3359: $contrib{'subject'} = $oldcontrib{$oldidx.':subject'}.$contrib{'subject'};
3360: } else {
3361: $contrib{'subject'} = '<version num="0">'.&HTML::Entities::encode($oldcontrib{$oldidx.':subject'},'<>&"').'</version><version num="1">'.&HTML::Entities::encode($contrib{'subject'},'<>&"').'</version>';
3362: }
3363: }
3364: if (defined($oldcontrib{$oldidx.':message'})) {
3365: if ($oldcontrib{$oldidx.':message'} =~ /^<version num="0">/) {
3366: $contrib{'message'} = '<version num="'.$numnewver.'">'.&HTML::Entities::encode($contrib{'message'},'<>&"').'</version>';
3367: $contrib{'message'} = $oldcontrib{$oldidx.':message'}.$contrib{'message'};
3368: } else {
3369: $contrib{'message'} = '<version num="0">'.&HTML::Entities::encode($oldcontrib{$oldidx.':message'},'<>&"').'</version><version num="1">'.&HTML::Entities::encode($contrib{'message'},'<>&"').'</version>';
3370: }
3371: }
3372: $contrib{'history'} .= $oldcontrib{$oldidx.':timestamp'};
3373: my $put_reply = &Apache::lonnet::putstore($env{'request.course.id'},
3374: $oldsymb,$oldidx,\%contrib,
3375: $env{'course.'.$env{'request.course.id'}.'.domain'},
3376: $env{'course.'.$env{'request.course.id'}.'.num'});
3377: $status='Editing class discussion'.($anon?' (anonymous)':'');
3378: } else {
3379: $status='Adding to class discussion'.($anon?' (anonymous)':'').': '.
3380: &Apache::lonnet::store(\%contrib,$symb,$env{'request.course.id'},
3381: $env{'course.'.$env{'request.course.id'}.'.domain'},
3382: $env{'course.'.$env{'request.course.id'}.'.num'});
3383: }
3384: my %storenewentry=($symb => $now);
3385: $status.='<br />'.&mt('Updating discussion time').': '.
3386: &Apache::lonnet::put('discussiontimes',\%storenewentry,
3387: $env{'course.'.$env{'request.course.id'}.'.domain'},
3388: $env{'course.'.$env{'request.course.id'}.'.num'});
3389: }
3390: my %record=&getdiscussionrecords();
3391: my ($temp)=keys(%record);
3392: unless ($temp=~/^error\:/) {
3393: my %newrecord=();
3394: $newrecord{'resource'}=$symb;
3395: $newrecord{'subnumber'}=$record{'subnumber'}+1;
3396: $status.='<br />'.&mt('Registering').': '.
3397: &Apache::lonnet::cstore(\%newrecord,'_discussion');
3398: &updatekarma();
3399: }
3400: } else {
3401: $status.='Failed.';
3402: }
3403: return $status.'<br />';
3404: }
3405:
3406:
3407: # Routine to get the complete discussion records
3408:
3409: sub getdiscussionrecords {
3410: my ($uname,$udom,$course)=@_;
3411: unless ($uname) { $uname=$env{'user.name'}; }
3412: unless ($udom) { $udom=$env{'user.domain'}; }
3413: unless ($course) { $course=$env{'request.course.id'}; }
3414: my %record=&Apache::lonnet::restore('_discussion',$course,$udom,$uname);
3415: return %record;
3416: }
3417:
3418: # Routine to get discussion statistics
3419:
3420: sub getdiscussionstats {
3421: my %record=&getdiscussionrecords(@_);
3422: my $totalvotes = $record{'totallikes'} + $record{'totalunlikes'};
3423: return ($record{'subnumber'},$record{'points'},$record{'totallikes'},$totalvotes);
3424: }
3425:
3426: # Calculate discussion karma
3427:
3428: sub calcdiscussionkarma {
3429: my ($subs,$pts,$likes,$votes)=&getdiscussionstats(@_);
3430: my $karma=0;
3431: if ($votes>0) {
3432: $karma=int(.1+5.*(1.-exp(-$subs/10.))*$likes/$votes);
3433: if ($karma<0) { $karma=0; }
3434: if ($karma>5) { $karma=5; }
3435: }
3436: return $karma;
3437: }
3438:
3439: # Update karma
3440:
3441: sub updatekarma {
3442: my ($uname,$udom,$course)=@_;
3443: unless ($uname) { $uname=$env{'user.name'}; }
3444: unless ($udom) { $udom=$env{'user.domain'}; }
3445: unless ($course) { $course=$env{'request.course.id'}; }
3446: my $karma=&calcdiscussionkarma($uname,$udom,$course);
3447: &Apache::lonnet::cstore({ 'karma' => $karma },'_discussion',$course,$udom,$uname);
3448: &Apache::lonnet::do_cache_new('karma',$uname.':'.$udom.':'.$course,$karma,3600);
3449: return $karma;
3450: }
3451:
3452: # Retrieve karma
3453:
3454: sub userkarma {
3455: my ($uname,$udom,$course)=@_;
3456: unless ($uname) { $uname=$env{'user.name'}; }
3457: unless ($udom) { $udom=$env{'user.domain'}; }
3458: unless ($course) { $course=$env{'request.course.id'}; }
3459: my $hashkey=$uname.':'.$udom.':'.$course;
3460: my ($karma,$cached)=&Apache::lonnet::is_cached_new('karma',$hashkey);
3461: if ($cached) {
3462: return $karma;
3463: }
3464: my %userdisc=&getdiscussionrecords($uname,$udom,$course);
3465: $karma=$userdisc{'karma'};
3466: &Apache::lonnet::do_cache_new('karma',$hashkey,$karma,3600);
3467: return $karma;
3468: }
3469:
3470: # Store discussion credit
3471:
3472: sub storediscussionpoints {
3473: my ($points,$uname,$udom,$course)=@_;
3474: unless ($points) { $points=0; }
3475: unless ($uname) { $uname=$env{'user.name'}; }
3476: unless ($udom) { $udom=$env{'user.domain'}; }
3477: unless ($course) { $course=$env{'request.course.id'}; }
3478: my %record=('grader_user' => $env{'user.name'},
3479: 'grader_domain' => $env{'user.domain'},
3480: 'points' => $points);
3481: return &Apache::lonnet::cstore(\%record,'_discussion',$course,$udom,$uname);
3482: }
3483:
3484: # Store discussion "likes"
3485:
3486: sub storediscussionlikes {
3487: my ($chglikes,$chgunlikes,$uname,$udom,$course,$context)=@_;
3488: unless ($uname) { $uname=$env{'user.name'}; }
3489: unless ($udom) { $udom=$env{'user.domain'}; }
3490: unless ($course) { $course=$env{'request.course.id'}; }
3491: my %record=&getdiscussionrecords($uname,$udom,$course);
3492: my $totallikes=$record{'totallikes'};
3493: my $totalunlikes=$record{'totalunlikes'};
3494: $totallikes += $chglikes;
3495: $totalunlikes += $chgunlikes;
3496: my %newrecord=('likes_user' => $env{'user.name'},
3497: 'likes_domain' => $env{'user.domain'},
3498: 'totallikes' => $totallikes,
3499: 'totalunlikes' => $totalunlikes,
3500: 'context' => $context);
3501: my $status=&Apache::lonnet::cstore(\%newrecord,'_discussion',$course,$udom,$uname);
3502: if ($status eq 'ok') {
3503: &updatekarma($uname,$udom,$course);
3504: }
3505: return $status;
3506: }
3507:
3508: sub get_discussion_info {
3509: my ($idx,%contrib) = @_;
3510: my $changelast = 0;
3511: my $count = 0;
3512: my $hiddenflag = 0;
3513: my $deletedflag = 0;
3514: my ($hidden,$deleted,%info,$newlastdisc);
3515: my $version = $contrib{'version'};
3516: if ($version) {
3517: for (my $id=$version; $id>0; $id--) {
3518: my $vkeys=$contrib{$id.':keys'};
3519: my @keys=split(/:/,$vkeys);
3520: if (grep(/^hidden$/,@keys)) {
3521: if (!$hiddenflag) {
3522: $hidden = $contrib{$id.':hidden'};
3523: $hiddenflag = 1;
3524: }
3525: } elsif (grep(/^deleted$/,@keys)) {
3526: if (!$deletedflag) {
3527: $deleted = $contrib{$id.':deleted'};
3528: $deletedflag = 1;
3529: }
3530: } else {
3531: if (($hidden !~/\.$id\./) && ($deleted !~/\.$id\./)) {
3532: $count++;
3533: $info{$count}{'id'} = $id;
3534: $info{$count}{'timestamp'}=$contrib{$id.':timestamp'};
3535: }
3536: }
3537: }
3538: if ($info{'1'}{'id'} == $idx) {
3539: $changelast = 1;
3540: if ($count > 1) {
3541: $newlastdisc = $info{'2'}{'timestamp'};
3542: } else {
3543: $newlastdisc = 0;
3544: }
3545: }
3546: }
3547: return ($changelast,$newlastdisc);
3548: }
3549:
3550: # ----------------------------------------------------------- Preview function
3551:
3552: sub show_preview {
3553: my ($r) = @_;
3554: &Apache::loncommon::content_type($r,'text/html');
3555: $r->send_http_header;
3556: my $start_page=
3557: &Apache::loncommon::start_page('Preview',undef,
3558: {'only_body' => 1,});
3559:
3560: my $message=&clear_out_html($env{'form.comment'});
3561: &newline_to_br(\$message);
3562: $message=&Apache::lonspeller::markeduptext($message);
3563: $message=&Apache::lontexconvert::msgtexconverted($message);
3564: my $subject=&clear_out_html($env{'form.subject'});
3565: $subject=~s/\n/\<br \/\>/g;
3566: $subject=&Apache::lontexconvert::msgtexconverted($subject);
3567:
3568: my $end_page = &Apache::loncommon::end_page();
3569:
3570: $r->print($start_page
3571: .'<h1>'.&mt('Preview').'</h1>'
3572: .&Apache::lonhtmlcommon::start_pick_box()
3573: .&Apache::lonhtmlcommon::row_title(&mt('Subject'))
3574: .$subject
3575: .&Apache::lonhtmlcommon::row_closure()
3576: .&Apache::lonhtmlcommon::row_title(&mt('Message'))
3577: .$message
3578: .&Apache::lonhtmlcommon::row_closure(1)
3579: .&Apache::lonhtmlcommon::end_pick_box()
3580: .$end_page
3581: );
3582: }
3583:
3584: sub contains_block_html {
3585: my ($message)=@_;
3586: return ($message =~ m{
3587: <(br|h1|h2|h3|h4|h5|h6|p|ol|ul|table|pre|address|blockquote|center|div)
3588: \s*
3589: (\w+\=['"]\w+['"])*
3590: \s*
3591: (
3592: \s*/>|
3593: >.*</\1\s*>
3594: )}xs
3595: );
3596: }
3597:
3598: sub tidy_html {
3599: my ($message)=@_;
3600: # my $tidy = HTML::Tidy::libXML->new();
3601: # my $xhtml = $tidy->clean($message, 'utf-8', 1);
3602: # $xhtml =~ m/<body>(.*)<\/body>/is;
3603: # my $clean = $1;
3604: # # remove any empty block-level tags
3605: # $clean =~ s/<(table|p|div|tbody|blockquote|m|pre|algebra|center|ol|ul|span|h1|h2|h3|h4|h5|h6)\s*\/>//i;
3606: # $message=$clean;
3607: return $message;
3608: }
3609:
3610: sub newline_to_br {
3611: my ($message)=@_;
3612: my $newmessage;
3613: my $parser=HTML::LCParser->new($message);
3614: while (my $token=$parser->get_token()) {
3615: if ($token->[0] eq 'T') {
3616: my $text=$token->[1];
3617: $text=~s/\n/\<br \/\>/g;
3618: $newmessage.=$text;
3619: } elsif ($token->[0] eq 'D' || $token->[0] eq 'C') {
3620: $newmessage.=$token->[1];
3621: } elsif ($token->[0] eq 'PI' || $token->[0] eq 'E') {
3622: $newmessage.=$token->[2];
3623: } elsif ($token->[0] eq 'S') {
3624: $newmessage.=$token->[4];
3625: }
3626:
3627: }
3628: $$message=$newmessage;
3629: }
3630:
3631: sub generate_preview_button {
3632: my ($formname,$fieldname)=@_;
3633: unless ($formname) { $formname='mailform'; }
3634: unless ($fieldname) { $fieldname='comment'; }
3635: my $pre=&mt("Show Preview and Check Spelling");
3636: return(<<ENDPREVIEW);
3637: <br />
3638: <form name="preview" action="/adm/feedback?preview=1" method="post" target="preview">
3639: <input type="hidden" name="subject" />
3640: <input type="hidden" name="comment" />
3641: <input type="button" value="$pre"
3642: onclick="if (typeof(document.$formname.onsubmit)=='function') {document.$formname.onsubmit();};this.form.comment.value=document.$formname.$fieldname.value;this.form.subject.value=document.$formname.subject.value;this.form.submit();" />
3643: </form>
3644: ENDPREVIEW
3645: }
3646:
3647: sub modify_attachments {
3648: my ($r,$currnewattach,$currdelold,$symb,$idx,$attachmenturls,
3649: $attachmaxtext,$toolarge)=@_;
3650:
3651: my %lt = &Apache::lonlocal::texthash(
3652: 'subj' => 'Subject',
3653: 'chth' => 'Check the checkboxes for any you wish to remove.',
3654: 'thef' => 'The following attachments have been uploaded for inclusion with this posting.',
3655: 'adda' => 'Add a new attachment to this post',
3656: 'stch' => 'Save Changes',
3657: 'clic' => 'Add/remove attachments',
3658: );
3659: my $js = <<END;
3660: <script type="text/javascript">
3661: function setAction () {
3662: document.modattachments.action = document.modattachments.origpage.value;
3663: document.modattachments.submit();
3664: }
3665: </script>
3666: <script type="text/javascript" src="/res/adm/includes/file_upload.js"></script>
3667: END
3668:
3669: # Breadcrumbs
3670: my $brcrum = [{'href' => '',
3671: 'text' => 'Discussion Post Attachments'}];
3672: my %parms=('only_body' => 1);
3673: if ($env{'form.modal'} ne 'yes') { $parms{'bread_crumbs'} = $brcrum; }
3674:
3675: my $start_page =
3676: &Apache::loncommon::start_page('Discussion Post Attachments',$js,\%parms);
3677:
3678: my $orig_subject = &unescape($env{'form.subject'});
3679: my $subject=&clear_out_html($orig_subject);
3680: $subject=~s/\n/\<br \/\>/g;
3681: $subject=&Apache::lontexconvert::msgtexconverted($subject);
3682: my $timestamp=$env{'form.timestamp'};
3683: my $numoldver=$env{'form.numoldver'};
3684:
3685: my $msg = '';
3686: my %attachments = ();
3687: my %currattach = ();
3688: if ($idx) {
3689: &extract_attachments($attachmenturls,$idx,$numoldver,\$msg,\%attachments,\%currattach,$currdelold);
3690: }
3691: &Apache::lonenc::check_encrypt(\$symb);
3692:
3693: my $end_page =
3694: &Apache::loncommon::end_page();
3695:
3696: $r->print(<<END);
3697: $start_page
3698: $toolarge
3699: <form name="modattachments" method="post" enctype="multipart/form-data" action="/adm/feedback?attach=$symb">
3700:
3701: <h1>$lt{'clic'}</h1>
3702: END
3703: $r->print(&Apache::lonhtmlcommon::start_pick_box());
3704: $r->print(&Apache::lonhtmlcommon::row_title(&mt('Subject')));
3705: $r->print('<b>'.$subject.'</b>');
3706: $r->print(&Apache::lonhtmlcommon::row_closure());
3707: $r->print(&Apache::lonhtmlcommon::row_title($lt{'adda'}));
3708: $r->print('<input type="file" name="addnewattach" class="LC_flUpload" />'
3709: .'<input type="hidden" id="LC_free_space" value="131072" />'
3710: .'<input type="button" name="upload" value="Upload" '
3711: .'onclick="this.form.submit()" /> '.$attachmaxtext);
3712: if(($idx)||(ref($currnewattach) eq 'ARRAY') && (@{$currnewattach} > 0)){
3713: $r->print(&Apache::lonhtmlcommon::row_closure());
3714: $r->print(&Apache::lonhtmlcommon::row_title(&mt('Attachments')));
3715: if ($idx) {
3716: if ($attachmenturls) {
3717: my @currold = keys(%currattach);
3718: if (@currold > 0) {
3719: $r->print($lt{'thfo'}.'<br />'.$lt{'chth'}.'<br />'."\n");
3720: foreach my $id (@currold) {
3721: my $attachurl = &HTML::Entities::decode($attachments{$id}{'filename'});
3722: $attachurl =~ m#/([^/]+)$#;
3723: $r->print('<label><input type="checkbox" name="deloldattach" value="'.$id.'" /> '.$1.'</label><br />'."\n");
3724: }
3725: $r->print("<br />");
3726: }
3727: }
3728: }
3729: if ((ref($currnewattach) eq 'ARRAY') && (@{$currnewattach} > 0)) {
3730: $r->print($lt{'chth'}.'<br />'."\n");
3731: foreach my $attach (@{$currnewattach}) {
3732: $attach =~ m#/([^/]+)$#;
3733: $r->print('<label><input type="checkbox" name="delnewattach" value="'.$attach.'" /> '.$1.'</label><br />'."\n");
3734: }
3735: }
3736: }
3737: $r->print(&Apache::lonhtmlcommon::row_closure(1));
3738: $r->print(&Apache::lonhtmlcommon::end_pick_box());
3739: $r->print(<<END);
3740: <input type="hidden" name="subject" value="$env{'form.subject'}" />
3741: <input type="hidden" name="comment" value="$env{'form.comment'}" />
3742: <input type="hidden" name="timestamp" value="$env{'form.timestamp'}" />
3743: <input type="hidden" name="idx" value="$env{'form.idx'}" />
3744: <input type="hidden" name="numoldver" value="$env{'form.numoldver'}" />
3745: <input type="hidden" name="origpage" value="$env{'form.origpage'}" />
3746: <input type="hidden" name="blog" value="$env{'form.blog'}" />
3747: <input type="hidden" name="discuss" value="$env{'form.discuss'}" />
3748: END
3749: foreach my $item (@{$currnewattach}) {
3750: $r->print('<input type="hidden" name="currnewattach" value="'.$item.'" />'."\n");
3751: }
3752: foreach my $item (@{$currdelold}) {
3753: $r->print('<input type="hidden" name="deloldattach" value="'.$item.'" />'."\n");
3754: }
3755: $r->print(<<END);
3756: <input type="button" name="rtntoedit" value="$lt{'stch'}" onclick="setAction()" />
3757: </form>
3758: $end_page
3759: END
3760: return;
3761: }
3762:
3763: sub process_attachments {
3764: my ($currnewattach,$currdelold,$keepold) = @_;
3765:
3766: @{$currnewattach}=
3767: &Apache::loncommon::get_env_multiple('form.currnewattach');
3768: @{$currdelold}=
3769: &Apache::loncommon::get_env_multiple('form.deloldattach');
3770: if (exists($env{'form.delnewattach'})) {
3771: my @currdelnew =
3772: &Apache::loncommon::get_env_multiple('form.delnewattach');
3773: my @currnew = ();
3774: foreach my $newone (@{$currnewattach}) {
3775: my $delflag = 0;
3776: foreach my $item (@currdelnew) {
3777: if ($newone eq $item) {
3778: $delflag = 1;
3779: last;
3780: }
3781: }
3782: unless ($delflag) {
3783: push(@currnew, $newone);
3784: }
3785: }
3786: @{$currnewattach} = @currnew;
3787: }
3788: @{$keepold} = &Apache::loncommon::get_env_multiple('form.keepold');
3789: }
3790:
3791: sub generate_attachments_button {
3792: my ($idx,$attachnum,$ressymb,$now,$currnewattach,$deloldattach,
3793: $numoldver,$mode,$blockblog) = @_;
3794: my $origpage = $ENV{'REQUEST_URI'};
3795: my $att=$attachnum.' '.&mt("attachments");
3796: my %lt = &Apache::lonlocal::texthash(
3797: 'clic' => 'Add/remove attachments',
3798: );
3799: my $response = (<<END);
3800: <br />
3801: <form name="attachment" action="/adm/feedback?attach=$ressymb" method="post">
3802: <input type="button" value="$lt{'clic'}"
3803: onclick="if (typeof(document.mailform.onsubmit)=='function') {document.mailform.onsubmit();};this.form.comment.value=escape(document.mailform.comment.value);this.form.subject.value=escape(document.mailform.subject.value);
3804: END
3805: if (!$blockblog) {
3806: $response .= 'setblogvalue();';
3807: }
3808: unless ($mode eq 'board') {
3809: $response .= 'javascript:anonchk();';
3810: }
3811: $response .= (<<ENDATTACH);
3812: this.form.submit();" />
3813: <input type="hidden" name="origpage" value="$origpage" />
3814: <input type="hidden" name="idx" value="$idx" />
3815: <input type="hidden" name="timestamp" value="$now" />
3816: <input type="hidden" name="subject" />
3817: <input type="hidden" name="comment" />
3818: <input type="hidden" name="blog" value="0" />
3819: <input type="hidden" name="discuss" value="0" />
3820: <input type="hidden" name="numoldver" value="$numoldver" />
3821: ENDATTACH
3822: if (defined($deloldattach)) {
3823: if (@{$deloldattach} > 0) {
3824: foreach my $delatt (@{$deloldattach}) {
3825: $response .= '<input type="hidden" name="deloldattach" value="'.$delatt.'" />'."\n";
3826: }
3827: }
3828: }
3829: if (defined($currnewattach)) {
3830: if (@{$currnewattach} > 0) {
3831: foreach my $attach (@{$currnewattach}) {
3832: $response .= '<input type="hidden" name="currnewattach" value="'.$attach.'" />'."\n";
3833: }
3834: }
3835: }
3836: $response .= '</form>';
3837: return $response;
3838: }
3839:
3840: sub extract_attachments {
3841: my ($attachmenturls,$idx,$numoldver,$message,$attachments,$currattach,$currdelold) = @_;
3842: %{$attachments}=();
3843: &get_post_attachments($attachments,$attachmenturls);
3844: foreach my $id (sort(keys(%{$attachments}))) {
3845: if (exists($$attachments{$id}{$numoldver})) {
3846: if (defined($currdelold)) {
3847: if (@{$currdelold} > 0) {
3848: unless (grep/^$id$/,@{$currdelold}) {
3849: $$currattach{$id} = $$attachments{$id}{$numoldver};
3850: }
3851: } else {
3852: $$currattach{$id} = $$attachments{$id}{$numoldver};
3853: }
3854: } else {
3855: $$currattach{$id} = $$attachments{$id}{$numoldver};
3856: }
3857: }
3858: }
3859: my @attached = (sort { $a <=> $b } keys(%{$currattach}));
3860: if (@attached == 1) {
3861: my $id = $attached[0];
3862: my $attachurl;
3863: if ($attachmenturls =~ m/^<attachment id="0">/) {
3864: $attachurl = &HTML::Entities::decode($$attachments{$id}{'filename'});
3865: } else {
3866: $attachurl = $$attachments{$id}{'filename'};
3867: }
3868: $attachurl=~m|/([^/]+)$|;
3869: $$message.='<br /><a href="'.$attachurl.'"><tt>'.
3870: $1.'</tt></a><br />';
3871: &Apache::lonnet::allowuploaded('/adm/feedback',
3872: $attachurl);
3873: } elsif (@attached > 1) {
3874: $$message.='<ol>';
3875: foreach my $attach (@attached) {
3876: my $id = $attach;
3877: my $attachurl = &HTML::Entities::decode($$attachments{$id}{'filename'});
3878: my ($fname)
3879: =($attachurl=~m|/([^/]+)$|);
3880: $$message .= '<li><a href="'.$attachurl.
3881: '"><tt>'.
3882: $fname.'</tt></a></li>';
3883: &Apache::lonnet::allowuploaded('/adm/feedback',
3884: $attachurl);
3885: }
3886: $$message .= '</ol>';
3887: }
3888: }
3889:
3890: sub construct_attachmenturl {
3891: my ($currnewattach,$keepold,$symb,$idx)=@_;
3892: my $oldattachmenturl;
3893: my $newattachmenturl;
3894: my $startnum = 0;
3895: my $currver = 0;
3896: if (($env{'form.editdisc'}) && ($idx)) {
3897: my %contrib=&Apache::lonnet::restore($symb,$env{'request.course.id'},
3898: $env{'course.'.$env{'request.course.id'}.'.domain'},
3899: $env{'course.'.$env{'request.course.id'}.'.num'});
3900: $oldattachmenturl = $contrib{$idx.':attachmenturl'};
3901: if ($contrib{$idx.':history'}) {
3902: if ($contrib{$idx.':history'} =~ /:/) {
3903: my @oldversions = split(/:/,$contrib{$idx.':history'});
3904: $currver = 1 + scalar(@oldversions);
3905: } else {
3906: $currver = 2;
3907: }
3908: } else {
3909: $currver = 1;
3910: }
3911: if ($oldattachmenturl) {
3912: if ($oldattachmenturl =~ m/^<attachment id="0">/) {
3913: my %attachments = ();
3914: my $prevver = $currver-1;
3915: &get_post_attachments(\%attachments,$oldattachmenturl);
3916: my $numattach = scalar(keys(%attachments));
3917: $startnum += $numattach;
3918: foreach my $num (sort {$a <=> $b} keys(%attachments)) {
3919: $newattachmenturl .= '<attachment id="'.$num.'"><filename>'.$attachments{$num}{'filename'}.'</filename>';
3920: foreach my $item (sort {$a <=> $b} keys(%{$attachments{$num}})) {
3921: unless ($item eq 'filename') {
3922: $newattachmenturl .= '<post id="'.$item.'">'.$attachments{$num}{$item}.'</post>';
3923: }
3924: }
3925: if (grep/^$num$/,@{$keepold}) {
3926: $newattachmenturl .= '<post id="'.$currver.'">'.$attachments{$num}{$prevver}.'</post>';
3927: }
3928: $newattachmenturl .= '</attachment>';
3929: }
3930: } else {
3931: $newattachmenturl = '<attachment id="0"><filename>'.&HTML::Entities::encode($oldattachmenturl).'</filename><post id="0">n</post>';
3932: unless (grep/^0$/,@{$keepold}) {
3933: $newattachmenturl .= '<post id="1">n</post>';
3934: }
3935: $newattachmenturl .= '</attachment>';
3936: $startnum ++;
3937: }
3938: }
3939: }
3940: for (my $i=0; $i<@{$currnewattach}; $i++) {
3941: my $attachnum = $startnum + $i;
3942: $newattachmenturl .= '<attachment id="'.$attachnum.'"><filename>'.&HTML::Entities::encode($$currnewattach[$i]).'</filename><post id="'.$currver.'">n</post></attachment>';
3943: }
3944: return $newattachmenturl;
3945: }
3946:
3947: sub add_blog_checkbox {
3948: my ($crstype) = @_;
3949: my $checkstatus;
3950: if ($env{'form.blog'}) {
3951: $checkstatus = 'checked="checked"';
3952: }
3953: my $output = '
3954: <script type="text/javascript">
3955: function setblogvalue() {
3956: if (document.mailform.blog.checked) {
3957: document.attachment.blog.value = 1;
3958: } else {
3959: document.attachment.blog.value = 0;
3960: }
3961: }
3962: </script><br />
3963: <label><input type="checkbox" name="blog" '.$checkstatus.' /> ';
3964: if ($crstype eq 'Community') {
3965: $output .= &mt('Add to my public community blog');
3966: } else {
3967: $output .= &mt('Add to my public course blog');
3968: }
3969: $output .= '</label><br />'."\n";
3970: return $output;
3971: }
3972:
3973: sub has_discussion {
3974: my $resourcesref = shift;
3975: my $navmap = Apache::lonnavmaps::navmap->new();
3976: if (defined($navmap)) {
3977: my @allres=$navmap->retrieveResources();
3978: foreach my $resource (@allres) {
3979: if ($resource->hasDiscussion()) {
3980: my $ressymb = $resource->wrap_symb();
3981: if (ref($resourcesref) eq 'ARRAY') {
3982: push(@{$resourcesref}, $ressymb);
3983: }
3984: }
3985: }
3986: } else {
3987: &Apache::lonnet::logthis('Has discussion check failed - could not create navmap object.');
3988: }
3989: return;
3990: }
3991:
3992: sub sort_filter_names {
3993: my ($sort_types,$role_types,$status_types,$crstype) = @_;
3994: if (ref($sort_types) eq 'HASH') {
3995: %{$sort_types} = (
3996: ascdate => 'Date order - oldest first',
3997: descdate => 'Date order - newest first',
3998: thread => 'Threaded',
3999: subject => 'By subject',
4000: username => 'By domain and username',
4001: lastfirst => 'By last name, first name'
4002: );
4003: }
4004: my @courseroles = qw(st in ta ep ad);
4005: if ($crstype eq 'Community') {
4006: push(@courseroles,'co');
4007: } else {
4008: push(@courseroles,'cc');
4009: }
4010: if (ref($role_types) eq 'HASH') {
4011: foreach my $role (@courseroles) {
4012: $role_types->{$role} = &Apache::lonnet::plaintext($role,$crstype);
4013: }
4014: $role_types->{'all'} = 'All roles';
4015: $role_types->{'cr'} = 'Custom role';
4016: }
4017: if (ref($status_types) eq 'HASH') {
4018: %{$status_types} = (
4019: all => 'Roles of any status',
4020: Active => 'Only active roles',
4021: Expired => 'Only past roles',
4022: Future => 'Only future roles',
4023: );
4024: }
4025: }
4026:
4027: sub handler {
4028: my $r = shift;
4029: if ($r->header_only) {
4030: &Apache::loncommon::content_type($r,'text/html');
4031: $r->send_http_header;
4032: return OK;
4033: }
4034:
4035: # --------------------------- Get query string for limited number of parameters
4036:
4037: &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
4038: ['like','unlike','modal','hide','unhide','deldisc','undeleteall','postdata','preview','replydisc','editdisc','cmd','symb','onlyunread','allposts','onlyunmark','previous','markread','markonread','markondisp','toggoff','toggon','modifydisp','changes','navtime','navmaps','navurl','sortposts','applysort','rolefilter','statusfilter','sectionpick','groupick','posterlist','userpick','attach','origpage','currnewattach','deloldattach','keepold','allversions','export','sendmessageonly','group','ref']);
4039: my $group = $env{'form.group'};
4040: my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
4041: my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
4042:
4043: my %attachmax = (
4044: text => &mt('(128 KB max size)'),
4045: num => 131072,
4046: );
4047: if ($env{'form.editdisc'}) {
4048: if (!(&editing_allowed($env{'form.editdisc'},$group))) {
4049: my $symb=(split(/\:\:\:/,$env{'form.editdisc'}))[0];
4050: my ($map,$id,$url)=&Apache::lonnet::decode_symb($symb);
4051: my $feedurl=&Apache::lonnet::clutter($url);
4052: &redirect_back($r,$feedurl,&mt('Editing not permitted').'<br />',
4053: '0','0','','',$env{'form.previous'},undef,undef,undef,
4054: undef,undef,undef,$group);
4055: return OK;
4056: }
4057: }
4058: if ($env{'form.discsymb'}) {
4059: my ($symb,$feedurl) = &get_feedurl_and_clean_symb($env{'form.discsymb'});
4060: my $readkey = $symb.'_read';
4061: my $chgcount = 0;
4062: my %readinghash = &Apache::lonnet::get('nohist_'.$env{'request.course.id'}.'_discuss',[$readkey],$env{'user.domain'},$env{'user.name'});
4063: foreach my $key (keys(%env)) {
4064: if ($key =~ m/^form\.postunread_(\d+)/) {
4065: if ($readinghash{$readkey} =~ /\.$1\./) {
4066: $readinghash{$readkey} =~ s/\.$1\.//;
4067: $chgcount ++;
4068: }
4069: } elsif ($key =~ m/^form\.postread_(\d+)/) {
4070: unless ($readinghash{$readkey} =~ /\.$1\./) {
4071: $readinghash{$readkey} .= '.'.$1.'.';
4072: $chgcount ++;
4073: }
4074: }
4075: }
4076: if ($chgcount > 0) {
4077: &Apache::lonnet::put('nohist_'.$env{'request.course.id'}.'_discuss',
4078: \%readinghash,$env{'user.domain'},$env{'user.name'});
4079: }
4080: &redirect_back($r,$feedurl,&mt('Marked postings read/unread').'<br />',
4081: '0','0','','',$env{'form.previous'},'','','',
4082: undef,undef,undef,$group);
4083: return OK;
4084: }
4085: if ($env{'form.allversions'}) {
4086: &Apache::loncommon::content_type($r,'text/html');
4087: &Apache::loncommon::no_cache($r);
4088: $r->send_http_header;
4089:
4090: # Breadcrumbs
4091: my $brcrum = [{'href' => '',
4092: 'text' => 'Discussion Post Versions'}];
4093:
4094: my %parms=();
4095: if ($env{'form.modal'} ne 'yes') { $parms{'bread_crumbs'} = $brcrum; }
4096:
4097: $r->print(&Apache::loncommon::start_page('Discussion Post Versions',undef,\%parms));
4098:
4099: my ($symb,$idx)=split(/\:\:\:/,$env{'form.allversions'});
4100: ($symb, my $feedurl)=&get_feedurl_and_clean_symb($symb);
4101: my $ressymb = &wrap_symb($symb);
4102: my $seeid;
4103: if (&Apache::lonnet::allowed('rin',$env{'request.course.id'}.($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:''))) {
4104: $seeid = 1;
4105: }
4106: my $seehidden = &can_see_hidden('',$ressymb,$feedurl,$group,$cdom,$cnum);
4107: if ($idx > 0) {
4108: my %messages = ();
4109: my %subjects = ();
4110: my %attachmsgs = ();
4111: my %allattachments = ();
4112: my %imsfiles = ();
4113: my ($screenname,$plainname,$showaboutme);
4114: my %contrib=&Apache::lonnet::restore($symb,$env{'request.course.id'},
4115: $cdom,$cnum);
4116: $r->print(&get_post_contents(\%contrib,$idx,$seeid,$seehidden,'allversions',\%messages,\%subjects,\%allattachments,\%attachmsgs,\%imsfiles,\$screenname,\$plainname,\$showaboutme));
4117: }
4118: $r->print(&Apache::loncommon::end_page());
4119: return OK;
4120: }
4121: if ($env{'form.posterlist'}) {
4122: my ($symb,$feedurl)=&get_feedurl_and_clean_symb($env{'form.applysort'});
4123: &print_showposters($r,$symb,$env{'form.previous'},$feedurl,
4124: $env{'form.sortposts'});
4125: return OK;
4126: }
4127: if ($env{'form.userpick'}) {
4128: my @posters = &Apache::loncommon::get_env_multiple('form.stuinfo');
4129: my ($symb,$feedurl)=&get_feedurl_and_clean_symb($env{'form.userpick'});
4130: my $numpicks = @posters;
4131: my %discinfo;
4132: $discinfo{$symb.'_userpick'} = join('&',@posters);
4133: &Apache::lonnet::put('nohist_'.$env{'request.course.id'}.'_discuss',
4134: \%discinfo,$env{'user.domain'},$env{'user.name'});
4135: &redirect_back($r,$feedurl,&mt('Changed sort/filter').'<br />','0','0','',
4136: '',$env{'form.previous'},$env{'form.sortposts'},'','','',
4137: '',$numpicks,$group);
4138: return OK;
4139: }
4140: if ($env{'form.applysort'}) {
4141: my ($symb,$feedurl)=&get_feedurl_and_clean_symb($env{'form.applysort'});
4142: &redirect_back($r,$feedurl,&mt('Changed sort/filter').'<br />','0','0','',
4143: '',$env{'form.previous'},$env{'form.sortposts'},
4144: $env{'form.rolefilter'},$env{'form.statusfilter'},
4145: $env{'form.sectionpick'},$env{'form.grouppick'},
4146: undef,$group);
4147: return OK;
4148: } elsif ($env{'form.cmd'} eq 'sortfilter') {
4149: my ($symb,$feedurl)=&get_feedurl_and_clean_symb($env{'form.symb'});
4150: &print_sortfilter_options($r,$symb,$env{'form.previous'},$feedurl);
4151: return OK;
4152: } elsif ($env{'form.navtime'}) {
4153: my %discinfo = ();
4154: my @resources = ();
4155: if (defined($env{'form.navmaps'})) {
4156: if ($env{'form.navmaps'} =~ /:/) {
4157: @resources = split(/:/,$env{'form.navmaps'});
4158: } else {
4159: @resources = ("$env{'form.navmaps'}");
4160: }
4161: } else {
4162: &has_discussion(\@resources);
4163: }
4164: my $numitems = @resources;
4165: my $feedurl = '/adm/navmaps';
4166: if ($env{'form.navurl'}) { $feedurl .= '?'.$env{'form.navurl'}; }
4167: my %lt = &Apache::lonlocal::texthash(
4168: 'twnp' => 'There are currently no resources or discussion boards with unread discussion postings.'
4169: );
4170: foreach my $res (@resources) {
4171: my $ressymb=$res;
4172: &Apache::lonenc::check_decrypt(\$ressymb);
4173: my $lastkey = $ressymb.'_lastread';
4174: $discinfo{$lastkey} = $env{'form.navtime'};
4175: }
4176: my $textline = '<b>'.
4177: &mt('Marked "New" posts as read in a total of [_1] resources/discussion boards.',
4178: $numitems).
4179: '</b>';
4180: if ($numitems > 0) {
4181: &Apache::lonnet::put('nohist_'.$env{'request.course.id'}.'_discuss',
4182: \%discinfo,$env{'user.domain'},$env{'user.name'});
4183: } else {
4184: $textline = "<b>$lt{'twnp'}</b>";
4185: }
4186: &Apache::loncommon::content_type($r,'text/html');
4187: $r->send_http_header;
4188: my $logo=&Apache::loncommon::lonhttpdurl('/adm/lonIcons/lonlogos.gif');
4189: my %onload;
4190:
4191: my $start_page=
4192: &Apache::loncommon::start_page('New posts marked as read',undef,
4193: {'redirect' => [2,$feedurl],
4194: 'only_body' => 1,
4195: 'add_entries' => \%onload});
4196: my $end_page = &Apache::loncommon::end_page();
4197: my $windowname = 'loncapaclient';
4198: if ($env{'request.lti.login'}) {
4199: $windowname .= 'lti';
4200: }
4201: $r->print (<<ENDREDIR);
4202: $start_page
4203: <img align="right" src="$logo" />
4204: $textline
4205: <form name="reldt" action="$feedurl" target="$windowname">
4206: </form>
4207: <br />
4208: $end_page
4209: ENDREDIR
4210: return OK;
4211: } elsif ($env{'form.modifydisp'}) {
4212: my ($symb,$feedurl)=&get_feedurl_and_clean_symb($env{'form.modifydisp'});
4213: my ($dispchgA,$dispchgB,$markchg,$toggchg) =
4214: split(/_/,$env{'form.changes'});
4215: &print_display_options($r,$symb,$env{'form.previous'},$dispchgA,
4216: $dispchgB,$markchg,$toggchg,$feedurl);
4217: return OK;
4218: } elsif ($env{'form.markondisp'} || $env{'form.markonread'} ||
4219: $env{'form.allposts'} || $env{'form.onlyunread'} ||
4220: $env{'form.onlyunmark'} || $env{'form.toggoff'} ||
4221: $env{'form.toggon'} || $env{'form.markread'}) {
4222: my ($symb,$feedurl)=&get_feedurl_and_clean_symb($env{'form.symb'});
4223: my %discinfo;
4224: # ------------------------ Modify setting for read/unread toggle for each post
4225: if ($env{'form.toggoff'}) { $discinfo{$symb.'_readtoggle'}=0; }
4226: if ($env{'form.toggon'}) { $discinfo{$symb.'_readtoggle'}=1; }
4227: # --------- Modify setting for identification of 'NEW' posts in this discussion
4228: if ($env{'form.markondisp'}) {
4229: $discinfo{$symb.'_lastread'} = time;
4230: $discinfo{$symb.'_markondisp'} = 1;
4231: }
4232: if ($env{'form.markonread'}) {
4233: if ( $env{'form.previous'} > 0 ) {
4234: $discinfo{$symb.'_lastread'} = $env{'form.previous'};
4235: }
4236: $discinfo{$symb.'_markondisp'} = 0;
4237: }
4238: # --------------------------------- Modify display setting for this discussion
4239: if ($env{'form.allposts'}) {
4240: $discinfo{$symb.'_showonlyunread'} = 0;
4241: $discinfo{$symb.'_showonlyunmark'} = 0;
4242: }
4243: if ($env{'form.onlyunread'}) { $discinfo{$symb.'_showonlyunread'} = 1; }
4244: if ($env{'form.onlyunmark'}) { $discinfo{$symb.'_showonlyunmark'} = 1; }
4245: # ----------------------------------------------------- Mark new posts not NEW
4246: if ($env{'form.markread'}) { $discinfo{$symb.'_lastread'} = time; }
4247: &Apache::lonnet::put('nohist_'.$env{'request.course.id'}.'_discuss',
4248: \%discinfo,$env{'user.domain'},$env{'user.name'});
4249: my $previous=$env{'form.previous'};
4250: if ($env{'form.markondisp'}) { $previous=undef; }
4251: &redirect_back($r,$feedurl,&mt('Changed display status').'<br />',
4252: '0','0','','',$previous,'','','','','','',$group);
4253: return OK;
4254: } elsif (($env{'form.hide'}) || ($env{'form.unhide'})) {
4255: # ----------------------------------------------------------------- Hide/unhide
4256: my $entry=$env{'form.hide'}?$env{'form.hide'}:$env{'form.unhide'};
4257: my ($symb,$idx)=split(/\:\:\:/,$entry);
4258: ($symb,my $feedurl)=&get_feedurl_and_clean_symb($symb);
4259: my $ressymb = &wrap_symb($symb);
4260:
4261: my $seehidden = &can_see_hidden('',$ressymb,$feedurl,$group,$cdom,$cnum);
4262: unless (($seehidden) || (&editing_allowed($env{'form.hide'},$group))) {
4263: &redirect_back($r,$feedurl,&mt('Hiding not permitted').'<br />',
4264: '0','0','','',$env{'form.previous'},'','','','',
4265: undef,undef,$group);
4266: return OK;
4267: }
4268:
4269: my %contrib=&Apache::lonnet::restore($symb,$env{'request.course.id'},
4270: $cdom,$cnum);
4271:
4272: my $currenthidden=$contrib{'hidden'};
4273: my $currentstudenthidden=$contrib{'studenthidden'};
4274:
4275: if ($env{'form.hide'}) {
4276: $currenthidden.='.'.$idx.'.';
4277: unless ($seehidden) {
4278: $currentstudenthidden.='.'.$idx.'.';
4279: }
4280: } else {
4281: $currenthidden=~s/\.$idx\.//g;
4282: }
4283: my %newhash=('hidden' => $currenthidden);
4284: if ( ($env{'form.hide'}) && (!$seehidden) ) {
4285: $newhash{'studenthidden'} = $currentstudenthidden;
4286: }
4287: if ($env{'form.hide'}) {
4288: my $changelast = 0;
4289: my $newlast;
4290: ($changelast,$newlast) = &get_discussion_info($idx,%contrib);
4291: if ($changelast) {
4292: &Apache::lonnet::put('discussiontimes',{$symb => $newlast},
4293: $cdom,$cnum);
4294: }
4295: }
4296: my $result;
4297: if (&Apache::lonnet::store(\%newhash,$symb,$env{'request.course.id'},
4298: $cdom,$cnum) eq 'ok') {
4299: my $prefix=$symb.':'.$idx.':';
4300: my %likes=&Apache::lonnet::dump('disclikes',$cdom,$cnum,
4301: '^'.$prefix);
4302: my ($totallikes,$totalunlikes);
4303: if (ref($likes{$prefix.'likers'}) eq 'HASH') {
4304: $totallikes = scalar(keys(%{$likes{$prefix.'likers'}}));
4305: }
4306: if (ref($likes{$prefix.'unlikers'}) eq 'HASH') {
4307: $totalunlikes = scalar(keys(%{$likes{$prefix.'unlikers'}}));
4308: }
4309: if ($totallikes || $totalunlikes) {
4310: my ($chglikes,$chgunlikes,$context);
4311: if ($env{'form.hide'}) {
4312: $chglikes = -1 * $totallikes;
4313: $chgunlikes = -1 * $totalunlikes;
4314: $context = 'hide';
4315: } else {
4316: $chglikes = $totallikes;
4317: $chgunlikes = $totalunlikes;
4318: $context = 'unhide';
4319: }
4320: &storediscussionlikes($chglikes,$chgunlikes,
4321: $contrib{$idx.':sendername'},
4322: $contrib{$idx.':senderdomain'},
4323: $env{'request.course.id'},
4324: $context);
4325:
4326: }
4327: $result = &mt('Changed discussion status');
4328: } else {
4329: $result = &mt('Discussion status unchanged');
4330: }
4331: &redirect_back($r,$feedurl,$result.'<br />','0','0','','',
4332: $env{'form.previous'},undef,undef,undef,
4333: undef,undef,undef,$group);
4334: return OK;
4335: } elsif (($env{'form.like'}) || ($env{'form.unlike'})) {
4336: # ----------------------------------------------------------------- Like/unlike
4337: my $entry=$env{'form.like'}?$env{'form.like'}:$env{'form.unlike'};
4338: my ($symb,$idx)=split(/\:\:\:/,$entry);
4339: ($symb,my $feedurl)=&get_feedurl_and_clean_symb($symb);
4340: my $result;
4341: if ($idx > 0) {
4342: my $realsymb = &get_realsymb($symb);
4343: my $status='OPEN';
4344: if ($Apache::lonhomework::parsing_a_problem ||
4345: $Apache::lonhomework::parsing_a_task) {
4346: $status=$Apache::inputtags::status[-1];
4347: }
4348: if (&discussion_vote_available($status,$realsymb)) {
4349: my %contrib=&Apache::lonnet::restore($symb,$env{'request.course.id'},
4350: $cdom,$cnum);
4351: my $ownpost;
4352: if (($contrib{$idx.':sendername'} eq $env{'user.name'}) &&
4353: ($contrib{$idx.':senderdomain'} eq $env{'user.domain'})) {
4354: $ownpost = 1;
4355: }
4356: if ($ownpost || $contrib{$idx.':hidden'} || $contrib{$idx.':deleted'}) {
4357: $result = &mt('Vote not registered.').' ';
4358: }
4359: if ($ownpost) {
4360: $result .= &mt('No voting for your own posts.');
4361: } elsif ($contrib{$idx.':hidden'}) {
4362: $result .= &mt('No voting for hidden posts.');
4363: } elsif ($contrib{$idx.':deleted'}) {
4364: $result .= &mt('No voting for deleted posts.');
4365: } else {
4366: #
4367: # Likes and unlikes are in db-file "disclikes" of the course
4368: # The prefix is the $symb to identify the resource discussion,
4369: # and the $idx to identify the entry
4370: #
4371: my $prefix=$symb.':'.$idx.':';
4372: my %likes=&Apache::lonnet::dump('disclikes',$cdom,$cnum,
4373: '^'.$prefix);
4374:
4375: # Get current like or unlike status for the $idx for this user.
4376: my $thisuser=$env{'user.name'}.':'.$env{'user.domain'};
4377: my ($userlikes,$userunlikes);
4378: if (ref($likes{$prefix.'likers'}) eq 'HASH') {
4379: if (exists($likes{$prefix.'likers'}{$thisuser})) {
4380: $userlikes = 1;
4381: }
4382: }
4383: if (ref($likes{$prefix.'unlikers'}) eq 'HASH') {
4384: if (exists($likes{$prefix.'unlikers'}{$thisuser})) {
4385: $userunlikes = 1;
4386: }
4387: }
4388: # Get the current "likes" count
4389: my $likescount=$likes{$prefix.'likes'};
4390: # Find out if they already voted
4391: # Users cannot like a post twice, or unlike it twice.
4392: # They can change their mind, though.
4393: my $alreadyflag=0;
4394: my $votetype;
4395: if ($env{'form.like'}) {
4396: if ($userlikes) {
4397: $alreadyflag=1;
4398: } elsif ($userunlikes) {
4399: delete($likes{$prefix.'unlikers'}{$thisuser});
4400: $votetype = 'switch';
4401: $likescount++;
4402: } else {
4403: if (ref($likes{$prefix.'likers'}) eq 'HASH') {
4404: $likes{$prefix.'likers'}{$thisuser} = 1;
4405: } else {
4406: $likes{$prefix.'likers'} = {$thisuser => 1};
4407: }
4408: $likescount++;
4409: }
4410: } else {
4411: if ($userunlikes) {
4412: $alreadyflag=1;
4413: } elsif ($userlikes) {
4414: delete($likes{$prefix.'likers'}{$thisuser});
4415: $votetype = 'switch';
4416: $likescount--;
4417: } else {
4418: if (ref($likes{$prefix.'unlikers'}) eq 'HASH') {
4419: $likes{$prefix.'unlikers'}{$thisuser} = 1;
4420: } else {
4421: $likes{$prefix.'unlikers'} = {$thisuser => 1};
4422: }
4423: $likescount--;
4424: }
4425: }
4426: # $alreadyflag would be 1 if they tried to double-like or double-unlike
4427: if ($alreadyflag) {
4428: if ($env{'form.like'}) {
4429: $result= &mt("'Like' already registered");
4430: } else {
4431: $result= &mt("'Unlike' already registered");
4432: }
4433: } else {
4434: my %newhash=($prefix.'likes' => $likescount,
4435: $prefix.'likers' => $likes{$prefix.'likers'},
4436: $prefix.'unlikers' => $likes{$prefix.'unlikers'});
4437: # Store data in db-file "disclikes"
4438: if (&Apache::lonnet::put('disclikes',\%newhash,$cdom,$cnum) eq 'ok') {
4439: # Also store with the person who posted the liked/unliked entry
4440: my ($chglike,$chgunlike);
4441: if ($env{'form.like'}) {
4442: if ($votetype eq 'switch') {
4443: $chglike = 0;
4444: $chgunlike = -1;
4445: } else {
4446: $chglike = 1;
4447: $chgunlike = 0;
4448: }
4449: &storediscussionlikes($chglike,$chgunlike,
4450: $contrib{$idx.':sendername'},
4451: $contrib{$idx.':senderdomain'},
4452: $env{'request.course.id'},'like');
4453: $result=&mt("Registered 'Like'");
4454: } else {
4455: if ($votetype eq 'switch') {
4456: $chglike = -1;
4457: $chgunlike = 0;
4458: } else {
4459: $chglike = 0;
4460: $chgunlike = 1;
4461: }
4462: &storediscussionlikes($chglike,$chgunlike,
4463: $contrib{$idx.':sendername'},
4464: $contrib{$idx.':senderdomain'},
4465: $env{'request.course.id'},'unlike');
4466: $result=&mt("Registered 'Unlike'");
4467: }
4468: } else {
4469: # Oops, something went wrong
4470: $result=&mt("Failed to register vote");
4471: }
4472: }
4473: }
4474: } else {
4475: $result=&mt('Voting unavailable for this discussion');
4476: }
4477: } else {
4478: $result=&mt('Invalid post number');
4479: }
4480: &redirect_back($r,$feedurl,$result.'<br />',
4481: '0','0','','',$env{'form.previous'},undef,undef,undef,
4482: undef,undef,undef,$group);
4483: return OK;
4484: } elsif ($env{'form.cmd'}=~/^(threadedoff|threadedon)$/) {
4485: my ($symb,$feedurl)=&get_feedurl_and_clean_symb($env{'form.symb'});
4486: if ($env{'form.cmd'} eq 'threadedoff') {
4487: &Apache::lonnet::put('environment',{'unthreadeddiscussion' => 'on'});
4488: &Apache::lonnet::appenv({'environment.unthreadeddiscussion' => 'on'});
4489: &Apache::lonnet::del('environment',['threadeddiscussion']);
4490: &Apache::lonnet::delenv('environment.threadeddiscussion');
4491: } else {
4492: &Apache::lonnet::put('environment',{'threadeddiscussion' => 'on'});
4493: &Apache::lonnet::appenv({'environment.threadeddiscussion' => 'on'});
4494: &Apache::lonnet::del('environment',['unthreadeddiscussion']);
4495: &Apache::lonnet::delenv('environment.unthreadeddiscussion');
4496: }
4497: &redirect_back($r,$feedurl,&mt('Changed discussion view mode').'<br />',
4498: '0','0','','',$env{'form.previous'},undef,undef,undef,
4499: undef,undef,undef,$group);
4500: return OK;
4501: } elsif ($env{'form.deldisc'}) {
4502: # --------------------------------------------------------------- Hide for good
4503: my ($symb,$idx)=split(/\:\:\:/,$env{'form.deldisc'});
4504: ($symb,my $feedurl)=&get_feedurl_and_clean_symb($symb);
4505: my $ressymb=&wrap_symb($symb);
4506:
4507: unless (&can_see_hidden('',$ressymb,$feedurl,$group,$cdom,$cnum)) {
4508: &redirect_back($r,$feedurl,&mt('Deletion not permitted').'<br />',
4509: '0','0','','',$env{'form.previous'},'','','','',
4510: undef,undef,$group);
4511: return OK;
4512: }
4513: my %contrib=&Apache::lonnet::restore($symb,$env{'request.course.id'},
4514: $cdom,$cnum);
4515: my ($changelast,$newlast) = &get_discussion_info($idx,%contrib);
4516: if ($changelast) {
4517: &Apache::lonnet::put('discussiontimes',{$symb => $newlast},
4518: $cdom,$cnum);
4519: }
4520: my %newhash=('deleted' => $contrib{'deleted'}.".$idx.");
4521:
4522: my $result;
4523: if (&Apache::lonnet::store(\%newhash,$symb,$env{'request.course.id'},
4524: $cdom,$cnum) eq 'ok') {
4525: $result = &mt('Changed discussion status');
4526: my $prefix=$symb.':'.$idx.':';
4527: my %likes=&Apache::lonnet::dump('disclikes',$cdom,$cnum,
4528: '^'.$prefix);
4529: my ($totallikes,$totalunlikes);
4530: if (ref($likes{$prefix.'likers'}) eq 'HASH') {
4531: $totallikes = scalar(keys(%{$likes{$prefix.'likers'}}));
4532: }
4533: if (ref($likes{$prefix.'unlikers'}) eq 'HASH') {
4534: $totalunlikes = scalar(keys(%{$likes{$prefix.'unlikers'}}));
4535: }
4536: if ($totallikes || $totalunlikes) {
4537: my $chglikes = -1 * $totallikes;
4538: my $chgunlikes = -1 * $totalunlikes;
4539: &storediscussionlikes($chglikes,$chgunlikes,
4540: $contrib{$idx.':sendername'},
4541: $contrib{$idx.':senderdomain'},
4542: $env{'request.course.id'},
4543: 'delete');
4544: }
4545: } else {
4546: $result = &mt('Discussion status unchanged');
4547: }
4548: &redirect_back($r,$feedurl,$result.'<br />','0','0','','',
4549: $env{'form.previous'},undef,undef,undef,
4550: undef,undef,undef,$group);
4551: return OK;
4552: } elsif ($env{'form.preview'}) {
4553: # -------------------------------------------------------- User wants a preview
4554: &show_preview($r);
4555: return OK;
4556: } elsif ($env{'form.attach'}) {
4557: # -------------------------------------------------------- Work on attachments
4558: &Apache::loncommon::content_type($r,'text/html');
4559: $r->send_http_header;
4560: &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['subject','comment','addnewattach','delnewattach','timestamp','numoldver','idx','discuss','blog']);
4561: my (@currnewattach,@currdelold,@keepold,$toolarge);
4562: &process_attachments(\@currnewattach,\@currdelold,\@keepold);
4563: if (exists($env{'form.addnewattach.filename'})) {
4564: if (length($env{'form.addnewattach'})<=$attachmax{'num'}) {
4565: my $subdir = 'feedback/'.$env{'form.timestamp'};
4566: my $newattachment=&Apache::lonnet::userfileupload('addnewattach',undef,$subdir);
4567: push(@currnewattach, $newattachment);
4568: } else {
4569: $toolarge = '<p><span class="LC_warning">'.&mt('Attachment not included - exceeded permitted length').'</span><br /></p>';
4570: }
4571: }
4572: my $attachmenturls;
4573: my ($symb) = &get_feedurl_and_clean_symb($env{'form.attach'});
4574: my $idx = $env{'form.idx'};
4575: if ($idx) {
4576: my %contrib=&Apache::lonnet::restore($symb,$env{'request.course.id'},
4577: $cdom,$cnum);
4578: $attachmenturls = $contrib{$idx.':attachmenturl'};
4579: }
4580: &modify_attachments($r,\@currnewattach,\@currdelold,$symb,$idx,
4581: $attachmenturls,$attachmax{'text'},$toolarge);
4582: return OK;
4583: } elsif ($env{'form.export'}) {
4584: &Apache::loncommon::content_type($r,'text/html');
4585: $r->send_http_header;
4586: my ($symb,$feedurl) = &get_feedurl_and_clean_symb($env{'form.export'});
4587: my $mode='board';
4588: my $status='OPEN';
4589: my $previous=$env{'form.previous'};
4590: if ($feedurl =~ /$LONCAPA::assess_re/) {
4591: $mode='problem';
4592: $status=$Apache::inputtags::status[-1];
4593: }
4594:
4595: my $discussion = &list_discussion($mode,$status,$symb);
4596: my $start_page =
4597: &Apache::loncommon::start_page('Resource Feedback and Discussion');
4598: my $end_page =
4599: &Apache::loncommon::end_page();
4600: $r->print($start_page.$discussion.$end_page);
4601: return OK;
4602:
4603: } elsif ($env{'form.undeleteall'}) {
4604: &Apache::loncommon::content_type($r,'text/html');
4605: $r->send_http_header;
4606: my ($symb,$feedurl) = &get_feedurl_and_clean_symb($env{'form.undeleteall'});
4607: my $ressymb=&wrap_symb($symb);
4608: $r->print(&Apache::loncommon::start_page('Undelete all deleted discussion entries'));
4609: if (&can_see_hidden('',$ressymb,$feedurl,$group,$cdom,$cnum)) {
4610: my %contrib=&Apache::lonnet::restore($symb,$env{'request.course.id'},
4611: $cdom,$cnum);
4612: $contrib{'deleted'} =~ s/^\.//;
4613: $contrib{'deleted'} =~ s/\.$//;
4614: my $confirm_msg;
4615: if ($contrib{'deleted'} ne '') {
4616: if (&Apache::lonnet::store({'deleted' => ''},$symb,$env{'request.course.id'},
4617: $cdom,$cnum) eq 'ok') {
4618: my %likes=&Apache::lonnet::dump('disclikes',$cdom,$cnum,'^'.$symb.':');
4619: my @ids = split(/\.\./,$contrib{'deleted'});
4620: my (%chglikes,%chgunlikes);
4621: foreach my $idx (@ids) {
4622: my $uname = $contrib{$idx.':sendername'};
4623: my $udom = $contrib{$idx.':senderdomain'};
4624: my ($totallikes,$totalunlikes);
4625: if (ref($likes{$symb.':'.$idx.':likers'}) eq 'HASH') {
4626: $totallikes = scalar(keys(%{$likes{$symb.':'.$idx.':likers'}}));
4627: }
4628: if (ref($likes{$symb.':'.$idx.':unlikers'}) eq 'HASH') {
4629: $totalunlikes = scalar(keys(%{$likes{$symb.':'.$idx.':unlikers'}}));
4630: }
4631: if ($totallikes || $totalunlikes) {
4632: $chglikes{$uname.':'.$udom} += $totallikes;
4633: $chgunlikes{$uname.':'.$udom} += $totalunlikes;
4634: }
4635: }
4636: foreach my $user (keys(%chglikes)) {
4637: my ($uname,$udom) = split(/:/,$user);
4638: &storediscussionlikes($chglikes{$user},$chgunlikes{$user},
4639: $uname,$udom,$env{'request.course.id'},
4640: 'undelete');
4641: }
4642: $confirm_msg = &Apache::lonhtmlcommon::confirm_success(&mt("Undeleted all entries"));
4643: } else {
4644: $confirm_msg = &Apache::lonhtmlcommon::confirm_success(&mt("Failed to undelete entries"),1);
4645: }
4646: } else {
4647: $confirm_msg = &Apache::lonhtmlcommon::confirm_success(&mt("No entries to undelete"),1);
4648: }
4649: $r->print(
4650: '<br />'
4651: .&Apache::loncommon::confirmwrapper($confirm_msg)
4652: .&Apache::lonhtmlcommon::actionbox(
4653: ["<a href='$feedurl'>".&mt("Return and reload")."</a>"])
4654: );
4655: }
4656: $r->print(&Apache::loncommon::end_page());
4657: return OK;
4658: } else {
4659: # ------------------------------------------------------------- Normal feedback
4660: my $feedurl=$env{'form.postdata'};
4661: $feedurl=~s/^https?\:\/\///;
4662: $feedurl=~s/^$ENV{'SERVER_NAME'}//;
4663: $feedurl=~s/^$ENV{'HTTP_HOST'}//;
4664: $feedurl=~s/\?.+$//;
4665:
4666: my $symb;
4667: if ($env{'form.replydisc'}) {
4668: $symb=(split(/\:\:\:/,$env{'form.replydisc'}))[0];
4669: } elsif ($env{'form.editdisc'}) {
4670: $symb=(split(/\:\:\:/,$env{'form.editdisc'}))[0];
4671: } elsif ($env{'form.origpage'}) {
4672: $symb="";
4673: } elsif ($env{'form.sendmessageonly'}) {
4674: $symb=(split(/\:\:\:/,$env{'form.sendmessageonly'}))[0];
4675: } else {
4676: $symb=&Apache::lonnet::symbread($feedurl);
4677: }
4678: unless ($symb) {
4679: $symb=$env{'form.symb'};
4680: }
4681: if (defined($symb)) {
4682: ($symb,$feedurl)=&get_feedurl_and_clean_symb($symb);
4683: } else {
4684: # backward compatibility (bulletin boards used to be 'wrapped')
4685: &Apache::lonenc::check_decrypt(\$feedurl);
4686: &dewrapper(\$feedurl);
4687: }
4688: my $goahead=1;
4689: if ($feedurl=~/$LONCAPA::assess_re/) {
4690: unless ($symb) { $goahead=0; }
4691: }
4692: if (!$goahead) {
4693: # Ambiguous Problem Resource
4694: $r->internal_redirect('/adm/ambiguous');
4695: return OK;
4696: }
4697: if ($feedurl eq '') {
4698: &Apache::loncommon::content_type($r,'text/html');
4699: $r->send_http_header;
4700: &no_redirect_back($r);
4701: return OK;
4702: }
4703: # Go ahead with feedback, no ambiguous reference
4704: unless (
4705: (
4706: ($feedurl=~m:^/res:) && ($feedurl!~m:^/res/adm:)
4707: )
4708: ||
4709: ($env{'request.course.id'} && ($feedurl!~m:^/adm:))
4710: ||
4711: ($env{'request.course.id'} && ($symb=~/^bulletin\_\_\_/))
4712: ||
4713: (($env{'request.course.id'}) && ($feedurl =~ /ext\.tool$/))
4714: ) {
4715: &Apache::loncommon::content_type($r,'text/html');
4716: $r->send_http_header;
4717: # Unable to give feedback
4718: &Apache::lonenc::check_encrypt(\$feedurl);
4719: &no_redirect_back($r,$feedurl,2);
4720: return OK;
4721: }
4722: # --------------------------------------------------- Print login screen header
4723: unless ($env{'form.sendit'}) {
4724: &Apache::lonenc::check_encrypt(\$feedurl);
4725: &Apache::loncommon::content_type($r,'text/html');
4726: $r->send_http_header;
4727: if (($env{'form.replydisc'}) || ($env{'form.editdisc'})) {
4728: my ($blocked,$blocktext) =
4729: &Apache::loncommon::blocking_status('boards');
4730: if ($blocked) {
4731: $r->print(&blocked_reply_or_edit($blocktext));
4732: return OK;
4733: }
4734: }
4735: my $options=&screen_header($feedurl,$symb,$group);
4736: if ($options) {
4737: &mail_screen($r,$feedurl,$options,$symb,$attachmax{'text'});
4738: } else {
4739: &fail_redirect($r,$feedurl,1);
4740: }
4741: return OK;
4742: }
4743:
4744: # Get previous user input
4745: my $prevattempts=&Apache::loncommon::get_previous_attempt(
4746: $symb,$env{'user.name'},$env{'user.domain'},
4747: $env{'request.course.id'});
4748:
4749: # Get output from resource
4750: &Apache::lonenc::check_encrypt(\$feedurl);
4751: my $usersaw=&resource_output($feedurl);
4752:
4753: # Get resource answer (need to allow student to view grades for this to work)
4754: &Apache::lonnet::appenv({'allowed.vgr'=>'F'});
4755: my $usersymb = &Apache::lonenc::check_encrypt($symb);
4756: my $useranswer=
4757: &Apache::loncommon::get_student_answers(
4758: $usersymb,$env{'user.name'},$env{'user.domain'},
4759: $env{'request.course.id'});
4760: &Apache::lonnet::delenv('allowed.vgr');
4761: # Get attachments, if any, and not too large
4762: my $attachmenturl='';
4763: my $toolarge='';
4764: if (($env{'form.origpage'}) || ($env{'form.editdisc'}) ||
4765: ($env{'form.replydisc'})) {
4766: my ($symb,$idx);
4767: if ($env{'form.replydisc'}) {
4768: ($symb,$idx)=split(/\:\:\:/,$env{'form.replydisc'});
4769: } elsif ($env{'form.editdisc'}) {
4770: ($symb,$idx)=split(/\:\:\:/,$env{'form.editdisc'});
4771: } elsif ($env{'form.origpage'}) {
4772: $symb = $env{'form.symb'};
4773: }
4774: &Apache::lonenc::check_decrypt(\$symb);
4775: my @currnewattach = ();
4776: my @deloldattach = ();
4777: my @keepold = ();
4778: &process_attachments(\@currnewattach,\@deloldattach,\@keepold);
4779: $symb=~s|(bulletin___\d+___)adm/wrapper/|$1|;
4780: $attachmenturl=&construct_attachmenturl(\@currnewattach,\@keepold,$symb,$idx);
4781: } elsif ($env{'form.attachment.filename'}) {
4782: if (length($env{'form.attachment'})<=$attachmax{'num'}) {
4783: my $now = time;
4784: my $subdir = 'feedback/'.$now;
4785: $attachmenturl=&Apache::lonnet::userfileupload('attachment',undef,$subdir);
4786: } else {
4787: $toolarge = '<p><span class="LC_warning">'.&mt('Attachment not included - exceeded permitted length').'</span><br /></p>';
4788: }
4789: }
4790: # Filter HTML out of message (could be nasty)
4791: my $override;
4792: if ($env{'form.discuss'} =~ /^(?:author|question|course|policy)$/) {
4793: $override = 1;
4794: }
4795: my $message=&clear_out_html($env{'form.comment'},$override);
4796:
4797: # Assemble email
4798: my ($email,$citations)=&assemble_email($message,$prevattempts,
4799: $usersaw,$useranswer);
4800:
4801: # Who gets this?
4802: my ($typestyle,%to) = &Apache::lonmsg::decide_receiver($feedurl);
4803:
4804: # Actually send mail
4805: my $clientip = &Apache::lonnet::get_requestor_ip($r);
4806: my ($status,$numsent,$delay)=&send_msg(&clear_out_html($env{'form.subject'}),
4807: $feedurl,$email,$citations,
4808: $attachmenturl,$usersymb,$clientip,%to);
4809:
4810: # Discussion? Store that.
4811: my $numpost=0;
4812: if ( ($env{'form.discuss'} ne ''
4813: && $env{'form.discuss'} !~ /^(?:author|question|course|policy)/)
4814: || $env{'form.anondiscuss'} ne '') {
4815: my $subject = &clear_out_html($env{'form.subject'});
4816: my $anonmode;
4817: if (&Apache::lonnet::allowed('pac',$env{'request.course.id'}.
4818: ($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:''))) {
4819: $anonmode=($env{'form.discuss'} eq 'anon' || $env{'form.anondiscuss'} );
4820: }
4821: $typestyle.=&adddiscuss($symb,$message,$anonmode,$attachmenturl,
4822: $subject,$group);
4823: $numpost++;
4824: }
4825:
4826: # Add to blog?
4827:
4828: my $blog='';
4829: if ($env{'form.blog'}) {
4830: my $subject = &clear_out_html($env{'form.subject'});
4831: $status.=&Apache::lonrss::addentry($env{'user.name'},
4832: $env{'user.domain'},
4833: 'CourseBlog_'.$env{'request.course.id'},
4834: $subject,$message,$feedurl,'public');
4835: if (&Apache::loncommon::course_type() eq 'Community') {
4836: $blog='<br />'.&mt('Added to my community blog').'<br />';
4837: } else {
4838: $blog='<br />'.&mt('Added to my course blog').'<br />';
4839: }
4840: }
4841:
4842: # Receipt screen and redirect back to where came from
4843: &redirect_back($r,$feedurl,$typestyle,$numsent,$numpost,$blog,$status,$env{'form.previous'},
4844: undef,undef,undef,undef,undef,undef,$group,$toolarge,$delay);
4845: }
4846: return OK;
4847: }
4848:
4849: sub blocked_reply_or_edit {
4850: my ($blocktext) = @_;
4851: return
4852: &Apache::loncommon::start_page('Resource Feedback and Discussion').
4853: $blocktext.'<br /><br /><a href="javascript:history.go(-1)">'.
4854: &mt('Back to previous page').
4855: &Apache::loncommon::end_page();
4856: }
4857:
4858: sub wrap_symb {
4859: my ($ressymb)=@_;
4860: if ($ressymb =~ /bulletin___\d+___/) {
4861: unless ($ressymb =~ m|bulletin___\d+___adm/wrapper|) {
4862: $ressymb=~s|(bulletin___\d+___)|$1adm/wrapper|;
4863: }
4864: }
4865: return $ressymb;
4866: }
4867:
4868: sub dewrapper {
4869: my ($feedurl)=@_;
4870: if ($$feedurl=~m|^/adm/wrapper/adm/.*/bulletinboard$|) {
4871: $$feedurl=~s|^/adm/wrapper||;
4872: }
4873: }
4874:
4875: sub get_feedurl {
4876: my ($symb)=@_;
4877: my ($map,$ind,$url)=&Apache::lonnet::decode_symb($symb);
4878: my $feedurl = &Apache::lonnet::clutter($url);
4879: &dewrapper(\$feedurl);
4880: return $feedurl;
4881: }
4882:
4883: sub get_feedurl_and_clean_symb {
4884: my ($symb)=@_;
4885: &Apache::lonenc::check_decrypt(\$symb);
4886: # backward compatibility (bulletin boards used to be 'wrapped')
4887: unless ($symb =~ m|bulletin___\d+___adm/wrapper|) {
4888: $symb=~s|(bulletin___\d+___)|$1adm/wrapper|;
4889: }
4890: my $feedurl = &get_feedurl($symb);
4891: return ($symb,$feedurl);
4892: }
4893:
4894: sub editing_allowed {
4895: my ($postid,$group) = @_;
4896: $postid = &unescape($postid);
4897: my $can_edit = 0;
4898: if ($group ne '') {
4899: my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
4900: my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
4901: if ($postid =~ m|^bulletin___\d+___adm/wrapper(/adm/\Q$cdom\E/\Q$cnum\E/\d+/bulletinboard)|) {
4902: if (&check_group_priv($group,'egp') eq 'ok') {
4903: $can_edit = 1;
4904: }
4905: return $can_edit;
4906: }
4907: }
4908: my $cid = $env{'request.course.id'};
4909: my $role = (split(/\./,$env{'request.role'}))[0];
4910: my $section = $env{'request.course.sec'};
4911: my $allow_editing_config =
4912: $env{'course.'.$cid.'.allow_discussion_post_editing'};
4913: if ($allow_editing_config =~ m/^\s*yes\s*$/i) {
4914: $can_edit = 1;
4915: } else {
4916: foreach my $editor (split(/,/,$allow_editing_config)) {
4917: my ($editor_role,$editor_sec) = split(/:/,$editor);
4918: if ($editor_role eq $role
4919: && defined($editor_sec)
4920: && defined($section)
4921: && $editor_sec eq $section) {
4922: $can_edit = 1;
4923: last;
4924: }
4925: if ($editor_role eq $role
4926: && !defined($editor_sec)) {
4927: $can_edit = 1;
4928: }
4929: }
4930: }
4931: return $can_edit;
4932: }
4933:
4934: sub check_group_priv {
4935: my ($group,$grp_priv) = @_;
4936: foreach my $priv ('mdg','vcg') {
4937: my $checkcourse = $env{'request.course.id'}.
4938: ($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:'');
4939: if (&Apache::lonnet::allowed($priv,$checkcourse)) {
4940: return 'ok';
4941: }
4942: }
4943: if ($grp_priv && $group ne '') {
4944: if (&Apache::lonnet::allowed($grp_priv,$env{'request.course.id'}.'/'.$group)) {
4945: return 'ok';
4946: }
4947: }
4948: return '';
4949: }
4950:
4951: sub group_args {
4952: my ($group) = @_;
4953: if ($group eq '') { return ''; }
4954: my $extra_args = '&group='.$group;
4955: if (exists($env{'form.ref'})) {
4956: $extra_args .= '&ref='.$env{'form.ref'};
4957: }
4958: return $extra_args;
4959: }
4960:
4961: sub get_resource_title {
4962: my ($symb,$feedurl) = @_;
4963: my ($restitle,$plainurl);
4964: if (defined($symb)) {
4965: my $plain_symb = &Apache::lonenc::check_decrypt($symb);
4966: $restitle = &Apache::lonnet::gettitle($plain_symb);
4967: }
4968: if (defined($feedurl)) {
4969: $plainurl = &Apache::lonenc::check_decrypt($feedurl);
4970: }
4971: if (!defined($restitle)) {
4972: if (defined($feedurl)) {
4973: $restitle = &Apache::lonnet::gettitle($plainurl);
4974: }
4975: }
4976: if ($plainurl ne $feedurl) {
4977: my ($plain_filename) = ($plainurl =~ m-/([^/]+)$-);
4978: if ($plain_filename eq $restitle) {
4979: $restitle = &mt('Untitled resource');
4980: }
4981: }
4982: if ($restitle eq '') {
4983: $restitle = &mt('Untitled resource');
4984: }
4985: return $restitle;
4986: }
4987:
4988: 1;
4989: __END__
4990:
4991:
4992: =pod
4993:
4994: =head1 NAME
4995:
4996: Apache::lonfeedback.pm
4997:
4998: =head1 SYNOPSIS
4999:
5000: Handles feedback from students to instructors and system administrators.
5001:
5002: Provides a screenshot of the current resource, as well as previous attempts if the resource was a homework.
5003:
5004: Used by lonmsg.pm.
5005:
5006: This is part of the LearningOnline Network with CAPA project
5007: described at http://www.lon-capa.org.
5008:
5009: =head1 OVERVIEW
5010:
5011: None
5012:
5013: =head1 SUBROUTINES
5014:
5015: =over
5016:
5017: =item discussion_open()
5018:
5019: =item discussion_visible()
5020:
5021: =item discussion_vote_available()
5022:
5023: =item get_realsymb()
5024:
5025: =item list_discussion()
5026:
5027: =item can_see_hidden()
5028:
5029: =item discussion_link()
5030:
5031: =item send_feedback_link()
5032:
5033: =item send_message_link()
5034:
5035: =item action_links_bar()
5036:
5037: =item postingform_display()
5038:
5039: =item build_posting_display
5040:
5041: =item filter_regexp()
5042:
5043: =item get_post_contents()
5044:
5045: =item replicate_attachments()
5046:
5047: =item mail_screen()
5048:
5049: =item print_display_options()
5050:
5051: =item print_sortfilter_options()
5052:
5053: =item print_showposters()
5054:
5055: =item get_post_versions()
5056:
5057: =item get_post_attachments()
5058:
5059: =item fail_redirect()
5060:
5061: =item redirect_back()
5062:
5063: =item no_redirect_back()
5064:
5065: =item screen_header()
5066:
5067: =item resource_output()
5068:
5069: =item clear_out_html()
5070:
5071: =item assemble_email()
5072:
5073: =item feedback_available()
5074:
5075: =item send_msg()
5076:
5077: =item adddiscuss()
5078:
5079: =item get_discussion_info()
5080:
5081: =item show_preview()
5082:
5083: =item newline_to_br()
5084:
5085: =item tidy_html()
5086:
5087: =item generate_preview_button()
5088:
5089: =item modify_attachments()
5090:
5091: =item process_attachments()
5092:
5093: =item generate_attachments_button()
5094:
5095: =item extract_attachments()
5096:
5097: =item construct_attachmenturl()
5098:
5099: =item add_blog_checkbox()
5100:
5101: =item has_discussion()
5102:
5103: =item sort_filter_names()
5104:
5105: =item handler()
5106:
5107: =item blocked_reply_or_edit()
5108:
5109: =item wrap_symb()
5110:
5111: =item dewrapper()
5112:
5113: =item get_feedurl()
5114:
5115: =item get_feedurl_and_clean_symb()
5116:
5117: =item editing_allowed()
5118:
5119: =item check_group_priv()
5120:
5121: =item group_args()
5122:
5123: =item get_resource_title()
5124:
5125: =back
5126:
5127: =cut
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>