[system] / branches / rel-2-3-dev / webwork-modperl / lib / WeBWorK / ContentGenerator / Instructor / SetMaker.pm Repository:
ViewVC logotype

Diff of /branches/rel-2-3-dev/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

Revision 2283 Revision 3379
1################################################################################ 1################################################################################
2# WeBWorK Online Homework Delivery System 2# WeBWorK Online Homework Delivery System
3# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ 3# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/
4# $CVSHeader: webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm,v 1.19 2004/06/07 02:50:52 jj Exp $ 4# $CVSHeader$
5# 5#
6# This program is free software; you can redistribute it and/or modify it under 6# This program is free software; you can redistribute it and/or modify it under
7# the terms of either: (a) the GNU General Public License as published by the 7# the terms of either: (a) the GNU General Public License as published by the
8# Free Software Foundation; either version 2, or (at your option) any later 8# Free Software Foundation; either version 2, or (at your option) any later
9# version, or (b) the "Artistic License" which comes with this package. 9# version, or (b) the "Artistic License" which comes with this package.
10# 10#
11# This program is distributed in the hope that it will be useful, but WITHOUT 11# This program is distributed in the hope that it will be useful, but WITHOUT
12# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 12# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the 13# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
14# Artistic License for more details. 14# Artistic License for more details.
15################################################################################ 15################################################################################
16 16
17 17
18package WeBWorK::ContentGenerator::Instructor::SetMaker; 18package WeBWorK::ContentGenerator::Instructor::SetMaker;
27use strict; 27use strict;
28use warnings; 28use warnings;
29 29
30use CGI::Pretty qw(); 30use CGI::Pretty qw();
31use WeBWorK::Form; 31use WeBWorK::Form;
32use WeBWorK::Utils qw(readDirectory max); 32use WeBWorK::Utils qw(readDirectory max sortByName);
33use WeBWorK::Utils::Tasks qw(renderProblems); 33use WeBWorK::Utils::Tasks qw(renderProblems);
34 34
35require WeBWorK::Utils::ListingDB; 35require WeBWorK::Utils::ListingDB;
36 36
37use constant MAX_SHOW_DEFAULT => 20; 37use constant MAX_SHOW_DEFAULT => 20;
38use constant NO_LOCAL_SET_STRING => 'There are no local sets yet'; 38use constant NO_LOCAL_SET_STRING => 'There are no local sets yet';
39use constant SELECT_SET_STRING => 'Select a Set for This Course'; 39use constant SELECT_SET_STRING => 'Select a Set for This Course';
40use constant SELECT_LOCAL_STRING => 'Select a Local Problem Collection'; 40use constant SELECT_LOCAL_STRING => 'Select a Problem Collection';
41use constant MY_PROBLEMS => ' My Problems ';
42use constant MAIN_PROBLEMS => ' Main Problems ';
41 43
42## Flags for operations on files 44## Flags for operations on files
43 45
44use constant ADDED => 1; 46use constant ADDED => 1;
45use constant HIDDEN => (1 << 1); 47use constant HIDDEN => (1 << 1);
46use constant SUCCESS => (1 << 2); 48use constant SUCCESS => (1 << 2);
47 49
50## for additional problib buttons
51my %problib; ## filled in in global.conf
52my %ignoredir = (
53 '.' => 1, '..' => 1, 'Library' => 1,
54 'headers' => 1, 'macros' => 1, 'email' => 1,
55);
56
57##
48## This is for searching the disk for directories containing pg files. 58## This is for searching the disk for directories containing pg files.
49## to make the recursion work, this returns an array where the first 59## to make the recursion work, this returns an array where the first
50## item is 1 or 0 depending on whether or not the current 60## item is the number of pg files in the directory. The second is a
51## directory has any pg files. The second is a list of directories 61## list of directories which contain pg files.
52## which contain pg files. 62##
63## If a directory contains only one pg file and at least one other
64## file, the directory is considered to be part of the parent
65## directory (it is probably in a separate directory only because
66## it has auxiliarly files that want to be kept together with the
67## pg file).
68##
69## If a directory has a file named "=library-ignore", it is never
70## included in the directory menu. If a directory contains a file
71## called "=library-combine-up", then its pg are included with those
72## in the parent directory (and the directory does not appear in the
73## menu). If it has a file called "=library-no-combine" then it is
74## always listed as a separate directory even if it contains only one
75## pg file.
76##
77
53sub get_library_sets { 78sub get_library_sets {
54 my $amtop = shift; 79 my $top = shift; my $dir = shift;
55 my $topdir = shift; 80 # ignore directories that give us an error
81 my @lis = eval { readDirectory($dir) };
82 if ($@) {
83 warn $@;
84 return (0);
85 }
86 return (0) if grep /^=library-ignore$/, @lis;
87
88 my @pgdirs;
89
90 my $pgcount = scalar(grep { m/\.pg$/ and (not m/(Header|-text)\.pg$/) and -f "$dir/$_"} @lis);
91 my $others = scalar(grep { (!m/\.pg$/ || m/(Header|-text)\.pg$/) &&
92 !m/(\.(tmp|bak)|~)$/ && -f "$dir/$_" } @lis);
93
94 my @dirs = grep {!$ignoredir{$_} and -d "$dir/$_"} @lis;
95 if ($top == 1) {@dirs = grep {!$problib{$_}} @dirs}
96 foreach my $subdir (@dirs) {
97 my @results = get_library_sets(0, "$dir/$subdir");
98 $pgcount += shift @results; push(@pgdirs,@results);
99 }
100
101 return ($pgcount, @pgdirs) if $top || $pgcount == 0 || grep /^=library-combine-up$/, @lis;
102 return (0,@pgdirs,$dir) if $pgcount > 1 || $others == 0 || grep /^=library-no-combine$/, @lis;
103 return ($pgcount, @pgdirs);
104}
105
106sub get_library_pgs {
107 my $top = shift; my $base = shift; my $dir = shift;
56 my @lis = readDirectory($topdir); 108 my @lis = readDirectory("$base/$dir");
109 return () if grep /^=library-ignore$/, @lis;
110 return () if !$top && grep /^=library-no-combine$/, @lis;
111
57 my @pgs = grep { m/\.pg$/ and (not m/Header\.pg/) and -f "$topdir/$_"} @lis; 112 my @pgs = grep { m/\.pg$/ and (not m/(Header|-text)\.pg$/) and -f "$base/$dir/$_"} @lis;
58 my $havepg = scalar(@pgs)>0 ? 1 : 0; 113 my $others = scalar(grep { (!m/\.pg$/ || m/(Header|-text)\.pg$/) &&
59 my @mdirs = grep {$_ ne "." and $_ ne ".." and $_ ne "Library" 114 !m/(\.(tmp|bak)|~)$/ && -f "$base/$dir/$_" } @lis);
60 and -d "$topdir/$_"} @lis;
61 if($amtop) { # we don't want the library
62 @mdirs = grep {$_ ne "Library"} @mdirs;
63 }
64 my ($adir, @results, @thisresult);
65 for $adir (@mdirs) {
66 @results = get_library_sets(0, "$topdir/$adir");
67 my $isadirok = shift @results;
68 @thisresult = (@thisresult, @results);
69 if ($isadirok) {
70 @thisresult = ("$topdir/$adir", @thisresult);
71 }
72 }
73 return(($havepg, @thisresult));
74}
75 115
76## List all the pg files in the requested directory 116 my @dirs = grep {!$ignoredir{$_} and -d "$base/$dir/$_"} @lis;
117 if ($top == 1) {@dirs = grep {!$problib{$_}} @dirs}
118 foreach my $subdir (@dirs) {push(@pgs, get_library_pgs(0,"$base/$dir",$subdir))}
119
120 return () unless $top || (scalar(@pgs) == 1 && $others) || grep /^=library-combine-up$/, @lis;
121 return (map {"$dir/$_"} @pgs);
122}
123
77sub list_pg_files { 124sub list_pg_files {
78 my $templatedir = shift; 125 my ($templates,$dir) = @_;
79 my $topdir = shift; 126 my $top = ($dir eq '.')? 1 : 2;
80 127 my @pgs = get_library_pgs($top,$templates,$dir);
81 my @lis = readDirectory("$templatedir/$topdir"); 128 return sortByName(undef,@pgs);
82 my @pgs = grep { m/\.pg$/ and (not m/Header\.pg/) and -f "$templatedir/$topdir/$_"} @lis;
83 @pgs = map { "$topdir/$_" } @pgs;
84 return(@pgs);
85} 129}
86 130
87## go through past page getting a list of identifiers for the problems 131## go through past page getting a list of identifiers for the problems
88## and whether or not they are selected, and whether or not they should 132## and whether or not they are selected, and whether or not they should
89## be hidden 133## be hidden
90 134
91sub get_past_problem_files { 135sub get_past_problem_files {
92 my $r = shift; 136 my $r = shift;
93 my @found=(); 137 my @found=();
94 my $count =1; 138 my $count =1;
95 while (defined($r->param("filetrial$count"))) { 139 while (defined($r->param("filetrial$count"))) {
96 my $val = 0; 140 my $val = 0;
97 $val |= ADDED if($r->param("trial$count")); 141 $val |= ADDED if($r->param("trial$count"));
98 $val |= HIDDEN if($r->param("hideme$count")); 142 $val |= HIDDEN if($r->param("hideme$count"));
99 push @found, [$r->param("filetrial$count"), $val]; 143 push @found, [$r->param("filetrial$count"), $val];
100 $count++; 144 $count++;
101 } 145 }
102 return(\@found); 146 return(\@found);
103} 147}
104 148
105#### For adding new problems 149#### For adding new problems
106 150
107sub add_selected { 151sub add_selected {
108 my $self = shift; 152 my $self = shift;
109 my $db = shift; 153 my $db = shift;
110 my $setName = shift; 154 my $setName = shift;
111 my @past_problems = @{$self->{past_problems}}; 155 my @past_problems = @{$self->{past_problems}};
112 my @selected = @past_problems; 156 my @selected = @past_problems;
113 my (@path, $file, $selected, $freeProblemID); 157 my (@path, $file, $selected, $freeProblemID);
114 $freeProblemID = max($db->listGlobalProblems($setName)) + 1; 158 $freeProblemID = max($db->listGlobalProblems($setName)) + 1;
115 my $addedcount=0; 159 my $addedcount=0;
116 160
117 for $selected (@selected) { 161 for $selected (@selected) {
118 if($selected->[1] & ADDED) { 162 if($selected->[1] & ADDED) {
119 $file = $selected->[0]; 163 $file = $selected->[0];
120 my $problemRecord = $self->addProblemToSet(setName => $setName, 164 my $problemRecord = $self->addProblemToSet(setName => $setName,
121 sourceFile => $file, problemID => $freeProblemID); 165 sourceFile => $file, problemID => $freeProblemID);
122 $freeProblemID++; 166 $freeProblemID++;
123 $self->assignProblemToAllSetUsers($problemRecord); 167 $self->assignProblemToAllSetUsers($problemRecord);
124 $selected->[1] |= SUCCESS; 168 $selected->[1] |= SUCCESS;
125 $addedcount++; 169 $addedcount++;
126 } 170 }
127 } 171 }
128 return($addedcount); 172 return($addedcount);
129} 173}
130 174
131 175
132############# List of sets of problems in templates directory 176############# List of sets of problems in templates directory
133 177
134sub get_problem_directories { 178sub get_problem_directories {
135 my $ce = shift; 179 my $ce = shift;
180 my $lib = shift;
181 my $source = $ce->{courseDirs}{templates};
182 my $main = MY_PROBLEMS; my $isTop = 1;
183 if ($lib) {$source .= "/$lib"; $main = MAIN_PROBLEMS; $isTop = 2}
136 my @all_problem_directories = get_library_sets(1, $ce->{courseDirs}->{templates}); 184 my @all_problem_directories = get_library_sets($isTop, $source);
137 my $includetop = shift @all_problem_directories; 185 my $includetop = shift @all_problem_directories;
138 my $j; 186 my $j;
139 for ($j=0; $j<scalar(@all_problem_directories); $j++) { 187 for ($j=0; $j<scalar(@all_problem_directories); $j++) {
140 $all_problem_directories[$j] =~ s|^$ce->{courseDirs}->{templates}/?||; 188 $all_problem_directories[$j] =~ s|^$ce->{courseDirs}->{templates}/?||;
141 } 189 }
142 @all_problem_directories = sort @all_problem_directories; 190 @all_problem_directories = sortByName(undef, @all_problem_directories);
143 unshift @all_problem_directories, ' My Problems ' if($includetop); 191 unshift @all_problem_directories, $main if($includetop);
144 return (\@all_problem_directories); 192 return (\@all_problem_directories);
145} 193}
146 194
147############# Everyone has a view problems line. Abstract it 195############# Everyone has a view problems line. Abstract it
148sub view_problems_line { 196sub view_problems_line {
149 my $internal_name = shift; 197 my $internal_name = shift;
150 my $label = shift; 198 my $label = shift;
151 my $r = shift; # so we can get parameter values 199 my $r = shift; # so we can get parameter values
152 my $result = CGI::submit(-name=>"$internal_name", -value=>$label); 200 my $result = CGI::submit(-name=>"$internal_name", -value=>$label);
153 201
154 my %display_modes = %{WeBWorK::PG::DISPLAY_MODES()}; 202 my %display_modes = %{WeBWorK::PG::DISPLAY_MODES()};
155 my @active_modes = grep { exists $display_modes{$_} } 203 my @active_modes = grep { exists $display_modes{$_} }
156 @{$r->ce->{pg}->{displayModes}}; 204 @{$r->ce->{pg}->{displayModes}};
157 push @active_modes, 'None'; 205 push @active_modes, 'None';
158 # We have our own displayMode since its value may be None, which is illegal 206 # We have our own displayMode since its value may be None, which is illegal
159 # in other modules. 207 # in other modules.
160 my $mydisplayMode = $r->param('mydisplayMode') || $r->ce->{pg}->{options}->{displayMode}; 208 my $mydisplayMode = $r->param('mydisplayMode') || $r->ce->{pg}->{options}->{displayMode};
161 $result .= '&nbsp;Display&nbsp;Mode:&nbsp;'.CGI::popup_menu(-name=> 'mydisplayMode', 209 $result .= '&nbsp;Display&nbsp;Mode:&nbsp;'.CGI::popup_menu(-name=> 'mydisplayMode',
162 -values=>\@active_modes, 210 -values=>\@active_modes,
163 -default=> $mydisplayMode); 211 -default=> $mydisplayMode);
164 # Now we give a choice of the number of problems to show 212 # Now we give a choice of the number of problems to show
165 my $defaultMax = $r->param('max_shown') || MAX_SHOW_DEFAULT; 213 my $defaultMax = $r->param('max_shown') || MAX_SHOW_DEFAULT;
166 $result .= '&nbsp;Max. Shown:&nbsp'. 214 $result .= '&nbsp;Max. Shown:&nbsp'.
167 CGI::popup_menu(-name=> 'max_shown', 215 CGI::popup_menu(-name=> 'max_shown',
168 -values=>[5,10,15,20,25,30,50,'All'], 216 -values=>[5,10,15,20,25,30,50,'All'],
169 -default=> $defaultMax); 217 -default=> $defaultMax);
170 218
171 return($result); 219 return($result);
172} 220}
173 221
174 222
175### The browsing panel has three versions 223### The browsing panel has three versions
176##### Version 1 is local problems 224##### Version 1 is local problems
177sub browse_local_panel { 225sub browse_local_panel {
178 my $self = shift; 226 my $self = shift;
179 my $library_selected = shift; 227 my $library_selected = shift;
228 my $lib = shift || ''; $lib =~ s/^browse_//;
229 my $name = ($lib eq '')? 'Local' : $problib{$lib};
180 230
181 my $list_of_prob_dirs= get_problem_directories($self->r->ce); 231 my $list_of_prob_dirs= get_problem_directories($self->r->ce,$lib);
182 if(scalar(@$list_of_prob_dirs) == 0) { 232 if(scalar(@$list_of_prob_dirs) == 0) {
183 $library_selected = "Found no directories containing problems"; 233 $library_selected = "Found no directories containing problems";
184 unshift @{$list_of_prob_dirs}, $library_selected; 234 unshift @{$list_of_prob_dirs}, $library_selected;
185 } else { 235 } else {
186 my $default_value = SELECT_LOCAL_STRING; 236 my $default_value = SELECT_LOCAL_STRING;
187 if (not $library_selected or $library_selected eq $default_value) { 237 if (not $library_selected or $library_selected eq $default_value) {
188 unshift @{$list_of_prob_dirs}, $default_value; 238 unshift @{$list_of_prob_dirs}, $default_value;
189 $library_selected = $default_value; 239 $library_selected = $default_value;
190 } 240 }
191 } 241 }
192 my $view_problem_line = view_problems_line('view_local_set', 'View Problems', $self->r); 242 my $view_problem_line = view_problems_line('view_local_set', 'View Problems', $self->r);
193 print CGI::Tr(CGI::td({-class=>"InfoPanel", -align=>"left"}, "Local Problems: ", 243 print CGI::Tr(CGI::td({-class=>"InfoPanel", -align=>"left"}, "$name Problems: ",
194 CGI::popup_menu(-name=> 'library_sets', 244 CGI::popup_menu(-name=> 'library_sets',
195 -values=>$list_of_prob_dirs, 245 -values=>$list_of_prob_dirs,
196 -default=> $library_selected), 246 -default=> $library_selected),
197 CGI::br(), 247 CGI::br(),
198 $view_problem_line, 248 $view_problem_line,
199 )); 249 ));
200} 250}
201 251
202##### Version 2 is local problem sets 252##### Version 2 is local problem sets
203sub browse_mysets_panel { 253sub browse_mysets_panel {
204 my $self = shift; 254 my $self = shift;
205 my $library_selected = shift; 255 my $library_selected = shift;
206 my $list_of_local_sets = shift; 256 my $list_of_local_sets = shift;
207 my $default_value = "Select a Problem Set"; 257 my $default_value = "Select a Homework Set";
208 258
209 if(scalar(@$list_of_local_sets) == 0) { 259 if(scalar(@$list_of_local_sets) == 0) {
210 $list_of_local_sets = [NO_LOCAL_SET_STRING]; 260 $list_of_local_sets = [NO_LOCAL_SET_STRING];
211 } elsif (not $library_selected or $library_selected eq $default_value) { 261 } elsif (not $library_selected or $library_selected eq $default_value) {
212 unshift @{$list_of_local_sets}, $default_value; 262 unshift @{$list_of_local_sets}, $default_value;
213 $library_selected = $default_value; 263 $library_selected = $default_value;
214 } 264 }
215 265
216 my $view_problem_line = view_problems_line('view_mysets_set', 'View Problems', $self->r); 266 my $view_problem_line = view_problems_line('view_mysets_set', 'View Problems', $self->r);
217 print CGI::Tr(CGI::td({-class=>"InfoPanel", -align=>"left"}, "Browse from: ", 267 print CGI::Tr(CGI::td({-class=>"InfoPanel", -align=>"left"}, "Browse from: ",
218 CGI::popup_menu(-name=> 'library_sets', 268 CGI::popup_menu(-name=> 'library_sets',
219 -values=>$list_of_local_sets, 269 -values=>$list_of_local_sets,
220 -default=> $library_selected), 270 -default=> $library_selected),
221 CGI::br(), 271 CGI::br(),
222 $view_problem_line 272 $view_problem_line
223 )); 273 ));
224} 274}
225 275
226##### Version 3 is the problem library 276##### Version 3 is the problem library
227 277
228 278
229# There a different levels, and you can pick a new chapter, 279# There a different levels, and you can pick a new chapter,
230# pick a new section, pick all from chapter, pick all from section 280# pick a new section, pick all from chapter, pick all from section
231# 281#
232# Incoming data - current chapter, current section 282# Incoming data - current chapter, current section
233sub browse_library_panel { 283sub browse_library_panel {
234 my $self = shift; 284 my $self = shift;
235 my $r = $self->r; 285 my $r = $self->r;
236 my $ce = $r->ce; 286 my $ce = $r->ce;
237 287
238 my $libraryRoot = $r->{ce}->{problemLibrary}->{root}; 288 my $libraryRoot = $r->{ce}->{problemLibrary}->{root};
239 289
240 unless($libraryRoot) { 290 unless($libraryRoot) {
241 print CGI::Tr(CGI::td(CGI::div({class=>'ResultsWithError', align=>"center"}, 291 print CGI::Tr(CGI::td(CGI::div({class=>'ResultsWithError', align=>"center"},
242 "The problem library has not been installed."))); 292 "The problem library has not been installed.")));
243 return; 293 return;
244 } 294 }
245 # Test if the Library directory exists. If not, try to make it 295 # Test if the Library directory exists. If not, try to make it
246 unless(-d "$ce->{courseDirs}->{templates}/Library") { 296 unless(-d "$ce->{courseDirs}->{templates}/Library") {
247 unless(symlink($libraryRoot, "$ce->{courseDirs}->{templates}/Library")) { 297 unless(symlink($libraryRoot, "$ce->{courseDirs}->{templates}/Library")) {
248 my $msg = <<"HERE"; 298 my $msg = <<"HERE";
249You are missing the directory <code>templates/Library</code>, which is needed 299You are missing the directory <code>templates/Library</code>, which is needed
250for the Problem Library to function. It should be a link pointing to 300for the Problem Library to function. It should be a link pointing to
251<code>$libraryRoot</code>, which you set in <code>conf/global.conf</code>. 301<code>$libraryRoot</code>, which you set in <code>conf/global.conf</code>.
252I tried to make the link for you, but that failed. Check the permissions 302I tried to make the link for you, but that failed. Check the permissions
253in your <code>templates</code> directory. 303in your <code>templates</code> directory.
254HERE 304HERE
255 $self->addbadmessage($msg); 305 $self->addbadmessage($msg);
256 } 306 }
257 } 307 }
258 308
259 my $default_chap = "All Chapters"; 309 my $default_chap = "All Chapters";
260 my $default_sect = "All Sections"; 310 my $default_sect = "All Sections";
261 311
262 my @chaps = WeBWorK::Utils::ListingDB::getAllChapters($r->{ce}); 312 my @chaps = WeBWorK::Utils::ListingDB::getAllChapters($r->{ce});
263 unshift @chaps, $default_chap; 313 unshift @chaps, $default_chap;
264 my $chapter_selected = $r->param('library_chapters') || $default_chap; 314 my $chapter_selected = $r->param('library_chapters') || $default_chap;
265 315
266 my @sects=(); 316 my @sects=();
267 if ($chapter_selected ne $default_chap) { 317 if ($chapter_selected ne $default_chap) {
268 @sects = WeBWorK::Utils::ListingDB::getAllSections($r->{ce}, $chapter_selected); 318 @sects = WeBWorK::Utils::ListingDB::getAllSections($r->{ce}, $chapter_selected);
269 } 319 }
270 320
271 my @textbooks = ('Textbook info not ready'); 321 my @textbooks = ('Textbook info not ready');
272 322
273 unshift @sects, $default_sect; 323 unshift @sects, $default_sect;
274 my $section_selected = $r->param('library_sections') || $default_sect; 324 my $section_selected = $r->param('library_sections') || $default_sect;
275 my $view_problem_line = view_problems_line('lib_view', 'View Problems', $self->r); 325 my $view_problem_line = view_problems_line('lib_view', 'View Problems', $self->r);
276 326
277 print CGI::Tr(CGI::td({-class=>"InfoPanel", -align=>"left"}, 327 print CGI::Tr(CGI::td({-class=>"InfoPanel", -align=>"left"},
278 CGI::start_table(), 328 CGI::start_table(),
279 CGI::Tr( 329 CGI::Tr(
280 CGI::td(["Chapter:", 330 CGI::td(["Chapter:",
281 CGI::popup_menu(-name=> 'library_chapters', 331 CGI::popup_menu(-name=> 'library_chapters',
282 -values=>\@chaps, 332 -values=>\@chaps,
283 -default=> $chapter_selected, 333 -default=> $chapter_selected,
284 -onchange=>"submit();return true" 334 -onchange=>"submit();return true"
285 ), 335 ),
286 CGI::submit(-name=>"lib_select_chapter", -value=>"Update Section List")])), 336 CGI::submit(-name=>"lib_select_chapter", -value=>"Update Section List")])),
287 CGI::Tr( 337 CGI::Tr(
288 CGI::td("Section:"), 338 CGI::td("Section:"),
289 CGI::td({-colspan=>2}, 339 CGI::td({-colspan=>2},
290 CGI::popup_menu(-name=> 'library_sections', 340 CGI::popup_menu(-name=> 'library_sections',
291 -values=>\@sects, 341 -values=>\@sects,
292 -default=> $section_selected 342 -default=> $section_selected
293 ))), 343 ))),
294 344
295# CGI::Tr( 345 #CGI::Tr(
296# CGI::td("Textbook:"), 346 # CGI::td("Textbook:"),
297# CGI::td({-colspan=>2}, 347 # CGI::td({-colspan=>2},
298# CGI::popup_menu(-name=> 'library_textbooks', 348 # CGI::popup_menu(-name=> 'library_textbooks',
299# -values=>\@textbooks, 349 # -values=>\@textbooks,
300# # -default=> $section_selected 350 # #-default=> $section_selected
301# ))), 351 #))),
302 352
303# CGI::Tr( 353 #CGI::Tr(
304# CGI::td("Keywords:"), 354 # CGI::td("Keywords:"),
305# CGI::td({-colspan=>2}, CGI::textfield(-name=>"keywords",
306# -default=>"Keywords not implemented yet",
307# -override=>1, -size=>60))),
308 CGI::Tr(CGI::td({-colspan=>3}, 355 # CGI::td({-colspan=>2},
309 $view_problem_line)), 356 # CGI::textfield(-name=>"keywords",
357 # -default=>"Keywords not implemented yet",
358 # -override=>1, -size=>60
359 #))),
360 CGI::Tr(CGI::td({-colspan=>3}, $view_problem_line)),
310 CGI::end_table(), 361 CGI::end_table(),
311 )); 362 ));
312} 363}
313 364
314sub make_top_row { 365sub make_top_row {
315 my $self = shift; 366 my $self = shift;
316 my $r = $self->r; 367 my $r = $self->r;
368 my $ce = $r->ce;
317 my %data = @_; 369 my %data = @_;
318 370
319 my $list_of_local_sets = $data{all_set_defs}; 371 my $list_of_local_sets = $data{all_set_defs};
320 my $have_local_sets = scalar(@$list_of_local_sets); 372 my $have_local_sets = scalar(@$list_of_local_sets);
321 my $browse_which = $data{browse_which}; 373 my $browse_which = $data{browse_which};
322 my $library_selected = $r->param('library_sets'); 374 my $library_selected = $r->param('library_sets');
323 my $set_selected = $r->param('local_sets'); 375 my $set_selected = $r->param('local_sets');
324 376
325 my ($dis1, $dis2, $dis3) = ("","",""); 377 my ($dis1, $dis2, $dis3) = ("","","");
326 $dis1 = '-disabled' if($browse_which eq 'browse_library'); 378 $dis1 = '-disabled' if($browse_which eq 'browse_library');
327 $dis2 = '-disabled' if($browse_which eq 'browse_local'); 379 $dis2 = '-disabled' if($browse_which eq 'browse_local');
328 $dis3 = '-disabled' if($browse_which eq 'browse_mysets'); 380 $dis3 = '-disabled' if($browse_which eq 'browse_mysets');
329 381
382 ## Make buttons for additional problem libraries
383 my $libs = '';
384 foreach my $lib (sort(keys(%problib))) {
385 $libs .= ' '. CGI::submit(-name=>"browse_$lib", -value=>$problib{$lib},
386 ($browse_which eq "browse_$lib")? '-disabled': '')
387 if (-d "$ce->{courseDirs}{templates}/$lib");
388 }
389 $libs = CGI::br()."or Problems from".$libs if $libs ne '';
390
330 my $these_widths = "width: 27ex"; 391 my $these_widths = "width: 20ex";
331 print CGI::Tr(CGI::td({-class=>"InfoPanel", -align=>"center"}, 392 print CGI::Tr(CGI::td({-class=>"InfoPanel", -align=>"center"},
393 "Browse ",
332 CGI::submit(-name=>"browse_library", -value=>"Browse Problem Library", -style=>$these_widths, $dis1), 394 CGI::submit(-name=>"browse_library", -value=>"Problem Library", -style=>$these_widths, $dis1),
333 CGI::submit(-name=>"browse_local", -value=>"Browse Local Problems", -style=>$these_widths, $dis2), 395 CGI::submit(-name=>"browse_local", -value=>"Local Problems", -style=>$these_widths, $dis2),
334 CGI::submit(-name=>"browse_mysets", -value=>"Browse From This Course", -style=>$these_widths, $dis3), 396 CGI::submit(-name=>"browse_mysets", -value=>"From This Course", -style=>$these_widths, $dis3),
335 )); 397 $libs,
398 ));
336 399
337 print CGI::Tr(CGI::td({-bgcolor=>"black"})); 400 print CGI::Tr(CGI::td({-bgcolor=>"black"}));
338 401
339 if ($browse_which eq 'browse_local') { 402 if ($browse_which eq 'browse_local') {
340 $self->browse_local_panel($library_selected); 403 $self->browse_local_panel($library_selected);
341 } elsif ($browse_which eq 'browse_mysets') { 404 } elsif ($browse_which eq 'browse_mysets') {
342 $self->browse_mysets_panel($library_selected, $list_of_local_sets); 405 $self->browse_mysets_panel($library_selected, $list_of_local_sets);
343 } else { 406 } elsif ($browse_which eq 'browse_library') {
344 $self->browse_library_panel(); 407 $self->browse_library_panel();
345 } 408 } else { ## handle other problem libraries
409 $self->browse_local_panel($library_selected,$browse_which);
410 }
346 411
347 print CGI::Tr(CGI::td({-bgcolor=>"black"})); 412 print CGI::Tr(CGI::td({-bgcolor=>"black"}));
348 413
349 if($have_local_sets ==0) { 414 if($have_local_sets ==0) {
350 $list_of_local_sets = [NO_LOCAL_SET_STRING]; 415 $list_of_local_sets = [NO_LOCAL_SET_STRING];
351 } elsif (not $set_selected or $set_selected eq SELECT_SET_STRING) { 416 } elsif (not $set_selected or $set_selected eq SELECT_SET_STRING) {
352 if ($list_of_local_sets->[0] eq "Select a Problem Set") { 417 if ($list_of_local_sets->[0] eq "Select a Homework Set") {
353 shift @{$list_of_local_sets}; 418 shift @{$list_of_local_sets};
354 } 419 }
355 unshift @{$list_of_local_sets}, SELECT_SET_STRING; 420 unshift @{$list_of_local_sets}, SELECT_SET_STRING;
356 $set_selected = SELECT_SET_STRING; 421 $set_selected = SELECT_SET_STRING;
357 } 422 }
358 my $myjs = 'document.mainform.selfassign.value=confirm("Should I assign the new set to you now?\nUse OK for yes and Cancel for no.");true;'; 423 my $myjs = 'document.mainform.selfassign.value=confirm("Should I assign the new set to you now?\nUse OK for yes and Cancel for no.");true;';
359 424
360 print CGI::Tr(CGI::td({-class=>"InfoPanel", -align=>"left"}, "Adding Problems to ", 425 print CGI::Tr(CGI::td({-class=>"InfoPanel", -align=>"left"}, "Adding Problems to ",
361 CGI::b("Target Set: "), 426 CGI::b("Target Set: "),
362 CGI::popup_menu(-name=> 'local_sets', 427 CGI::popup_menu(-name=> 'local_sets',
363 -values=>$list_of_local_sets, 428 -values=>$list_of_local_sets,
364 -default=> $set_selected), 429 -default=> $set_selected),
365 CGI::submit(-name=>"edit_local", -value=>"Edit Target Set"), 430 CGI::submit(-name=>"edit_local", -value=>"Edit Target Set"),
366 CGI::hidden(-name=>"selfassign", -default=>[0]). 431 CGI::hidden(-name=>"selfassign", -default=>[0]).
367 CGI::br(), 432 CGI::br(),
368 CGI::br(), 433 CGI::br(),
369 CGI::submit(-name=>"new_local_set", -value=>"Create a New Set in This Course:", 434 CGI::submit(-name=>"new_local_set", -value=>"Create a New Set in This Course:",
370 -onclick=>$myjs 435 -onclick=>$myjs
371 ), 436 ),
372 " ", 437 " ",
373 CGI::textfield(-name=>"new_set_name", 438 CGI::textfield(-name=>"new_set_name",
374 -default=>"Name for new set here", 439 -default=>"Name for new set here",
375 -override=>1, -size=>30), 440 -override=>1, -size=>30),
376 CGI::br(), 441 CGI::br(),
377 )); 442 ));
378 443
379 print CGI::Tr(CGI::td({-bgcolor=>"black"})); 444 print CGI::Tr(CGI::td({-bgcolor=>"black"}));
380 445
381 print CGI::Tr(CGI::td({-class=>"InfoPanel", -align=>"center"}, 446 print CGI::Tr(CGI::td({-class=>"InfoPanel", -align=>"center"},
382 CGI::start_table({-border=>"0"}), 447 CGI::start_table({-border=>"0"}),
383 CGI::Tr( CGI::td({ -align=>"center"}, 448 CGI::Tr( CGI::td({ -align=>"center"},
384 CGI::submit(-name=>"select_all", -style=>$these_widths, 449 CGI::submit(-name=>"select_all", -style=>$these_widths,
385 -value=>"Mark All For Adding"), 450 -value=>"Mark All For Adding"),
386 CGI::submit(-name=>"select_none", -style=>$these_widths, 451 CGI::submit(-name=>"select_none", -style=>$these_widths,
387 -value=>"Clear All Marks"), 452 -value=>"Clear All Marks"),
388 )), 453 )),
389 CGI::Tr( CGI::td( 454 CGI::Tr(CGI::td(
390 CGI::submit(-name=>"update", -style=>$these_widths. "; font-weight:bold", 455 CGI::submit(-name=>"update", -style=>$these_widths. "; font-weight:bold",
391 -value=>"Update"), 456 -value=>"Update"),
392 CGI::submit(-name=>"rerandomize", 457 CGI::submit(-name=>"rerandomize",
393 -style=>$these_widths, 458 -style=>$these_widths,
394 -value=>"Rerandomize"), 459 -value=>"Rerandomize"),
395 CGI::submit(-name=>"cleardisplay", 460 CGI::submit(-name=>"cleardisplay",
396 -style=>$these_widths, 461 -style=>$these_widths,
397 -value=>"Clear Problem Display") 462 -value=>"Clear Problem Display")
398 )), 463 )),
399 CGI::end_table())); 464 CGI::end_table()));
400
401} 465}
402 466
403sub make_data_row { 467sub make_data_row {
404 my $self = shift; 468 my $self = shift;
405 my $sourceFileName = shift; 469 my $sourceFileName = shift;
406 my $pg = shift; 470 my $pg = shift;
407 my $cnt = shift; 471 my $cnt = shift;
408 my $mark = shift || 0; 472 my $mark = shift || 0;
409 473
410 $sourceFileName =~ s|^./||; # clean up top ugliness 474 $sourceFileName =~ s|^./||; # clean up top ugliness
411 475
412 my $urlpath = $self->r->urlpath; 476 my $urlpath = $self->r->urlpath;
413 my $problem_output = $pg->{flags}->{error_flag} ? 477 my $problem_output = $pg->{flags}->{error_flag} ?
414 CGI::div({class=>"ResultsWithError"}, CGI::em("This problem produced an error")) 478 CGI::div({class=>"ResultsWithError"}, CGI::em("This problem produced an error"))
415 : CGI::div({class=>"RenderSolo"}, $pg->{body_text}); 479 : CGI::div({class=>"RenderSolo"}, $pg->{body_text});
416 480
417 481
418 my $edit_link = ''; 482 my $edit_link = '';
419 #if($self->{r}->param('browse_which') ne 'browse_library') { 483 #if($self->{r}->param('browse_which') ne 'browse_library') {
484 my $problem_seed = $self->{r}->param('problem_seed') || 0;
420 if($sourceFileName !~ /^Library\//) { 485 if($sourceFileName !~ /^Library\//) {
486 $edit_link = CGI::a({href=>$self->systemLink(
421 $edit_link = CGI::a({href=>$self->systemLink($urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor", 487 $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor",
422 courseID =>$urlpath->arg("courseID"), 488 courseID =>$urlpath->arg("courseID"),
423 setID=>"Undefined_Set", 489 setID=>"Undefined_Set",
424 problemID=>"1"), 490 problemID=>"1"),
425 params=>{sourceFilePath => "$sourceFileName"} 491 params=>{sourceFilePath => "$sourceFileName", problemSeed=> $problem_seed}
426 )}, "Edit it" ); 492 )}, "Edit it" );
427 } 493 }
428 494
429 my $try_link = CGI::a({href=>$self->systemLink($urlpath->newFromModule("WeBWorK::ContentGenerator::Problem", 495 my $try_link = CGI::a({href=>$self->systemLink(
496 $urlpath->newFromModule("WeBWorK::ContentGenerator::Problem",
430 courseID =>$urlpath->arg("courseID"), 497 courseID =>$urlpath->arg("courseID"),
431 setID=>"Undefined_Set", 498 setID=>"Undefined_Set",
432 problemID=>"1"), 499 problemID=>"1"),
500 params =>{
433 params =>{effectiveUser => $self->r->param('user'), 501 effectiveUser => scalar($self->r->param('user')),
434 editMode => "SetMaker", 502 editMode => "SetMaker",
503 problemSeed=> $problem_seed,
435 sourceFilePath => "$sourceFileName"} )}, "Try it"); 504 sourceFilePath => "$sourceFileName"
505 }
506 )}, "Try it");
436 507
437 my %add_box_data = ( -name=>"trial$cnt",-value=>1,-label=>"Add me to the current set on the next update"); 508 my %add_box_data = ( -name=>"trial$cnt",-value=>1,-label=>"Add this problem to the current set on the next update");
438 if($mark & SUCCESS) { 509 if($mark & SUCCESS) {
439 $add_box_data{ -label } .= " (just added this problem)"; 510 $add_box_data{ -label } .= " (just added this problem)";
440 } elsif($mark & ADDED) { 511 } elsif($mark & ADDED) {
441 $add_box_data{ -checked } = 1; 512 $add_box_data{ -checked } = 1;
442 } 513 }
443 514
444 print CGI::Tr({-align=>"left"}, CGI::td( 515 print CGI::Tr({-align=>"left"}, CGI::td(
445
446 CGI::div({-style=>"background-color: #DDDDDD; margin: 0px auto"}, 516 CGI::div({-style=>"background-color: #DDDDDD; margin: 0px auto"},
447CGI::span({-style=>"float:left ; text-align: left"},"File name: $sourceFileName "), 517 CGI::span({-style=>"float:left ; text-align: left"},"File name: $sourceFileName "),
448CGI::span({-style=>"float:right ; text-align: right"}, $edit_link, " ", $try_link) 518 CGI::span({-style=>"float:right ; text-align: right"}, $edit_link, " ", $try_link)
449 ), CGI::br(), 519 ), CGI::br(),
450
451
452
453
454 CGI::checkbox(-name=>"hideme$cnt",-value=>1,-label=>"Don't show me on the next update"), 520 CGI::checkbox(-name=>"hideme$cnt",-value=>1,-label=>"Don't show this problem on the next update"),
455 CGI::br(), 521 CGI::br(),
456 CGI::checkbox((%add_box_data)), 522 CGI::checkbox((%add_box_data)),
457 CGI::hidden(-name=>"filetrial$cnt", -default=>[$sourceFileName]). 523 CGI::hidden(-name=>"filetrial$cnt", -default=>[$sourceFileName]).
458 CGI::p($problem_output), 524 CGI::p($problem_output),
459 )); 525 ));
460} 526}
461 527
462 528
463sub pre_header_initialize { 529sub pre_header_initialize {
464 my ($self) = @_; 530 my ($self) = @_;
465 my $r = $self->r; 531 my $r = $self->r;
466 ## For all cases, lets set some things 532 ## For all cases, lets set some things
467 $self->{error}=0; 533 $self->{error}=0;
468 my $ce = $r->ce; 534 my $ce = $r->ce;
469 my $db = $r->db; 535 my $db = $r->db;
470 my $maxShown = $r->param('max_shown') || MAX_SHOW_DEFAULT; 536 my $maxShown = $r->param('max_shown') || MAX_SHOW_DEFAULT;
471 $maxShown = 10000000 if($maxShown eq 'All'); # let's hope there aren't more 537 $maxShown = 10000000 if($maxShown eq 'All'); # let's hope there aren't more
472 538
539 ## These directories will have individual buttons
540 %problib = %{$ce->{courseFiles}{problibs}} if $ce->{courseFiles}{problibs};
473 541
474 my $userName = $r->param('user'); 542 my $userName = $r->param('user');
475 my $user = $db->getUser($userName); # checked 543 my $user = $db->getUser($userName); # checked
476 die "record for user $userName (real user) does not exist." 544 die "record for user $userName (real user) does not exist."
477 unless defined $user; 545 unless defined $user;
478 my $authz = $r->authz; 546 my $authz = $r->authz;
479 unless ($authz->hasPermissions($userName, "modify_problem_sets")) { 547 unless ($authz->hasPermissions($userName, "modify_problem_sets")) {
480 return(""); # Error message already produced in the body 548 return(""); # Error message already produced in the body
481 } 549 }
482 550
483 ## Now one action we have to deal with here 551 ## Now one action we have to deal with here
484 if ($r->param('edit_local')) { 552 if ($r->param('edit_local')) {
485 my $urlpath = $r->urlpath; 553 my $urlpath = $r->urlpath;
486 my $db = $r->db; 554 my $db = $r->db;
487 my $checkset = $db->getGlobalSet($r->param('local_sets')); 555 my $checkset = $db->getGlobalSet($r->param('local_sets'));
488 if (not defined($checkset)) { 556 if (not defined($checkset)) {
489 $self->{error} = 1; 557 $self->{error} = 1;
490 $self->addbadmessage('You need to select a "Target Set" before you can edit it.'); 558 $self->addbadmessage('You need to select a "Target Set" before you can edit it.');
491 } else { 559 } else {
492 my $page = $urlpath->newFromModule('WeBWorK::ContentGenerator::Instructor::ProblemSetEditor', setID=>$r->param('local_sets'), courseID=>$urlpath->arg("courseID")); 560 my $page = $urlpath->newFromModule('WeBWorK::ContentGenerator::Instructor::ProblemSetDetail', setID=>$r->param('local_sets'), courseID=>$urlpath->arg("courseID"));
493 my $url = $self->systemLink($page); 561 my $url = $self->systemLink($page);
494 $self->reply_with_redirect($url); 562 $self->reply_with_redirect($url);
495 } 563 }
496 } 564 }
497 565
498 ## Next, lots of set up so that errors can be reported with message() 566 ## Next, lots of set up so that errors can be reported with message()
499 567
500 ############# List of problems we have already printed 568 ############# List of problems we have already printed
501 569
502 $self->{past_problems} = get_past_problem_files($r); 570 $self->{past_problems} = get_past_problem_files($r);
503 # if we don't end up reusing problems, this will be wiped out 571 # if we don't end up reusing problems, this will be wiped out
504 # if we do redisplay the same problems, we must adjust this accordingly 572 # if we do redisplay the same problems, we must adjust this accordingly
505 my @past_marks = map {$_->[1]} @{$self->{past_problems}}; 573 my @past_marks = map {$_->[1]} @{$self->{past_problems}};
506 my $none_shown = scalar(@{$self->{past_problems}})==0; 574 my $none_shown = scalar(@{$self->{past_problems}})==0;
507 my @pg_files=(); 575 my @pg_files=();
508 my $use_previous_problems = 1; 576 my $use_previous_problems = 1;
509 my $first_shown = $r->param('first_shown') || 0; 577 my $first_shown = $r->param('first_shown') || 0;
510 my $last_shown = $r->param('last_shown'); 578 my $last_shown = $r->param('last_shown');
511 if (not defined($last_shown)) { 579 if (not defined($last_shown)) {
512 $last_shown = -1; 580 $last_shown = -1;
513 } 581 }
514 my @all_past_list = (); # these are include requested, but not shown 582 my @all_past_list = (); # these are include requested, but not shown
515 my $j = 0; 583 my $j = 0;
516 while (defined($r->param("all_past_list$j"))) { 584 while (defined($r->param("all_past_list$j"))) {
517 push @all_past_list, $r->param("all_past_list$j"); 585 push @all_past_list, $r->param("all_past_list$j");
518 $j++; 586 $j++;
519 } 587 }
520 588
521 ############# Default of which problem selector to display 589 ############# Default of which problem selector to display
522 590
523 my $browse_which = $r->param('browse_which') || 'browse_local'; 591 my $browse_which = $r->param('browse_which') || 'browse_local';
524 592
525 my $problem_seed = $r->param('problem_seed') || 0; 593 my $problem_seed = $r->param('problem_seed') || 0;
526 $r->param('problem_seed', $problem_seed); # if it wasn't defined before 594 $r->param('problem_seed', $problem_seed); # if it wasn't defined before
527 595
596 ## check for problem lib buttons
597 my $browse_lib = '';
598 foreach my $lib (keys %problib) {
599 if ($r->param("browse_$lib")) {
600 $browse_lib = "browse_$lib";
601 last;
602 }
603 }
604
528 ########### Start the logic through if elsif elsif ... 605 ########### Start the logic through if elsif elsif ...
529 606
530 ##### Asked to browse certain problems 607 ##### Asked to browse certain problems
608 if ($browse_lib ne '') {
609 $browse_which = $browse_lib;
610 $r->param('library_sets', "");
611 $use_previous_problems = 0; @pg_files = (); ## clear old problems
531 if ($r->param('browse_library')) { 612 } elsif ($r->param('browse_library')) {
532 $browse_which = 'browse_library'; 613 $browse_which = 'browse_library';
533 $r->param('library_sets', ""); 614 $r->param('library_sets', "");
615 $use_previous_problems = 0; @pg_files = (); ## clear old problems
534 } elsif ($r->param('browse_local')) { 616 } elsif ($r->param('browse_local')) {
535 $browse_which = 'browse_local'; 617 $browse_which = 'browse_local';
536 $r->param('library_sets', ""); 618 $r->param('library_sets', "");
619 $use_previous_problems = 0; @pg_files = (); ## clear old problems
537 } elsif ($r->param('browse_mysets')) { 620 } elsif ($r->param('browse_mysets')) {
538 $browse_which = 'browse_mysets'; 621 $browse_which = 'browse_mysets';
539 $r->param('library_sets', ""); 622 $r->param('library_sets', "");
623 $use_previous_problems = 0; @pg_files = (); ## clear old problems
540 624
541 ##### Change the seed value 625 ##### Change the seed value
542 626
543 } elsif ($r->param('rerandomize')) { 627 } elsif ($r->param('rerandomize')) {
544 $problem_seed++; 628 $problem_seed++;
545 $r->param('problem_seed', $problem_seed); 629 $r->param('problem_seed', $problem_seed);
546 $self->addbadmessage('Changing the problem seed for display, but there are no problems showing.') if $none_shown; 630 $self->addbadmessage('Changing the problem seed for display, but there are no problems showing.') if $none_shown;
547 631
548 ##### Clear the display 632 ##### Clear the display
549 633
550 } elsif ($r->param('cleardisplay')) { 634 } elsif ($r->param('cleardisplay')) {
551 @pg_files = (); 635 @pg_files = ();
552 $use_previous_problems=0; 636 $use_previous_problems=0;
553 $self->addbadmessage('The display was already cleared.') if $none_shown; 637 $self->addbadmessage('The display was already cleared.') if $none_shown;
554 638
555 ##### View problems selected from the local list 639 ##### View problems selected from the local list
556 640
557 } elsif ($r->param('view_local_set')) { 641 } elsif ($r->param('view_local_set')) {
558 642
559 my $set_to_display = $r->param('library_sets'); 643 my $set_to_display = $r->param('library_sets');
560 if (not defined($set_to_display) or $set_to_display eq SELECT_LOCAL_STRING or $set_to_display eq "Found no directories containing problems") { 644 if (not defined($set_to_display) or $set_to_display eq SELECT_LOCAL_STRING or $set_to_display eq "Found no directories containing problems") {
561 $self->addbadmessage('You need to select a set to view.'); 645 $self->addbadmessage('You need to select a set to view.');
562 } else { 646 } else {
563 $set_to_display = '.' if $set_to_display eq ' My Problems '; 647 $set_to_display = '.' if $set_to_display eq MY_PROBLEMS;
648 $set_to_display = substr($browse_which,7) if $set_to_display eq MAIN_PROBLEMS;
564 @pg_files = list_pg_files($ce->{courseDirs}->{templates}, 649 @pg_files = list_pg_files($ce->{courseDirs}->{templates},
565 "$set_to_display"); 650 "$set_to_display");
566 $use_previous_problems=0; 651 $use_previous_problems=0;
567 } 652 }
568 653
569 ##### View problems selected from the a set in this course 654 ##### View problems selected from the a set in this course
570 655
571 } elsif ($r->param('view_mysets_set')) { 656 } elsif ($r->param('view_mysets_set')) {
572 657
573 my $set_to_display = $r->param('library_sets'); 658 my $set_to_display = $r->param('library_sets');
574 if (not defined($set_to_display) 659 if (not defined($set_to_display)
575 or $set_to_display eq "Select a Problem Set" 660 or $set_to_display eq "Select a Homework Set"
576 or $set_to_display eq NO_LOCAL_SET_STRING) { 661 or $set_to_display eq NO_LOCAL_SET_STRING) {
577 $self->addbadmessage("You need to select a set from this course to view."); 662 $self->addbadmessage("You need to select a set from this course to view.");
578 } else { 663 } else {
579 my @problemList = $db->listGlobalProblems($set_to_display); 664 my @problemList = $db->listGlobalProblems($set_to_display);
580 my $problem; 665 my $problem;
581 @pg_files=(); 666 @pg_files=();
582 for $problem (@problemList) { 667 for $problem (@problemList) {
583 my $problemRecord = $db->getGlobalProblem($set_to_display, $problem); # checked 668 my $problemRecord = $db->getGlobalProblem($set_to_display, $problem); # checked
584 die "global $problem for set $set_to_display not found." unless 669 die "global $problem for set $set_to_display not found." unless
585 $problemRecord; 670 $problemRecord;
586 push @pg_files, $problemRecord->source_file; 671 push @pg_files, $problemRecord->source_file;
587 672
588 } 673 }
589 $use_previous_problems=0; 674 $use_previous_problems=0;
590 } 675 }
591 676
592 ##### View whole chapter from the library 677 ##### View whole chapter from the library
593 ## This will change somewhat later 678 ## This will change somewhat later
594 679
595 } elsif ($r->param('lib_view')) { 680 } elsif ($r->param('lib_view')) {
596 681
597 @pg_files=(); 682 @pg_files=();
598 my $chap = $r->param('library_chapters') || ""; 683 my $chap = $r->param('library_chapters') || "";
599 $chap = "" if($chap eq "All Chapters"); 684 $chap = "" if($chap eq "All Chapters");
600 my $sect = $r->param('library_sections') || ""; 685 my $sect = $r->param('library_sections') || "";
601 $sect = "" if($sect eq "All Sections"); 686 $sect = "" if($sect eq "All Sections");
602 my @dbsearch = WeBWorK::Utils::ListingDB::getSectionListings($r->{ce}, "$chap", "$sect"); 687 my @dbsearch = WeBWorK::Utils::ListingDB::getSectionListings($r->{ce}, "$chap", "$sect");
603 my ($result, $tolibpath); 688 my ($result, $tolibpath);
604 for $result (@dbsearch) { 689 for $result (@dbsearch) {
605 $tolibpath = "Library/$result->{path}/$result->{filename}"; 690 $tolibpath = "Library/$result->{path}/$result->{filename}";
606 691
607 ## Too clunky!!!! 692 ## Too clunky!!!!
608 push @pg_files, $tolibpath; 693 push @pg_files, $tolibpath;
609 } 694 }
610 $use_previous_problems=0; 695 $use_previous_problems=0;
611 696
612 ##### Edit the current local problem set 697 ##### Edit the current local problem set
613 698
614 } elsif ($r->param('edit_local')) { ## Jump to set edit page 699 } elsif ($r->param('edit_local')) { ## Jump to set edit page
615 700
616 ; # already handled 701 ; # already handled
617 702
618 703
619 ##### Make a new local problem set 704 ##### Make a new local problem set
620 705
621 } elsif ($r->param('new_local_set')) { 706 } elsif ($r->param('new_local_set')) {
622 if ($r->param('new_set_name') !~ /^[\w.-]*$/) { 707 if ($r->param('new_set_name') !~ /^[\w.-]*$/) {
623 $self->addbadmessage("The name ".$r->param('new_set_name')." is not a valid set name. Use only letters, digits, -, _, and ."); 708 $self->addbadmessage("The name ".$r->param('new_set_name')." is not a valid set name. Use only letters, digits, -, _, and .");
624 } else { 709 } else {
625 my $newSetName = $r->param('new_set_name'); 710 my $newSetName = $r->param('new_set_name');
626 $newSetName =~ s/^set//; 711 # if we want to munge the input set name, do it here
627 $newSetName =~ s/\.def$//;
628 $r->param('local_sets',$newSetName); 712 $r->param('local_sets',$newSetName);
629 my $newSetRecord = $db->getGlobalSet($newSetName); 713 my $newSetRecord = $db->getGlobalSet($newSetName);
630 if (defined($newSetRecord)) { 714 if (defined($newSetRecord)) {
631 $self->addbadmessage("The set name $newSetName is already in use. Pick a different name if you would like to start a new set."); 715 $self->addbadmessage("The set name $newSetName is already in use. Pick a different name if you would like to start a new set.");
632 } else { # Do it! 716 } else { # Do it!
633 $newSetRecord = $db->{set}->{record}->new(); 717 $newSetRecord = $db->{set}->{record}->new();
634 $newSetRecord->set_id($newSetName); 718 $newSetRecord->set_id($newSetName);
635 $newSetRecord->set_header(""); 719 $newSetRecord->set_header("");
636 $newSetRecord->problem_header(""); 720 $newSetRecord->hardcopy_header("");
637 $newSetRecord->open_date(time()+60*60*24*7); # in one week 721 $newSetRecord->open_date(time()+60*60*24*7); # in one week
638 $newSetRecord->due_date(time()+60*60*24*7*2); # in two weeks 722 $newSetRecord->due_date(time()+60*60*24*7*2); # in two weeks
639 $newSetRecord->answer_date(time()+60*60*24*7*3); # in three weeks 723 $newSetRecord->answer_date(time()+60*60*24*7*3); # in three weeks
640 eval {$db->addGlobalSet($newSetRecord)}; 724 eval {$db->addGlobalSet($newSetRecord)};
641 $self->addgoodmessage("Set $newSetName has been created."); 725 $self->addgoodmessage("Set $newSetName has been created.");
642 my $selfassign = $r->param('selfassign') || ""; 726 my $selfassign = $r->param('selfassign') || "";
643 $selfassign = "" if($selfassign =~ /false/i); # deal with javascript false 727 $selfassign = "" if($selfassign =~ /false/i); # deal with javascript false
644 if($selfassign) { 728 if($selfassign) {
645 $self->assignSetToUser($userName, $newSetRecord); 729 $self->assignSetToUser($userName, $newSetRecord);
646 $self->addgoodmessage("Set $newSetName was assigned to $userName."); 730 $self->addgoodmessage("Set $newSetName was assigned to $userName.");
647 } 731 }
648 } 732 }
649 } 733 }
650 734
651 ##### Add selected problems to the current local set 735 ##### Add selected problems to the current local set
652 736
653 } elsif ($r->param('update')) { 737 } elsif ($r->param('update')) {
654 ## first handle problems to be added before we hide them 738 ## first handle problems to be added before we hide them
655 my($localSet, @selected); 739 my($localSet, @selected);
656 740
657 @pg_files = grep {($_->[1] & ADDED) != 0 } @{$self->{past_problems}}; 741 @pg_files = grep {($_->[1] & ADDED) != 0 } @{$self->{past_problems}};
658 @selected = map {$_->[0]} @pg_files; 742 @selected = map {$_->[0]} @pg_files;
659 743
660 my @action_files = grep {$_->[1] > 0 } @{$self->{past_problems}}; 744 my @action_files = grep {$_->[1] > 0 } @{$self->{past_problems}};
661 # There are now good reasons to do an update without selecting anything. 745 # There are now good reasons to do an update without selecting anything.
662 #if(scalar(@action_files) == 0) { 746 #if(scalar(@action_files) == 0) {
663 # $self->addbadmessage('Update requested, but no problems were marked.'); 747 # $self->addbadmessage('Update requested, but no problems were marked.');
664 #} 748 #}
665 749
666 if (scalar(@selected)>0) { # if some are to be added, they need a place to go 750 if (scalar(@selected)>0) { # if some are to be added, they need a place to go
667 $localSet = $r->param('local_sets'); 751 $localSet = $r->param('local_sets');
668 if (not defined($localSet) or 752 if (not defined($localSet) or
669 $localSet eq SELECT_SET_STRING or 753 $localSet eq SELECT_SET_STRING or
670 $localSet eq NO_LOCAL_SET_STRING) { 754 $localSet eq NO_LOCAL_SET_STRING) {
671 $self->addbadmessage('You are trying to add problems to something, but you did not select a "Target Set" name as a target.'); 755 $self->addbadmessage('You are trying to add problems to something, but you did not select a "Target Set" name as a target.');
672 } else { 756 } else {
673 my $newSetRecord = $db->getGlobalSet($localSet); 757 my $newSetRecord = $db->getGlobalSet($localSet);
674 if (not defined($newSetRecord)) { 758 if (not defined($newSetRecord)) {
675 $self->addbadmessage("You are trying to add problems to $localSet, but that set does not seem to exist! I bet you used your \"Back\" button."); 759 $self->addbadmessage("You are trying to add problems to $localSet, but that set does not seem to exist! I bet you used your \"Back\" button.");
676 } else { 760 } else {
677 my $addcount = add_selected($self, $db, $localSet); 761 my $addcount = add_selected($self, $db, $localSet);
678 if($addcount > 0) { 762 if($addcount > 0) {
679 $self->addgoodmessage("Added $addcount problem".(($addcount>1)?'s':''). 763 $self->addgoodmessage("Added $addcount problem".(($addcount>1)?'s':'').
680 " to $localSet."); 764 " to $localSet.");
681 }
682 } 765 }
683 } 766 }
684 } 767 }
768 }
685 ## now handle problems to be hidden 769 ## now handle problems to be hidden
686 770
687 ## only keep the ones which are not hidden 771 ## only keep the ones which are not hidden
688 @pg_files = grep {($_->[1] & HIDDEN) ==0 } @{$self->{past_problems}}; 772 @pg_files = grep {($_->[1] & HIDDEN) ==0 } @{$self->{past_problems}};
689 @past_marks = map {$_->[1]} @pg_files; 773 @past_marks = map {$_->[1]} @pg_files;
690 @pg_files = map {$_->[0]} @pg_files; 774 @pg_files = map {$_->[0]} @pg_files;
691 @all_past_list = (@all_past_list[0..($first_shown-1)], 775 @all_past_list = (@all_past_list[0..($first_shown-1)],
692 @pg_files, 776 @pg_files,
693 @all_past_list[($last_shown+1)..(scalar(@all_past_list)-1)]); 777 @all_past_list[($last_shown+1)..(scalar(@all_past_list)-1)]);
694 $last_shown = $first_shown+$maxShown -1; 778 $last_shown = $first_shown+$maxShown -1;
695 $last_shown = (scalar(@all_past_list)-1) if($last_shown>=scalar(@all_past_list)); 779 $last_shown = (scalar(@all_past_list)-1) if($last_shown>=scalar(@all_past_list));
696 780
697 } elsif ($r->param('next_page')) { 781 } elsif ($r->param('next_page')) {
698 $first_shown = $last_shown+1; 782 $first_shown = $last_shown+1;
699 $last_shown = $first_shown+$maxShown-1; 783 $last_shown = $first_shown+$maxShown-1;
700 $last_shown = (scalar(@all_past_list)-1) if($last_shown>=scalar(@all_past_list)); 784 $last_shown = (scalar(@all_past_list)-1) if($last_shown>=scalar(@all_past_list));
701 @past_marks = (); 785 @past_marks = ();
702 } elsif ($r->param('prev_page')) { 786 } elsif ($r->param('prev_page')) {
703 $last_shown = $first_shown-1; 787 $last_shown = $first_shown-1;
704 $first_shown = $last_shown - $maxShown+1; 788 $first_shown = $last_shown - $maxShown+1;
705 789
706 $first_shown = 0 if($first_shown<0); 790 $first_shown = 0 if($first_shown<0);
707 @past_marks = (); 791 @past_marks = ();
708 792
709 } elsif ($r->param('select_all')) { 793 } elsif ($r->param('select_all')) {
710 @past_marks = map {1} @past_marks; 794 @past_marks = map {1} @past_marks;
711 } elsif ($r->param('select_none')) { 795 } elsif ($r->param('select_none')) {
712 @past_marks = (); 796 @past_marks = ();
713 797
714 ##### No action requested, probably our first time here 798 ##### No action requested, probably our first time here
715 799
716 } else { 800 } else {
717 #my $c = $r->connection; 801 #my $c = $r->connection;
718 #print "Debug info: ". $r->get_remote_host ."<p>". $c->remote_ip ; 802 #print "Debug info: ". $r->get_remote_host ."<p>". $c->remote_ip ;
719 ; 803 ;
720 } ##### end of the if elsif ... 804 } ##### end of the if elsif ...
721 805
722 806
723 ############# List of local sets 807 ############# List of local sets
724 808
725 my @all_set_defs = $db->listGlobalSets; 809 my @all_set_defs = $db->listGlobalSets;
726 for ($j=0; $j<scalar(@all_set_defs); $j++) { 810 @all_set_defs = sortByName(undef, @all_set_defs);
727 $all_set_defs[$j] =~ s|^set||;
728 $all_set_defs[$j] =~ s|\.def||;
729 }
730 811
731 if ($use_previous_problems) { 812 if ($use_previous_problems) {
732 @pg_files = @all_past_list; 813 @pg_files = @all_past_list;
733 } else { 814 } else {
734 $first_shown = 0; 815 $first_shown = 0;
735 $last_shown = scalar(@pg_files)<$maxShown ? scalar(@pg_files) : $maxShown; 816 $last_shown = scalar(@pg_files)<$maxShown ? scalar(@pg_files) : $maxShown;
736 $last_shown--; # to make it an array index 817 $last_shown--; # to make it an array index
737 @past_marks = (); 818 @past_marks = ();
738 } 819 }
739 ############# Now store data in self for retreival by body 820 ############# Now store data in self for retreival by body
740 $self->{first_shown} = $first_shown; 821 $self->{first_shown} = $first_shown;
741 $self->{last_shown} = $last_shown; 822 $self->{last_shown} = $last_shown;
742 $self->{browse_which} = $browse_which; 823 $self->{browse_which} = $browse_which;
743 $self->{problem_seed} = $problem_seed; 824 $self->{problem_seed} = $problem_seed;
744 $self->{pg_files} = \@pg_files; 825 $self->{pg_files} = \@pg_files;
745 $self->{past_marks} = \@past_marks; 826 $self->{past_marks} = \@past_marks;
746 $self->{all_set_defs} = \@all_set_defs; 827 $self->{all_set_defs} = \@all_set_defs;
747 828
748} 829}
749 830
750 831
751sub title { 832sub title {
752 return "Problem Set Maker"; 833 return "Homework Set Maker";
753} 834}
754 835
755sub body { 836sub body {
756 my ($self) = @_; 837 my ($self) = @_;
757 838
758 my $r = $self->r; 839 my $r = $self->r;
759 my $ce = $r->ce; # course environment 840 my $ce = $r->ce; # course environment
760 my $db = $r->db; # database 841 my $db = $r->db; # database
761 my $j; # garden variety counter 842 my $j; # garden variety counter
762 843
763 my $userName = $r->param('user'); 844 my $userName = $r->param('user');
764 845
765 my $user = $db->getUser($userName); # checked 846 my $user = $db->getUser($userName); # checked
766 die "record for user $userName (real user) does not exist." 847 die "record for user $userName (real user) does not exist."
767 unless defined $user; 848 unless defined $user;
768 849
769 ### Check that this is a professor 850 ### Check that this is a professor
770 my $authz = $r->authz; 851 my $authz = $r->authz;
771 unless ($authz->hasPermissions($userName, "modify_problem_sets")) { 852 unless ($authz->hasPermissions($userName, "modify_problem_sets")) {
772 print "User $userName returned " . 853 print "User $userName returned " .
773 $authz->hasPermissions($user, "modify_problem_sets") . 854 $authz->hasPermissions($user, "modify_problem_sets") .
774 " for permission"; 855 " for permission";
775 return(CGI::div({class=>'ResultsWithError'}, 856 return(CGI::div({class=>'ResultsWithError'},
776 CGI::em("You are not authorized to access the Instructor tools."))); 857 CGI::em("You are not authorized to access the Instructor tools.")));
777 } 858 }
778 859
779 ########## Extract information computed in pre_header_initialize 860 ########## Extract information computed in pre_header_initialize
780 861
781 my $first_shown = $self->{first_shown}; 862 my $first_shown = $self->{first_shown};
782 my $last_shown = $self->{last_shown}; 863 my $last_shown = $self->{last_shown};
783 my $browse_which = $self->{browse_which}; 864 my $browse_which = $self->{browse_which};
784 my $problem_seed = $self->{problem_seed}; 865 my $problem_seed = $self->{problem_seed};
785 my @pg_files = @{$self->{pg_files}}; 866 my @pg_files = @{$self->{pg_files}};
786 my @all_set_defs = @{$self->{all_set_defs}}; 867 my @all_set_defs = @{$self->{all_set_defs}};
787 868
788 my @pg_html=($last_shown>=$first_shown) ? 869 my @pg_html=($last_shown>=$first_shown) ?
789 renderProblems(r=> $r, 870 renderProblems(r=> $r,
790 user => $user, 871 user => $user,
791 problem_list => [@pg_files[$first_shown..$last_shown]], 872 problem_list => [@pg_files[$first_shown..$last_shown]],
792 displayMode => $r->param('mydisplayMode')) : (); 873 displayMode => $r->param('mydisplayMode')) : ();
793 874
794 ########## Top part 875 ########## Top part
795 print CGI::startform({-method=>"POST", -action=>$r->uri, -name=>'mainform'}), 876 print CGI::startform({-method=>"POST", -action=>$r->uri, -name=>'mainform'}),
796 $self->hidden_authen_fields, 877 $self->hidden_authen_fields,
797 '<div align="center">', 878 '<div align="center">',
798 CGI::start_table({-border=>2}); 879 CGI::start_table({-border=>2});
799 $self->make_top_row('all_set_defs'=>\@all_set_defs, 880 $self->make_top_row('all_set_defs'=>\@all_set_defs,
800 'browse_which'=> $browse_which); 881 'browse_which'=> $browse_which);
801 print CGI::hidden(-name=>'browse_which', -default=>[$browse_which]), 882 print CGI::hidden(-name=>'browse_which', -default=>[$browse_which]),
802 CGI::hidden(-name=>'problem_seed', -default=>[$problem_seed]); 883 CGI::hidden(-name=>'problem_seed', -default=>[$problem_seed]);
803 for ($j = 0 ; $j < scalar(@pg_files) ; $j++) { 884 for ($j = 0 ; $j < scalar(@pg_files) ; $j++) {
804 print CGI::hidden(-name=>"all_past_list$j", -default=>$pg_files[$j]); 885 print CGI::hidden(-name=>"all_past_list$j", -default=>$pg_files[$j]);
805 } 886 }
806 887
807 print CGI::hidden(-name=>'first_shown', -default=>[$first_shown]); 888 print CGI::hidden(-name=>'first_shown', -default=>[$first_shown]);
808 print CGI::hidden(-name=>'last_shown', -default=>[$last_shown]); 889 print CGI::hidden(-name=>'last_shown', -default=>[$last_shown]);
809 890
810 891
811 ########## Now print problems 892 ########## Now print problems
812 my $jj; 893 my $jj;
813 for ($jj=0; $jj<scalar(@pg_html); $jj++) { 894 for ($jj=0; $jj<scalar(@pg_html); $jj++) {
814 $pg_files[$jj] =~ s|^$ce->{courseDirs}->{templates}/?||; 895 $pg_files[$jj] =~ s|^$ce->{courseDirs}->{templates}/?||;
815 $self->make_data_row($pg_files[$jj+$first_shown], $pg_html[$jj], $jj+1, $self->{past_marks}->[$jj]); 896 $self->make_data_row($pg_files[$jj+$first_shown], $pg_html[$jj], $jj+1, $self->{past_marks}->[$jj]);
816 } 897 }
817 898
818 ########## Finish things off 899 ########## Finish things off
819 print CGI::end_table(); 900 print CGI::end_table();
820 print '</div>'; 901 print '</div>';
821 # if($first_shown>0 or (1+$last_shown)<scalar(@pg_files)) { 902 # if($first_shown>0 or (1+$last_shown)<scalar(@pg_files)) {
822 my ($next_button, $prev_button) = ("", ""); 903 my ($next_button, $prev_button) = ("", "");
823 if ($first_shown > 0) { 904 if ($first_shown > 0) {
824 $prev_button = CGI::submit(-name=>"prev_page", -style=>"width:15ex", 905 $prev_button = CGI::submit(-name=>"prev_page", -style=>"width:15ex",
825 -value=>"Previous page"); 906 -value=>"Previous page");
826 } 907 }
827 if ((1+$last_shown)<scalar(@pg_files)) { 908 if ((1+$last_shown)<scalar(@pg_files)) {
828 $next_button = CGI::submit(-name=>"next_page", -style=>"width:15ex", 909 $next_button = CGI::submit(-name=>"next_page", -style=>"width:15ex",
829 -value=>"Next page"); 910 -value=>"Next page");
830 } 911 }
831 if (scalar(@pg_files)>0) { 912 if (scalar(@pg_files)>0) {
832 print CGI::p(($first_shown+1)."-".($last_shown+1)." of ".scalar(@pg_files). 913 print CGI::p(($first_shown+1)."-".($last_shown+1)." of ".scalar(@pg_files).
833 " shown.", $prev_button, " ", $next_button); 914 " shown.", $prev_button, " ", $next_button);
834 } 915 }
835 # } 916 # }
836 print CGI::endform(), "\n"; 917 print CGI::endform(), "\n";
837 918
838 return ""; 919 return "";
839} 920}
840 921
841############################################## End of Body 922############################################## End of Body
842 923
843# SKEL: To emit your own HTTP header, uncomment this: 924# SKEL: To emit your own HTTP header, uncomment this:
844# 925#
845#sub header { 926#sub header {
846# my ($self) = @_; 927# my ($self) = @_;
847# 928#
848# # Generate your HTTP header here. 929# # Generate your HTTP header here.
849# 930#
850# # If you return something, it will be used as the HTTP status code for this 931# # If you return something, it will be used as the HTTP status code for this
851# # request. The Apache::Constants module might be useful for gerating status 932# # request. The Apache::Constants module might be useful for gerating status
852# # codes. If you don't return anything, the status code "OK" will be used. 933# # codes. If you don't return anything, the status code "OK" will be used.
853# return ""; 934# return "";
854#} 935#}
855 936
856# SKEL: If you need to do any processing after the HTTP header is sent, but before 937# SKEL: If you need to do any processing after the HTTP header is sent, but before
857# any template processing occurs, or you need to calculate values that will be 938# any template processing occurs, or you need to calculate values that will be
858# used in multiple methods, do it in this method: 939# used in multiple methods, do it in this method:
862#} 943#}
863 944
864# SKEL: If you need to add tags to the document <HEAD>, uncomment this method: 945# SKEL: If you need to add tags to the document <HEAD>, uncomment this method:
865# 946#
866#sub head { 947#sub head {
867# my ($self) = @_; 948# my ($self) = @_;
868# 949#
869# # You can print head tags here, like <META>, <SCRIPT>, etc. 950# # You can print head tags here, like <META>, <SCRIPT>, etc.
870# 951#
871# return ""; 952# return "";
872#} 953#}
873 954
874# SKEL: To fill in the "info" box (to the right of the main body), use this 955# SKEL: To fill in the "info" box (to the right of the main body), use this
875# method: 956# method:
876# 957#
877#sub info { 958#sub info {
878# my ($self) = @_; 959# my ($self) = @_;
879# 960#
880# # Print HTML here. 961# # Print HTML here.
881# 962#
882# return ""; 963# return "";
883#} 964#}
884 965
885# SKEL: To provide navigation links, use this method: 966# SKEL: To provide navigation links, use this method:
886# 967#
887#sub nav { 968#sub nav {
888# my ($self, $args) = @_; 969# my ($self, $args) = @_;
889# 970#
890# # See the documentation of path() and pathMacro() in 971# # See the documentation of path() and pathMacro() in
891# # WeBWorK::ContentGenerator for more information. 972# # WeBWorK::ContentGenerator for more information.
892# 973#
893# return ""; 974# return "";
894#} 975#}
895 976
896# SKEL: For a little box for display options, etc., use this method: 977# SKEL: For a little box for display options, etc., use this method:
897# 978#
898#sub options { 979#sub options {
899# my ($self) = @_; 980# my ($self) = @_;
900# 981#
901# # Print HTML here. 982# # Print HTML here.
902# 983#
903# return ""; 984# return "";
904#} 985#}
905 986
906# SKEL: For a list of sibling objects, use this method: 987# SKEL: For a list of sibling objects, use this method:
907# 988#
908#sub siblings { 989#sub siblings {
909# my ($self, $args) = @_; 990# my ($self, $args) = @_;
910# 991#
911# # See the documentation of siblings() and siblingsMacro() in 992# # See the documentation of siblings() and siblingsMacro() in
912# # WeBWorK::ContentGenerator for more information. 993# # WeBWorK::ContentGenerator for more information.
913# # 994# #
914# # Refer to implementations in ProblemSet and Problem. 995# # Refer to implementations in ProblemSet and Problem.
915# 996#
916# return ""; 997# return "";
917#} 998#}
918 999
919=head1 AUTHOR 1000=head1 AUTHOR
920 1001
921Written by John Jones, jj (at) asu.edu. 1002Written by John Jones, jj (at) asu.edu.

Legend:
Removed from v.2283  
changed lines
  Added in v.3379

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9