Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
integration
prechecker
Commits
afa2a7f4
Commit
afa2a7f4
authored
Jun 28, 2022
by
Plugins bot
Browse files
PLUGIN-26928 qbehaviour_opaque: cibot precheck request
parent
ceb41588
Changes
21
Hide whitespace changes
Inline
Side-by-side
question/behaviour/opaque/.github/workflows/ci.yml
0 → 100644
View file @
afa2a7f4
name
:
Moodle plugin CI
on
:
[
push
,
pull_request
]
jobs
:
test
:
runs-on
:
'
ubuntu-latest'
strategy
:
fail-fast
:
false
matrix
:
include
:
-
php
:
'
7.4'
moodle-branch
:
'
master'
database
:
'
pgsql'
-
php
:
'
7.4'
moodle-branch
:
'
MOODLE_311_STABLE'
database
:
'
mariadb'
services
:
postgres
:
image
:
postgres
env
:
POSTGRES_USER
:
'
postgres'
POSTGRES_HOST_AUTH_METHOD
:
'
trust'
options
:
>-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 3
ports
:
-
5432:5432
mariadb
:
image
:
mariadb
env
:
MYSQL_USER
:
'
root'
MYSQL_ALLOW_EMPTY_PASSWORD
:
"
true"
ports
:
-
3306:3306
options
:
--health-cmd="mysqladmin ping" --health-interval 10s --health-timeout 5s --health-retries
3
steps
:
-
name
:
Checkout
uses
:
actions/checkout@v2
with
:
path
:
plugin
-
name
:
Install node
uses
:
actions/setup-node@v1
with
:
node-version
:
'
14.15.0'
-
name
:
Setup PHP
uses
:
shivammathur/setup-php@v2
with
:
php-version
:
${{ matrix.php }}
extensions
:
mbstring, pgsql, mysqli
-
name
:
Deploy moodle-plugin-ci
run
:
|
composer create-project -n --no-dev --prefer-dist moodlehq/moodle-plugin-ci ci ^3
# Add dirs to $PATH
echo $(cd ci/bin; pwd) >> $GITHUB_PATH
echo $(cd ci/vendor/bin; pwd) >> $GITHUB_PATH
# PHPUnit depends on en_AU.UTF-8 locale
sudo locale-gen en_AU.UTF-8
-
name
:
Install Moodle
run
:
|
moodle-plugin-ci add-plugin --branch main moodleou/moodle-qtype_opaque
moodle-plugin-ci install --plugin ./plugin --db-host=127.0.0.1
env
:
DB
:
${{ matrix.database }}
MOODLE_BRANCH
:
${{ matrix.moodle-branch }}
-
name
:
phplint
if
:
${{ always() }}
run
:
moodle-plugin-ci phplint
-
name
:
phpcpd
if
:
${{ always() }}
run
:
moodle-plugin-ci phpcpd ||
true
-
name
:
phpmd
if
:
${{ always() }}
run
:
moodle-plugin-ci phpmd
-
name
:
codechecker
if
:
${{ always() }}
run
:
moodle-plugin-ci codechecker
-
name
:
validate
if
:
${{ always() }}
run
:
moodle-plugin-ci validate
-
name
:
savepoints
if
:
${{ always() }}
run
:
moodle-plugin-ci savepoints
-
name
:
mustache
if
:
${{ always() }}
run
:
moodle-plugin-ci mustache
-
name
:
grunt
if
:
${{ always() }}
run
:
moodle-plugin-ci grunt
-
name
:
phpunit
if
:
${{ always() }}
run
:
moodle-plugin-ci phpunit
-
name
:
behat
if
:
${{ always() }}
run
:
moodle-plugin-ci behat --profile chrome
question/behaviour/opaque/README.txt
0 → 100644
View file @
afa2a7f4
The Opaque question type and behaviour
https://moodle.org/plugins/qtype_opaque
Opaque (http://docs.moodle.org/en/Development:Opaque) is the Open protocol for
accessing question engines.
The Opaque protocol was originally created by sam marshall of the Open
University (http://www.open.ac.uk/) as part of the OpenMark project
(http://java.net/projects/openmark/). The Moodle implementation of Opaque was
done by Tim Hunt.
As well as OpenMark, this question type can also be used to connect to
ounit (http://code.google.com/p/ounit/) and possibly other question systems
we don't know about.
Opaque has been available since Moodle 1.8, but this version is compatible with
Moodle 3.4.
This question behaviour also requires the Opaque question type to be installed.
https://moodle.org/plugins/qbehaviour_opaque
You can install from the Moodle plugins database using the links above.
Or to install using git, type this command in the root of your Moodle install
git clone https://github.com/moodleou/moodle-qtype_opaque.git question/type/opaque
echo '/question/type/opaque/' >> .git/info/exclude
git clone https://github.com/moodleou/moodle-qbehaviour_opaque.git question/behaviour/opaque
echo '/question/behaviour/opaque/' >> .git/info/exclude
Once installed you need to go to the question type settings page
(Site administration -> Plugins -> Question types -> Opaque) to
set up the URLs of the question engines you wish to use.
https://github.com/moodleou/moodle-local_testopaqueqe can be used to test that
Opaque is working.
To be able to run all the unit tests, you need a working OpenMark install, then you need to add
define('QTYPE_OPAQUE_TEST_ENGINE_QE', 'http://example.com/om-qe/services/Om');
define('QTYPE_OPAQUE_TEST_ENGINE_TN', 'http://example.com/openmark/!question');
define('QTYPE_OPAQUE_TEST_ENGINE_PASSKEY', 'abc123');
define('QTYPE_OPAQUE_TEST_ENGINE_TIMEOUT', '5');
to your config.php file. Of these, only the first is required. The remaining
ones are optional. Specify them if your set-up needs them.
question/behaviour/opaque/behaviour.php
0 → 100644
View file @
afa2a7f4
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* This behaviour specifically for use with the Opaque question type.
*
* @package qbehaviour_opaque
* @copyright 2010 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined
(
'MOODLE_INTERNAL'
)
||
die
();
require_once
(
$CFG
->
dirroot
.
'/question/type/opaque/connection.php'
);
require_once
(
$CFG
->
dirroot
.
'/question/behaviour/opaque/connection.php'
);
require_once
(
$CFG
->
dirroot
.
'/question/behaviour/opaque/statecache.php'
);
require_once
(
$CFG
->
dirroot
.
'/question/behaviour/opaque/resourcecache.php'
);
require_once
(
$CFG
->
dirroot
.
'/question/behaviour/opaque/legacy.php'
);
require_once
(
$CFG
->
dirroot
.
'/question/behaviour/opaque/opaquestate.php'
);
/**
* This behaviour is specifically for use with the Opaque question type.
*
* @copyright 2010 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class
qbehaviour_opaque
extends
question_behaviour
{
/** @var string */
protected
$preferredbehaviour
;
/** @var string */
protected
$questionsummary
;
public
function
__construct
(
question_attempt
$qa
,
$preferredbehaviour
)
{
parent
::
__construct
(
$qa
,
$preferredbehaviour
);
$this
->
preferredbehaviour
=
$preferredbehaviour
;
}
public
function
is_compatible_question
(
question_definition
$question
)
{
return
$question
instanceof
qtype_opaque_question
;
}
public
function
get_state_string
(
$showcorrectness
)
{
$state
=
$this
->
qa
->
get_state
();
$omstate
=
$this
->
qa
->
get_last_behaviour_var
(
'_statestring'
);
if
(
$state
->
is_finished
())
{
return
$state
->
default_string
(
$showcorrectness
);
}
else
if
(
$omstate
)
{
return
$omstate
;
}
else
{
return
get_string
(
'notcomplete'
,
'qbehaviour_opaque'
);
}
}
public
function
init_first_step
(
question_attempt_step
$step
,
$variant
)
{
global
$USER
;
parent
::
init_first_step
(
$step
,
$variant
);
// We no longer set the random seed as such (we pass a fixed conventional
// value instead). Instead we pass the variant as OpenMark's 'attempt'
// paramter.
$step
->
set_behaviour_var
(
'_randomseed'
,
123456789
);
$step
->
set_behaviour_var
(
'_attempt'
,
$variant
);
$step
->
set_behaviour_var
(
'_userid'
,
$USER
->
id
);
$step
->
set_behaviour_var
(
'_language'
,
current_language
());
$step
->
set_behaviour_var
(
'_preferredbehaviour'
,
$this
->
preferredbehaviour
);
$opaquestate
=
new
qbehaviour_opaque_state
(
$this
->
qa
,
$step
);
$step
->
set_behaviour_var
(
'_statestring'
,
$opaquestate
->
get_progress_info
());
// Remember the question summary.
$this
->
questionsummary
=
html_to_text
(
$opaquestate
->
get_xhtml
(),
0
,
false
);
}
public
function
adjust_display_options
(
question_display_options
$options
)
{
if
(
!
$this
->
qa
->
has_marks
())
{
$options
->
correctness
=
false
;
}
if
(
$this
->
qa
->
get_state
()
->
is_finished
())
{
$options
->
readonly
=
true
;
}
}
public
function
get_question_summary
()
{
return
$this
->
questionsummary
;
}
protected
function
is_same_response
(
question_attempt_step
$pendingstep
)
{
$newdata
=
$pendingstep
->
get_submitted_data
();
$newdata
=
qbehaviour_opaque_fix_up_submitted_data
(
$newdata
,
$pendingstep
);
// If an omact_ button has been clicked, never treat this as a duplicate submission.
if
(
qbehaviour_opaque_response_contains_om_action
(
$newdata
))
{
return
false
;
}
$laststep
=
$this
->
qa
->
get_last_step
();
$olddata
=
$laststep
->
get_submitted_data
();
$olddata
=
qbehaviour_opaque_fix_up_submitted_data
(
$olddata
,
$laststep
);
return
question_utils
::
arrays_have_same_keys_and_values
(
$newdata
,
$olddata
);
}
public
function
summarise_action
(
question_attempt_step
$step
)
{
if
(
$step
->
has_behaviour_var
(
'_randomseed'
))
{
return
$this
->
summarise_start
(
$step
);
}
else
if
(
$step
->
has_behaviour_var
(
'finish'
))
{
return
$this
->
summarise_finish
(
$step
);
}
else
if
(
$step
->
has_behaviour_var
(
'comment'
))
{
return
$this
->
summarise_manual_comment
(
$step
);
}
else
if
(
$step
->
has_qt_var
(
'omact_ok'
))
{
// This is always a 'Try again' event in OpenMark. In this case we
// use button label.
return
$step
->
get_qt_var
(
'omact_ok'
);
}
else
{
$summary
=
$this
->
question
->
summarise_response
(
qbehaviour_opaque_state
::
submitted_data
(
$step
));
if
(
$this
->
step_has_a_submitted_response
(
$step
))
{
return
get_string
(
'submitted'
,
'question'
,
$summary
);
}
else
{
return
get_string
(
'saved'
,
'question'
,
$summary
);
}
}
}
public
function
process_action
(
question_attempt_pending_step
$pendingstep
)
{
if
(
$pendingstep
->
has_behaviour_var
(
'finish'
))
{
return
$this
->
process_finish
(
$pendingstep
);
}
else
if
(
$pendingstep
->
has_behaviour_var
(
'comment'
))
{
return
$this
->
process_comment
(
$pendingstep
);
}
else
if
(
$this
->
is_same_response
(
$pendingstep
)
||
$this
->
qa
->
get_state
()
->
is_finished
())
{
return
question_attempt
::
DISCARD
;
}
else
{
return
$this
->
process_remote_action
(
$pendingstep
);
}
}
public
function
process_finish
(
question_attempt_pending_step
$pendingstep
)
{
if
(
$this
->
qa
->
get_state
()
->
is_finished
())
{
return
question_attempt
::
DISCARD
;
}
// Try to get the question to stop.
$result
=
$this
->
process_remote_action
(
$pendingstep
);
if
(
$result
==
question_attempt
::
KEEP
&&
!
$pendingstep
->
get_state
()
->
is_finished
())
{
// They tried to finish but the question is not finished, so all we
// can do is to set the state to gave up. This lets the renderer
// handle the review page appropriately.
$pendingstep
->
set_state
(
question_state
::
$gaveup
);
}
return
$result
;
}
public
function
process_remote_action
(
question_attempt_pending_step
$pendingstep
)
{
$opaquestate
=
new
qbehaviour_opaque_state
(
$this
->
qa
,
$pendingstep
);
if
(
$opaquestate
->
get_results_sequence_number
()
!=
$this
->
qa
->
get_num_steps
())
{
if
(
$opaquestate
->
get_progress_info
()
===
'Answer saved'
)
{
$pendingstep
->
set_state
(
question_state
::
$complete
);
}
else
{
$pendingstep
->
set_state
(
question_state
::
$todo
);
}
$pendingstep
->
set_behaviour_var
(
'_statestring'
,
$opaquestate
->
get_progress_info
());
}
else
{
// Look for a score on the default axis.
$pendingstep
->
set_fraction
(
0
);
$results
=
$opaquestate
->
get_results
();
foreach
(
$results
->
scores
as
$score
)
{
if
(
$score
->
axis
==
''
)
{
$pendingstep
->
set_fraction
(
$score
->
marks
/
$this
->
question
->
defaultmark
);
}
}
if
(
$results
->
attempts
>
0
)
{
$pendingstep
->
set_state
(
question_state
::
$gradedright
);
}
else
{
$pendingstep
->
set_state
(
question_state
::
graded_state_for_fraction
(
$pendingstep
->
get_fraction
()));
}
if
(
!
empty
(
$results
->
questionLine
))
{
$this
->
qa
->
set_question_summary
(
$this
->
cleanup_results
(
$results
->
questionLine
));
if
(
preg_match
(
'~variant\s*=\s*(\d+)~i'
,
$results
->
questionLine
,
$matches
))
{
$pendingstep
->
set_new_variant_number
(
$matches
[
1
]);
}
else
{
$pendingstep
->
set_new_variant_number
(
1
);
}
}
if
(
!
empty
(
$results
->
answerLine
))
{
$pendingstep
->
set_new_response_summary
(
$this
->
cleanup_results
(
$results
->
answerLine
));
}
if
(
!
empty
(
$results
->
actionSummary
))
{
$pendingstep
->
set_behaviour_var
(
'_actionsummary'
,
$this
->
cleanup_results
(
$results
->
actionSummary
));
}
}
return
question_attempt
::
KEEP
;
}
protected
function
cleanup_results
(
$line
)
{
return
preg_replace
(
'/\\s+/'
,
' '
,
$line
);
}
public
function
step_has_a_submitted_response
(
$step
)
{
if
(
$step
->
has_behaviour_var
(
'finish'
))
{
return
false
;
}
else
if
(
$step
->
has_behaviour_var
(
'comment'
))
{
return
false
;
}
else
{
$foundomact
=
false
;
foreach
(
qbehaviour_opaque_state
::
submitted_data
(
$step
)
as
$name
=>
$value
)
{
if
(
$name
===
'omact_ok'
)
{
// This is a Try again state.
return
false
;
}
if
(
substr
(
$name
,
0
,
6
)
===
'omact_'
)
{
$foundomact
=
true
;
}
}
return
$foundomact
;
}
}
}
question/behaviour/opaque/behaviourtype.php
0 → 100644
View file @
afa2a7f4
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Question behaviour type specifically for use with the Opaque question type.
*
* @package qbehaviour_opaque
* @copyright 2014 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Question behaviour type information specifically for use with the Opaque question type.
*
* @copyright 2014 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class
qbehaviour_opaque_type
extends
question_behaviour_type
{
public
function
can_questions_finish_during_the_attempt
()
{
return
true
;
}
}
question/behaviour/opaque/changes.md
0 → 100644
View file @
afa2a7f4
# Change log for the The Opaque question behaviour
## Changes in 2.9
*
This version works with Moodle 4.0.
## Changes in 2.8
*
Privacy API implementation.
*
Update to use the newer editor_ousupsub, instead of editor_supsub.
*
Setup Travis-CI automated testing integration.
*
Fix some coding style.
*
Due to privacy API support, this version now only works in Moodle 3.4+
For older Moodles, you will need to use a previous version of this plugin.
## 2.7 and before
Changes were not documented here.
question/behaviour/opaque/classes/privacy/provider.php
0 → 100644
View file @
afa2a7f4
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Privacy Subsystem implementation for qbehaviour_opaque.
*
* @package qbehaviour_opaque
* @copyright 2018 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace
qbehaviour_opaque\privacy
;
/**
* Privacy Subsystem for qbehaviour_opaque implementing null_provider.
*
* @copyright 2018 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class
provider
implements
\
core_privacy\local\metadata\null_provider
{
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
*
* @return string
*/
public
static
function
get_reason
()
:
string
{
return
'privacy:metadata'
;
}
}
question/behaviour/opaque/connection.php
0 → 100644
View file @
afa2a7f4
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Defines the qbehaviour_opaque_connection class.
*
* @package qbehaviour_opaque
* @copyright 2011 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
// In config.php, you can set
// $CFG->qtype_opaque_soap_class = 'qtype_opaque_soap_client_with_logging';
// To log every SOAP call in huge detail. Lots are writted to moodledata/temp.
/**
* Wraps the SOAP connection to the question engine, exposing the methods used
* when processing question attempts.
*
* @copyright 2011 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class
qbehaviour_opaque_connection
extends
qtype_opaque_connection
{
/**
* @param string $secret the secret string for this question engine.
* @param int $userid the id of the user attempting this question.
* @return string the passkey that needs to be sent to the quetion engine to
* show that we are allowed to start a question session for this user.
*/
protected
function
generate_passkey
(
$userid
)
{
return
md5
(
$this
->
passkeysalt
.
$userid
);
}
/**
* @param string $remoteid identifies the question.
* @param string $remoteversion identifies the specific version of the quetsion.
* @param aray $data feeds into the initialParams.
* @param question_display_options|null $options controls how the question is displayed.
* @return object and Opaque StartReturn structure.
*/
public
function
start
(
$remoteid
,
$remoteversion
,
$data
,
$cachedresources
,
$options
=
null
)
{
$initialparams
=
array
(
'randomseed'
=>
$data
[
'-_randomseed'
],
'userid'
=>
$data
[
'-_userid'
],
'language'
=>
$data
[
'-_language'
],
'passKey'
=>
$this
->
generate_passkey
(
$data
[
'-_userid'
]),
'preferredbehaviour'
=>
$data
[
'-_preferredbehaviour'
],
);
if
(
array_key_exists
(
'-_attempt'
,
$data
))
{
$initialparams
[
'attempt'
]
=
$data
[
'-_attempt'
];
$initialparams
[
'navigatorVersion'
]
=
'1.9.9'
;
}
if
(
!
is_null
(
$options
))
{
$initialparams
[
'display_readonly'
]
=
(
int
)
$options
->
readonly
;
$initialparams
[
'display_marks'
]
=
(
int
)
$options
->
marks
;
$initialparams
[
'display_markdp'
]
=
(
int
)
$options
->
markdp
;
$initialparams
[
'display_correctness'
]
=
(
int
)
$options
->
correctness
;
$initialparams
[
'display_feedback'
]
=
(
int
)
$options
->
feedback
;
$initialparams
[
'display_generalfeedback'
]
=
(
int
)
$options
->
generalfeedback
;
}
return
$this
->
soapclient
->
start
(
$remoteid
,
$remoteversion
,
$this
->
question_base_url
(),
array_keys
(
$initialparams
),
array_values
(
$initialparams
),
$cachedresources
);
}
/**
* @param string $questionsessionid the question session.
* @param array $respones the post date to process.
*/
public
function
process
(
$questionsessionid
,
$response
)
{
return
$this
->
soapclient
->
process
(
$questionsessionid
,
array_keys
(
$response
),
array_values
(
$response
));
}
/**
* @param string $questionsessionid the question session to stop.
*/
public
function
stop
(
$questionsessionid
)
{