Annotation of loncom/interface/lonhelper.pm, revision 1.68
1.1 bowersj2 1: # The LearningOnline Network with CAPA
2: # .helper XML handler to implement the LON-CAPA helper
3: #
1.68 ! sakharuk 4: # $Id: lonhelper.pm,v 1.67 2004/03/31 05:23:59 albertel Exp $
1.1 bowersj2 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: # (Page Handler
29: #
30: # (.helper handler
31: #
32:
1.3 bowersj2 33: =pod
34:
1.44 bowersj2 35: =head1 NAME
1.3 bowersj2 36:
1.44 bowersj2 37: lonhelper - implements helper framework
38:
39: =head1 SYNOPSIS
40:
41: lonhelper implements the helper framework for LON-CAPA, and provides
42: many generally useful components for that framework.
43:
44: Helpers are little programs which present the user with a sequence of
45: simple choices, instead of one monolithic multi-dimensional
46: choice. They are also referred to as "wizards", "druids", and
47: other potentially trademarked or semantically-loaded words.
48:
49: =head1 OVERVIEWX<lonhelper>
50:
51: Helpers are well-established UI widgets that users
1.3 bowersj2 52: feel comfortable with. It can take a complicated multidimensional problem the
53: user has and turn it into a series of bite-sized one-dimensional questions.
54:
55: For developers, helpers provide an easy way to bundle little bits of functionality
56: for the user, without having to write the tedious state-maintenence code.
57:
58: Helpers are defined as XML documents, placed in the /home/httpd/html/adm/helpers
59: directory and having the .helper file extension. For examples, see that directory.
60:
61: All classes are in the Apache::lonhelper namespace.
62:
1.44 bowersj2 63: =head1 lonhelper XML file formatX<lonhelper, XML file format>
1.3 bowersj2 64:
65: A helper consists of a top-level <helper> tag which contains a series of states.
66: Each state contains one or more state elements, which are what the user sees, like
67: messages, resource selections, or date queries.
68:
69: The helper tag is required to have one attribute, "title", which is the name
1.31 bowersj2 70: of the helper itself, such as "Parameter helper". The helper tag may optionally
71: have a "requiredpriv" attribute, specifying the priviledge a user must have
72: to use the helper, or get denied access. See loncom/auth/rolesplain.tab for
73: useful privs. Default is full access, which is often wrong!
1.3 bowersj2 74:
75: =head2 State tags
76:
77: State tags are required to have an attribute "name", which is the symbolic
1.7 bowersj2 78: name of the state and will not be directly seen by the user. The helper is
79: required to have one state named "START", which is the state the helper
1.5 bowersj2 80: will start with. By convention, this state should clearly describe what
1.3 bowersj2 81: the helper will do for the user, and may also include the first information
82: entry the user needs to do for the helper.
83:
84: State tags are also required to have an attribute "title", which is the
85: human name of the state, and will be displayed as the header on top of
86: the screen for the user.
87:
88: =head2 Example Helper Skeleton
89:
90: An example of the tags so far:
91:
92: <helper title="Example Helper">
93: <state name="START" title="Demonstrating the Example Helper">
94: <!-- notice this is the START state the wizard requires -->
95: </state>
96: <state name="GET_NAME" title="Enter Student Name">
97: </state>
98: </helper>
99:
100: Of course this does nothing. In order for the wizard to do something, it is
101: necessary to put actual elements into the wizard. Documentation for each
102: of these elements follows.
103:
1.44 bowersj2 104: =head1 Creating a Helper With Code, Not XML
1.13 bowersj2 105:
106: In some situations, such as the printing wizard (see lonprintout.pm),
107: writing the helper in XML would be too complicated, because of scope
108: issues or the fact that the code actually outweighs the XML. It is
109: possible to create a helper via code, though it is a little odd.
110:
111: Creating a helper via code is more like issuing commands to create
112: a helper then normal code writing. For instance, elements will automatically
113: be added to the last state created, so it's important to create the
114: states in the correct order.
115:
116: First, create a new helper:
117:
118: use Apache::lonhelper;
119:
120: my $helper = Apache::lonhelper::new->("Helper Title");
121:
122: Next you'll need to manually add states to the helper:
123:
124: Apache::lonhelper::state->new("STATE_NAME", "State's Human Title");
125:
126: You don't need to save a reference to it because all elements up until
127: the next state creation will automatically be added to this state.
128:
129: Elements are created by populating the $paramHash in
130: Apache::lonhelper::paramhash. To prevent namespace issues, retrieve
131: a reference to that has with getParamHash:
132:
133: my $paramHash = Apache::lonhelper::getParamHash();
134:
135: You will need to do this for each state you create.
136:
137: Populate the $paramHash with the parameters for the element you wish
138: to add next; the easiest way to find out what those entries are is
139: to read the code. Some common ones are 'variable' to record the variable
140: to store the results in, and NEXTSTATE to record a next state transition.
141:
142: Then create your element:
143:
144: $paramHash->{MESSAGETEXT} = "This is a message.";
145: Apache::lonhelper::message->new();
146:
147: The creation will take the $paramHash and bless it into a
148: Apache::lonhelper::message object. To create the next element, you need
149: to get a reference to the new, empty $paramHash:
150:
151: $paramHash = Apache::lonhelper::getParamHash();
152:
153: and you can repeat creating elements that way. You can add states
154: and elements as needed.
155:
156: See lonprintout.pm, subroutine printHelper for an example of this, where
157: we dynamically add some states to prevent security problems, for instance.
158:
159: Normally the machinery in the XML format is sufficient; dynamically
160: adding states can easily be done by wrapping the state in a <condition>
161: tag. This should only be used when the code dominates the XML content,
162: the code is so complicated that it is difficult to get access to
1.44 bowersj2 163: all of the information you need because of scoping issues, or would-be <exec> or
164: <eval> blocks using the {DATA} mechanism results in hard-to-read
165: and -maintain code. (See course.initialization.helper for a borderline
166: case.)
1.13 bowersj2 167:
168: It is possible to do some of the work with an XML fragment parsed by
1.17 bowersj2 169: lonxml; again, see lonprintout.pm for an example. In that case it is
170: imperative that you call B<Apache::lonhelper::registerHelperTags()>
171: before parsing XML fragments and B<Apache::lonhelper::unregisterHelperTags()>
172: when you are done. See lonprintout.pm for examples of this usage in the
173: printHelper subroutine.
1.13 bowersj2 174:
1.57 albertel 175: =head2 Localization
176:
177: The helper framework tries to handle as much localization as
178: possible. The text is always run through
179: Apache::lonlocal::normalize_string, so be sure to run the keys through
180: that function for maximum usefulness and robustness.
181:
1.3 bowersj2 182: =cut
183:
1.1 bowersj2 184: package Apache::lonhelper;
1.2 bowersj2 185: use Apache::Constants qw(:common);
186: use Apache::File;
1.3 bowersj2 187: use Apache::lonxml;
1.57 albertel 188: use Apache::lonlocal;
1.2 bowersj2 189:
1.7 bowersj2 190: # Register all the tags with the helper, so the helper can
191: # push and pop them
192:
193: my @helperTags;
194:
195: sub register {
196: my ($namespace, @tags) = @_;
197:
198: for my $tag (@tags) {
199: push @helperTags, [$namespace, $tag];
200: }
201: }
202:
1.2 bowersj2 203: BEGIN {
1.7 bowersj2 204: Apache::lonxml::register('Apache::lonhelper',
205: ('helper'));
206: register('Apache::lonhelper', ('state'));
1.2 bowersj2 207: }
208:
1.7 bowersj2 209: # Since all helpers are only three levels deep (helper tag, state tag,
1.3 bowersj2 210: # substate type), it's easier and more readble to explicitly track
211: # those three things directly, rather then futz with the tag stack
212: # every time.
213: my $helper;
214: my $state;
215: my $substate;
1.4 bowersj2 216: # To collect parameters, the contents of the subtags are collected
217: # into this paramHash, then passed to the element object when the
218: # end of the element tag is located.
219: my $paramHash;
1.2 bowersj2 220:
1.25 bowersj2 221: # Note from Jeremy 5-8-2003: It is *vital* that the real handler be called
222: # as a subroutine from the handler, or very mysterious things might happen.
223: # I don't know exactly why, but it seems that the scope where the Apache
224: # server enters the perl handler is treated differently from the rest of
225: # the handler. This also seems to manifest itself in the debugger as entering
226: # the perl handler in seemingly random places (sometimes it starts in the
227: # compiling phase, sometimes in the handler execution phase where it runs
228: # the code and stepping into the "1;" the module ends with goes into the handler,
229: # sometimes starting directly with the handler); I think the cause is related.
230: # In the debugger, this means that breakpoints are ignored until you step into
231: # a function and get out of what must be a "faked up scope" in the Apache->
232: # mod_perl connection. In this code, it was manifesting itself in the existence
1.65 www 233: # of two separate file-scoped $helper variables, one set to the value of the
1.25 bowersj2 234: # helper in the helper constructor, and one referenced by the handler on the
1.44 bowersj2 235: # "$helper->process()" line. Using the debugger, one could actually
236: # see the two different $helper variables, as hashes at completely
237: # different addresses. The second was therefore never set, and was still
1.25 bowersj2 238: # undefined when I tried to call process on it.
239: # By pushing the "real handler" down into the "real scope", everybody except the
240: # actual handler function directly below this comment gets the same $helper and
241: # everybody is happy.
242: # The upshot of all of this is that for safety when a handler is using
243: # file-scoped variables in LON-CAPA, the handler should be pushed down one
244: # call level, as I do here, to ensure that the top-level handler function does
245: # not get a different file scope from the rest of the code.
246: sub handler {
247: my $r = shift;
248: return real_handler($r);
249: }
250:
1.13 bowersj2 251: # For debugging purposes, one can send a second parameter into this
252: # function, the 'uri' of the helper you wish to have rendered, and
253: # call this from other handlers.
1.25 bowersj2 254: sub real_handler {
1.3 bowersj2 255: my $r = shift;
1.13 bowersj2 256: my $uri = shift;
257: if (!defined($uri)) { $uri = $r->uri(); }
258: $ENV{'request.uri'} = $uri;
259: my $filename = '/home/httpd/html' . $uri;
1.2 bowersj2 260: my $fh = Apache::File->new($filename);
261: my $file;
1.3 bowersj2 262: read $fh, $file, 100000000;
263:
1.27 bowersj2 264:
1.3 bowersj2 265: # Send header, don't cache this page
266: if ($r->header_only) {
267: if ($ENV{'browser.mathml'}) {
1.57 albertel 268: $r->content_type('text/xml; charset=UTF-8');
1.3 bowersj2 269: } else {
1.57 albertel 270: $r->content_type('text/html; charset=UTF-8');
1.3 bowersj2 271: }
272: $r->send_http_header;
273: return OK;
274: }
275: if ($ENV{'browser.mathml'}) {
276: $r->content_type('text/xml');
277: } else {
278: $r->content_type('text/html');
279: }
280: $r->send_http_header;
281: $r->rflush();
1.2 bowersj2 282:
1.3 bowersj2 283: # Discard result, we just want the objects that get created by the
284: # xml parsing
285: &Apache::lonxml::xmlparse($r, 'helper', $file);
1.2 bowersj2 286:
1.31 bowersj2 287: my $allowed = $helper->allowedCheck();
288: if (!$allowed) {
289: $ENV{'user.error.msg'} = $ENV{'request.uri'}.':'.$helper->{REQUIRED_PRIV}.
290: ":0:0:Permission denied to access this helper.";
291: return HTTP_NOT_ACCEPTABLE;
292: }
293:
1.13 bowersj2 294: $helper->process();
295:
1.3 bowersj2 296: $r->print($helper->display());
1.31 bowersj2 297: return OK;
1.2 bowersj2 298: }
299:
1.13 bowersj2 300: sub registerHelperTags {
301: for my $tagList (@helperTags) {
302: Apache::lonxml::register($tagList->[0], $tagList->[1]);
303: }
304: }
305:
306: sub unregisterHelperTags {
307: for my $tagList (@helperTags) {
308: Apache::lonxml::deregister($tagList->[0], $tagList->[1]);
309: }
310: }
311:
1.2 bowersj2 312: sub start_helper {
313: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
314:
315: if ($target ne 'helper') {
316: return '';
317: }
1.7 bowersj2 318:
1.13 bowersj2 319: registerHelperTags();
320:
1.31 bowersj2 321: Apache::lonhelper::helper->new($token->[2]{'title'}, $token->[2]{'requiredpriv'});
1.4 bowersj2 322: return '';
1.2 bowersj2 323: }
324:
325: sub end_helper {
326: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
327:
1.3 bowersj2 328: if ($target ne 'helper') {
329: return '';
330: }
1.7 bowersj2 331:
1.13 bowersj2 332: unregisterHelperTags();
1.7 bowersj2 333:
1.4 bowersj2 334: return '';
1.2 bowersj2 335: }
1.1 bowersj2 336:
1.3 bowersj2 337: sub start_state {
338: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
339:
340: if ($target ne 'helper') {
341: return '';
342: }
343:
1.13 bowersj2 344: Apache::lonhelper::state->new($token->[2]{'name'},
345: $token->[2]{'title'});
1.3 bowersj2 346: return '';
347: }
348:
1.13 bowersj2 349: # Use this to get the param hash from other files.
350: sub getParamHash {
351: return $paramHash;
352: }
353:
354: # Use this to get the helper, if implementing elements in other files
355: # (like lonprintout.pm)
356: sub getHelper {
357: return $helper;
358: }
359:
1.3 bowersj2 360: # don't need this, so ignore it
361: sub end_state {
362: return '';
363: }
364:
1.1 bowersj2 365: 1;
366:
1.3 bowersj2 367: package Apache::lonhelper::helper;
368:
369: use Digest::MD5 qw(md5_hex);
1.57 albertel 370: use HTML::Entities();
1.3 bowersj2 371: use Apache::loncommon;
372: use Apache::File;
1.57 albertel 373: use Apache::lonlocal;
1.3 bowersj2 374:
375: sub new {
376: my $proto = shift;
377: my $class = ref($proto) || $proto;
378: my $self = {};
379:
380: $self->{TITLE} = shift;
1.31 bowersj2 381: $self->{REQUIRED_PRIV} = shift;
1.3 bowersj2 382:
383: # If there is a state from the previous form, use that. If there is no
384: # state, use the start state parameter.
385: if (defined $ENV{"form.CURRENT_STATE"})
386: {
387: $self->{STATE} = $ENV{"form.CURRENT_STATE"};
388: }
389: else
390: {
391: $self->{STATE} = "START";
392: }
393:
394: $self->{TOKEN} = $ENV{'form.TOKEN'};
395: # If a token was passed, we load that in. Otherwise, we need to create a
396: # new storage file
397: # Tried to use standard Tie'd hashes, but you can't seem to take a
398: # reference to a tied hash and write to it. I'd call that a wart.
399: if ($self->{TOKEN}) {
400: # Validate the token before trusting it
401: if ($self->{TOKEN} !~ /^[a-f0-9]{32}$/) {
402: # Not legit. Return nothing and let all hell break loose.
403: # User shouldn't be doing that!
404: return undef;
405: }
406:
407: # Get the hash.
408: $self->{FILENAME} = $Apache::lonnet::tmpdir . md5_hex($self->{TOKEN}); # Note the token is not the literal file
409:
410: my $file = Apache::File->new($self->{FILENAME});
411: my $contents = <$file>;
1.5 bowersj2 412:
413: # Now load in the contents
414: for my $value (split (/&/, $contents)) {
415: my ($name, $value) = split(/=/, $value);
416: $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
417: $self->{VARS}->{$name} = $value;
418: }
419:
1.3 bowersj2 420: $file->close();
421: } else {
422: # Only valid if we're just starting.
423: if ($self->{STATE} ne 'START') {
424: return undef;
425: }
426: # Must create the storage
427: $self->{TOKEN} = md5_hex($ENV{'user.name'} . $ENV{'user.domain'} .
428: time() . rand());
429: $self->{FILENAME} = $Apache::lonnet::tmpdir . md5_hex($self->{TOKEN});
430: }
431:
432: # OK, we now have our persistent storage.
433:
434: if (defined $ENV{"form.RETURN_PAGE"})
435: {
436: $self->{RETURN_PAGE} = $ENV{"form.RETURN_PAGE"};
437: }
438: else
439: {
440: $self->{RETURN_PAGE} = $ENV{REFERER};
441: }
442:
443: $self->{STATES} = {};
444: $self->{DONE} = 0;
445:
1.9 bowersj2 446: # Used by various helpers for various things; see lonparm.helper
447: # for an example.
448: $self->{DATA} = {};
449:
1.13 bowersj2 450: $helper = $self;
451:
452: # Establish the $paramHash
453: $paramHash = {};
454:
1.3 bowersj2 455: bless($self, $class);
456: return $self;
457: }
458:
459: # Private function; returns a string to construct the hidden fields
460: # necessary to have the helper track state.
461: sub _saveVars {
462: my $self = shift;
463: my $result = "";
464: $result .= '<input type="hidden" name="CURRENT_STATE" value="' .
1.67 albertel 465: HTML::Entities::encode($self->{STATE},'<>&"') . "\" />\n";
1.3 bowersj2 466: $result .= '<input type="hidden" name="TOKEN" value="' .
467: $self->{TOKEN} . "\" />\n";
468: $result .= '<input type="hidden" name="RETURN_PAGE" value="' .
1.67 albertel 469: HTML::Entities::encode($self->{RETURN_PAGE},'<>&"') . "\" />\n";
1.3 bowersj2 470:
471: return $result;
472: }
473:
474: # Private function: Create the querystring-like representation of the stored
475: # data to write to disk.
476: sub _varsInFile {
477: my $self = shift;
478: my @vars = ();
479: for my $key (keys %{$self->{VARS}}) {
480: push @vars, &Apache::lonnet::escape($key) . '=' .
481: &Apache::lonnet::escape($self->{VARS}->{$key});
482: }
483: return join ('&', @vars);
484: }
485:
1.5 bowersj2 486: # Use this to declare variables.
487: # FIXME: Document this
488: sub declareVar {
489: my $self = shift;
490: my $var = shift;
491:
492: if (!defined($self->{VARS}->{$var})) {
493: $self->{VARS}->{$var} = '';
494: }
495:
496: my $envname = 'form.' . $var . '.forminput';
497: if (defined($ENV{$envname})) {
1.28 bowersj2 498: if (ref($ENV{$envname})) {
499: $self->{VARS}->{$var} = join('|||', @{$ENV{$envname}});
500: } else {
501: $self->{VARS}->{$var} = $ENV{$envname};
502: }
1.5 bowersj2 503: }
504: }
505:
1.31 bowersj2 506: sub allowedCheck {
507: my $self = shift;
508:
509: if (!defined($self->{REQUIRED_PRIV})) {
510: return 1;
511: }
512:
513: return Apache::lonnet::allowed($self->{REQUIRED_PRIV}, $ENV{'request.course.id'});
514: }
515:
1.3 bowersj2 516: sub changeState {
517: my $self = shift;
518: $self->{STATE} = shift;
519: }
520:
521: sub registerState {
522: my $self = shift;
523: my $state = shift;
524:
525: my $stateName = $state->name();
526: $self->{STATES}{$stateName} = $state;
527: }
528:
1.13 bowersj2 529: sub process {
1.3 bowersj2 530: my $self = shift;
531:
532: # Phase 1: Post processing for state of previous screen (which is actually
533: # the "current state" in terms of the helper variables), if it wasn't the
534: # beginning state.
1.57 albertel 535: if ($self->{STATE} ne "START" || $ENV{"form.SUBMIT"} eq &mt("Next ->")) {
1.3 bowersj2 536: my $prevState = $self->{STATES}{$self->{STATE}};
1.13 bowersj2 537: $prevState->postprocess();
1.3 bowersj2 538: }
539:
540: # Note, to handle errors in a state's input that a user must correct,
541: # do not transition in the postprocess, and force the user to correct
542: # the error.
543:
544: # Phase 2: Preprocess current state
545: my $startState = $self->{STATE};
1.17 bowersj2 546: my $state = $self->{STATES}->{$startState};
1.3 bowersj2 547:
1.13 bowersj2 548: # For debugging, print something here to determine if you're going
549: # to an undefined state.
1.3 bowersj2 550: if (!defined($state)) {
1.13 bowersj2 551: return;
1.3 bowersj2 552: }
553: $state->preprocess();
554:
555: # Phase 3: While the current state is different from the previous state,
556: # keep processing.
1.17 bowersj2 557: while ( $startState ne $self->{STATE} &&
558: defined($self->{STATES}->{$self->{STATE}}) )
1.3 bowersj2 559: {
560: $startState = $self->{STATE};
1.17 bowersj2 561: $state = $self->{STATES}->{$startState};
1.3 bowersj2 562: $state->preprocess();
563: }
564:
1.13 bowersj2 565: return;
566: }
567:
568: # 1: Do the post processing for the previous state.
569: # 2: Do the preprocessing for the current state.
570: # 3: Check to see if state changed, if so, postprocess current and move to next.
571: # Repeat until state stays stable.
572: # 4: Render the current state to the screen as an HTML page.
573: sub display {
574: my $self = shift;
575:
576: my $state = $self->{STATES}{$self->{STATE}};
577:
578: my $result = "";
579:
1.17 bowersj2 580: if (!defined($state)) {
581: $result = "<font color='#ff0000'>Error: state '$state' not defined!</font>";
582: return $result;
583: }
584:
1.3 bowersj2 585: # Phase 4: Display.
1.68 ! sakharuk 586: my $stateTitle=&mt($state->title());
1.57 albertel 587: my $helperTitle = &mt($self->{TITLE});
588: my $bodytag = &Apache::loncommon::bodytag($helperTitle,'','');
589: my $previous = HTML::Entities::encode(&mt("<- Previous"), '<>&"');
590: my $next = HTML::Entities::encode(&mt("Next ->"), '<>&"');
591: # FIXME: This should be parameterized, not concatenated - Jeremy
592: my $loncapaHelper = &mt("LON-CAPA Helper:");
1.3 bowersj2 593:
594: $result .= <<HEADER;
595: <html>
1.68 ! sakharuk 596: <script type="text/javascript" language="Javascript" >
! 597: var editbrowser;
! 598: function openbrowser(formname,elementname,only,omit) {
! 599: var url = '/res/?';
! 600: if (editbrowser == null) {
! 601: url += 'launch=1&';
! 602: }
! 603: url += 'catalogmode=interactive&';
! 604: url += 'mode=parmset&';
! 605: url += 'form=' + formname + '&';
! 606: if (only != null) {
! 607: url += 'only=' + only + '&';
! 608: }
! 609: if (omit != null) {
! 610: url += 'omit=' + omit + '&';
! 611: }
! 612: url += 'element=' + elementname + '';
! 613: var title = 'Browser';
! 614: var options = 'scrollbars=1,resizable=1,menubar=0';
! 615: options += ',width=700,height=600';
! 616: editbrowser = open(url,title,options,'1');
! 617: editbrowser.focus();
! 618: }
! 619: </script>
1.3 bowersj2 620: <head>
1.57 albertel 621: <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
622: <title>$loncapaHelper: $helperTitle</title>
1.3 bowersj2 623: </head>
624: $bodytag
625: HEADER
1.28 bowersj2 626: if (!$state->overrideForm()) { $result.="<form name='helpform' method='POST'>"; }
1.3 bowersj2 627: $result .= <<HEADER;
1.31 bowersj2 628: <table border="0" width='100%'><tr><td>
1.3 bowersj2 629: <h2><i>$stateTitle</i></h2>
630: HEADER
631:
1.31 bowersj2 632: $result .= "<table cellpadding='10' width='100%'><tr><td rowspan='2' valign='top'>";
1.30 bowersj2 633:
1.3 bowersj2 634: if (!$state->overrideForm()) {
635: $result .= $self->_saveVars();
636: }
1.30 bowersj2 637: $result .= $state->render();
638:
1.31 bowersj2 639: $result .= "</td><td valign='top' align='right'>";
1.3 bowersj2 640:
1.30 bowersj2 641: # Warning: Copy and pasted from below, because it's too much trouble to
642: # turn this into a subroutine
1.3 bowersj2 643: if (!$state->overrideForm()) {
644: if ($self->{STATE} ne $self->{START_STATE}) {
645: #$result .= '<input name="SUBMIT" type="submit" value="<- Previous" /> ';
646: }
647: if ($self->{DONE}) {
648: my $returnPage = $self->{RETURN_PAGE};
1.57 albertel 649: $result .= "<a href=\"$returnPage\">" . &mt("End Helper") . "</a>";
1.3 bowersj2 650: }
651: else {
1.30 bowersj2 652: $result .= '<nobr><input name="back" type="button" ';
1.57 albertel 653: $result .= 'value="' . $previous . '" onclick="history.go(-1)" /> ';
654: $result .= '<input name="SUBMIT" type="submit" value="' . $next . '" /></nobr>';
1.30 bowersj2 655: }
656: }
657:
1.31 bowersj2 658: $result .= "</td></tr><tr><td valign='bottom' align='right'>";
1.30 bowersj2 659:
660: # Warning: Copy and pasted from above, because it's too much trouble to
661: # turn this into a subroutine
662: if (!$state->overrideForm()) {
663: if ($self->{STATE} ne $self->{START_STATE}) {
664: #$result .= '<input name="SUBMIT" type="submit" value="<- Previous" /> ';
665: }
666: if ($self->{DONE}) {
667: my $returnPage = $self->{RETURN_PAGE};
1.57 albertel 668: $result .= "<a href=\"$returnPage\">" . &mt('End Helper') . "</a>";
1.30 bowersj2 669: }
670: else {
671: $result .= '<nobr><input name="back" type="button" ';
1.57 albertel 672: $result .= 'value="' . $previous . '" onclick="history.go(-1)" /> ';
673: $result .= '<input name="SUBMIT" type="submit" value="' . $next . '" /></nobr>';
1.3 bowersj2 674: }
675: }
676:
1.13 bowersj2 677: #foreach my $key (keys %{$self->{VARS}}) {
678: # $result .= "|$key| -> " . $self->{VARS}->{$key} . "<br />";
679: #}
1.5 bowersj2 680:
1.30 bowersj2 681: $result .= "</td></tr></table>";
682:
1.3 bowersj2 683: $result .= <<FOOTER;
684: </td>
685: </tr>
686: </table>
687: </form>
688: </body>
689: </html>
690: FOOTER
691:
692: # Handle writing out the vars to the file
693: my $file = Apache::File->new('>'.$self->{FILENAME});
1.33 bowersj2 694: print $file $self->_varsInFile();
1.3 bowersj2 695:
696: return $result;
697: }
698:
699: 1;
700:
701: package Apache::lonhelper::state;
702:
703: # States bundle things together and are responsible for compositing the
1.4 bowersj2 704: # various elements together. It is not generally necessary for users to
705: # use the state object directly, so it is not perldoc'ed.
706:
707: # Basically, all the states do is pass calls to the elements and aggregate
708: # the results.
1.3 bowersj2 709:
710: sub new {
711: my $proto = shift;
712: my $class = ref($proto) || $proto;
713: my $self = {};
714:
715: $self->{NAME} = shift;
716: $self->{TITLE} = shift;
717: $self->{ELEMENTS} = [];
718:
719: bless($self, $class);
720:
721: $helper->registerState($self);
722:
1.13 bowersj2 723: $state = $self;
724:
1.3 bowersj2 725: return $self;
726: }
727:
728: sub name {
729: my $self = shift;
730: return $self->{NAME};
731: }
732:
733: sub title {
734: my $self = shift;
735: return $self->{TITLE};
736: }
737:
1.4 bowersj2 738: sub preprocess {
739: my $self = shift;
740: for my $element (@{$self->{ELEMENTS}}) {
741: $element->preprocess();
742: }
743: }
744:
1.6 bowersj2 745: # FIXME: Document that all postprocesses must return a true value or
746: # the state transition will be overridden
1.4 bowersj2 747: sub postprocess {
748: my $self = shift;
1.6 bowersj2 749:
750: # Save the state so we can roll it back if we need to.
751: my $originalState = $helper->{STATE};
752: my $everythingSuccessful = 1;
753:
1.4 bowersj2 754: for my $element (@{$self->{ELEMENTS}}) {
1.6 bowersj2 755: my $result = $element->postprocess();
756: if (!$result) { $everythingSuccessful = 0; }
757: }
758:
759: # If not all the postprocesses were successful, override
760: # any state transitions that may have occurred. It is the
761: # responsibility of the states to make sure they have
762: # error handling in that case.
763: if (!$everythingSuccessful) {
764: $helper->{STATE} = $originalState;
1.4 bowersj2 765: }
766: }
767:
1.13 bowersj2 768: # Override the form if any element wants to.
769: # two elements overriding the form will make a mess, but that should
770: # be considered helper author error ;-)
1.4 bowersj2 771: sub overrideForm {
1.13 bowersj2 772: my $self = shift;
773: for my $element (@{$self->{ELEMENTS}}) {
774: if ($element->overrideForm()) {
775: return 1;
776: }
777: }
1.4 bowersj2 778: return 0;
779: }
780:
781: sub addElement {
782: my $self = shift;
783: my $element = shift;
784:
785: push @{$self->{ELEMENTS}}, $element;
786: }
787:
788: sub render {
789: my $self = shift;
790: my @results = ();
791:
792: for my $element (@{$self->{ELEMENTS}}) {
793: push @results, $element->render();
794: }
1.28 bowersj2 795:
1.4 bowersj2 796: return join("\n", @results);
797: }
798:
799: 1;
800:
801: package Apache::lonhelper::element;
802: # Support code for elements
803:
804: =pod
805:
1.44 bowersj2 806: =head1 Element Base Class
1.4 bowersj2 807:
1.28 bowersj2 808: The Apache::lonhelper::element base class provides support for elements
809: and defines some generally useful tags for use in elements.
1.4 bowersj2 810:
1.44 bowersj2 811: =head2 finalcode tagX<finalcode>
1.25 bowersj2 812:
813: Each element can contain a "finalcode" tag that, when the special FINAL
814: helper state is used, will be executed, surrounded by "sub { my $helper = shift;"
815: and "}". It is expected to return a string describing what it did, which
816: may be an empty string. See course initialization helper for an example. This is
817: generally intended for helpers like the course initialization helper, which consist
818: of several panels, each of which is performing some sort of bite-sized functionality.
819:
1.44 bowersj2 820: =head2 defaultvalue tagX<defaultvalue>
1.25 bowersj2 821:
822: Each element that accepts user input can contain a "defaultvalue" tag that,
823: when surrounded by "sub { my $helper = shift; my $state = shift; " and "}",
824: will form a subroutine that when called will provide a default value for
825: the element. How this value is interpreted by the element is specific to
826: the element itself, and possibly the settings the element has (such as
827: multichoice vs. single choice for <choices> tags).
828:
829: This is also intended for things like the course initialization wizard, where the
830: user is setting various parameters. By correctly grabbing current settings
831: and including them into the helper, it allows the user to come back to the
832: helper later and re-execute it, without needing to worry about overwriting
833: some setting accidentally.
834:
835: Again, see the course initialization helper for examples.
836:
1.44 bowersj2 837: =head2 validator tagX<validator>
1.38 bowersj2 838:
839: Some elements that accepts user input can contain a "validator" tag that,
840: when surrounded by "sub { my $helper = shift; my $state = shift; my $element = shift; my $val = shift "
841: and "}", where "$val" is the value the user entered, will form a subroutine
842: that when called will verify whether the given input is valid or not. If it
843: is valid, the routine will return a false value. If invalid, the routine
844: will return an error message to be displayed for the user.
845:
846: Consult the documentation for each element to see whether it supports this
847: tag.
848:
1.44 bowersj2 849: =head2 getValue methodX<getValue (helper elements)>
1.31 bowersj2 850:
851: If the element stores the name of the variable in a 'variable' member, which
852: the provided ones all do, you can retreive the value of the variable by calling
853: this method.
854:
1.4 bowersj2 855: =cut
856:
1.5 bowersj2 857: BEGIN {
1.7 bowersj2 858: &Apache::lonhelper::register('Apache::lonhelper::element',
1.25 bowersj2 859: ('nextstate', 'finalcode',
1.38 bowersj2 860: 'defaultvalue', 'validator'));
1.5 bowersj2 861: }
862:
1.4 bowersj2 863: # Because we use the param hash, this is often a sufficent
864: # constructor
865: sub new {
866: my $proto = shift;
867: my $class = ref($proto) || $proto;
868: my $self = $paramHash;
869: bless($self, $class);
870:
871: $self->{PARAMS} = $paramHash;
872: $self->{STATE} = $state;
873: $state->addElement($self);
874:
875: # Ensure param hash is not reused
876: $paramHash = {};
877:
878: return $self;
879: }
880:
1.5 bowersj2 881: sub start_nextstate {
882: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
883:
884: if ($target ne 'helper') {
885: return '';
886: }
887:
888: $paramHash->{NEXTSTATE} = &Apache::lonxml::get_all_text('/nextstate',
889: $parser);
890: return '';
891: }
892:
893: sub end_nextstate { return ''; }
894:
1.25 bowersj2 895: sub start_finalcode {
896: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
897:
898: if ($target ne 'helper') {
899: return '';
900: }
901:
902: $paramHash->{FINAL_CODE} = &Apache::lonxml::get_all_text('/finalcode',
903: $parser);
904: return '';
905: }
906:
907: sub end_finalcode { return ''; }
908:
909: sub start_defaultvalue {
910: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
911:
912: if ($target ne 'helper') {
913: return '';
914: }
915:
916: $paramHash->{DEFAULT_VALUE} = &Apache::lonxml::get_all_text('/defaultvalue',
917: $parser);
918: $paramHash->{DEFAULT_VALUE} = 'sub { my $helper = shift; my $state = shift;' .
919: $paramHash->{DEFAULT_VALUE} . '}';
920: return '';
921: }
922:
923: sub end_defaultvalue { return ''; }
924:
1.57 albertel 925: # Validators may need to take language specifications
1.38 bowersj2 926: sub start_validator {
927: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
928:
929: if ($target ne 'helper') {
930: return '';
931: }
932:
933: $paramHash->{VALIDATOR} = &Apache::lonxml::get_all_text('/validator',
934: $parser);
935: $paramHash->{VALIDATOR} = 'sub { my $helper = shift; my $state = shift; my $element = shift; my $val = shift;' .
936: $paramHash->{VALIDATOR} . '}';
937: return '';
938: }
939:
940: sub end_validator { return ''; }
941:
1.4 bowersj2 942: sub preprocess {
943: return 1;
944: }
945:
946: sub postprocess {
947: return 1;
948: }
949:
950: sub render {
951: return '';
952: }
953:
1.13 bowersj2 954: sub overrideForm {
955: return 0;
956: }
957:
1.31 bowersj2 958: sub getValue {
959: my $self = shift;
960: return $helper->{VARS}->{$self->{'variable'}};
961: }
962:
1.4 bowersj2 963: 1;
964:
965: package Apache::lonhelper::message;
966:
967: =pod
968:
1.48 bowersj2 969: =head1 Elements
970:
971: =head2 Element: messageX<message, helper element>
1.4 bowersj2 972:
1.44 bowersj2 973: Message elements display their contents, and
974: transition directly to the state in the <nextstate> attribute. Example:
1.4 bowersj2 975:
1.44 bowersj2 976: <message nextstate='GET_NAME'>
977: This is the <b>message</b> the user will see,
978: <i>HTML allowed</i>.
1.4 bowersj2 979: </message>
980:
1.44 bowersj2 981: This will display the HTML message and transition to the 'nextstate' if
1.7 bowersj2 982: given. The HTML will be directly inserted into the helper, so if you don't
1.44 bowersj2 983: want text to run together, you'll need to manually wrap the message text
1.4 bowersj2 984: in <p> tags, or whatever is appropriate for your HTML.
985:
1.5 bowersj2 986: Message tags do not add in whitespace, so if you want it, you'll need to add
987: it into states. This is done so you can inline some elements, such as
988: the <date> element, right between two messages, giving the appearence that
989: the <date> element appears inline. (Note the elements can not be embedded
990: within each other.)
991:
1.4 bowersj2 992: This is also a good template for creating your own new states, as it has
993: very little code beyond the state template.
994:
1.57 albertel 995: =head3 Localization
996:
997: The contents of the message tag will be run through the
998: normalize_string function and that will be used as a call to &mt.
999:
1.4 bowersj2 1000: =cut
1001:
1002: no strict;
1003: @ISA = ("Apache::lonhelper::element");
1004: use strict;
1.57 albertel 1005: use Apache::lonlocal;
1.4 bowersj2 1006:
1007: BEGIN {
1.7 bowersj2 1008: &Apache::lonhelper::register('Apache::lonhelper::message',
1.10 bowersj2 1009: ('message'));
1.3 bowersj2 1010: }
1011:
1.5 bowersj2 1012: sub new {
1013: my $ref = Apache::lonhelper::element->new();
1014: bless($ref);
1015: }
1.4 bowersj2 1016:
1017: # CONSTRUCTION: Construct the message element from the XML
1018: sub start_message {
1019: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
1020:
1021: if ($target ne 'helper') {
1022: return '';
1023: }
1.10 bowersj2 1024:
1025: $paramHash->{MESSAGE_TEXT} = &Apache::lonxml::get_all_text('/message',
1026: $parser);
1027:
1028: if (defined($token->[2]{'nextstate'})) {
1029: $paramHash->{NEXTSTATE} = $token->[2]{'nextstate'};
1030: }
1.4 bowersj2 1031: return '';
1.3 bowersj2 1032: }
1033:
1.10 bowersj2 1034: sub end_message {
1.4 bowersj2 1035: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
1036:
1037: if ($target ne 'helper') {
1038: return '';
1039: }
1.10 bowersj2 1040: Apache::lonhelper::message->new();
1041: return '';
1.5 bowersj2 1042: }
1043:
1044: sub render {
1045: my $self = shift;
1046:
1.57 albertel 1047: return &mtn($self->{MESSAGE_TEXT});
1.5 bowersj2 1048: }
1049: # If a NEXTSTATE was given, switch to it
1050: sub postprocess {
1051: my $self = shift;
1052: if (defined($self->{NEXTSTATE})) {
1053: $helper->changeState($self->{NEXTSTATE});
1054: }
1.6 bowersj2 1055:
1056: return 1;
1.5 bowersj2 1057: }
1058: 1;
1059:
1060: package Apache::lonhelper::choices;
1061:
1062: =pod
1063:
1.44 bowersj2 1064: =head2 Element: choicesX<choices, helper element>
1.5 bowersj2 1065:
1066: Choice states provide a single choice to the user as a text selection box.
1067: A "choice" is two pieces of text, one which will be displayed to the user
1068: (the "human" value), and one which will be passed back to the program
1069: (the "computer" value). For instance, a human may choose from a list of
1070: resources on disk by title, while your program wants the file name.
1071:
1072: <choices> takes an attribute "variable" to control which helper variable
1073: the result is stored in.
1074:
1075: <choices> takes an attribute "multichoice" which, if set to a true
1076: value, will allow the user to select multiple choices.
1077:
1.26 bowersj2 1078: <choices> takes an attribute "allowempty" which, if set to a true
1079: value, will allow the user to select none of the choices without raising
1080: an error message.
1081:
1.44 bowersj2 1082: =head3 SUB-TAGS
1.5 bowersj2 1083:
1.44 bowersj2 1084: <choices> can have the following subtags:X<choice, helper tag>
1.5 bowersj2 1085:
1086: =over 4
1087:
1088: =item * <nextstate>state_name</nextstate>: If given, this will cause the
1.44 bowersj2 1089: choice element to transition to the given state after executing.
1090: This will override the <nextstate> passed to <choices> (if any).
1.5 bowersj2 1091:
1092: =item * <choice />: If the choices are static,
1093: this element will allow you to specify them. Each choice
1094: contains attribute, "computer", as described above. The
1095: content of the tag will be used as the human label.
1096: For example,
1097: <choice computer='234-12-7312'>Bobby McDormik</choice>.
1098:
1.44 bowersj2 1099: <choice> can take a parameter "eval", which if set to
1100: a true value, will cause the contents of the tag to be
1101: evaluated as it would be in an <eval> tag; see <eval> tag
1102: below.
1.13 bowersj2 1103:
1.5 bowersj2 1104: <choice> may optionally contain a 'nextstate' attribute, which
1.44 bowersj2 1105: will be the state transistioned to if the choice is made, if
1106: the choice is not multichoice. This will override the nextstate
1107: passed to the parent C<choices> tag.
1.5 bowersj2 1108:
1109: =back
1110:
1111: To create the choices programmatically, either wrap the choices in
1112: <condition> tags (prefered), or use an <exec> block inside the <choice>
1113: tag. Store the choices in $state->{CHOICES}, which is a list of list
1114: references, where each list has three strings. The first is the human
1115: name, the second is the computer name. and the third is the option
1116: next state. For example:
1117:
1118: <exec>
1119: for (my $i = 65; $i < 65 + 26; $i++) {
1120: push @{$state->{CHOICES}}, [chr($i), $i, 'next'];
1121: }
1122: </exec>
1123:
1124: This will allow the user to select from the letters A-Z (in ASCII), while
1125: passing the ASCII value back into the helper variables, and the state
1126: will in all cases transition to 'next'.
1127:
1128: You can mix and match methods of creating choices, as long as you always
1129: "push" onto the choice list, rather then wiping it out. (You can even
1130: remove choices programmatically, but that would probably be bad form.)
1131:
1.44 bowersj2 1132: =head3 defaultvalue support
1.25 bowersj2 1133:
1134: Choices supports default values both in multichoice and single choice mode.
1135: In single choice mode, have the defaultvalue tag's function return the
1136: computer value of the box you want checked. If the function returns a value
1137: that does not correspond to any of the choices, the default behavior of selecting
1138: the first choice will be preserved.
1139:
1140: For multichoice, return a string with the computer values you want checked,
1141: delimited by triple pipes. Note this matches how the result of the <choices>
1142: tag is stored in the {VARS} hash.
1143:
1.5 bowersj2 1144: =cut
1145:
1146: no strict;
1147: @ISA = ("Apache::lonhelper::element");
1148: use strict;
1.57 albertel 1149: use Apache::lonlocal;
1.5 bowersj2 1150:
1151: BEGIN {
1.7 bowersj2 1152: &Apache::lonhelper::register('Apache::lonhelper::choices',
1.5 bowersj2 1153: ('choice', 'choices'));
1154: }
1155:
1156: sub new {
1157: my $ref = Apache::lonhelper::element->new();
1158: bless($ref);
1159: }
1160:
1161: # CONSTRUCTION: Construct the message element from the XML
1162: sub start_choices {
1163: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
1164:
1165: if ($target ne 'helper') {
1166: return '';
1167: }
1168:
1169: # Need to initialize the choices list, so everything can assume it exists
1.24 sakharuk 1170: $paramHash->{'variable'} = $token->[2]{'variable'} if (!defined($paramHash->{'variable'}));
1.5 bowersj2 1171: $helper->declareVar($paramHash->{'variable'});
1172: $paramHash->{'multichoice'} = $token->[2]{'multichoice'};
1.26 bowersj2 1173: $paramHash->{'allowempty'} = $token->[2]{'allowempty'};
1.5 bowersj2 1174: $paramHash->{CHOICES} = [];
1175: return '';
1176: }
1177:
1178: sub end_choices {
1179: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
1180:
1181: if ($target ne 'helper') {
1182: return '';
1183: }
1184: Apache::lonhelper::choices->new();
1185: return '';
1186: }
1187:
1188: sub start_choice {
1189: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
1190:
1191: if ($target ne 'helper') {
1192: return '';
1193: }
1194:
1195: my $computer = $token->[2]{'computer'};
1196: my $human = &Apache::lonxml::get_all_text('/choice',
1197: $parser);
1198: my $nextstate = $token->[2]{'nextstate'};
1.13 bowersj2 1199: my $evalFlag = $token->[2]{'eval'};
1200: push @{$paramHash->{CHOICES}}, [$human, $computer, $nextstate,
1201: $evalFlag];
1.5 bowersj2 1202: return '';
1203: }
1204:
1205: sub end_choice {
1206: return '';
1207: }
1208:
1209: sub render {
1210: my $self = shift;
1211: my $var = $self->{'variable'};
1212: my $buttons = '';
1213: my $result = '';
1214:
1215: if ($self->{'multichoice'}) {
1.6 bowersj2 1216: $result .= <<SCRIPT;
1.5 bowersj2 1217: <script>
1.18 bowersj2 1218: function checkall(value, checkName) {
1.15 bowersj2 1219: for (i=0; i<document.forms.helpform.elements.length; i++) {
1.18 bowersj2 1220: ele = document.forms.helpform.elements[i];
1221: if (ele.name == checkName + '.forminput') {
1222: document.forms.helpform.elements[i].checked=value;
1223: }
1.5 bowersj2 1224: }
1225: }
1226: </script>
1227: SCRIPT
1.25 bowersj2 1228: }
1229:
1230: # Only print "select all" and "unselect all" if there are five or
1231: # more choices; fewer then that and it looks silly.
1232: if ($self->{'multichoice'} && scalar(@{$self->{CHOICES}}) > 4) {
1.68 ! sakharuk 1233: my %lt=&Apache::lonlocal::texthash(
! 1234: 'sa' => "Select All",
! 1235: 'ua' => "Unselect All");
1.5 bowersj2 1236: $buttons = <<BUTTONS;
1237: <br />
1.68 ! sakharuk 1238: <input type="button" onclick="checkall(true, '$var')" value="$lt{'sa'}" />
! 1239: <input type="button" onclick="checkall(false, '$var')" value="$lt{'ua'}" />
1.6 bowersj2 1240: <br />
1.5 bowersj2 1241: BUTTONS
1242: }
1243:
1.16 bowersj2 1244: if (defined $self->{ERROR_MSG}) {
1.6 bowersj2 1245: $result .= '<br /><font color="#FF0000">' . $self->{ERROR_MSG} . '</font><br />';
1.5 bowersj2 1246: }
1247:
1248: $result .= $buttons;
1.6 bowersj2 1249:
1.5 bowersj2 1250: $result .= "<table>\n\n";
1251:
1.25 bowersj2 1252: my %checkedChoices;
1253: my $checkedChoicesFunc;
1254:
1255: if (defined($self->{DEFAULT_VALUE})) {
1256: $checkedChoicesFunc = eval ($self->{DEFAULT_VALUE});
1257: die 'Error in default value code for variable ' .
1.34 bowersj2 1258: $self->{'variable'} . ', Perl said: ' . $@ if $@;
1.25 bowersj2 1259: } else {
1260: $checkedChoicesFunc = sub { return ''; };
1261: }
1262:
1263: # Process which choices should be checked.
1264: if ($self->{'multichoice'}) {
1265: for my $selectedChoice (split(/\|\|\|/, (&$checkedChoicesFunc($helper, $self)))) {
1266: $checkedChoices{$selectedChoice} = 1;
1267: }
1268: } else {
1269: # single choice
1270: my $selectedChoice = &$checkedChoicesFunc($helper, $self);
1271:
1272: my $foundChoice = 0;
1273:
1274: # check that the choice is in the list of choices.
1275: for my $choice (@{$self->{CHOICES}}) {
1276: if ($choice->[1] eq $selectedChoice) {
1277: $checkedChoices{$choice->[1]} = 1;
1278: $foundChoice = 1;
1279: }
1280: }
1281:
1282: # If we couldn't find the choice, pick the first one
1283: if (!$foundChoice) {
1284: $checkedChoices{$self->{CHOICES}->[0]->[1]} = 1;
1285: }
1286: }
1287:
1.5 bowersj2 1288: my $type = "radio";
1289: if ($self->{'multichoice'}) { $type = 'checkbox'; }
1290: foreach my $choice (@{$self->{CHOICES}}) {
1291: $result .= "<tr>\n<td width='20'> </td>\n";
1292: $result .= "<td valign='top'><input type='$type' name='$var.forminput'"
1293: . "' value='" .
1.67 albertel 1294: HTML::Entities::encode($choice->[1],'<>&"')
1.5 bowersj2 1295: . "'";
1.25 bowersj2 1296: if ($checkedChoices{$choice->[1]}) {
1.5 bowersj2 1297: $result .= " checked ";
1298: }
1.13 bowersj2 1299: my $choiceLabel = $choice->[0];
1300: if ($choice->[4]) { # if we need to evaluate this choice
1301: $choiceLabel = "sub { my $helper = shift; my $state = shift;" .
1302: $choiceLabel . "}";
1303: $choiceLabel = eval($choiceLabel);
1304: $choiceLabel = &$choiceLabel($helper, $self);
1305: }
1.68 ! sakharuk 1306: &Apache::lonnet::logthis("TITLE TRANSLATION >$choiceLabel<");
1.57 albertel 1307: $result .= "/></td><td> " . &mtn($choiceLabel) . "</td></tr>\n";
1.5 bowersj2 1308: }
1309: $result .= "</table>\n\n\n";
1310: $result .= $buttons;
1311:
1312: return $result;
1313: }
1314:
1315: # If a NEXTSTATE was given or a nextstate for this choice was
1316: # given, switch to it
1317: sub postprocess {
1318: my $self = shift;
1319: my $chosenValue = $ENV{'form.' . $self->{'variable'} . '.forminput'};
1320:
1.26 bowersj2 1321: if (!defined($chosenValue) && !$self->{'allowempty'}) {
1.59 bowersj2 1322: $self->{ERROR_MSG} =
1323: &mt("You must choose one or more choices to continue.");
1.6 bowersj2 1324: return 0;
1325: }
1326:
1.28 bowersj2 1327: if (ref($chosenValue)) {
1328: $helper->{VARS}->{$self->{'variable'}} = join('|||', @$chosenValue);
1.42 bowersj2 1329: }
1330:
1331: if (defined($self->{NEXTSTATE})) {
1332: $helper->changeState($self->{NEXTSTATE});
1333: }
1334:
1335: foreach my $choice (@{$self->{CHOICES}}) {
1336: if ($choice->[1] eq $chosenValue) {
1337: if (defined($choice->[2])) {
1338: $helper->changeState($choice->[2]);
1339: }
1340: }
1341: }
1342: return 1;
1343: }
1344: 1;
1345:
1346: package Apache::lonhelper::dropdown;
1347:
1348: =pod
1349:
1.44 bowersj2 1350: =head2 Element: dropdownX<dropdown, helper tag>
1.42 bowersj2 1351:
1352: A drop-down provides a drop-down box instead of a radio button
1353: box. Because most people do not know how to use a multi-select
1354: drop-down box, that option is not allowed. Otherwise, the arguments
1355: are the same as "choices", except "allowempty" is also meaningless.
1356:
1357: <dropdown> takes an attribute "variable" to control which helper variable
1358: the result is stored in.
1359:
1.44 bowersj2 1360: =head3 SUB-TAGS
1.42 bowersj2 1361:
1362: <choice>, which acts just as it does in the "choices" element.
1363:
1364: =cut
1365:
1.57 albertel 1366: # This really ought to be a sibling class to "choice" which is itself
1367: # a child of some abstract class.... *shrug*
1368:
1.42 bowersj2 1369: no strict;
1370: @ISA = ("Apache::lonhelper::element");
1371: use strict;
1.57 albertel 1372: use Apache::lonlocal;
1.42 bowersj2 1373:
1374: BEGIN {
1375: &Apache::lonhelper::register('Apache::lonhelper::dropdown',
1376: ('dropdown'));
1377: }
1378:
1379: sub new {
1380: my $ref = Apache::lonhelper::element->new();
1381: bless($ref);
1382: }
1383:
1384: # CONSTRUCTION: Construct the message element from the XML
1385: sub start_dropdown {
1386: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
1387:
1388: if ($target ne 'helper') {
1389: return '';
1390: }
1391:
1392: # Need to initialize the choices list, so everything can assume it exists
1393: $paramHash->{'variable'} = $token->[2]{'variable'} if (!defined($paramHash->{'variable'}));
1394: $helper->declareVar($paramHash->{'variable'});
1395: $paramHash->{CHOICES} = [];
1396: return '';
1397: }
1398:
1399: sub end_dropdown {
1400: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
1401:
1402: if ($target ne 'helper') {
1403: return '';
1404: }
1405: Apache::lonhelper::dropdown->new();
1406: return '';
1407: }
1408:
1409: sub render {
1410: my $self = shift;
1411: my $var = $self->{'variable'};
1412: my $result = '';
1413:
1414: if (defined $self->{ERROR_MSG}) {
1415: $result .= '<br /><font color="#FF0000">' . $self->{ERROR_MSG} . '</font><br />';
1416: }
1417:
1418: my %checkedChoices;
1419: my $checkedChoicesFunc;
1420:
1421: if (defined($self->{DEFAULT_VALUE})) {
1422: $checkedChoicesFunc = eval ($self->{DEFAULT_VALUE});
1423: die 'Error in default value code for variable ' .
1424: $self->{'variable'} . ', Perl said: ' . $@ if $@;
1425: } else {
1426: $checkedChoicesFunc = sub { return ''; };
1427: }
1428:
1429: # single choice
1430: my $selectedChoice = &$checkedChoicesFunc($helper, $self);
1431:
1432: my $foundChoice = 0;
1433:
1434: # check that the choice is in the list of choices.
1435: for my $choice (@{$self->{CHOICES}}) {
1436: if ($choice->[1] eq $selectedChoice) {
1437: $checkedChoices{$choice->[1]} = 1;
1438: $foundChoice = 1;
1439: }
1440: }
1441:
1442: # If we couldn't find the choice, pick the first one
1443: if (!$foundChoice) {
1444: $checkedChoices{$self->{CHOICES}->[0]->[1]} = 1;
1445: }
1446:
1447: $result .= "<select name='${var}.forminput'>\n";
1448: foreach my $choice (@{$self->{CHOICES}}) {
1449: $result .= "<option value='" .
1.67 albertel 1450: HTML::Entities::encode($choice->[1],'<>&"')
1.42 bowersj2 1451: . "'";
1452: if ($checkedChoices{$choice->[1]}) {
1453: $result .= " selected";
1454: }
1455: my $choiceLabel = $choice->[0];
1456: if ($choice->[4]) { # if we need to evaluate this choice
1457: $choiceLabel = "sub { my $helper = shift; my $state = shift;" .
1458: $choiceLabel . "}";
1459: $choiceLabel = eval($choiceLabel);
1460: $choiceLabel = &$choiceLabel($helper, $self);
1461: }
1.57 albertel 1462: $result .= ">" . &mtn($choiceLabel) . "\n";
1.42 bowersj2 1463: }
1.43 bowersj2 1464: $result .= "</select>\n";
1.42 bowersj2 1465:
1466: return $result;
1467: }
1468:
1469: # If a NEXTSTATE was given or a nextstate for this choice was
1470: # given, switch to it
1471: sub postprocess {
1472: my $self = shift;
1473: my $chosenValue = $ENV{'form.' . $self->{'variable'} . '.forminput'};
1474:
1475: if (!defined($chosenValue) && !$self->{'allowempty'}) {
1476: $self->{ERROR_MSG} = "You must choose one or more choices to" .
1477: " continue.";
1478: return 0;
1.6 bowersj2 1479: }
1480:
1.5 bowersj2 1481: if (defined($self->{NEXTSTATE})) {
1482: $helper->changeState($self->{NEXTSTATE});
1483: }
1484:
1485: foreach my $choice (@{$self->{CHOICES}}) {
1486: if ($choice->[1] eq $chosenValue) {
1487: if (defined($choice->[2])) {
1488: $helper->changeState($choice->[2]);
1489: }
1490: }
1491: }
1.6 bowersj2 1492: return 1;
1.5 bowersj2 1493: }
1494: 1;
1495:
1496: package Apache::lonhelper::date;
1497:
1498: =pod
1499:
1.44 bowersj2 1500: =head2 Element: dateX<date, helper element>
1.5 bowersj2 1501:
1502: Date elements allow the selection of a date with a drop down list.
1503:
1504: Date elements can take two attributes:
1505:
1506: =over 4
1507:
1508: =item * B<variable>: The name of the variable to store the chosen
1509: date in. Required.
1510:
1511: =item * B<hoursminutes>: If a true value, the date will show hours
1512: and minutes, as well as month/day/year. If false or missing,
1513: the date will only show the month, day, and year.
1514:
1515: =back
1516:
1517: Date elements contain only an option <nextstate> tag to determine
1518: the next state.
1519:
1520: Example:
1521:
1522: <date variable="DUE_DATE" hoursminutes="1">
1523: <nextstate>choose_why</nextstate>
1524: </date>
1525:
1526: =cut
1527:
1528: no strict;
1529: @ISA = ("Apache::lonhelper::element");
1530: use strict;
1.57 albertel 1531: use Apache::lonlocal; # A localization nightmare
1.5 bowersj2 1532:
1533: use Time::localtime;
1534:
1535: BEGIN {
1.7 bowersj2 1536: &Apache::lonhelper::register('Apache::lonhelper::date',
1.5 bowersj2 1537: ('date'));
1538: }
1539:
1540: # Don't need to override the "new" from element
1541: sub new {
1542: my $ref = Apache::lonhelper::element->new();
1543: bless($ref);
1544: }
1545:
1546: my @months = ("January", "February", "March", "April", "May", "June", "July",
1547: "August", "September", "October", "November", "December");
1548:
1549: # CONSTRUCTION: Construct the message element from the XML
1550: sub start_date {
1551: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
1552:
1553: if ($target ne 'helper') {
1554: return '';
1555: }
1556:
1557: $paramHash->{'variable'} = $token->[2]{'variable'};
1558: $helper->declareVar($paramHash->{'variable'});
1559: $paramHash->{'hoursminutes'} = $token->[2]{'hoursminutes'};
1560: }
1561:
1562: sub end_date {
1563: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
1564:
1565: if ($target ne 'helper') {
1566: return '';
1567: }
1568: Apache::lonhelper::date->new();
1569: return '';
1570: }
1571:
1572: sub render {
1573: my $self = shift;
1574: my $result = "";
1575: my $var = $self->{'variable'};
1576:
1577: my $date;
1578:
1579: # Default date: The current hour.
1580: $date = localtime();
1581: $date->min(0);
1582:
1583: if (defined $self->{ERROR_MSG}) {
1584: $result .= '<font color="#FF0000">' . $self->{ERROR_MSG} . '</font><br /><br />';
1585: }
1586:
1587: # Month
1588: my $i;
1589: $result .= "<select name='${var}month'>\n";
1590: for ($i = 0; $i < 12; $i++) {
1591: if ($i == $date->mon) {
1592: $result .= "<option value='$i' selected>";
1593: } else {
1594: $result .= "<option value='$i'>";
1595: }
1.57 albertel 1596: $result .= &mt($months[$i]) . "</option>\n";
1.5 bowersj2 1597: }
1598: $result .= "</select>\n";
1599:
1600: # Day
1601: $result .= "<select name='${var}day'>\n";
1602: for ($i = 1; $i < 32; $i++) {
1603: if ($i == $date->mday) {
1604: $result .= '<option selected>';
1605: } else {
1606: $result .= '<option>';
1607: }
1608: $result .= "$i</option>\n";
1609: }
1610: $result .= "</select>,\n";
1611:
1612: # Year
1613: $result .= "<select name='${var}year'>\n";
1614: for ($i = 2000; $i < 2030; $i++) { # update this after 64-bit dates
1615: if ($date->year + 1900 == $i) {
1616: $result .= "<option selected>";
1617: } else {
1618: $result .= "<option>";
1619: }
1620: $result .= "$i</option>\n";
1621: }
1622: $result .= "</select>,\n";
1623:
1624: # Display Hours and Minutes if they are called for
1625: if ($self->{'hoursminutes'}) {
1.59 bowersj2 1626: # This needs parameterization for times.
1627: my $am = &mt('a.m.');
1628: my $pm = &mt('p.m.');
1.5 bowersj2 1629: # Build hour
1630: $result .= "<select name='${var}hour'>\n";
1631: $result .= "<option " . ($date->hour == 0 ? 'selected ':'') .
1.59 bowersj2 1632: " value='0'>" . &mt('midnight') . "</option>\n";
1.5 bowersj2 1633: for ($i = 1; $i < 12; $i++) {
1634: if ($date->hour == $i) {
1.59 bowersj2 1635: $result .= "<option selected value='$i'>$i $am</option>\n";
1.5 bowersj2 1636: } else {
1.59 bowersj2 1637: $result .= "<option value='$i'>$i $am</option>\n";
1.5 bowersj2 1638: }
1639: }
1640: $result .= "<option " . ($date->hour == 12 ? 'selected ':'') .
1.59 bowersj2 1641: " value='12'>" . &mt('noon') . "</option>\n";
1.5 bowersj2 1642: for ($i = 13; $i < 24; $i++) {
1643: my $printedHour = $i - 12;
1644: if ($date->hour == $i) {
1.59 bowersj2 1645: $result .= "<option selected value='$i'>$printedHour $pm</option>\n";
1.5 bowersj2 1646: } else {
1.59 bowersj2 1647: $result .= "<option value='$i'>$printedHour $pm</option>\n";
1.5 bowersj2 1648: }
1649: }
1650:
1651: $result .= "</select> :\n";
1652:
1653: $result .= "<select name='${var}minute'>\n";
1654: for ($i = 0; $i < 60; $i++) {
1655: my $printedMinute = $i;
1656: if ($i < 10) {
1657: $printedMinute = "0" . $printedMinute;
1658: }
1659: if ($date->min == $i) {
1660: $result .= "<option selected>";
1661: } else {
1662: $result .= "<option>";
1663: }
1664: $result .= "$printedMinute</option>\n";
1665: }
1666: $result .= "</select>\n";
1667: }
1668:
1669: return $result;
1670:
1671: }
1672: # If a NEXTSTATE was given, switch to it
1673: sub postprocess {
1674: my $self = shift;
1675: my $var = $self->{'variable'};
1676: my $month = $ENV{'form.' . $var . 'month'};
1677: my $day = $ENV{'form.' . $var . 'day'};
1678: my $year = $ENV{'form.' . $var . 'year'};
1679: my $min = 0;
1680: my $hour = 0;
1681: if ($self->{'hoursminutes'}) {
1682: $min = $ENV{'form.' . $var . 'minute'};
1683: $hour = $ENV{'form.' . $var . 'hour'};
1684: }
1685:
1.57 albertel 1686: my $chosenDate;
1687: eval {$chosenDate = Time::Local::timelocal(0, $min, $hour, $day, $month, $year);};
1688: my $error = $@;
1689:
1.5 bowersj2 1690: # Check to make sure that the date was not automatically co-erced into a
1691: # valid date, as we want to flag that as an error
1692: # This happens for "Feb. 31", for instance, which is coerced to March 2 or
1.57 albertel 1693: # 3, depending on if it's a leap year
1.5 bowersj2 1694: my $checkDate = localtime($chosenDate);
1695:
1.57 albertel 1696: if ($error || $checkDate->mon != $month || $checkDate->mday != $day ||
1.5 bowersj2 1697: $checkDate->year + 1900 != $year) {
1.57 albertel 1698: unless (Apache::lonlocal::current_language()== ~/^en/) {
1699: $self->{ERROR_MSG} = &mt("Invalid date entry");
1700: return 0;
1701: }
1702: # LOCALIZATION FIXME: Needs to be parameterized
1.5 bowersj2 1703: $self->{ERROR_MSG} = "Can't use " . $months[$month] . " $day, $year as a "
1704: . "date because it doesn't exist. Please enter a valid date.";
1.57 albertel 1705:
1.6 bowersj2 1706: return 0;
1.5 bowersj2 1707: }
1708:
1709: $helper->{VARS}->{$var} = $chosenDate;
1710:
1711: if (defined($self->{NEXTSTATE})) {
1712: $helper->changeState($self->{NEXTSTATE});
1713: }
1.6 bowersj2 1714:
1715: return 1;
1.5 bowersj2 1716: }
1717: 1;
1718:
1719: package Apache::lonhelper::resource;
1720:
1721: =pod
1722:
1.44 bowersj2 1723: =head2 Element: resourceX<resource, helper element>
1.5 bowersj2 1724:
1725: <resource> elements allow the user to select one or multiple resources
1726: from the current course. You can filter out which resources they can view,
1727: and filter out which resources they can select. The course will always
1728: be displayed fully expanded, because of the difficulty of maintaining
1729: selections across folder openings and closings. If this is fixed, then
1730: the user can manipulate the folders.
1731:
1732: <resource> takes the standard variable attribute to control what helper
1.44 bowersj2 1733: variable stores the results. It also takes a "multichoice"X<multichoice> attribute,
1.17 bowersj2 1734: which controls whether the user can select more then one resource. The
1735: "toponly" attribute controls whether the resource display shows just the
1736: resources in that sequence, or recurses into all sub-sequences, defaulting
1.29 bowersj2 1737: to false. The "suppressEmptySequences" attribute reflects the
1738: suppressEmptySequences argument to the render routine, which will cause
1739: folders that have all of their contained resources filtered out to also
1.46 bowersj2 1740: be filtered out. The 'addstatus' attribute, if true, will add the icon
1741: and long status display columns to the display.
1.5 bowersj2 1742:
1.44 bowersj2 1743: =head3 SUB-TAGS
1.5 bowersj2 1744:
1745: =over 4
1746:
1.44 bowersj2 1747: =item * <filterfunc>X<filterfunc>: If you want to filter what resources are displayed
1.5 bowersj2 1748: to the user, use a filter func. The <filterfunc> tag should contain
1749: Perl code that when wrapped with "sub { my $res = shift; " and "}" is
1750: a function that returns true if the resource should be displayed,
1751: and false if it should be skipped. $res is a resource object.
1752: (See Apache::lonnavmaps documentation for information about the
1753: resource object.)
1754:
1.44 bowersj2 1755: =item * <choicefunc>X<choicefunc>: Same as <filterfunc>, except that controls whether
1.5 bowersj2 1756: the given resource can be chosen. (It is almost always a good idea to
1757: show the user the folders, for instance, but you do not always want to
1758: let the user select them.)
1759:
1760: =item * <nextstate>: Standard nextstate behavior.
1761:
1.44 bowersj2 1762: =item * <valuefunc>X<valuefunc>: This function controls what is returned by the resource
1.5 bowersj2 1763: when the user selects it. Like filterfunc and choicefunc, it should be
1764: a function fragment that when wrapped by "sub { my $res = shift; " and
1765: "}" returns a string representing what you want to have as the value. By
1766: default, the value will be the resource ID of the object ($res->{ID}).
1767:
1.44 bowersj2 1768: =item * <mapurl>X<mapurl>: If the URL of a map is given here, only that map
1.48 bowersj2 1769: will be displayed, instead of the whole course. If the attribute
1770: "evaluate" is given and is true, the contents of the mapurl will be
1771: evaluated with "sub { my $helper = shift; my $state = shift;" and
1772: "}", with the return value used as the mapurl.
1.13 bowersj2 1773:
1.5 bowersj2 1774: =back
1775:
1776: =cut
1777:
1778: no strict;
1779: @ISA = ("Apache::lonhelper::element");
1780: use strict;
1781:
1782: BEGIN {
1.7 bowersj2 1783: &Apache::lonhelper::register('Apache::lonhelper::resource',
1.5 bowersj2 1784: ('resource', 'filterfunc',
1.13 bowersj2 1785: 'choicefunc', 'valuefunc',
1786: 'mapurl'));
1.5 bowersj2 1787: }
1788:
1789: sub new {
1790: my $ref = Apache::lonhelper::element->new();
1791: bless($ref);
1792: }
1793:
1794: # CONSTRUCTION: Construct the message element from the XML
1795: sub start_resource {
1796: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
1797:
1798: if ($target ne 'helper') {
1799: return '';
1800: }
1801:
1802: $paramHash->{'variable'} = $token->[2]{'variable'};
1803: $helper->declareVar($paramHash->{'variable'});
1.14 bowersj2 1804: $paramHash->{'multichoice'} = $token->[2]{'multichoice'};
1.29 bowersj2 1805: $paramHash->{'suppressEmptySequences'} = $token->[2]{'suppressEmptySequences'};
1.17 bowersj2 1806: $paramHash->{'toponly'} = $token->[2]{'toponly'};
1.46 bowersj2 1807: $paramHash->{'addstatus'} = $token->[2]{'addstatus'};
1.66 albertel 1808: $paramHash->{'closeallpages'} = $token->[2]{'closeallpages'};
1.5 bowersj2 1809: return '';
1810: }
1811:
1812: sub end_resource {
1813: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
1814:
1815: if ($target ne 'helper') {
1816: return '';
1817: }
1818: if (!defined($paramHash->{FILTER_FUNC})) {
1819: $paramHash->{FILTER_FUNC} = sub {return 1;};
1820: }
1821: if (!defined($paramHash->{CHOICE_FUNC})) {
1822: $paramHash->{CHOICE_FUNC} = sub {return 1;};
1823: }
1824: if (!defined($paramHash->{VALUE_FUNC})) {
1825: $paramHash->{VALUE_FUNC} = sub {my $res = shift; return $res->{ID}; };
1826: }
1827: Apache::lonhelper::resource->new();
1.4 bowersj2 1828: return '';
1829: }
1830:
1.5 bowersj2 1831: sub start_filterfunc {
1832: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
1833:
1834: if ($target ne 'helper') {
1835: return '';
1836: }
1837:
1838: my $contents = Apache::lonxml::get_all_text('/filterfunc',
1839: $parser);
1840: $contents = 'sub { my $res = shift; ' . $contents . '}';
1841: $paramHash->{FILTER_FUNC} = eval $contents;
1842: }
1843:
1844: sub end_filterfunc { return ''; }
1845:
1846: sub start_choicefunc {
1847: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
1848:
1849: if ($target ne 'helper') {
1850: return '';
1851: }
1852:
1853: my $contents = Apache::lonxml::get_all_text('/choicefunc',
1854: $parser);
1855: $contents = 'sub { my $res = shift; ' . $contents . '}';
1856: $paramHash->{CHOICE_FUNC} = eval $contents;
1857: }
1858:
1859: sub end_choicefunc { return ''; }
1860:
1861: sub start_valuefunc {
1862: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
1863:
1864: if ($target ne 'helper') {
1865: return '';
1866: }
1867:
1868: my $contents = Apache::lonxml::get_all_text('/valuefunc',
1869: $parser);
1870: $contents = 'sub { my $res = shift; ' . $contents . '}';
1871: $paramHash->{VALUE_FUNC} = eval $contents;
1872: }
1873:
1874: sub end_valuefunc { return ''; }
1875:
1.13 bowersj2 1876: sub start_mapurl {
1877: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
1878:
1879: if ($target ne 'helper') {
1880: return '';
1881: }
1882:
1883: my $contents = Apache::lonxml::get_all_text('/mapurl',
1884: $parser);
1.48 bowersj2 1885: $paramHash->{EVAL_MAP_URL} = $token->[2]{'evaluate'};
1.14 bowersj2 1886: $paramHash->{MAP_URL} = $contents;
1.13 bowersj2 1887: }
1888:
1889: sub end_mapurl { return ''; }
1890:
1.5 bowersj2 1891: # A note, in case I don't get to this before I leave.
1892: # If someone complains about the "Back" button returning them
1893: # to the previous folder state, instead of returning them to
1894: # the previous helper state, the *correct* answer is for the helper
1895: # to keep track of how many times the user has manipulated the folders,
1896: # and feed that to the history.go() call in the helper rendering routines.
1897: # If done correctly, the helper itself can keep track of how many times
1898: # it renders the same states, so it doesn't go in just this state, and
1899: # you can lean on the browser back button to make sure it all chains
1900: # correctly.
1901: # Right now, though, I'm just forcing all folders open.
1902:
1903: sub render {
1904: my $self = shift;
1905: my $result = "";
1906: my $var = $self->{'variable'};
1907: my $curVal = $helper->{VARS}->{$var};
1908:
1.15 bowersj2 1909: my $buttons = '';
1910:
1911: if ($self->{'multichoice'}) {
1912: $result = <<SCRIPT;
1913: <script>
1.18 bowersj2 1914: function checkall(value, checkName) {
1.15 bowersj2 1915: for (i=0; i<document.forms.helpform.elements.length; i++) {
1916: ele = document.forms.helpform.elements[i];
1.18 bowersj2 1917: if (ele.name == checkName + '.forminput') {
1.15 bowersj2 1918: document.forms.helpform.elements[i].checked=value;
1919: }
1920: }
1921: }
1922: </script>
1923: SCRIPT
1.68 ! sakharuk 1924: my %lt=&Apache::lonlocal::texthash(
! 1925: 'sar' => "Select All Resources",
! 1926: 'uar' => "Unselect All Resources");
! 1927:
1.15 bowersj2 1928: $buttons = <<BUTTONS;
1929: <br />
1.68 ! sakharuk 1930: <input type="button" onclick="checkall(true, '$var')" value="$lt{'sar'}" />
! 1931: <input type="button" onclick="checkall(false, '$var')" value="$lt{'uar'}" />
1.15 bowersj2 1932: <br />
1933: BUTTONS
1934: }
1935:
1.5 bowersj2 1936: if (defined $self->{ERROR_MSG}) {
1.14 bowersj2 1937: $result .= '<br /><font color="#FF0000">' . $self->{ERROR_MSG} . '</font><br /><br />';
1.5 bowersj2 1938: }
1939:
1.15 bowersj2 1940: $result .= $buttons;
1941:
1.5 bowersj2 1942: my $filterFunc = $self->{FILTER_FUNC};
1943: my $choiceFunc = $self->{CHOICE_FUNC};
1944: my $valueFunc = $self->{VALUE_FUNC};
1.14 bowersj2 1945: my $multichoice = $self->{'multichoice'};
1.5 bowersj2 1946:
1.48 bowersj2 1947: # Evaluate the map url as needed
1948: my $mapUrl;
1.49 bowersj2 1949: if ($self->{EVAL_MAP_URL}) {
1.48 bowersj2 1950: my $mapUrlFunc = eval('sub { my $helper = shift; my $state = shift; ' .
1951: $self->{MAP_URL} . '}');
1952: $mapUrl = &$mapUrlFunc($helper, $self);
1953: } else {
1954: $mapUrl = $self->{MAP_URL};
1955: }
1956:
1.5 bowersj2 1957: # Create the composite function that renders the column on the nav map
1958: # have to admit any language that lets me do this can't be all bad
1959: # - Jeremy (Pythonista) ;-)
1960: my $checked = 0;
1961: my $renderColFunc = sub {
1962: my ($resource, $part, $params) = @_;
1.14 bowersj2 1963:
1964: my $inputType;
1965: if ($multichoice) { $inputType = 'checkbox'; }
1966: else {$inputType = 'radio'; }
1967:
1.5 bowersj2 1968: if (!&$choiceFunc($resource)) {
1969: return '<td> </td>';
1970: } else {
1.14 bowersj2 1971: my $col = "<td><input type='$inputType' name='${var}.forminput' ";
1972: if (!$checked && !$multichoice) {
1.5 bowersj2 1973: $col .= "checked ";
1974: $checked = 1;
1975: }
1.37 bowersj2 1976: if ($multichoice) { # all resources start checked; see bug 1174
1977: $col .= "checked ";
1978: $checked = 1;
1979: }
1.5 bowersj2 1980: $col .= "value='" .
1.67 albertel 1981: HTML::Entities::encode(&$valueFunc($resource),'<>&"')
1.5 bowersj2 1982: . "' /></td>";
1983: return $col;
1984: }
1985: };
1986:
1.17 bowersj2 1987: $ENV{'form.condition'} = !$self->{'toponly'};
1.46 bowersj2 1988: my $cols = [$renderColFunc, Apache::lonnavmaps::resource()];
1989: if ($self->{'addstatus'}) {
1990: push @$cols, (Apache::lonnavmaps::part_status_summary());
1991:
1992: }
1.5 bowersj2 1993: $result .=
1.46 bowersj2 1994: &Apache::lonnavmaps::render( { 'cols' => $cols,
1.5 bowersj2 1995: 'showParts' => 0,
1996: 'filterFunc' => $filterFunc,
1.13 bowersj2 1997: 'resource_no_folder_link' => 1,
1.66 albertel 1998: 'closeAllPages' => $self->{'closeallpages'},
1.29 bowersj2 1999: 'suppressEmptySequences' => $self->{'suppressEmptySequences'},
1.13 bowersj2 2000: 'iterator_map' => $mapUrl }
1.5 bowersj2 2001: );
1.15 bowersj2 2002:
2003: $result .= $buttons;
1.5 bowersj2 2004:
2005: return $result;
2006: }
2007:
2008: sub postprocess {
2009: my $self = shift;
1.14 bowersj2 2010:
2011: if ($self->{'multichoice'} && !$helper->{VARS}->{$self->{'variable'}}) {
2012: $self->{ERROR_MSG} = 'You must choose at least one resource to continue.';
2013: return 0;
2014: }
2015:
1.5 bowersj2 2016: if (defined($self->{NEXTSTATE})) {
2017: $helper->changeState($self->{NEXTSTATE});
2018: }
1.6 bowersj2 2019:
2020: return 1;
1.5 bowersj2 2021: }
2022:
2023: 1;
2024:
2025: package Apache::lonhelper::student;
2026:
2027: =pod
2028:
1.44 bowersj2 2029: =head2 Element: studentX<student, helper element>
1.5 bowersj2 2030:
2031: Student elements display a choice of students enrolled in the current
2032: course. Currently it is primitive; this is expected to evolve later.
2033:
1.48 bowersj2 2034: Student elements take the following attributes:
2035:
2036: =over 4
2037:
2038: =item * B<variable>:
2039:
2040: Does what it usually does: declare which helper variable to put the
2041: result in.
2042:
2043: =item * B<multichoice>:
2044:
2045: If true allows the user to select multiple students. Defaults to false.
2046:
2047: =item * B<coursepersonnel>:
2048:
2049: If true adds the course personnel to the top of the student
2050: selection. Defaults to false.
2051:
2052: =item * B<activeonly>:
2053:
2054: If true, only active students and course personnel will be
2055: shown. Defaults to false.
2056:
2057: =back
1.5 bowersj2 2058:
2059: =cut
2060:
2061: no strict;
2062: @ISA = ("Apache::lonhelper::element");
2063: use strict;
1.59 bowersj2 2064: use Apache::lonlocal;
1.5 bowersj2 2065:
2066:
2067: BEGIN {
1.7 bowersj2 2068: &Apache::lonhelper::register('Apache::lonhelper::student',
1.5 bowersj2 2069: ('student'));
2070: }
2071:
2072: sub new {
2073: my $ref = Apache::lonhelper::element->new();
2074: bless($ref);
2075: }
1.4 bowersj2 2076:
1.5 bowersj2 2077: sub start_student {
1.4 bowersj2 2078: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
2079:
2080: if ($target ne 'helper') {
2081: return '';
2082: }
2083:
1.5 bowersj2 2084: $paramHash->{'variable'} = $token->[2]{'variable'};
2085: $helper->declareVar($paramHash->{'variable'});
2086: $paramHash->{'multichoice'} = $token->[2]{'multichoice'};
1.39 bowersj2 2087: $paramHash->{'coursepersonnel'} = $token->[2]{'coursepersonnel'};
1.48 bowersj2 2088: $paramHash->{'sctiveonly'} = $token->[2]{'activeonly'};
1.12 bowersj2 2089: if (defined($token->[2]{'nextstate'})) {
2090: $paramHash->{NEXTSTATE} = $token->[2]{'nextstate'};
2091: }
2092:
1.5 bowersj2 2093: }
2094:
2095: sub end_student {
2096: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
2097:
2098: if ($target ne 'helper') {
2099: return '';
2100: }
2101: Apache::lonhelper::student->new();
1.3 bowersj2 2102: }
1.5 bowersj2 2103:
2104: sub render {
2105: my $self = shift;
2106: my $result = '';
2107: my $buttons = '';
1.18 bowersj2 2108: my $var = $self->{'variable'};
1.5 bowersj2 2109:
2110: if ($self->{'multichoice'}) {
2111: $result = <<SCRIPT;
2112: <script>
1.18 bowersj2 2113: function checkall(value, checkName) {
1.15 bowersj2 2114: for (i=0; i<document.forms.helpform.elements.length; i++) {
1.18 bowersj2 2115: ele = document.forms.helpform.elements[i];
2116: if (ele.name == checkName + '.forminput') {
2117: document.forms.helpform.elements[i].checked=value;
2118: }
1.5 bowersj2 2119: }
2120: }
1.58 sakharuk 2121: function checksec(value) {
1.53 sakharuk 2122: for (i=0; i<document.forms.helpform.elements.length; i++) {
2123: comp = document.forms.helpform.elements.chksec.value;
2124: if (document.forms.helpform.elements[i].value.indexOf(':'+comp+':') != -1) {
1.58 sakharuk 2125: document.forms.helpform.elements[i].checked=value;
1.53 sakharuk 2126: }
2127: }
2128: }
2129: function checkactive() {
2130: for (i=0; i<document.forms.helpform.elements.length; i++) {
2131: if (document.forms.helpform.elements[i].value.indexOf(':Active') != -1) {
2132: document.forms.helpform.elements[i].checked=true;
2133: }
2134: }
2135: }
1.5 bowersj2 2136: </script>
2137: SCRIPT
1.59 bowersj2 2138:
1.68 ! sakharuk 2139: my %lt=&Apache::lonlocal::texthash(
! 2140: 'ocs' => "Select Only Current Students",
! 2141: 'sas' => "Select All Students",
! 2142: 'uas' => "Unselect All Students",
! 2143: 'sfsg' => "Select for Section/Group",
! 2144: 'ufsg' => "Unselect for Section/Group");
! 2145:
1.5 bowersj2 2146: $buttons = <<BUTTONS;
2147: <br />
1.68 ! sakharuk 2148: <input type="button" onclick="checkactive()" value="$lt{'ocs'}" />
! 2149: <input type="button" onclick="checkall(true, '$var')" value="$lt{'sas'}" />
! 2150: <input type="button" onclick="checkall(false, '$var')" value="$lt{'uas'}" />
! 2151: <input type="button" onclick="checksec(true)" value="$lt{'sfsg'}">
1.53 sakharuk 2152: <input type="text" size="5" name="chksec">
1.68 ! sakharuk 2153: <input type="button" onclick="checksec(false)" value="$lt{'ufsg'}">
1.5 bowersj2 2154: <br />
2155: BUTTONS
2156: }
2157:
2158: if (defined $self->{ERROR_MSG}) {
2159: $result .= '<font color="#FF0000">' . $self->{ERROR_MSG} . '</font><br /><br />';
2160: }
2161:
1.39 bowersj2 2162: my $choices = [];
2163:
2164: # Load up the non-students, if necessary
2165: if ($self->{'coursepersonnel'}) {
2166: my %coursepersonnel = Apache::lonnet::get_course_adv_roles();
2167: for (sort keys %coursepersonnel) {
2168: for my $role (split /,/, $coursepersonnel{$_}) {
2169: # extract the names so we can sort them
2170: my @people;
2171:
2172: for (split /,/, $role) {
2173: push @people, [split /:/, $role];
2174: }
2175:
2176: @people = sort { $a->[0] cmp $b->[0] } @people;
2177:
2178: for my $person (@people) {
2179: push @$choices, [join(':', @$person), $person->[0], '', $_];
2180: }
2181: }
2182: }
2183: }
1.5 bowersj2 2184:
2185: # Constants
2186: my $section = Apache::loncoursedata::CL_SECTION();
2187: my $fullname = Apache::loncoursedata::CL_FULLNAME();
1.48 bowersj2 2188: my $status = Apache::loncoursedata::CL_STATUS();
1.5 bowersj2 2189:
1.39 bowersj2 2190: # Load up the students
2191: my $classlist = &Apache::loncoursedata::get_classlist();
2192: my @keys = keys %{$classlist};
1.5 bowersj2 2193: # Sort by: Section, name
2194: @keys = sort {
1.39 bowersj2 2195: if ($classlist->{$a}->[$section] ne $classlist->{$b}->[$section]) {
2196: return $classlist->{$a}->[$section] cmp $classlist->{$b}->[$section];
1.5 bowersj2 2197: }
1.39 bowersj2 2198: return $classlist->{$a}->[$fullname] cmp $classlist->{$b}->[$fullname];
1.5 bowersj2 2199: } @keys;
2200:
1.39 bowersj2 2201: # username, fullname, section, type
2202: for (@keys) {
1.48 bowersj2 2203: # Filter out inactive students if we've set "activeonly"
2204: if (!$self->{'activeonly'} || $classlist->{$_}->[$status] eq
2205: 'Active') {
2206: push @$choices, [$_, $classlist->{$_}->[$fullname],
1.53 sakharuk 2207: $classlist->{$_}->[$section],
2208: $classlist->{$_}->[$status], 'Student'];
1.48 bowersj2 2209: }
1.39 bowersj2 2210: }
2211:
1.68 ! sakharuk 2212: my $name = $self->{'coursepersonnel'} ? &mt('Name') : &mt('Student Name');
! 2213: &Apache::lonnet::logthis("THE NAME IS >$name<");
1.5 bowersj2 2214: my $type = 'radio';
2215: if ($self->{'multichoice'}) { $type = 'checkbox'; }
2216: $result .= "<table cellspacing='2' cellpadding='2' border='0'>\n";
1.39 bowersj2 2217: $result .= "<tr><td></td><td align='center'><b>$name</b></td>".
1.59 bowersj2 2218: "<td align='center'><b>" . &mt('Section') . "</b></td>" .
1.68 ! sakharuk 2219: "<td align='center'><b>".&mt('Status')."</b></td>" .
! 2220: "<td align='center'><b>" . &mt("Role") . "</b></td>" .
! 2221: "<td align='center'><b>".&mt('Username').":".&mt('Domain')."</b></td></tr>";
1.5 bowersj2 2222:
2223: my $checked = 0;
1.39 bowersj2 2224: for my $choice (@$choices) {
1.5 bowersj2 2225: $result .= "<tr><td><input type='$type' name='" .
2226: $self->{'variable'} . '.forminput' . "'";
2227:
2228: if (!$self->{'multichoice'} && !$checked) {
2229: $result .= " checked ";
2230: $checked = 1;
2231: }
2232: $result .=
1.67 albertel 2233: " value='" . HTML::Entities::encode($choice->[0] . ':' . $choice->[2] . ':' . $choice->[1] . ':' . $choice->[3],'<>&"')
1.5 bowersj2 2234: . "' /></td><td>"
1.67 albertel 2235: . HTML::Entities::encode($choice->[1],'<>&"')
1.5 bowersj2 2236: . "</td><td align='center'>"
1.67 albertel 2237: . HTML::Entities::encode($choice->[2],'<>&"')
1.39 bowersj2 2238: . "</td>\n<td>"
1.67 albertel 2239: . HTML::Entities::encode($choice->[3],'<>&"')
1.53 sakharuk 2240: . "</td>\n<td>"
1.67 albertel 2241: . HTML::Entities::encode($choice->[4],'<>&"')
1.53 sakharuk 2242: . "</td>\n<td>"
1.67 albertel 2243: . HTML::Entities::encode($choice->[0],'<>&"')
1.53 sakharuk 2244: . "</td></tr>\n";
1.5 bowersj2 2245: }
2246:
2247: $result .= "</table>\n\n";
2248: $result .= $buttons;
1.4 bowersj2 2249:
1.5 bowersj2 2250: return $result;
2251: }
2252:
1.6 bowersj2 2253: sub postprocess {
2254: my $self = shift;
2255:
2256: my $result = $ENV{'form.' . $self->{'variable'} . '.forminput'};
2257: if (!$result) {
1.59 bowersj2 2258: $self->{ERROR_MSG} =
2259: &mt('You must choose at least one student to continue.');
1.6 bowersj2 2260: return 0;
2261: }
2262:
2263: if (defined($self->{NEXTSTATE})) {
2264: $helper->changeState($self->{NEXTSTATE});
2265: }
2266:
2267: return 1;
2268: }
2269:
1.5 bowersj2 2270: 1;
2271:
2272: package Apache::lonhelper::files;
2273:
2274: =pod
2275:
1.44 bowersj2 2276: =head2 Element: filesX<files, helper element>
1.5 bowersj2 2277:
2278: files allows the users to choose files from a given directory on the
2279: server. It is always multichoice and stores the result as a triple-pipe
2280: delimited entry in the helper variables.
2281:
2282: Since it is extremely unlikely that you can actually code a constant
2283: representing the directory you wish to allow the user to search, <files>
2284: takes a subroutine that returns the name of the directory you wish to
2285: have the user browse.
2286:
2287: files accepts the attribute "variable" to control where the files chosen
2288: are put. It accepts the attribute "multichoice" as the other attribute,
2289: defaulting to false, which if true will allow the user to select more
2290: then one choice.
2291:
1.44 bowersj2 2292: <files> accepts three subtags:
2293:
2294: =over 4
2295:
2296: =item * B<nextstate>: works as it does with the other tags.
2297:
2298: =item * B<filechoice>: When the contents of this tag are surrounded by
2299: "sub {" and "}", will return a string representing what directory
2300: on the server to allow the user to choose files from.
2301:
2302: =item * B<filefilter>: Should contain Perl code that when surrounded
2303: by "sub { my $filename = shift; " and "}", returns a true value if
2304: the user can pick that file, or false otherwise. The filename
2305: passed to the function will be just the name of the file, with no
2306: path info. By default, a filter function will be used that will
2307: mask out old versions of files. This function is available as
2308: Apache::lonhelper::files::not_old_version if you want to use it to
2309: composite your own filters.
2310:
2311: =back
2312:
2313: B<General security note>: You should ensure the user can not somehow
2314: pass something into your code that would allow them to look places
2315: they should not be able to see, like the C</etc/> directory. However,
2316: the security impact would be minimal, since it would only expose
2317: the existence of files, there should be no way to parlay that into
2318: viewing the files.
1.5 bowersj2 2319:
2320: =cut
2321:
2322: no strict;
2323: @ISA = ("Apache::lonhelper::element");
2324: use strict;
1.59 bowersj2 2325: use Apache::lonlocal;
1.5 bowersj2 2326:
1.32 bowersj2 2327: use Apache::lonpubdir; # for getTitleString
2328:
1.5 bowersj2 2329: BEGIN {
1.7 bowersj2 2330: &Apache::lonhelper::register('Apache::lonhelper::files',
2331: ('files', 'filechoice', 'filefilter'));
1.5 bowersj2 2332: }
2333:
1.44 bowersj2 2334: sub not_old_version {
2335: my $file = shift;
2336:
2337: # Given a file name, return false if it is an "old version" of a
2338: # file, or true if it is not.
2339:
2340: if ($file =~ /^.*\.[0-9]+\.[A-Za-z]+(\.meta)?$/) {
2341: return 0;
2342: }
2343: return 1;
2344: }
2345:
1.5 bowersj2 2346: sub new {
2347: my $ref = Apache::lonhelper::element->new();
2348: bless($ref);
2349: }
2350:
2351: sub start_files {
2352: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
2353:
2354: if ($target ne 'helper') {
2355: return '';
2356: }
2357: $paramHash->{'variable'} = $token->[2]{'variable'};
2358: $helper->declareVar($paramHash->{'variable'});
2359: $paramHash->{'multichoice'} = $token->[2]{'multichoice'};
2360: }
2361:
2362: sub end_files {
2363: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
2364:
2365: if ($target ne 'helper') {
2366: return '';
2367: }
2368: if (!defined($paramHash->{FILTER_FUNC})) {
2369: $paramHash->{FILTER_FUNC} = sub { return 1; };
2370: }
2371: Apache::lonhelper::files->new();
2372: }
2373:
2374: sub start_filechoice {
2375: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
2376:
2377: if ($target ne 'helper') {
2378: return '';
2379: }
2380: $paramHash->{'filechoice'} = Apache::lonxml::get_all_text('/filechoice',
2381: $parser);
2382: }
2383:
2384: sub end_filechoice { return ''; }
2385:
2386: sub start_filefilter {
2387: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
2388:
2389: if ($target ne 'helper') {
2390: return '';
2391: }
2392:
2393: my $contents = Apache::lonxml::get_all_text('/filefilter',
2394: $parser);
2395: $contents = 'sub { my $filename = shift; ' . $contents . '}';
2396: $paramHash->{FILTER_FUNC} = eval $contents;
2397: }
2398:
2399: sub end_filefilter { return ''; }
1.3 bowersj2 2400:
2401: sub render {
2402: my $self = shift;
1.5 bowersj2 2403: my $result = '';
2404: my $var = $self->{'variable'};
2405:
2406: my $subdirFunc = eval('sub {' . $self->{'filechoice'} . '}');
1.11 bowersj2 2407: die 'Error in resource filter code for variable ' .
2408: {'variable'} . ', Perl said:' . $@ if $@;
2409:
1.5 bowersj2 2410: my $subdir = &$subdirFunc();
2411:
2412: my $filterFunc = $self->{FILTER_FUNC};
1.44 bowersj2 2413: if (!defined($filterFunc)) {
2414: $filterFunc = ¬_old_version;
2415: }
1.5 bowersj2 2416: my $buttons = '';
1.22 bowersj2 2417: my $type = 'radio';
2418: if ($self->{'multichoice'}) {
2419: $type = 'checkbox';
2420: }
1.5 bowersj2 2421:
2422: if ($self->{'multichoice'}) {
2423: $result = <<SCRIPT;
2424: <script>
1.18 bowersj2 2425: function checkall(value, checkName) {
1.15 bowersj2 2426: for (i=0; i<document.forms.helpform.elements.length; i++) {
2427: ele = document.forms.helpform.elements[i];
1.18 bowersj2 2428: if (ele.name == checkName + '.forminput') {
1.15 bowersj2 2429: document.forms.helpform.elements[i].checked=value;
1.5 bowersj2 2430: }
2431: }
2432: }
1.21 bowersj2 2433:
1.22 bowersj2 2434: function checkallclass(value, className) {
1.21 bowersj2 2435: for (i=0; i<document.forms.helpform.elements.length; i++) {
2436: ele = document.forms.helpform.elements[i];
1.22 bowersj2 2437: if (ele.type == "$type" && ele.onclick) {
1.21 bowersj2 2438: document.forms.helpform.elements[i].checked=value;
2439: }
2440: }
2441: }
1.5 bowersj2 2442: </script>
2443: SCRIPT
1.68 ! sakharuk 2444: my %lt=&Apache::lonlocal::texthash(
! 2445: 'saf' => "Select All Files",
! 2446: 'uaf' => "Unselect All Files");
! 2447: $buttons = <<BUTTONS;
1.5 bowersj2 2448: <br />
1.68 ! sakharuk 2449: <input type="button" onclick="checkall(true, '$var')" value="$lt{'saf'}" />
! 2450: <input type="button" onclick="checkall(false, '$var')" value="$lt{'uaf'}" />
1.23 bowersj2 2451: BUTTONS
2452:
1.68 ! sakharuk 2453: my %lt=&Apache::lonlocal::texthash(
! 2454: 'sap' => "Select All Published",
! 2455: 'uap' => "Unselect All Published");
1.23 bowersj2 2456: if ($helper->{VARS}->{'construction'}) {
1.68 ! sakharuk 2457: $buttons .= <<BUTTONS;
! 2458: <input type="button" onclick="checkallclass(true, 'Published')" value="$lt{'sap'}" />
! 2459: <input type="button" onclick="checkallclass(false, 'Published')" value="$lt{'uap'}" />
1.5 bowersj2 2460: <br />
2461: BUTTONS
1.23 bowersj2 2462: }
1.5 bowersj2 2463: }
2464:
2465: # Get the list of files in this directory.
2466: my @fileList;
2467:
2468: # If the subdirectory is in local CSTR space
1.47 albertel 2469: my $metadir;
2470: if ($subdir =~ m|/home/([^/]+)/public_html/(.*)|) {
1.5 bowersj2 2471: my $user = $1;
2472: my $domain = $Apache::lonnet::perlvar{'lonDefDomain'};
1.47 albertel 2473: $metadir='/res/'.$domain.'/'.$user.'/'.$2;
2474: @fileList = &Apache::lonnet::dirlist($subdir, $domain, $user, '');
2475: } elsif ($subdir =~ m|^~([^/]+)/(.*)$|) {
2476: $subdir='/home/'.$1.'/public_html/'.$2;
2477: my $user = $1;
2478: my $domain = $Apache::lonnet::perlvar{'lonDefDomain'};
2479: $metadir='/res/'.$domain.'/'.$user.'/'.$2;
1.5 bowersj2 2480: @fileList = &Apache::lonnet::dirlist($subdir, $domain, $user, '');
2481: } else {
2482: # local library server resource space
2483: @fileList = &Apache::lonnet::dirlist($subdir, $ENV{'user.domain'}, $ENV{'user.name'}, '');
2484: }
1.3 bowersj2 2485:
1.44 bowersj2 2486: # Sort the fileList into order
2487: @fileList = sort @fileList;
2488:
1.5 bowersj2 2489: $result .= $buttons;
2490:
1.6 bowersj2 2491: if (defined $self->{ERROR_MSG}) {
2492: $result .= '<br /><font color="#FF0000">' . $self->{ERROR_MSG} . '</font><br /><br />';
2493: }
2494:
1.20 bowersj2 2495: $result .= '<table border="0" cellpadding="2" cellspacing="0">';
1.5 bowersj2 2496:
2497: # Keeps track if there are no choices, prints appropriate error
2498: # if there are none.
2499: my $choices = 0;
2500: # Print each legitimate file choice.
2501: for my $file (@fileList) {
2502: $file = (split(/&/, $file))[0];
2503: if ($file eq '.' || $file eq '..') {
2504: next;
2505: }
2506: my $fileName = $subdir .'/'. $file;
2507: if (&$filterFunc($file)) {
1.24 sakharuk 2508: my $status;
2509: my $color;
2510: if ($helper->{VARS}->{'construction'}) {
2511: ($status, $color) = @{fileState($subdir, $file)};
2512: } else {
2513: $status = '';
2514: $color = '';
2515: }
1.22 bowersj2 2516:
1.32 bowersj2 2517: # Get the title
1.47 albertel 2518: my $title = Apache::lonpubdir::getTitleString(($metadir?$metadir:$subdir) .'/'. $file);
1.32 bowersj2 2519:
1.22 bowersj2 2520: # Netscape 4 is stupid and there's nowhere to put the
2521: # information on the input tag that the file is Published,
2522: # Unpublished, etc. In *real* browsers we can just say
2523: # "class='Published'" and check the className attribute of
2524: # the input tag, but Netscape 4 is too stupid to understand
2525: # that attribute, and un-comprehended attributes are not
2526: # reflected into the object model. So instead, what I do
2527: # is either have or don't have an "onclick" handler that
2528: # does nothing, give Published files the onclick handler, and
2529: # have the checker scripts check for that. Stupid and clumsy,
2530: # and only gives us binary "yes/no" information (at least I
2531: # couldn't figure out how to reach into the event handler's
2532: # actual code to retreive a value), but it works well enough
2533: # here.
1.23 bowersj2 2534:
1.22 bowersj2 2535: my $onclick = '';
1.23 bowersj2 2536: if ($status eq 'Published' && $helper->{VARS}->{'construction'}) {
1.22 bowersj2 2537: $onclick = 'onclick="a=1" ';
2538: }
1.20 bowersj2 2539: $result .= '<tr><td align="right"' . " bgcolor='$color'>" .
1.22 bowersj2 2540: "<input $onclick type='$type' name='" . $var
1.67 albertel 2541: . ".forminput' value='" . HTML::Entities::encode($fileName,'<>&"').
1.5 bowersj2 2542: "'";
2543: if (!$self->{'multichoice'} && $choices == 0) {
2544: $result .= ' checked';
2545: }
1.32 bowersj2 2546: $result .= "/></td><td bgcolor='$color'>" . $file . "</td>" .
2547: "<td bgcolor='$color'>$title</td>" .
2548: "<td bgcolor='$color'>$status</td>" . "</tr>\n";
1.5 bowersj2 2549: $choices++;
2550: }
2551: }
2552:
2553: $result .= "</table>\n";
2554:
2555: if (!$choices) {
1.47 albertel 2556: $result .= '<font color="#FF0000">There are no files available to select in this directory ('.$subdir.'). Please go back and select another option.</font><br /><br />';
1.5 bowersj2 2557: }
2558:
2559: $result .= $buttons;
2560:
2561: return $result;
1.20 bowersj2 2562: }
2563:
2564: # Determine the state of the file: Published, unpublished, modified.
2565: # Return the color it should be in and a label as a two-element array
2566: # reference.
2567: # Logic lifted from lonpubdir.pm, even though I don't know that it's still
2568: # the most right thing to do.
2569:
2570: sub fileState {
2571: my $constructionSpaceDir = shift;
2572: my $file = shift;
2573:
2574: my $docroot = $Apache::lonnet::perlvar{'lonDocRoot'};
2575: my $subdirpart = $constructionSpaceDir;
2576: $subdirpart =~ s/^\/home\/$ENV{'user.name'}\/public_html//;
2577: my $resdir = $docroot . '/res/' . $ENV{'user.domain'} . '/' . $ENV{'user.name'} .
2578: $subdirpart;
2579:
2580: my @constructionSpaceFileStat = stat($constructionSpaceDir . '/' . $file);
2581: my @resourceSpaceFileStat = stat($resdir . '/' . $file);
2582: if (!@resourceSpaceFileStat) {
2583: return ['Unpublished', '#FFCCCC'];
2584: }
2585:
2586: my $constructionSpaceFileModified = $constructionSpaceFileStat[9];
2587: my $resourceSpaceFileModified = $resourceSpaceFileStat[9];
2588:
2589: if ($constructionSpaceFileModified > $resourceSpaceFileModified) {
2590: return ['Modified', '#FFFFCC'];
2591: }
2592: return ['Published', '#CCFFCC'];
1.4 bowersj2 2593: }
1.5 bowersj2 2594:
1.4 bowersj2 2595: sub postprocess {
2596: my $self = shift;
1.6 bowersj2 2597: my $result = $ENV{'form.' . $self->{'variable'} . '.forminput'};
2598: if (!$result) {
2599: $self->{ERROR_MSG} = 'You must choose at least one file '.
2600: 'to continue.';
2601: return 0;
2602: }
2603:
1.5 bowersj2 2604: if (defined($self->{NEXTSTATE})) {
2605: $helper->changeState($self->{NEXTSTATE});
1.3 bowersj2 2606: }
1.6 bowersj2 2607:
2608: return 1;
1.3 bowersj2 2609: }
1.8 bowersj2 2610:
2611: 1;
2612:
1.11 bowersj2 2613: package Apache::lonhelper::section;
2614:
2615: =pod
2616:
1.44 bowersj2 2617: =head2 Element: sectionX<section, helper element>
1.11 bowersj2 2618:
2619: <section> allows the user to choose one or more sections from the current
2620: course.
2621:
2622: It takes the standard attributes "variable", "multichoice", and
2623: "nextstate", meaning what they do for most other elements.
2624:
2625: =cut
2626:
2627: no strict;
2628: @ISA = ("Apache::lonhelper::choices");
2629: use strict;
2630:
2631: BEGIN {
2632: &Apache::lonhelper::register('Apache::lonhelper::section',
2633: ('section'));
2634: }
2635:
2636: sub new {
2637: my $ref = Apache::lonhelper::choices->new();
2638: bless($ref);
2639: }
2640:
2641: sub start_section {
2642: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
2643:
2644: if ($target ne 'helper') {
2645: return '';
2646: }
1.12 bowersj2 2647:
2648: $paramHash->{CHOICES} = [];
2649:
1.11 bowersj2 2650: $paramHash->{'variable'} = $token->[2]{'variable'};
2651: $helper->declareVar($paramHash->{'variable'});
2652: $paramHash->{'multichoice'} = $token->[2]{'multichoice'};
2653: if (defined($token->[2]{'nextstate'})) {
1.12 bowersj2 2654: $paramHash->{NEXTSTATE} = $token->[2]{'nextstate'};
1.11 bowersj2 2655: }
2656:
2657: # Populate the CHOICES element
2658: my %choices;
2659:
2660: my $section = Apache::loncoursedata::CL_SECTION();
2661: my $classlist = Apache::loncoursedata::get_classlist();
2662: foreach (keys %$classlist) {
2663: my $sectionName = $classlist->{$_}->[$section];
2664: if (!$sectionName) {
2665: $choices{"No section assigned"} = "";
2666: } else {
2667: $choices{$sectionName} = $sectionName;
2668: }
1.12 bowersj2 2669: }
2670:
1.11 bowersj2 2671: for my $sectionName (sort(keys(%choices))) {
1.12 bowersj2 2672:
1.11 bowersj2 2673: push @{$paramHash->{CHOICES}}, [$sectionName, $sectionName];
2674: }
2675: }
2676:
1.12 bowersj2 2677: sub end_section {
2678: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
1.11 bowersj2 2679:
1.12 bowersj2 2680: if ($target ne 'helper') {
2681: return '';
2682: }
2683: Apache::lonhelper::section->new();
2684: }
1.11 bowersj2 2685: 1;
2686:
1.34 bowersj2 2687: package Apache::lonhelper::string;
2688:
2689: =pod
2690:
1.44 bowersj2 2691: =head2 Element: stringX<string, helper element>
1.34 bowersj2 2692:
2693: string elements provide a string entry field for the user. string elements
2694: take the usual 'variable' and 'nextstate' parameters. string elements
2695: also pass through 'maxlength' and 'size' attributes to the input tag.
2696:
2697: string honors the defaultvalue tag, if given.
2698:
1.38 bowersj2 2699: string honors the validation function, if given.
2700:
1.34 bowersj2 2701: =cut
2702:
2703: no strict;
2704: @ISA = ("Apache::lonhelper::element");
2705: use strict;
2706:
2707: BEGIN {
2708: &Apache::lonhelper::register('Apache::lonhelper::string',
2709: ('string'));
2710: }
2711:
2712: sub new {
2713: my $ref = Apache::lonhelper::element->new();
2714: bless($ref);
2715: }
2716:
2717: # CONSTRUCTION: Construct the message element from the XML
2718: sub start_string {
2719: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
2720:
2721: if ($target ne 'helper') {
2722: return '';
2723: }
2724:
2725: $paramHash->{'variable'} = $token->[2]{'variable'};
2726: $helper->declareVar($paramHash->{'variable'});
2727: $paramHash->{'nextstate'} = $token->[2]{'nextstate'};
2728: $paramHash->{'maxlength'} = $token->[2]{'maxlength'};
2729: $paramHash->{'size'} = $token->[2]{'size'};
2730:
2731: return '';
2732: }
2733:
2734: sub end_string {
2735: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
2736:
2737: if ($target ne 'helper') {
2738: return '';
2739: }
2740: Apache::lonhelper::string->new();
2741: return '';
2742: }
2743:
2744: sub render {
2745: my $self = shift;
1.38 bowersj2 2746: my $result = '';
2747:
2748: if (defined $self->{ERROR_MSG}) {
2749: $result .= '<br /><font color="#FF0000">' . $self->{ERROR_MSG} . '</font><br /><br />';
2750: }
2751:
2752: $result .= '<input type="string" name="' . $self->{'variable'} . '.forminput"';
1.34 bowersj2 2753:
2754: if (defined($self->{'size'})) {
2755: $result .= ' size="' . $self->{'size'} . '"';
2756: }
2757: if (defined($self->{'maxlength'})) {
2758: $result .= ' maxlength="' . $self->{'maxlength'} . '"';
2759: }
2760:
2761: if (defined($self->{DEFAULT_VALUE})) {
2762: my $valueFunc = eval($self->{DEFAULT_VALUE});
2763: die 'Error in default value code for variable ' .
2764: $self->{'variable'} . ', Perl said: ' . $@ if $@;
2765: $result .= ' value="' . &$valueFunc($helper, $self) . '"';
2766: }
2767:
2768: $result .= ' />';
2769:
2770: return $result;
2771: }
2772:
2773: # If a NEXTSTATE was given, switch to it
2774: sub postprocess {
2775: my $self = shift;
1.38 bowersj2 2776:
2777: if (defined($self->{VALIDATOR})) {
2778: my $validator = eval($self->{VALIDATOR});
2779: die 'Died during evaluation of evaulation code; Perl said: ' . $@ if $@;
2780: my $invalid = &$validator($helper, $state, $self, $self->getValue());
2781: if ($invalid) {
2782: $self->{ERROR_MSG} = $invalid;
2783: return 0;
2784: }
2785: }
2786:
2787: if (defined($self->{'nextstate'})) {
2788: $helper->changeState($self->{'nextstate'});
1.34 bowersj2 2789: }
2790:
2791: return 1;
2792: }
2793:
2794: 1;
2795:
1.8 bowersj2 2796: package Apache::lonhelper::general;
2797:
2798: =pod
2799:
1.44 bowersj2 2800: =head2 General-purpose tag: <exec>X<exec, helper tag>
1.8 bowersj2 2801:
1.44 bowersj2 2802: The contents of the exec tag are executed as Perl code, B<not> inside a
1.8 bowersj2 2803: safe space, so the full range of $ENV and such is available. The code
2804: will be executed as a subroutine wrapped with the following code:
2805:
2806: "sub { my $helper = shift; my $state = shift;" and
2807:
2808: "}"
2809:
2810: The return value is ignored.
2811:
2812: $helper is the helper object. Feel free to add methods to the helper
2813: object to support whatever manipulation you may need to do (for instance,
2814: overriding the form location if the state is the final state; see
1.44 bowersj2 2815: parameter.helper for an example).
1.8 bowersj2 2816:
2817: $state is the $paramHash that has currently been generated and may
2818: be manipulated by the code in exec. Note that the $state is not yet
2819: an actual state B<object>, it is just a hash, so do not expect to
2820: be able to call methods on it.
2821:
2822: =cut
2823:
2824: BEGIN {
2825: &Apache::lonhelper::register('Apache::lonhelper::general',
1.11 bowersj2 2826: 'exec', 'condition', 'clause',
2827: 'eval');
1.8 bowersj2 2828: }
2829:
2830: sub start_exec {
2831: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
2832:
2833: if ($target ne 'helper') {
2834: return '';
2835: }
2836:
2837: my $code = &Apache::lonxml::get_all_text('/exec', $parser);
2838:
2839: $code = eval ('sub { my $helper = shift; my $state = shift; ' .
2840: $code . "}");
1.11 bowersj2 2841: die 'Error in <exec>, Perl said: '. $@ if $@;
1.8 bowersj2 2842: &$code($helper, $paramHash);
2843: }
2844:
2845: sub end_exec { return ''; }
2846:
2847: =pod
2848:
2849: =head2 General-purpose tag: <condition>
2850:
2851: The <condition> tag allows you to mask out parts of the helper code
2852: depending on some programatically determined condition. The condition
2853: tag contains a tag <clause> which contains perl code that when wrapped
2854: with "sub { my $helper = shift; my $state = shift; " and "}", returns
2855: a true value if the XML in the condition should be evaluated as a normal
2856: part of the helper, or false if it should be completely discarded.
2857:
2858: The <clause> tag must be the first sub-tag of the <condition> tag or
2859: it will not work as expected.
2860:
2861: =cut
2862:
2863: # The condition tag just functions as a marker, it doesn't have
2864: # to "do" anything. Technically it doesn't even have to be registered
2865: # with the lonxml code, but I leave this here to be explicit about it.
2866: sub start_condition { return ''; }
2867: sub end_condition { return ''; }
2868:
2869: sub start_clause {
2870: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
2871:
2872: if ($target ne 'helper') {
2873: return '';
2874: }
2875:
2876: my $clause = Apache::lonxml::get_all_text('/clause', $parser);
2877: $clause = eval('sub { my $helper = shift; my $state = shift; '
2878: . $clause . '}');
1.11 bowersj2 2879: die 'Error in clause of condition, Perl said: ' . $@ if $@;
1.8 bowersj2 2880: if (!&$clause($helper, $paramHash)) {
2881: # Discard all text until the /condition.
2882: &Apache::lonxml::get_all_text('/condition', $parser);
2883: }
2884: }
2885:
2886: sub end_clause { return ''; }
1.11 bowersj2 2887:
2888: =pod
2889:
1.44 bowersj2 2890: =head2 General-purpose tag: <eval>X<eval, helper tag>
1.11 bowersj2 2891:
2892: The <eval> tag will be evaluated as a subroutine call passed in the
2893: current helper object and state hash as described in <condition> above,
2894: but is expected to return a string to be printed directly to the
2895: screen. This is useful for dynamically generating messages.
2896:
2897: =cut
2898:
2899: # This is basically a type of message.
2900: # Programmatically setting $paramHash->{NEXTSTATE} would work, though
2901: # it's probably bad form.
2902:
2903: sub start_eval {
2904: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
2905:
2906: if ($target ne 'helper') {
2907: return '';
2908: }
2909:
2910: my $program = Apache::lonxml::get_all_text('/eval', $parser);
2911: $program = eval('sub { my $helper = shift; my $state = shift; '
2912: . $program . '}');
2913: die 'Error in eval code, Perl said: ' . $@ if $@;
2914: $paramHash->{MESSAGE_TEXT} = &$program($helper, $paramHash);
2915: }
2916:
2917: sub end_eval {
2918: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
2919:
2920: if ($target ne 'helper') {
2921: return '';
2922: }
2923:
2924: Apache::lonhelper::message->new();
2925: }
2926:
1.13 bowersj2 2927: 1;
2928:
1.27 bowersj2 2929: package Apache::lonhelper::final;
2930:
2931: =pod
2932:
1.44 bowersj2 2933: =head2 Element: finalX<final, helper tag>
1.27 bowersj2 2934:
2935: <final> is a special element that works with helpers that use the <finalcode>
1.44 bowersj2 2936: tagX<finalcode, helper tag>. It goes through all the states and elements, executing the <finalcode>
1.27 bowersj2 2937: snippets and collecting the results. Finally, it takes the user out of the
2938: helper, going to a provided page.
2939:
1.34 bowersj2 2940: If the parameter "restartCourse" is true, this will override the buttons and
2941: will make a "Finish Helper" button that will re-initialize the course for them,
2942: which is useful for the Course Initialization helper so the users never see
2943: the old values taking effect.
2944:
1.27 bowersj2 2945: =cut
2946:
2947: no strict;
2948: @ISA = ("Apache::lonhelper::element");
2949: use strict;
1.62 matthew 2950: use Apache::lonlocal;
1.27 bowersj2 2951: BEGIN {
2952: &Apache::lonhelper::register('Apache::lonhelper::final',
2953: ('final', 'exitpage'));
2954: }
2955:
2956: sub new {
2957: my $ref = Apache::lonhelper::element->new();
2958: bless($ref);
2959: }
2960:
1.34 bowersj2 2961: sub start_final {
2962: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
2963:
2964: if ($target ne 'helper') {
2965: return '';
2966: }
2967:
2968: $paramHash->{'restartCourse'} = $token->[2]{'restartCourse'};
2969:
2970: return '';
2971: }
1.27 bowersj2 2972:
2973: sub end_final {
2974: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
2975:
2976: if ($target ne 'helper') {
2977: return '';
2978: }
2979:
2980: Apache::lonhelper::final->new();
2981:
2982: return '';
2983: }
2984:
2985: sub start_exitpage {
2986: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
2987:
2988: if ($target ne 'helper') {
2989: return '';
2990: }
2991:
2992: $paramHash->{EXIT_PAGE} = &Apache::lonxml::get_all_text('/exitpage',
2993: $parser);
2994:
2995: return '';
2996: }
2997:
2998: sub end_exitpage { return ''; }
2999:
3000: sub render {
3001: my $self = shift;
3002:
3003: my @results;
3004:
3005: # Collect all the results
3006: for my $stateName (keys %{$helper->{STATES}}) {
3007: my $state = $helper->{STATES}->{$stateName};
3008:
3009: for my $element (@{$state->{ELEMENTS}}) {
3010: if (defined($element->{FINAL_CODE})) {
3011: # Compile the code.
1.31 bowersj2 3012: my $code = 'sub { my $helper = shift; my $element = shift; '
3013: . $element->{FINAL_CODE} . '}';
1.27 bowersj2 3014: $code = eval($code);
3015: die 'Error while executing final code for element with var ' .
3016: $element->{'variable'} . ', Perl said: ' . $@ if $@;
3017:
1.31 bowersj2 3018: my $result = &$code($helper, $element);
1.27 bowersj2 3019: if ($result) {
3020: push @results, $result;
3021: }
3022: }
3023: }
3024: }
3025:
1.40 bowersj2 3026: my $result;
1.27 bowersj2 3027:
1.40 bowersj2 3028: if (scalar(@results) != 0) {
3029: $result .= "<ul>\n";
3030: for my $re (@results) {
3031: $result .= ' <li>' . $re . "</li>\n";
3032: }
3033:
3034: if (!@results) {
1.59 bowersj2 3035: $result .= ' <li>' .
3036: &mt('No changes were made to current settings.') . '</li>';
1.40 bowersj2 3037: }
3038:
3039: $result .= '</ul>';
1.34 bowersj2 3040: }
3041:
3042: if ($self->{'restartCourse'}) {
1.45 bowersj2 3043: my $targetURL = '/adm/menu';
1.64 albertel 3044: if ($ENV{'course.'.$ENV{'request.course.id'}.'.url'}=~/^uploaded/) {
3045: $targetURL = '/adm/coursedocs';
3046: } else {
3047: $targetURL = '/adm/navmaps';
3048: }
1.45 bowersj2 3049: if ($ENV{'course.'.$ENV{'request.course.id'}.'.clonedfrom'}) {
3050: $targetURL = '/adm/parmset?overview=1';
3051: }
1.59 bowersj2 3052: my $previous = HTML::Entities::encode(&mt("<- Previous"), '<>&"');
3053: my $next = HTML::Entities::encode(&mt("Next ->"), '<>&"');
1.34 bowersj2 3054: $result .= "<center>\n" .
3055: "<form action='/adm/roles' method='post' target='loncapaclient'>\n" .
1.59 bowersj2 3056: "<input type='button' onclick='history.go(-1)' value='$previous' />" .
1.45 bowersj2 3057: "<input type='hidden' name='orgurl' value='$targetURL' />" .
1.34 bowersj2 3058: "<input type='hidden' name='selectrole' value='1' />\n" .
3059: "<input type='hidden' name='" . $ENV{'request.role'} .
1.59 bowersj2 3060: "' value='1' />\n<input type='submit' value='" .
1.63 matthew 3061: &mt('Finish Course Initialization') . "' />\n" .
1.34 bowersj2 3062: "</form></center>";
3063: }
3064:
1.40 bowersj2 3065: return $result;
1.34 bowersj2 3066: }
3067:
3068: sub overrideForm {
3069: my $self = shift;
3070: return $self->{'restartCourse'};
1.27 bowersj2 3071: }
3072:
3073: 1;
3074:
1.13 bowersj2 3075: package Apache::lonhelper::parmwizfinal;
3076:
3077: # This is the final state for the parmwizard. It is not generally useful,
3078: # so it is not perldoc'ed. It does its own processing.
3079: # It is represented with <parmwizfinal />, and
3080: # should later be moved to lonparmset.pm .
3081:
3082: no strict;
3083: @ISA = ('Apache::lonhelper::element');
3084: use strict;
1.11 bowersj2 3085:
1.13 bowersj2 3086: BEGIN {
3087: &Apache::lonhelper::register('Apache::lonhelper::parmwizfinal',
3088: ('parmwizfinal'));
3089: }
3090:
3091: use Time::localtime;
3092:
3093: sub new {
3094: my $ref = Apache::lonhelper::choices->new();
3095: bless ($ref);
3096: }
3097:
3098: sub start_parmwizfinal { return ''; }
3099:
3100: sub end_parmwizfinal {
3101: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
3102:
3103: if ($target ne 'helper') {
3104: return '';
3105: }
3106: Apache::lonhelper::parmwizfinal->new();
3107: }
3108:
3109: # Renders a form that, when submitted, will form the input to lonparmset.pm
3110: sub render {
3111: my $self = shift;
3112: my $vars = $helper->{VARS};
3113:
3114: # FIXME: Unify my designators with the standard ones
1.48 bowersj2 3115: my %dateTypeHash = ('open_date' => "opening date",
3116: 'due_date' => "due date",
3117: 'answer_date' => "answer date",
3118: 'tries' => 'number of tries',
3119: 'weight' => 'problem weight'
1.38 bowersj2 3120: );
1.13 bowersj2 3121: my %parmTypeHash = ('open_date' => "0_opendate",
3122: 'due_date' => "0_duedate",
1.38 bowersj2 3123: 'answer_date' => "0_answerdate",
1.48 bowersj2 3124: 'tries' => '0_maxtries',
3125: 'weight' => '0_weight' );
1.13 bowersj2 3126:
3127: my $affectedResourceId = "";
3128: my $parm_name = $parmTypeHash{$vars->{ACTION_TYPE}};
3129: my $level = "";
1.27 bowersj2 3130: my $resourceString;
3131: my $symb;
3132: my $paramlevel;
3133:
1.13 bowersj2 3134: # Print the granularity, depending on the action
3135: if ($vars->{GRANULARITY} eq 'whole_course') {
1.27 bowersj2 3136: $resourceString .= '<li>for <b>all resources in the course</b></li>';
1.13 bowersj2 3137: $level = 9; # general course, see lonparmset.pm perldoc
3138: $affectedResourceId = "0.0";
1.27 bowersj2 3139: $symb = 'a';
3140: $paramlevel = 'general';
1.13 bowersj2 3141: } elsif ($vars->{GRANULARITY} eq 'map') {
1.41 bowersj2 3142: my $navmap = Apache::lonnavmaps::navmap->new();
1.35 bowersj2 3143: my $res = $navmap->getByMapPc($vars->{RESOURCE_ID});
1.13 bowersj2 3144: my $title = $res->compTitle();
1.27 bowersj2 3145: $symb = $res->symb();
1.13 bowersj2 3146: $navmap->untieHashes();
1.27 bowersj2 3147: $resourceString .= "<li>for the map named <b>$title</b></li>";
1.13 bowersj2 3148: $level = 8;
3149: $affectedResourceId = $vars->{RESOURCE_ID};
1.27 bowersj2 3150: $paramlevel = 'map';
1.13 bowersj2 3151: } else {
1.41 bowersj2 3152: my $navmap = Apache::lonnavmaps::navmap->new();
1.13 bowersj2 3153: my $res = $navmap->getById($vars->{RESOURCE_ID});
1.27 bowersj2 3154: $symb = $res->symb();
1.13 bowersj2 3155: my $title = $res->compTitle();
3156: $navmap->untieHashes();
1.27 bowersj2 3157: $resourceString .= "<li>for the resource named <b>$title</b></li>";
1.13 bowersj2 3158: $level = 7;
3159: $affectedResourceId = $vars->{RESOURCE_ID};
1.27 bowersj2 3160: $paramlevel = 'full';
1.13 bowersj2 3161: }
3162:
1.27 bowersj2 3163: my $result = "<form name='helpform' method='get' action='/adm/parmset#$affectedResourceId&$parm_name&$level'>\n";
1.61 albertel 3164: $result .= '<p>Confirm that this information is correct, then click "Finish Helper" to complete setting the parameter.<ul>';
1.27 bowersj2 3165:
3166: # Print the type of manipulation:
1.38 bowersj2 3167: $result .= '<li>Setting the <b>' . $dateTypeHash{$vars->{ACTION_TYPE}} . '</b>';
3168: if ($vars->{ACTION_TYPE} eq 'tries') {
3169: $result .= ' to <b>' . $vars->{TRIES} . '</b>';
3170: }
1.48 bowersj2 3171: if ($vars->{ACTION_TYPE} eq 'weight') {
3172: $result .= ' to <b>' . $vars->{WEIGHT} . '</b>';
3173: }
1.38 bowersj2 3174: $result .= "</li>\n";
1.27 bowersj2 3175: if ($vars->{ACTION_TYPE} eq 'due_date' ||
3176: $vars->{ACTION_TYPE} eq 'answer_date') {
3177: # for due dates, we default to "date end" type entries
3178: $result .= "<input type='hidden' name='recent_date_end' " .
3179: "value='" . $vars->{PARM_DATE} . "' />\n";
3180: $result .= "<input type='hidden' name='pres_value' " .
3181: "value='" . $vars->{PARM_DATE} . "' />\n";
3182: $result .= "<input type='hidden' name='pres_type' " .
3183: "value='date_end' />\n";
3184: } elsif ($vars->{ACTION_TYPE} eq 'open_date') {
3185: $result .= "<input type='hidden' name='recent_date_start' ".
3186: "value='" . $vars->{PARM_DATE} . "' />\n";
3187: $result .= "<input type='hidden' name='pres_value' " .
3188: "value='" . $vars->{PARM_DATE} . "' />\n";
3189: $result .= "<input type='hidden' name='pres_type' " .
3190: "value='date_start' />\n";
1.38 bowersj2 3191: } elsif ($vars->{ACTION_TYPE} eq 'tries') {
3192: $result .= "<input type='hidden' name='pres_value' " .
3193: "value='" . $vars->{TRIES} . "' />\n";
1.48 bowersj2 3194: } elsif ($vars->{ACTION_TYPE} eq 'weight') {
3195: $result .= "<input type='hidden' name='pres_value' " .
3196: "value='" . $vars->{WEIGHT} . "' />\n";
1.38 bowersj2 3197: }
1.27 bowersj2 3198:
3199: $result .= $resourceString;
3200:
1.13 bowersj2 3201: # Print targets
3202: if ($vars->{TARGETS} eq 'course') {
3203: $result .= '<li>for <b>all students in course</b></li>';
3204: } elsif ($vars->{TARGETS} eq 'section') {
3205: my $section = $vars->{SECTION_NAME};
3206: $result .= "<li>for section <b>$section</b></li>";
3207: $level -= 3;
3208: $result .= "<input type='hidden' name='csec' value='" .
1.67 albertel 3209: HTML::Entities::encode($section,'<>&"') . "' />\n";
1.13 bowersj2 3210: } else {
3211: # FIXME: This is probably wasteful! Store the name!
3212: my $classlist = Apache::loncoursedata::get_classlist();
1.27 bowersj2 3213: my $username = $vars->{USER_NAME};
3214: # Chop off everything after the last colon (section)
3215: $username = substr($username, 0, rindex($username, ':'));
3216: my $name = $classlist->{$username}->[6];
1.13 bowersj2 3217: $result .= "<li>for <b>$name</b></li>";
3218: $level -= 6;
3219: my ($uname, $udom) = split /:/, $vars->{USER_NAME};
3220: $result .= "<input type='hidden' name='uname' value='".
1.67 albertel 3221: HTML::Entities::encode($uname,'<>&"') . "' />\n";
1.13 bowersj2 3222: $result .= "<input type='hidden' name='udom' value='".
1.67 albertel 3223: HTML::Entities::encode($udom,'<>&"') . "' />\n";
1.13 bowersj2 3224: }
3225:
3226: # Print value
1.48 bowersj2 3227: if ($vars->{ACTION_TYPE} ne 'tries' && $vars->{ACTION_TYPE} ne 'weight') {
1.38 bowersj2 3228: $result .= "<li>to <b>" . ctime($vars->{PARM_DATE}) . "</b> (" .
3229: Apache::lonnavmaps::timeToHumanString($vars->{PARM_DATE})
3230: . ")</li>\n";
3231: }
3232:
1.13 bowersj2 3233: # print pres_marker
3234: $result .= "\n<input type='hidden' name='pres_marker'" .
3235: " value='$affectedResourceId&$parm_name&$level' />\n";
1.27 bowersj2 3236:
3237: # Make the table appear
3238: $result .= "\n<input type='hidden' value='true' name='prevvisit' />";
3239: $result .= "\n<input type='hidden' value='all' name='pschp' />";
3240: $result .= "\n<input type='hidden' value='$symb' name='pssymb' />";
3241: $result .= "\n<input type='hidden' value='$paramlevel' name='parmlev' />";
1.13 bowersj2 3242:
3243: $result .= "<br /><br /><center><input type='submit' value='Finish Helper' /></center></form>\n";
3244:
3245: return $result;
3246: }
3247:
3248: sub overrideForm {
3249: return 1;
3250: }
1.5 bowersj2 3251:
1.4 bowersj2 3252: 1;
1.3 bowersj2 3253:
1.1 bowersj2 3254: __END__
1.3 bowersj2 3255:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>