$rcm = $rc->getMethod($function);
$rcm->setAccessible(true);
- $this->setExpectedException('moodle_exception', 'A required parameter (sesskey) was missing');
+ $this->expectException('moodle_exception');
$rcm->invokeArgs($manager, $arguments);
}
$document->load(realpath(__DIR__ . '/fixtures/input.xml'));
$xpath = new \DOMXpath($document);
- $this->setExpectedException("coding_exception");
+ $this->expectException('coding_exception');
$function->invokeArgs(null, [$xpath, $parameters]);
}
ENTER: 13,
SPACE: 32,
ESCAPE: 27,
- COMMA: 188,
+ COMMA: 44,
UP: 38
};
// We handled this event, so prevent it.
e.preventDefault();
return false;
- case KEYS.COMMA:
- if (options.tags) {
- // If we are allowing tags, comma should create a tag (or enter).
- createItem(options, state, originalSelect);
- }
- // We handled this event, so prevent it.
- e.preventDefault();
- return false;
case KEYS.UP:
// Choose the previous active item.
activatePreviousItem(state);
}
return true;
});
+ // Support multi lingual COMMA keycode (44).
+ inputElement.on('keypress', function(e) {
+ if (e.keyCode === KEYS.COMMA) {
+ if (options.tags) {
+ // If we are allowing tags, comma should create a tag (or enter).
+ createItem(options, state, originalSelect);
+ }
+ // We handled this event, so prevent it.
+ e.preventDefault();
+ return false;
+ }
+ return true;
+ });
// Handler used to force set the value from behat.
inputElement.on('behat:set-value', function() {
var suggestionsElement = $(document.getElementById(state.suggestionsId));
}
} else {
- if (array_key_exists($key, $related) && $related[$key] instanceof $classname) {
+ $scalartypes = ['string', 'int', 'bool', 'float'];
+ $scalarcheck = 'is_' . $classname;
+ if (array_key_exists($key, $related) &&
+ ((in_array($classname, $scalartypes) && $scalarcheck($related[$key])) ||
+ ($related[$key] instanceof $classname))) {
$this->related[$key] = $related[$key];
} else {
throw new coding_exception($missingdataerr . $key . ' => ' . $classname);
if (!isset($definition['null'])) {
$customprops[$property]['null'] = NULL_NOT_ALLOWED;
}
+ if (!isset($definition['description'])) {
+ $customprops[$property]['description'] = $property;
+ }
}
$properties += $customprops;
return $properties;
if (!isset($definition['null'])) {
$properties[$property]['null'] = NULL_NOT_ALLOWED;
}
+ if (!isset($definition['description'])) {
+ $properties[$property]['description'] = $property;
+ }
}
return $properties;
}
* Return the list of properties.
*
* The format of the array returned by this method has to match the structure
- * defined in {@link \core\persistent::define_properties()}.
+ * defined in {@link \core\persistent::define_properties()}. Howewer you can
+ * add a new attribute "description" to describe the parameter for documenting the API.
*
* Note that the type PARAM_TEXT should ONLY be used for strings which need to
* go through filters (multilang, etc...) and do not have a FORMAT_* associated
$returns += self::get_context_structure();
} else {
- $returns[$property] = new external_value($definition['type'], $property, $required, $default, $definition['null']);
+ $returns[$property] = new external_value($definition['type'], $definition['description'], $required, $default,
+ $definition['null']);
// Magically treat the format properties.
if ($formatproperty = self::get_format_field($properties, $property)) {
// PARAM_TEXT always becomes PARAM_RAW because filters may be applied.
$type = PARAM_RAW;
}
- $thisvalue = new external_value($type, $property, $proprequired, $propdefault, $definition['null']);
+ $thisvalue = new external_value($type, $definition['description'], $proprequired, $propdefault, $definition['null']);
}
if (!empty($definition['multiple'])) {
- $returns[$property] = new external_multiple_structure($thisvalue, '', $proprequired, $propdefault);
+ $returns[$property] = new external_multiple_structure($thisvalue, $definition['description'], $proprequired,
+ $propdefault);
} else {
$returns[$property] = $thisvalue;
$returns += self::get_context_structure();
} else {
- $returns[$property] = new external_value($definition['type'], $property, $required, $default, $definition['null']);
+ $returns[$property] = new external_value($definition['type'], $definition['description'], $required, $default,
+ $definition['null']);
// Magically treat the format properties.
if ($formatproperty = self::get_format_field($properties, $property)) {
'description' => 'Retrieve the template data for the conversation list',
'type' => 'read',
'ajax' => true,
+ 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
),
'core_message_data_for_messagearea_contacts' => array(
'classname' => 'core_message_external',
'description' => 'Retrieve the template data for the contact list',
'type' => 'read',
'ajax' => true,
+ 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
),
'core_message_data_for_messagearea_messages' => array(
'classname' => 'core_message_external',
'description' => 'Retrieve the template data for the messages',
'type' => 'read',
'ajax' => true,
+ 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
),
'core_message_data_for_messagearea_get_most_recent_message' => array(
'classname' => 'core_message_external',
public function setUp() {
$s = new stdClass();
- $this->validrelated = array('simplestdClass' => $s, 'arrayofstdClass' => array($s, $s), 'context' => null);
- $this->invalidrelated = array('simplestdClass' => 'a string', 'arrayofstdClass' => 5, 'context' => null);
+ $this->validrelated = array(
+ 'simplestdClass' => $s,
+ 'arrayofstdClass' => array($s, $s),
+ 'context' => null,
+ 'aint' => 5,
+ 'astring' => 'valid string',
+ 'abool' => false
+ );
+ $this->invalidrelated = array(
+ 'simplestdClass' => 'a string',
+ 'arrayofstdClass' => 5,
+ 'context' => null,
+ 'aint' => false,
+ 'astring' => 4,
+ 'abool' => 'not a boolean'
+ );
$this->validdata = array('stringA' => 'A string', 'stringAformat' => FORMAT_HTML, 'intB' => 4);
$this->assertEquals($expected, $result->stringA);
$this->assertEquals(FORMAT_HTML, $result->stringAformat);
}
+
+ public function test_properties_description() {
+ $properties = core_testable_exporter::read_properties_definition();
+ // Properties default description.
+ $this->assertEquals('stringA', $properties['stringA']['description']);
+ $this->assertEquals('stringAformat', $properties['stringAformat']['description']);
+ // Properties custom description.
+ $this->assertEquals('intB description', $properties['intB']['description']);
+ // Other properties custom description.
+ $this->assertEquals('otherstring description', $properties['otherstring']['description']);
+ // Other properties default description.
+ $this->assertEquals('otherstrings', $properties['otherstrings']['description']);
+ }
}
/**
protected static function define_related() {
// We cache the context so it does not need to be retrieved from the course.
- return array('simplestdClass' => 'stdClass', 'arrayofstdClass' => 'stdClass[]', 'context' => 'context?');
+ return array('simplestdClass' => 'stdClass', 'arrayofstdClass' => 'stdClass[]', 'context' => 'context?',
+ 'astring' => 'string', 'abool' => 'bool', 'aint' => 'int');
}
protected function get_other_values(renderer_base $output) {
),
'intB' => array(
'type' => PARAM_INT,
+ 'description' => 'intB description',
)
);
}
return array(
'otherstring' => array(
'type' => PARAM_TEXT,
+ 'description' => 'otherstring description',
),
'otherstrings' => array(
'type' => PARAM_TEXT,
public function test_get_popup_notifications_no_user_exception() {
$this->resetAfterTest(true);
- $this->setExpectedException('moodle_exception');
+ $this->expectException('moodle_exception');
$result = message_popup_external::get_popup_notifications(-2132131, false, 0, 0);
}
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
- $this->setExpectedException('moodle_exception');
+ $this->expectException('moodle_exception');
$result = message_popup_external::get_popup_notifications($sender->id, false, 0, 0);
}
public function test_get_unread_popup_notification_count_invalid_user_exception() {
$this->resetAfterTest(true);
- $this->setExpectedException('moodle_exception');
+ $this->expectException('moodle_exception');
$result = message_popup_external::get_unread_popup_notification_count(-2132131, 0);
}
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
- $this->setExpectedException('moodle_exception');
+ $this->expectException('moodle_exception');
$result = message_popup_external::get_unread_popup_notification_count($sender->id, 0);
}
$this->words = $value;
}
+ /**
+ * Forum ID setter search criteria.
+ *
+ * @param int $forumid The forum ID.
+ */
+ public function set_forumid($forumid) {
+ $this->forumid = $forumid;
+ }
+
public function export_for_template(renderer_base $output) {
$data = new stdClass();
. html_writer::select_time('hours', 'tohour', $dateto)
. html_writer::select_time('minutes', 'tominute', $dateto);
+ if ($this->forumid && !empty($this->forumoptions)) {
+ foreach ($this->forumoptions as $index => $option) {
+ if ($option['value'] == $this->forumid) {
+ $this->forumoptions[$index]['selected'] = true;
+ } else {
+ $this->forumoptions[$index]['selected'] = false;
+ }
+ }
+ }
$data->forumoptions = $this->forumoptions;
return $data;
* @return void The function prints the form.
*/
function forum_print_big_search_form($course) {
- global $PAGE, $words, $subject, $phrase, $user, $userid, $fullwords, $notwords, $datefrom, $dateto, $OUTPUT;
+ global $PAGE, $words, $subject, $phrase, $user, $fullwords, $notwords, $datefrom, $dateto, $forumid;
$renderable = new \mod_forum\output\big_search_form($course, $user);
$renderable->set_words($words);
$renderable->set_dateto($dateto);
$renderable->set_subject($subject);
$renderable->set_user($user);
+ $renderable->set_forumid($forumid);
$output = $PAGE->get_renderer('mod_forum');
echo $output->render($renderable);
<td class="c1">
<select name="forumid" id="menuforumid">
{{#forumoptions}}
- <option value="{{value}}">{{name}}</option>
+ <option value="{{value}}" {{#selected}}selected{{/selected}}>{{name}}</option>
{{/forumoptions}}
</select>
</td>
And I follow "Remove heading 'Heading 2'"
And I should see "Are you sure you want to remove the 'Heading 2' section heading?"
And I click on "Yes" "button" in the "Confirm" "dialogue"
+ And I wait until the page is ready
And I wait until "Heading 2" "text" does not exist
Then I should see "Heading 1"
And I should not see "Heading 2"
public function check_file_access($qa, $options, $component, $filearea, $args, $forcedownload) {
if ($component == 'question' && in_array($filearea,
array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback'))) {
- return $this->check_combined_feedback_file_access($qa, $options, $filearea);
+ return $this->check_combined_feedback_file_access($qa, $options, $filearea, $args);
} else if ($component == 'question' && $filearea == 'hint') {
return $this->check_hint_file_access($qa, $options, $args);
} else if ($component == 'question' && in_array($filearea,
array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback'))) {
- return $this->check_combined_feedback_file_access($qa, $options, $filearea);
+ return $this->check_combined_feedback_file_access($qa, $options, $filearea, $args);
} else if ($component == 'question' && $filearea == 'hint') {
return $this->check_hint_file_access($qa, $options, $args);
public function check_file_access($qa, $options, $component, $filearea, $args, $forcedownload) {
if ($component == 'question' && in_array($filearea,
array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback'))) {
- return $this->check_combined_feedback_file_access($qa, $options, $filearea);
+ return $this->check_combined_feedback_file_access($qa, $options, $filearea, $args);
} else if ($component == 'question' && $filearea == 'answer') {
$answerid = reset($args); // Itemid is answer id.
* @param question_attempt $qa the question attempt being displayed.
* @param question_display_options $options the options that control display of the question.
* @param string $filearea the name of the file area.
+ * @param array $args the remaining bits of the file path.
* @return bool whether access to the file should be allowed.
*/
- protected function check_combined_feedback_file_access($qa, $options, $filearea) {
+ protected function check_combined_feedback_file_access($qa, $options, $filearea, $args = null) {
$state = $qa->get_state();
+ if ($args === null) {
+ debugging('You must pass $args as the fourth argument to check_combined_feedback_file_access.',
+ DEBUG_DEVELOPER);
+ $args = array($this->id); // Fake it for now, so the rest of this method works.
+ }
+
if (!$state->is_finished()) {
$response = $qa->get_last_qt_data();
if (!$this->is_gradable_response($response)) {
This files describes API changes for question type plugins.
+=== 3.1.5, 3.2.2, 3.3 ===
+
+* If you are using check_combined_feedback_file_access in your check_file_access method,
+ then you must now pass $args as the 4th argument, so the correct permission checks
+ can be performed. If you don't, you will get a developer debug notice.
+
=== 3.1 ===
* The following functions, previously used (exclusively) by upgrade steps are not available
}
// Check the context actually exists.
-list($context, $course, $cm) = get_context_info_array($contextid);
+try {
+ list($context, $course, $cm) = get_context_info_array($contextid);
+} catch (dml_missing_record_exception $e) {
+ rss_error();
+}
$PAGE->set_context($context);
<td class="c1">
<select name="forumid" id="menuforumid" class="form-control">
{{#forumoptions}}
- <option value="{{value}}">{{name}}</option>
+ <option value="{{value}}" {{#selected}}selected{{/selected}}>{{name}}</option>
{{/forumoptions}}
</select>
</td>
$anotheruser = self::getDataGenerator()->create_user();
$this->setUser($anotheruser);
- $this->setExpectedException('required_capability_exception');
+ $this->expectException('required_capability_exception');
$result = core_user_external::get_user_preferences('', $user->id);
}
defined('MOODLE_INTERNAL') || die();
-$version = 2017020200.00; // YYYYMMDD = weekly release date of this DEV branch.
+$version = 2017020200.01; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.