Index: openacs-4/packages/xowf/xowf.info =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/xowf.info,v diff -u -N -r1.12.2.64 -r1.12.2.65 --- openacs-4/packages/xowf/xowf.info 3 Jan 2022 16:00:36 -0000 1.12.2.64 +++ openacs-4/packages/xowf/xowf.info 9 Jan 2022 20:46:52 -0000 1.12.2.65 @@ -10,18 +10,18 @@ t xowf - + Gustaf Neumann XoWiki Content Flow - an XoWiki based workflow system implementing state-based behavior of wiki pages and forms 2021-09-15 WU Vienna BSD-Style 2 - + - - + + Index: openacs-4/packages/xowf/catalog/xowf.de_DE.ISO-8859-1.xml =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/catalog/xowf.de_DE.ISO-8859-1.xml,v diff -u -N -r1.2.2.66 -r1.2.2.67 --- openacs-4/packages/xowf/catalog/xowf.de_DE.ISO-8859-1.xml 3 Jan 2022 16:00:36 -0000 1.2.2.66 +++ openacs-4/packages/xowf/catalog/xowf.de_DE.ISO-8859-1.xml 9 Jan 2022 20:46:52 -0000 1.2.2.67 @@ -180,6 +180,8 @@ Zeige Studierenden w�hrend der Pr�fung eigene IP-Adresse Zeitbudget Erh�he die Bearbeitungsdauer auf den angef�hrten relativen Wert + Countdown Zeitmesser mit akkustischem Signal + Inkludiere optionales Audiosignal f�r den Zeitmesser (ungeeigenet f�r Pr�fungen in Schulungsr�umen) Beschr�nkte Fragenzahl Beschr�nke die Zahl der Fragen auf @@ -213,8 +215,8 @@ Detaileinstellungen Zeitablauf - Fragen - Sicherheit + Fragenenstellungen + Sicherheitseinstellungen Optionen f�r Online-Beaufsichtigung Detailangaben, welche Funktionen der Online-Beaufsichtigung genutzt werden sollen Aufnahmen speichern @@ -264,5 +266,6 @@ Benotungsschema Das Benotungsschema definiertdie Notengrenzen und die Rundungsregeln, um von dem erreichten Ergebnis (in Prozenten) eine Note abzuleiten Keine Notenvergabe + Einstellungen Index: openacs-4/packages/xowf/catalog/xowf.en_US.ISO-8859-1.xml =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/catalog/xowf.en_US.ISO-8859-1.xml,v diff -u -N -r1.2.2.73 -r1.2.2.74 --- openacs-4/packages/xowf/catalog/xowf.en_US.ISO-8859-1.xml 3 Jan 2022 16:00:36 -0000 1.2.2.73 +++ openacs-4/packages/xowf/catalog/xowf.en_US.ISO-8859-1.xml 9 Jan 2022 20:46:52 -0000 1.2.2.74 @@ -239,7 +239,7 @@ Detailed Configuration Time Management Question Management - Security + Security Configuration Proctoring Options Options to tailor proctoring behavior Record proctoring @@ -292,5 +292,6 @@ Grading Scheme The grading scheme defines grade boundaries and rounding rules for determining grades from achieved percentages No grading - + Configuration + Index: openacs-4/packages/xowf/lib/inclass-exam-answer.wf =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/lib/inclass-exam-answer.wf,v diff -u -N -r1.1.2.53 -r1.1.2.54 --- openacs-4/packages/xowf/lib/inclass-exam-answer.wf 3 Jan 2022 16:00:36 -0000 1.1.2.53 +++ openacs-4/packages/xowf/lib/inclass-exam-answer.wf 9 Jan 2022 20:46:52 -0000 1.1.2.54 @@ -19,7 +19,7 @@ # question manager. Use a different delegation instance in case of # strong customization. # -set :QM ::xowf::test_item::question_manager +:forward QM ::xowf::test_item::question_manager ######################################################################## @@ -241,20 +241,20 @@ # In case shuffling is required, fetch via the shuffled position. # set shuffle_id [expr {[$parent_obj property shuffle_items 0] ? [$obj creation_user] : -1}] - set position [${:QM} shuffled_index -shuffle_id $shuffle_id $parent_obj $item_nr] + set position [:QM shuffled_index -shuffle_id $shuffle_id $parent_obj $item_nr] # # Load the form. # - set form_obj [${:QM} nth_question_obj $parent_obj $position] + set form_obj [:QM nth_question_obj $parent_obj $position] # # Substitute markup in the constant part of the form in the context # of the original form object, setting the resolve context in the # background to be able to refer to links relative to the from, when # it was created. # - set d [${:QM} item_substitute_markup \ + set d [:QM item_substitute_markup \ -obj $obj \ -position $item_nr \ -form_obj $form_obj] @@ -291,7 +291,7 @@ # foreach p {paste spellcheck} { if {![$parent_obj property allow_$p true]} { - ${:QM} disallow_$p $form_obj + :QM disallow_$p $form_obj } } #$form_obj lappend form_constraints {__item_nr:hidden} @@ -330,7 +330,7 @@ -anon_instances t \ ] } - #ns_log notice "==================================== done_form_loader DONE" + #ns_log notice "==================================== done_form_loader DONE" return $result } @@ -351,7 +351,7 @@ set parent_obj [::xo::db::CrClass get_instance_from_db -item_id [$obj parent_id]] if {$for_question && [$obj state] in {initial working}} { if {$form_info eq ""} { - set form_info [${:QM} nth_question_form \ + set form_info [:QM nth_question_form \ -with_numbers \ -with_title=false \ -with_minutes=$with_minutes \ @@ -375,8 +375,8 @@ :plain_template -prevent_multiple_tabs $prevent_multiple_tabs $obj if {[$parent_obj state] eq "published" && [$obj state] ne "done"} { - set base_time [${:QM} exam_base_time -manager $parent_obj -answer_obj $obj] - set target_time [${:QM} exam_target_time \ + set base_time [:QM exam_base_time -manager $parent_obj -answer_obj $obj] + set target_time [:QM exam_target_time \ -manager $parent_obj \ -base_time $base_time \ ] @@ -428,7 +428,7 @@ #:msg "summary_form_loader $form_title /$form_objs/ [$obj instance_attributes]" set shuffle_id [expr {[$parent_obj property shuffle_items 0] ? [$obj creation_user] : -1}] - set form_info [${:QM} combined_question_form \ + set form_info [:QM combined_question_form \ -with_numbers \ -with_title \ -with_points \ @@ -439,7 +439,7 @@ $parent_obj] #ns_log notice "SUMMARY FORM shuffle_id $shuffle_id $form_info" - set summary_form [${:QM} aggregated_form -with_grading_box true $form_info] + set summary_form [:QM aggregated_form -with_grading_box true $form_info] set summary_fc [dict get $form_info disabled_form_constraints] # @@ -527,13 +527,13 @@ set container [$ctx wf_container] ${container}::Property ip -default [expr {[ns_conn isconnected] ? [ad_conn peeraddr] : "nowhere"}] - set :QM [$container set QM] - ${:QM} initialize -wfi [self] + :forward QM ::xowf::test_item::question_manager + :QM initialize -wfi [self] #ns_log notice "==== object-specific inclass-exam-answer [self] QM initialized with [self]" set parent_obj [::xo::db::CrClass get_instance_from_db -item_id ${:parent_id}] - :log "inclass-exam-answer state ${:state}" + #:log "inclass-exam-answer state ${:state}" set ctx [:wf_context] set container [$ctx wf_container] if {$ctx ne $container} { @@ -545,9 +545,9 @@ if {${:state} in {initial working done}} { set parent_obj [::xo::db::CrClass get_instance_from_db -item_id [:parent_id]] - set question_count [${:QM} question_count $parent_obj] + set question_count [:QM question_count $parent_obj] if {${:state} eq "initial" && [:property seeds] eq ""} { - ${:QM} add_seeds \ + :QM add_seeds \ -obj [self] \ -seed ${:creation_user} \ -number $question_count @@ -558,7 +558,7 @@ # #ns_log notice "==== object-specific inclass-exam-answer [self] replace_pool_questions" - ${:QM} replace_pool_questions \ + :QM replace_pool_questions \ -answer_obj [self] \ -exam_obj $parent_obj #ns_log notice "==== object-specific inclass-exam-answer [self] replace_pool_questions DONE" @@ -582,7 +582,7 @@ foreach revision_set $revision_sets { dict set positions [dict get [ns_set get $revision_set instance_attributes] position] 1 } - set pagination [${:QM} pagination_actions \ + set pagination [:QM pagination_actions \ -container $container \ -visited [dict keys $positions] \ -flagged [:property flagged {}] \ @@ -624,11 +624,11 @@ # submission is overdue. # set parent_obj [::xo::db::CrClass get_instance_from_db -item_id ${:parent_id}] - set base_time [${:QM} exam_base_time -manager $parent_obj -answer_obj [self]] + set base_time [:QM exam_base_time -manager $parent_obj -answer_obj [self]] set base_clock [clock scan [::xo::db::tcl_date $base_time tz secfrac]] set seconds_working [expr {[clock seconds] - $base_clock}] - set total_minutes [${:QM} total_minutes_for_exam -manager $parent_obj] + set total_minutes [:QM total_minutes_for_exam -manager $parent_obj] set timeLeft [expr {$total_minutes*60 - $seconds_working}] # Index: openacs-4/packages/xowf/lib/inclass-exam.wf =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/lib/inclass-exam.wf,v diff -u -N -r1.1.2.87 -r1.1.2.88 --- openacs-4/packages/xowf/lib/inclass-exam.wf 3 Jan 2022 16:00:36 -0000 1.1.2.87 +++ openacs-4/packages/xowf/lib/inclass-exam.wf 9 Jan 2022 20:46:52 -0000 1.1.2.88 @@ -50,7 +50,7 @@ # - "print-answers" (for lecturers), # - "print-answer-table" (for lecturers), # - "print-participants" (for lecturers), -# - "exam-summary" (for lecturers), +# - "question-summary" (for lecturers), # - "delete" (for lecturers), # - "qrcode" (for lecturers) # @@ -61,9 +61,32 @@ set :debug 0 set :live_updates 1 set :QM ::xowf::test_item::question_manager +:forward QM ::xowf::test_item::question_manager +set :fc_repository { + {countdown_audio_alarm:boolean,horizontal=true,default=t,label=#xowf.Countdown_audio_alarm#,help_text=#xowf.Countdown_audio_alarm_help_text#} + {shuffle_items:boolean,horizontal=true,label=#xowf.randomized_items#,help_text=#xowf.randomized_items_help_text#} + {max_items:number,min=1,label=#xowf.Max_items#,help_text=#xowf.Max_items_help_text#} + {allow_paste:boolean,horizontal=true,default=t,label=#xowf.Allow_paste#,help_text=#xowf.Allow_paste_help_text#} + {allow_spellcheck:boolean,horizontal=true,default=t,label=#xowf.Allow_spellcheck#,help_text=#xowf.Allow_spellcheck_help_text#} + {show_minutes:boolean,horizontal=true,default=t,label=#xowf.Show_minutes#,help_text=#xowf.Show_minutes_help_text#} + {show_points:boolean,horizontal=true,default=t,label=#xowf.Show_points#,help_text=#xowf.Show_points_help_text#} + {show_ip:boolean,horizontal=true,default=t,label=#xowf.Show_IP#,help_text=#xowf.Show_IP_help_text#} + {time_budget:range,default=100,min=100,max=300,step=5,with_output=t,form_item_wrapper_CSSclass=form-inline,output_suffix=%,label=#xowf.Time_budget#,help_text=#xowf.Time_budget_help_text#} + {synchronized:boolean,horizontal=true,default=f,label=#xowf.Synchronized#,help_text=#xowf.Synchronized_help_text#} + {time_window:time_span,label=#xowf.Exam_time_window#,help_text=#xowf.Exam_time_window_help_text#} + {proctoring:boolean,horizontal=true,default=f,label=#xowf.Proctoring#,help_text=#xowf.Proctoring_help_text#} + {proctoring_options:checkbox,horizontal=true,options={Desktop d} {Camera c} {Audio a} {Statement s},default=d c a s,label=#xowf.Proctoring_options#,help_text=#xowf.Proctoring_options_help_text#,swa?:disabled=1} + {proctoring_record:boolean,horizontal=true,default=t,label=#xowf.Proctoring_record#,help_text=#xowf.Proctoring_record_help_text#} + {signature:boolean,horizontal=true,default=f,label=#xowf.Signature#,help_text=#xowf.Signature_help_text#} + {grading:grading_scheme,required,default=none,label=#xowf.Grading_scheme#,help_text=#xowf.Grading_scheme_help_text#} +} + Property realexam -default 1 -allow_query_parameter true +######################################################################## +# Define actions: +# Action select -next_state created -label #xowf.online-exam-select# \ -title #xowf.online-exam-title-select# Action publish -next_state published -state_safe true -label #xowf.online-exam-publish# \ @@ -78,9 +101,13 @@ Action close_submission_review -next_state done -label #xowf.close_submission_review# \ -title #xowf.close_submission_review_title# +######################################################################## +# Define states: +# State parameter { {extra_css {/resources/xowf/test-item.css}} } + State initial -actions {select} -form en:select_question.form -view_method edit State created -actions {publish restart} -form_loader load_form -view_method edit \ -form "#xowf.inclass-exam-draft_exam#" @@ -176,29 +203,42 @@ } ######################################################################## -# form loader: create dynamically a form containing the disabled -# questions as a preview and the survey results (the results can be -# refreshed). +# form loader: create dynamically a form containing the overview exam +# page, containing the state of the exam and information about +# students in submission or working state. # :proc load_form {ctx title} { set obj [$ctx object] set state [$obj property _state] set proctoring [$obj property proctoring 0] - set combined_form_info [${:QM} combined_question_form -with_numbers $obj] - set fullQuestionForm [${:QM} aggregated_form $combined_form_info] - set full_fc [dict get $combined_form_info disabled_form_constraints] + set combined_form_info [:QM combined_question_form -with_numbers $obj] - #:log fullQuestionForm=$fullQuestionForm set text "

[ns_quotehtml $title]

" set menu "" - set text [${:QM} exam_info_block \ - -combined_form_info $combined_form_info \ - $obj] - set detail_link [$obj pretty_link -query m=exam-summary] - append text "

#xowf.detailed_summary#

" + # + append text [subst {

+

+
+
+ #xowf.exam_summary# + [:QM exam_configuration_popup $obj] +
+
+ [:QM exam_info_block -combined_form_info $combined_form_info $obj] +
+
+
+ }] + + set detail_link [$obj pretty_link -query m=question-summary] + append text [subst { + }] + set wf [xowf::test_item::answer_manager get_answer_wf $obj] if {$wf eq ""} { :msg "cannot get current workflow for [$obj name]" @@ -228,7 +268,7 @@ # } - set answers [xowf::test_item::answer_manager get_answers $wf] + set answers [xowf::test_item::answer_manager get_answer_attributes $wf] # # Per default, the entries are disabled. When there are answers, @@ -246,6 +286,7 @@ dict unset md listing } dict unset md results + #ns_log notice ALL=$text set menu "" dict for {name d} $md { @@ -255,25 +296,6 @@ } } - switch $state { - "created" - - "done" - - "submission_review" - - "published" { - # - # In inclass cases, never show all questions on screen, since - # the lecturer might have the screen on the projector. - # - template::add_script -src urn:ad:js:bootstrap3 - set fullQuestionForm [subst { - -
- $fullQuestionForm -
- }] - } - } - set extraAction "" switch $state { "created" { @@ -315,33 +337,29 @@ if {$state eq "published"} { set src [$obj pretty_link -query m=qrcode] set qrCode [subst {
}] - set target_time [${:QM} exam_target_time \ + set target_time [:QM exam_target_time \ -manager $obj -base_time [$obj last_modified]] set countdownHTML [xowf::test_item::answer_manager countdown_timer \ -target_time $target_time -id "countdown"] } + #ns_log notice ALL=$text - # Remove wrapping forms - regsub -all {]*>} $fullQuestionForm {} fullQuestionForm - append text [subst {
$answerStatus
$qrCode
-
$fullQuestionForm
$countdownHTML
}] } - set footer " $extraAction" set f [::xowiki::Form new \ -destroy_on_cleanup \ -set name en:question \ - -form [subst {
$text$marked$footer
text/html}] \ + -form [subst {
$text$marked $extraAction
text/html}] \ -text {} \ -anon_instances t \ - -form_constraints $full_fc \ + -form_constraints {@categories:off @cr_fields:hidden} \ ] } @@ -359,13 +377,13 @@ if {$ctx ne $container} { $ctx forward load_form $container %proc $ctx } - set :QM [$container set QM] + :forward QM ::xowf::test_item::question_manager ${container}::Property return_url -default "" -allow_query_parameter true if {${:state} eq "done"} { set done_actions republish - set combined_form_info [${:QM} combined_question_form [self]] + set combined_form_info [:QM combined_question_form [self]] # # We could allow open_submission_review only when autograde is @@ -394,7 +412,7 @@ # to some "render" method? # #ns_log notice "==== check for randomization" - set combined_form_info [${:QM} combined_question_form [self]] + set combined_form_info [:QM combined_question_form [self]] set randomizationOk [dict get $combined_form_info randomization_for_exam] #ns_log notice "==== check for randomization DONE" ${container}::${:state} actions \ @@ -430,7 +448,7 @@ set wf [xowf::test_item::answer_manager get_answer_wf [self]] if {$wf ne ""} { - #set form_info [${:QM} combined_question_form -with_numbers [self]] + #set form_info [:QM combined_question_form -with_numbers [self]] set items [xowf::test_item::answer_manager get_wf_instances $wf] set items2 [$items deep_copy] @@ -508,12 +526,12 @@ } ######################################################################## - # web-callable method "exam-summary" + # web-callable method "question-summary" # # Print a summary of the exam-questions. # - :proc www-exam-summary {} { - :www-view [${:QM} exam_summary [self]] + :proc www-question-summary {} { + :www-view [:QM question_summary [self]] } ######################################################################## @@ -546,7 +564,7 @@ -form_objs [:query_parameter fos:int32 ""] \ -export [:query_parameter export:boolean 0] \ -orderby [:query_parameter orderby:token "online-exam-userName"] \ - -grading [:query_parameter grading:alnum [:property grading]] \ + -grading [:query_parameter grading:token [:property grading]] \ -with_grading_table [expr {!$as_student}] \ [self]] @@ -750,7 +768,6 @@ :www-view $HTML } - ######################################################################## # AJAX call "poll", acts as responder # @@ -759,8 +776,8 @@ # Return statistics about working and finished exams. # set wf [xowf::test_item::answer_manager get_answer_wf [self]] - set answers [xowf::test_item::answer_manager get_answers $wf] - set answered [xowf::test_item::answer_manager get_answers -state done $wf] + set answers [xowf::test_item::answer_manager get_answer_attributes $wf] + set answered [xowf::test_item::answer_manager get_answer_attributes -state done $wf] ns_return 200 text/plain [llength $answered]/[llength $answers] #ns_log notice "MASTER POLL [self] ${:name}, returned [llength $answered]/[llength $answers]" ad_script_abort @@ -827,6 +844,35 @@ ad_script_abort } + ######################################################################## + # AJAX call "update-config", acts as responder + # + :proc www-update-config {} { + # + # Received updates for form + # + set field_names [:QM exam_configuration_modifiable_field_names [self]] + set form_fields [:create_form_fields_from_names -lookup \ + -form_constraints [:get_fc_repository] \ + $field_names] + #ns_log notice "UPDATE CONFIG <[::xo::cc array names form_parameter]> [ns_set array [ns_conn form]] // $form_fields" + + set last_instance_attributes ${:instance_attributes} + lassign [:get_form_data -field_names $field_names $form_fields] validation_errors category_ids + if {$validation_errors == 0} { + if {$last_instance_attributes eq ${:instance_attributes}} { + ns_log notice "UPDATE CONFIG ... nothing has changed" + } else { + ns_log notice "UPDATE CONFIG ... no validation_errors -> SAVE" + :update_attribute_from_slot [:find_slot instance_attributes] ${:instance_attributes} + } + ns_return 200 text/plain OK + } else { + ns_return 200 text/plain validation_errors + } + ad_script_abort + } + #ns_log notice "==== object-specific inclass-exam [self] state ${:state} DONE (took [expr {[clock clicks -milliseconds]-$t0}]ms)" } Index: openacs-4/packages/xowf/lib/inclass-quiz.wf =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/lib/inclass-quiz.wf,v diff -u -N -r1.1.2.16 -r1.1.2.17 --- openacs-4/packages/xowf/lib/inclass-quiz.wf 21 Nov 2021 20:14:29 -0000 1.1.2.16 +++ openacs-4/packages/xowf/lib/inclass-quiz.wf 9 Jan 2022 20:46:52 -0000 1.1.2.17 @@ -5,14 +5,14 @@ # # This teacher paced inclass quiz workflow lets a teacher choose from # a predefined set of quiz questions. The teacher selects one or -# several quiz question via drag and drop. +# several quiz question via drag and drop. # # When the quiz is published, the students can be offered a display of # the question with a QR code. Teacher can see the incoming answers in # the report (without manual refresh when "live_updates" are # activated). After every question the teacher can toggle to a # "results" display showing the actual answer with statistics as -# provided by the participants. +# provided by the participants. # # An admin might with to add the following entries to the folder to # ease creation of exercises and exams @@ -193,7 +193,7 @@ -wf $wf \ -current_question $current_question] } - + default { :msg "not handled: state=$state" } @@ -414,7 +414,7 @@ :proc www-poll {} { set wf [xowf::test_item::answer_manager get_answer_wf [self]] set current_question [xowf::test_item::question_manager current_question_obj [self]] - set answers [xowf::test_item::answer_manager get_answers $wf] + set answers [xowf::test_item::answer_manager get_answer_attributes $wf] set answered [xowf::test_item::renaming_form_loader answers_for_form [$current_question name] $answers] ns_return 200 text/plain [llength $answered]/[llength $answers] #ns_log notice "MASTER POLL [self] ${:name}, returned [llength $answered]/[llength $answers]" Index: openacs-4/packages/xowf/lib/online-exam.wf =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/lib/online-exam.wf,v diff -u -N -r1.6.2.27 -r1.6.2.28 --- openacs-4/packages/xowf/lib/online-exam.wf 21 Nov 2021 20:14:29 -0000 1.6.2.27 +++ openacs-4/packages/xowf/lib/online-exam.wf 9 Jan 2022 20:46:52 -0000 1.6.2.28 @@ -50,6 +50,26 @@ set :debug 0 set :live_updates 1 +set :fc_repository { + {countdown_audio_alarm:boolean,horizontal=true,default=t,label=#xowf.Countdown_audio_alarm#,help_text=#xowf.Countdown_audio_alarm_help_text#} + {shuffle_items:boolean,horizontal=true,label=#xowf.randomized_items#,help_text=#xowf.randomized_items_help_text#} + {max_items:number,min=1,label=#xowf.Max_items#,help_text=#xowf.Max_items_help_text#} + {allow_paste:boolean,horizontal=true,default=t,label=#xowf.Allow_paste#,help_text=#xowf.Allow_paste_help_text#} + {allow_spellcheck:boolean,horizontal=true,default=t,label=#xowf.Allow_spellcheck#,help_text=#xowf.Allow_spellcheck_help_text#} + {show_minutes:boolean,horizontal=true,default=t,label=#xowf.Show_minutes#,help_text=#xowf.Show_minutes_help_text#} + {show_points:boolean,horizontal=true,default=t,label=#xowf.Show_points#,help_text=#xowf.Show_points_help_text#} + {show_ip:boolean,horizontal=true,default=t,label=#xowf.Show_IP#,help_text=#xowf.Show_IP_help_text#} + {time_budget:range,default=100,min=100,max=300,step=5,with_output=t,form_item_wrapper_CSSclass=form-inline,output_suffix=%,label=#xowf.Time_budget#,help_text=#xowf.Time_budget_help_text#} + {synchronized:boolean,horizontal=true,default=f,label=#xowf.Synchronized#,help_text=#xowf.Synchronized_help_text#} + {time_window:time_span,label=#xowf.Exam_time_window#,help_text=#xowf.Exam_time_window_help_text#} + {proctoring:boolean,horizontal=true,default=f,label=#xowf.Proctoring#,help_text=#xowf.Proctoring_help_text#} + {proctoring_options:checkbox,horizontal=true,options={Desktop d} {Camera c} {Audio a} {Statement s},default=d c a s,label=#xowf.Proctoring_options#,help_text=#xowf.Proctoring_options_help_text#,swa?:disabled=1} + {proctoring_record:boolean,horizontal=true,default=t,label=#xowf.Proctoring_record#,help_text=#xowf.Proctoring_record_help_text#} + {signature:boolean,horizontal=true,default=f,label=#xowf.Signature#,help_text=#xowf.Signature_help_text#} + {grading:grading_scheme,required,default=none,label=#xowf.Grading_scheme#,help_text=#xowf.Grading_scheme_help_text#} +} + + Action select -next_state created -label #xowf.online-exam-select# \ -title #xowf.online-exam-title-select# Action publish -next_state published -label #xowf.online-exam-publish# \ @@ -161,7 +181,7 @@ # # If there are answers, include the full menu. # - set answers [xowf::test_item::answer_manager get_answers $wf] + set answers [xowf::test_item::answer_manager get_answer_attributes $wf] if {[llength $answers] > 0} { set lLink "$wf_pretty_link?m=list" @@ -396,8 +416,8 @@ # :proc www-poll {} { set wf [xowf::test_item::answer_manager get_answer_wf [self]] - set answers [xowf::test_item::answer_manager get_answers $wf] - set answered [xowf::test_item::answer_manager get_answers -state done $wf] + set answers [xowf::test_item::answer_manager get_answer_attributes $wf] + set answered [xowf::test_item::answer_manager get_answer_attributes -state done $wf] ns_return 200 text/plain [llength $answered]/[llength $answers] #ns_log notice "MASTER POLL [self] ${:name}, returned [llength $answered]/[llength $answers]" ad_script_abort Index: openacs-4/packages/xowf/resources/prototypes/select_question.form.page =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/resources/prototypes/select_question.form.page,v diff -u -N -r1.1.2.7 -r1.1.2.8 --- openacs-4/packages/xowf/resources/prototypes/select_question.form.page 3 Jan 2022 16:00:36 -0000 1.1.2.7 +++ openacs-4/packages/xowf/resources/prototypes/select_question.form.page 9 Jan 2022 20:46:52 -0000 1.1.2.8 @@ -8,10 +8,10 @@ @question@

#xowf.Detail_configuration#

- @shuffle_items@ @max_items@ @allow_paste@ @allow_spellcheck@ @show_minutes@ @show_points@ @show_ip@ @countdown_audio_alarm@ + @shuffle_items@ @max_items@ @allow_paste@ @allow_spellcheck@ @show_minutes@ @show_points@ @show_ip@

- @synchronized@ @time_window@ @time_budget@ + @synchronized@ @time_window@ @time_budget@ @countdown_audio_alarm@

@proctoring@ @@ -26,22 +26,22 @@ @cr_fields:hidden {_title:text,label=#xowf.online-exam-name#,default=#xowf.online-exam-default_name#} {question:form_page,multiple=true,keep_order=true,parent_id=.,form=en:edit-interaction.wf,required,help_text=#xowf.select_question_help_text#,label=#xowiki.questions#} - {countdown_audio_alarm:boolean,horizontal=true,default=t,label=#xowf.Countdown_audio_alarm#,help_text=#xowf.Countdown_audio_alarm_help_text#} - {shuffle_items:boolean,horizontal=true,label=#xowf.randomized_items#,help_text=#xowf.randomized_items_help_text#} - {max_items:number,min=1,label=#xowf.Max_items#,help_text=#xowf.Max_items_help_text#} - {allow_paste:boolean,horizontal=true,default=t,label=#xowf.Allow_paste#,help_text=#xowf.Allow_paste_help_text#} - {allow_spellcheck:boolean,horizontal=true,default=t,label=#xowf.Allow_spellcheck#,help_text=#xowf.Allow_spellcheck_help_text#} - {show_minutes:boolean,horizontal=true,default=t,label=#xowf.Show_minutes#,help_text=#xowf.Show_minutes_help_text#} - {show_points:boolean,horizontal=true,default=t,label=#xowf.Show_points#,help_text=#xowf.Show_points_help_text#} - {show_ip:boolean,horizontal=true,default=t,label=#xowf.Show_IP#,help_text=#xowf.Show_IP_help_text#} - {time_budget:range,default=100,min=100,max=300,step=5,with_output=t,form_item_wrapper_CSSclass=form-inline,output_suffix=%,label=#xowf.Time_budget#,help_text=#xowf.Time_budget_help_text#} - {synchronized:boolean,horizontal=true,default=f,label=#xowf.Synchronized#,help_text=#xowf.Synchronized_help_text#} - {time_window:time_span,label=#xowf.Exam_time_window#,help_text=#xowf.Exam_time_window_help_text#} - {signature:boolean,horizontal=true,default=f,label=#xowf.Signature#,help_text=#xowf.Signature_help_text#} - {proctoring:boolean,horizontal=true,default=f,label=#xowf.Proctoring#,help_text=#xowf.Proctoring_help_text#} - {proctoring_options:checkbox,horizontal=true,options={Desktop d} {Camera c} {Audio a} {Statement s},default=d c a s,label=#xowf.Proctoring_options#,help_text=#xowf.Proctoring_options_help_text#,swa?:disabled=1} - {proctoring_record:boolean,horizontal=true,default=t,label=#xowf.Proctoring_record#,help_text=#xowf.Proctoring_record_help_text#} - {grading:grading_scheme,required,default=none,label=#xowf.Grading_scheme#,help_text=#xowf.Grading_scheme_help_text#} + {countdown_audio_alarm:} + {shuffle_items:} + {max_items:} + {allow_paste:} + {allow_spellcheck:} + {show_minutes:} + {show_points:} + {show_ip:} + {time_budget:} + {synchronized:} + {time_window:} + {signature:} + {proctoring:} + {proctoring_options:} + {proctoring_record:} + {grading:} _description:omit _page_order:omit } Index: openacs-4/packages/xowf/tcl/test-item-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/tcl/test-item-procs.tcl,v diff -u -N -r1.7.2.192 -r1.7.2.193 --- openacs-4/packages/xowf/tcl/test-item-procs.tcl 4 Jan 2022 19:59:35 -0000 1.7.2.192 +++ openacs-4/packages/xowf/tcl/test-item-procs.tcl 9 Jan 2022 20:46:52 -0000 1.7.2.193 @@ -1526,7 +1526,7 @@ :public method answers_for_form {formName answers} { # # Return a list of dicts for the provided formName from the - # answers (as returned from [answer_manager get_answers ...]). + # answers (as returned from [answer_manager get_answer_attributes ...]). # set stem [:form_name_based_attribute_stem $formName] set result "" @@ -1624,7 +1624,8 @@ # - delete_all_answer_data # - get_answer_wf # - get_wf_instances - # - get_answers + # - get_answer_attributes + # - student_submissions_exist # # - runtime_panel # - render_answers_with_edit_history @@ -2045,15 +2046,17 @@ #---------------------------------------------------------------------- # Class: Answer_manager - # Method: get_answers + # Method: get_answer_attributes #---------------------------------------------------------------------- - :public method get_answers {{-state ""} {-extra_attributes {}} wf:object} { + :public method get_answer_attributes {{-state ""} {-extra_attributes {}} wf:object} { # - # Extracts wf instances as answers (e.g. extracting their + # Extracts wf instances as answers (e.g., extracting their # answer-specific attributes) # # @param wf the workflow # @param state retrieve only instances in this state + # @param extra_attributes return these attributes additionally + # as key/value pairs per tuple # # @return a list of dicts # @@ -2065,12 +2068,11 @@ continue } - set answerAttributes \ - [:FL answer_attributes [$i instance_attributes]] + set answerAttributes [:FL answer_attributes [$i instance_attributes]] foreach extra $extra_attributes { lappend answerAttributes $extra [$i property $extra] } - #ns_log notice "GETANSWERS $i: <$answerAttributes> ALL [$i instance_attributes]" + #ns_log notice "get_answer_attributes $i: <$answerAttributes> ALL [$i instance_attributes]" lappend results [list item $i answerAttributes $answerAttributes state [$i state]] } return $results @@ -2792,7 +2794,7 @@ return "" } - set submissions [:student_submissions -wf $wf] + set submissions [:submissions -wf $wf] set HTML [:render_submissions=edit_history -examWf $examWf -submissions $submissions] return $HTML @@ -2867,9 +2869,32 @@ #---------------------------------------------------------------------- # Class: Answer_manager - # Method: student_submissions + # Method: student_submissions_exist #---------------------------------------------------------------------- - :method student_submissions { + :public method student_submissions_exist {wf:object} { + # + # Returns 1 if there are student submissions. The method returns + # already true, when a student has started to work on this exam. + # + # This method could be optimized if necessary via caching the + # wf_instances or a more specific database query. + # + set items [:get_wf_instances $wf] + foreach i [$items children] { + if {[$i property try_out_mode] ne "1"} { + #ns_log notice "==================== student_submissions_exist 1" + return 1 + } + } + #ns_log notice "==================== student_submissions_exist 0" + return 0 + } + + #---------------------------------------------------------------------- + # Class: Answer_manager + # Method: submissions + #---------------------------------------------------------------------- + :method submissions { {-creation_user:integer,0..1 ""} {-filter_id:integer,0..1 ""} {-revision_id:integer,0..1 ""} @@ -3407,7 +3432,7 @@ #---------------------------------------------------------------------- :method grading_scheme { {-examWf:object,required} - {-grading:alnum,0..n ""} + {-grading:token,0..n ""} {-total_points} } { # @@ -3461,7 +3486,7 @@ {-form_objs:integer,0..n ""} {-export:boolean false} {-orderby:token "online-exam-userName"} - {-grading:alnum,0..n ""} + {-grading:token,0..n ""} {-with_grading_table:boolean false} examWf:object } { @@ -3498,7 +3523,7 @@ set :grade_dict {} set :grade_csv "" - set items [:student_submissions \ + set items [:submissions \ -creation_user $creation_user \ -filter_id $filter_id \ -revision_id $revision_id \ @@ -3565,7 +3590,7 @@ } if {$export} { - set recutil [:AM recutil_create \ + set recutil [:recutil_create \ -clear \ -exam_id [$wf parent_id] \ -fn [expr {$filter_id eq "" ? "all.rec" : "$filter_id.rec"}] @@ -4283,14 +4308,14 @@ # has to be provided with valid HTML markup. # - set answers [:AM get_answers $wf] + set answers [:get_answer_attributes $wf] set nrParticipants [llength $answers] if {$current_question ne ""} { set answered [:FL answers_for_form \ [$current_question name] \ $answers] } else { - set answered [:AM get_answers -state $target_state $wf] + set answered [:get_answer_attributes -state $target_state $wf] } set nrAnswered [llength $answered] @@ -4593,6 +4618,9 @@ # - nth_question_obj # - nth_question_form # + # - exam_configuration_popup + # - exam_configuration_modifiable_field_names + # # - combined_question_form # - question_objs # - question_names @@ -4609,7 +4637,7 @@ # - item_substitute_markup # # - describe_form - # - exam_summary + # - question_summary # - question_info_block # @@ -5602,8 +5630,8 @@ question_infos } { # - # Compute an aggregated form based on the chunks available in - # question_infos. + # Compute an aggregated form (containing potentially multiple + # questions) based on the chunks available in question_infos. # # @return HTML form content # @@ -5644,6 +5672,7 @@ } regsub -all {<[/]?form>} $full_form "" full_form + #ns_log notice "aggregated_form: STRIP FORM xxx times from full_form" return $full_form } @@ -6043,36 +6072,212 @@ #---------------------------------------------------------------------- # Class: Question_manager - # Method: exam_summary + # Method: exam_configuration_render_fields #---------------------------------------------------------------------- - :public method exam_summary {obj} { - # - # Provide a summary of all questions of an exam. - # - set HTML [subst { -
-
#xowf.exam_summary#
-
- [:exam_info_block $obj] + :method exam_configuration_render_fields {{-modifiable ""} fields} { + #ns_log notice "configuration_render called with modifiable <$modifiable>" + ::xo::require_html_procs + + set content "" + foreach f $fields { + if {[$f name] ni $modifiable} { + $f set_disabled true + $f help_text "" + } + append content [tdom_render { + $f render + }] + } + return $content + } + #---------------------------------------------------------------------- + # Class: Question_manager + # Method: exam_configuration_block + #---------------------------------------------------------------------- + :method exam_configuration_block { + {-modifiable ""} + -label + -id + -obj + -form_constraints + field_names + } { + set fields [$obj create_form_fields_from_names -lookup -set_values \ + -form_constraints $form_constraints \ + $field_names] + return [subst { +

+

+ [:exam_configuration_render_fields -modifiable $modifiable $fields]
-
}] + } - append HTML [:question_info_block $obj] + #---------------------------------------------------------------------- + # Class: Question_manager + # Method: exam_configuration_modifiable_field_names + #---------------------------------------------------------------------- + :public method exam_configuration_modifiable_field_names {obj} { + # + # Return the names of the modifiable field names in the current + # state. The state is in essence defined on whether or not + # students have started to work on this exam. This method can be + # used to correct small things, even when the students are + # already working on the exam. + # + set modifiable { + allow_paste allow_spellcheck show_minutes show_points show_ip + countdown_audio_alarm grading + } + set wf [:AM get_answer_wf $obj] + if {![:AM student_submissions_exist $wf]} { + lappend modifiable {*}{ + shuffle_items max_items + time_budget synchronized time_window + proctoring proctoring_options proctoring_record signature + } + } + return $modifiable + } + #---------------------------------------------------------------------- + # Class: Question_manager + # Method: exam_configuration_popup + #---------------------------------------------------------------------- + :public method exam_configuration_popup {obj} { + # + # Render the exam configuration popup, add it as a + # content_header (to avoid putting it to the main workflow form, + # since nested FORMS are not allowed) and return the rendering + # of the button for popping-ip the configuration modal. + # + # @return HTML + + set modifiable [:exam_configuration_modifiable_field_names $obj] + #ns_log notice "exam_configuration_popup modifiable '$modifiable'" + + set fcrepo [$obj get_fc_repository] + set content "" + append content \ + [:exam_configuration_block \ + -modifiable $modifiable \ + -label #xowf.Question_management# \ + -id config-question \ + -form_constraints $fcrepo \ + -obj $obj { + shuffle_items max_items allow_paste allow_spellcheck + show_minutes show_points show_ip + }] \ + [:exam_configuration_block \ + -modifiable $modifiable \ + -label #xowf.Time_management# \ + -id config-time \ + -form_constraints $fcrepo \ + -obj $obj { + time_budget synchronized time_window countdown_audio_alarm + }] \ + [:exam_configuration_block \ + -modifiable $modifiable \ + -label #xowf.Security# \ + -id config-security \ + -form_constraints $fcrepo \ + -obj $obj { + proctoring proctoring_options proctoring_record signature + }] \ + [:exam_configuration_render_fields -modifiable $modifiable \ + [$obj create_form_fields_from_names -lookup -set_values \ + -form_constraints $fcrepo \ + {grading}]] + + ::template::add_body_script -script [ns_trim -delimiter | [subst -novariables { + |$(document).ready(function() { + | $('.modal .confirm').on('click', function(ev) { + | // + | // Submit button of the dialog was pressed. + | // + | var data = new FormData(document.getElementById('configuration-form')); + | console.log(data); + | var xhttp = new XMLHttpRequest(); + | xhttp.open('POST', '[$obj pretty_link -query m=update-config]', true); + | xhttp.onload = function () { + | if (this.readyState == 4) { + | if (this.status == 200) { + | var text = this.responseText; + | console.log('sent OK ok ' + text); + | //window.location.reload(true); + | } else { + | console.log('sent NOT ok'); + | } + | } + | }; + | xhttp.send(data); + | }); + |}); + }]] + + $obj content_header_append [ns_trim -delimiter | [subst { + | + }]] + + return [ns_trim -delimiter | [subst { + | + | + | + }]] + } + + #---------------------------------------------------------------------- + # Class: Question_manager + # Method: question_summary + #---------------------------------------------------------------------- + :public method question_summary {obj} { + # + # Provide a summary of all questions of an exam. + # set results [$obj property __results] if {$results ne ""} { set href [$obj pretty_link -query m=exam-results] - append HTML [subst { + set results_summary [subst {

#xowf.export_results#: CSV }] + } else { + set results_summary "" } set return_url [::xo::cc query_parameter local_return_url:localurl [$obj pretty_link]] - append HTML "


#xowiki.back#

\n" - - return $HTML + return [ns_trim -delimiter | [subst { + | [:question_info_block $obj] + | $results_summary + |

#xowiki.back#

+ }]] } #---------------------------------------------------------------------- @@ -6717,15 +6922,16 @@ view admin poll admin send-participant-message admin - grade-single-item admin - edit admin - exam-results admin - print-answers admin + grade-single-item admin + edit admin + exam-results admin + question-summary admin + print-answers admin proctoring-display admin print-answer-table admin print-participants admin - delete admin - qrcode admin + delete admin + qrcode admin make-live-revision admin } } Index: openacs-4/packages/xowf/tcl/xowf-form-field-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/tcl/xowf-form-field-procs.tcl,v diff -u -N -r1.8.2.5 -r1.8.2.6 --- openacs-4/packages/xowf/tcl/xowf-form-field-procs.tcl 3 Jan 2022 16:00:36 -0000 1.8.2.5 +++ openacs-4/packages/xowf/tcl/xowf-form-field-procs.tcl 9 Jan 2022 20:46:52 -0000 1.8.2.6 @@ -192,11 +192,11 @@ # RelTypeRole (role definitions based on rel types) # Class create RelTypeRole -superclass Role -parameter {{rel_type ""}} - RelTypeRole instproc rel_type_clause {} { + RelTypeRole instproc rel_type_clause {} { if {${:rel_type} ne ""} { return {r.rel_type = :rel_type} } else { - return {r.rel_type <> 'composition_rel'} + return {r.rel_type <> 'composition_rel'} } } RelTypeRole instproc filtered_member_list {-group_id:required {-except ""}} { @@ -221,7 +221,7 @@ and r.object_id_two = :user_id and [:rel_type_clause] and r.rel_id = mr.rel_id - and mr.member_state = 'approved' + and mr.member_state = 'approved' }] return [xo::dc 0or1row check_membership $query] } @@ -256,7 +256,7 @@ #ns_log notice "IS MEMBER user_id $user_id -package_id $package_id group_id $group_id" return [:filtered_member_p -group_id $object_id -user_id $user_id] } - + RelTypeRole create member RelTypeRole create student -rel_type dotlrn_student_rel RelTypeRole create instructor -rel_type dotlrn_instructor_rel @@ -449,12 +449,12 @@ set grading [namespace tail $gso] list [$gso cget -title] $grading }]] - ns_log notice "#### available grading_scheme_objs (took [expr {[clock clicks -milliseconds]-$t1}]ms)\n[join [lsort ${:options}] \n]" + #ns_log notice "#### available grading_scheme_objs (took [expr {[clock clicks -milliseconds]-$t1}]ms)\n[join [lsort ${:options}] \n]" next - + set :__initialized 1 } - + ########################################################### # # ::xowiki::formfield::grade_boundary @@ -478,25 +478,25 @@ const grade3 = form.elements["grade3"]; const grade4 = form.elements["grade4"]; if (grade1.value < grade2.value) { - console.log('error grade 1'); + console.log('error grade 1'); grade2.setCustomValidity('percentage for grade 1 must by larger than grade 2'); } else { grade2.setCustomValidity(''); } if (grade2.value < grade3.value) { - console.log('error grade 2'); + console.log('error grade 2'); grade3.setCustomValidity('percentage for grade 2 must by larger than grade 3'); } else { grade3.setCustomValidity(''); } if (grade3.value < grade4.value) { - console.log('error grade 3'); + console.log('error grade 3'); grade4.setCustomValidity('percentage for grade 3 must by larger than grade 4'); } else { grade4.setCustomValidity(''); } } - } + } } ::xo::library source_dependent Index: openacs-4/packages/xowf/tcl/xowf-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/tcl/xowf-procs.tcl,v diff -u -N -r1.28.2.63 -r1.28.2.64 --- openacs-4/packages/xowf/tcl/xowf-procs.tcl 2 Jan 2022 19:38:53 -0000 1.28.2.63 +++ openacs-4/packages/xowf/tcl/xowf-procs.tcl 9 Jan 2022 20:46:52 -0000 1.28.2.64 @@ -1358,7 +1358,10 @@ return 0 } if {$role eq "creator"} { - # hmm, requires additional attribute + # + # Meaning: "creator of the object", requires the object as + # additional attribute. + # return [::xo::cc role=$role \ -object [self] \ -user_id [::xo::cc user_id] \ @@ -1627,6 +1630,15 @@ return [:assignee] } + WorkflowPage instproc get_fc_repository {} { + set container [[:wf_context] wf_container] + if {[$container exists fc_repository]} { + return [$container set fc_repository] + } + ns_log warning "get_fc_repository returns empty" + return "" + } + WorkflowPage instproc send_to_assignee { -subject -from @@ -2107,27 +2119,39 @@ next } } - WorkflowPage instproc constraints_as_array {c} { - array set __c "" + WorkflowPage instproc constraints_as_dict {{-fc_repository ""} c} { + set result "" foreach name_and_spec $c { - regexp {^([^:]+):(.*)$} $name_and_spec _ spec_name short_spec - set __c($spec_name) $short_spec + set p [string first : $name_and_spec] + if {$p > -1} { + set spec_name [string range $name_and_spec 0 $p-1] + set short_spec [string range $name_and_spec $p+1 end] + if {$short_spec eq "" && [dict exists $fc_repository $spec_name]} { + set short_spec [dict get $fc_repository $spec_name] + #:log "======= use fc_repository for <$spec_name> <$short_spec>" + } + dict set result $spec_name $short_spec + } else { + ns_log warning "ignore invalid fc: <$name_and_spec>" + } } - return [array get __c] + return $result } WorkflowPage instproc merge_constraints {c1 args} { # Load into the base_constraints c1 the constraints from the argument list. # The first constraints have the lowest priority - array set __c1 [:constraints_as_array $c1] + set fcrepo [:constraints_as_dict [:get_fc_repository]] + set merged [:constraints_as_dict -fc_repository $fcrepo $c1] foreach c2 $args { - foreach {att value} [:constraints_as_array $c2] { - set key __c1($att) - if {[info exists $key]} {append $key ",$value"} else {set $key $value} + foreach {att value} [:constraints_as_dict -fc_repository $fcrepo $c2] { + if {[dict exists $merged $att]} { + dict append merged $att ",$value" + } else { + dict set merged $att "$value" + } } } - set result [list] - foreach {att value} [array get __c1] {lappend result $att:$value} - return $result + return [lmap {att value} $merged {string cat $att:$value}] } WorkflowPage instproc wfi_merged_form_constraints {constraints_from_form} { set ctx [::xowf::Context require [self]] @@ -2157,6 +2181,7 @@ } WorkflowPage instproc get_form_constraints {{-trylocal false}} { + #:log "" if {[:istype ::xowiki::FormPage] && [:is_wf]} { #:msg "get_form_constraints is_wf" return [::xo::cc cache [list [self] wf_merged_form_constraints [next]]] Index: openacs-4/packages/xowf/www/resources/test-item.css =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/www/resources/test-item.css,v diff -u -N -r1.1.2.41 -r1.1.2.42 --- openacs-4/packages/xowf/www/resources/test-item.css 4 Jan 2022 19:58:34 -0000 1.1.2.41 +++ openacs-4/packages/xowf/www/resources/test-item.css 9 Jan 2022 20:46:52 -0000 1.1.2.42 @@ -365,6 +365,12 @@ } /* + * Configuration modal dialog + */ +#configuration-modal { + font-size: smaller; +} +/* * Editing grading schemes */ form.Form-edit-grading-scheme input.form-control.inline {