• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ProjektAdLer / MoodlePluginModAdleradaptivity / 9065348963

13 May 2024 03:08PM UTC coverage: 68.64% (-0.04%) from 68.675%
9065348963

push

github

Glutamat42
refactor module deletion code to throw exception on errors, instead of returning false (as suggested by documentation)

1 of 1 new or added line in 1 file covered. (100.0%)

2 existing lines in 1 file now uncovered.

626 of 912 relevant lines covered (68.64%)

6.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.48
/lib.php
1
<?php
2

3
use core_completion\api as completion_api;
4
use local_logging\logger;
5
use mod_adleradaptivity\local\db\adleradaptivity_attempt_repository;
6
use mod_adleradaptivity\local\db\adleradaptivity_question_repository;
7
use mod_adleradaptivity\local\db\adleradaptivity_repository;
8
use mod_adleradaptivity\local\db\adleradaptivity_task_repository;
9
use mod_adleradaptivity\local\helpers;
10

11

12
/**
13
 * Return if the plugin supports $feature.
14
 *
15
 * @param string $feature Constant representing the feature.
16
 * @return string|true|null True if the feature is supported, null otherwise. Or a string for FEATURE_MOD_PURPOSE (moodle logic ...)
17
 */
18
function adleradaptivity_supports($feature) {
19
    switch ($feature) {
20
//        case FEATURE_COMPLETION_TRACKS_VIEWS:  // seems to add the "Require view" checkbox to the "when conditions are met" in the "activity completion" section of the activity settings
21
        case FEATURE_COMPLETION_HAS_RULES:  // custom completion rules
22
        case FEATURE_USES_QUESTIONS:
23
        case FEATURE_MOD_INTRO:
24
        case FEATURE_BACKUP_MOODLE2:
25
            return true;
19✔
26
        case FEATURE_MOD_PURPOSE:  # new since moodle 4.0 https://moodledev.io/docs/4.1/devupdate#activity-icons
27
            return MOD_PURPOSE_ASSESSMENT;
×
28
        default:
29
            return null;
19✔
30
    }
31
}
32

33
/** The [modname]_add_instance() function is called when the activity
34
 * creation form is submitted. This function is only called when adding
35
 * an activity and should contain any logic required to add the activity.
36
 *
37
 *
38
 *
39
 * @param $instancedata
40
 * @param $mform
41
 * @return int
42
 * @throws dml_exception
43
 */
44
function adleradaptivity_add_instance($instancedata, $mform = null): int {
45
    $instancedata->timemodified = time();
20✔
46

47
    $id = (new adleradaptivity_repository())->create_adleradaptivity($instancedata);
20✔
48

49
    // Update completion date event. This is a default feature activated for all modules (create module -> Activity completion).
50
    $completiontimeexpected = !empty($instancedata->completionexpected) ? $instancedata->completionexpected : null;
20✔
51
    completion_api::update_completion_date_event($instancedata->coursemodule, 'adleradaptivity', $id, $completiontimeexpected);
20✔
52

53
    return $id;
20✔
54
}
55

56
/** The [modname]_update_instance() function is called when the activity
57
 * editing form is submitted.
58
 *
59
 * @param $moduleinstance
60
 * @param null $mform
61
 * @return bool
62
 * @throws moodle_exception
63
 */
64
function adleradaptivity_update_instance($moduleinstance, $mform = null): bool {
65
    throw new moodle_exception('unsupported', 'adleradaptivity', '', 'update_instance() is not supported');
1✔
66
}
67

68
/** The adleradaptivity_delete_instance() function is called when the activity
69
 * deletion is confirmed. It is responsible for removing all data associated
70
 * with the instance.
71
 * questions itself are not deleted here as they belong to the course, not to the module. The adleradaptivity_questions are deleted.
72
 *
73
 * @param $instance_id int The instance id of the module to delete.
74
 * @return bool true if success, false if failed.
75
 * @throws dml_transaction_exception if the transaction failed and could not be rolled back.
76
 * @throws dml_exception
77
 */
78
function adleradaptivity_delete_instance(int $instance_id): bool {
79
    global $DB;
80

81
    $logger = new logger('mod_adleradaptivity', 'lib.php');
6✔
82
    $adleradaptivity_attempt_repository = new adleradaptivity_attempt_repository();
6✔
83
    $adleradaptivity_tasks_repository = new adleradaptivity_task_repository();
6✔
84
    $adleradaptivity_question_repository = new adleradaptivity_question_repository();
6✔
85
    $adleradaptivity_repository = new adleradaptivity_repository();
6✔
86

87

88
    // there is no transaction above this level. Unsuccessful deletions basically result in unpredictable
89
    // behaviour. This at least ensures this module is either deleted completely or not at all.
90
    $transaction = $DB->start_delegated_transaction();
6✔
91

92
    try {
93
        // first ensure that the module instance exists
94
        $adleradaptivity_repository->get_instance_by_instance_id($instance_id);
6✔
95

96
        // load all attempts related to $instance_id
97
        $cm = get_coursemodule_from_instance('adleradaptivity', $instance_id, 0, false, MUST_EXIST);
5✔
98
        $attempts = $adleradaptivity_attempt_repository->get_adleradaptivity_attempt_by_cmid($cm->id);
5✔
99
        // delete all attempts
100
        foreach ($attempts as $attempt) {
5✔
101
            $adleradaptivity_attempt_repository->delete_adleradaptivity_attempt_by_question_usage_id($attempt->attempt_id);
3✔
102
            question_engine::delete_questions_usage_by_activity($attempt->attempt_id);
3✔
103
        }
104

105
        // delete the module itself and all related tasks and questions
106
        // load required data
107
        $adler_tasks = $adleradaptivity_tasks_repository->get_tasks_by_adleradaptivity_id($instance_id);
5✔
108
        $adler_questions = [];
5✔
109
        foreach ($adler_tasks as $task) {
5✔
110
            $adler_questions = array_merge($adler_questions, helpers::load_questions_by_task_id($task->id, true));
4✔
111
        }
112
        // perform deletion
113
        foreach ($adler_questions as $question) {
5✔
114
            $adleradaptivity_question_repository->delete_question_by_id($question->id);
4✔
115
        }
116
        foreach ($adler_tasks as $task) {
4✔
117
            $adleradaptivity_tasks_repository->delete_task_by_id($task->id);
3✔
118
        }
119
        $adleradaptivity_repository->delete_adleradaptivity_by_id($instance_id);
4✔
120

121
        $transaction->allow_commit();
4✔
122
    } catch (Exception $e) {
2✔
123
        $logger->error('Could not delete adleradaptivity instance with id ' . $instance_id);
2✔
124
        $logger->error($e->getMessage());
2✔
125
        // although the existing documentation suggests this method should return true|false depending
126
        // on whether the deletion succeeded, this does not actually lead to the desired behaviour.
127
        // In case of an error this should throw an exception as otherwise:
128
        // - postgresql databases are not rolled back
129
        // - course deletion succeeds without indication of an error
130
        // - only module deletion behaves as expected and shows an error
131
        $transaction->rollback($e);
2✔
132
    }
133

134
    return true;
4✔
135
}
136

137

138
// TODO: maybe test
139
/**
140
 * Add a get_coursemodule_info function to add 'extra' information
141
 *
142
 * Given a course_module object, this function returns any "extra" information that may be needed
143
 * when printing this activity in a course listing.  See get_array_of_activities() in course/lib.php.
144
 *
145
 * @param stdClass $coursemodule The coursemodule object (record).
146
 * @return cached_cm_info An object on information that the courses
147
 *                        will know about (most noticeably, an icon).
148
 * @throws dml_exception
149
 */
150
function adleradaptivity_get_coursemodule_info($coursemodule) {
151
    global $DB;
152

153
    $dbparams = ['id' => $coursemodule->instance];
17✔
154
    if (!$cm = $DB->get_record('adleradaptivity', $dbparams)) {
17✔
UNCOV
155
        return false;
×
156
    }
157

158
    $result = new cached_cm_info();
17✔
159
    $result->name = $cm->name;
17✔
160

161
    // This populates the description field in the course overview
162
    if ($coursemodule->showdescription) {
17✔
UNCOV
163
        $result->content = format_module_intro('adleradaptivity', $cm, $coursemodule->id, false);
×
164
    }
165

166
    // for some inexplicable reason moodle requires an object for each custom rule defined in get_defined_custom_rules(),
167
    // otherwise they at least don't fully work or even don't work at all.
168
    // If missing the completion requirement (completion rule) is not shown in the "completion list" in course overview and
169
    // completing the activity that has this rule active did not work in a short test.
170
    // As I don't have any data to append to the course_module object for my rule I just use some random data.
171
    if ($coursemodule->completion == COMPLETION_TRACKING_AUTOMATIC) {
17✔
172
        $result->customdata['customcompletionrules']['default_rule'] = "some random string to make the completion rule work";
17✔
173
    }
174
    return $result;
17✔
175
}
176

177
// --------
178
// methods required according to mod/README.md
179
// --------
180
// The following ones are actually not required and not implemented as they are not needed for the functionality of this module.
181
//
182
//
183
///**
184
// * Given a course and a date, prints a summary of the recent activity happened in this module.
185
// * As mod_quiz does not implement this method i assume it is not required.
186
// *
187
// * It is shown eg in in the "recent activity" sidebar block.
188
// * The actual output of this method (what is shown in the "recent activity") is outputted via echo.
189
// * Return value is just "success"
190
// *
191
// * @uses CONTEXT_MODULE
192
// * @param object $course
193
// * @param bool $viewfullnames capability
194
// * @param int $timestart
195
// * @return bool success
196
// */
197
//function adleradaptivity_print_recent_activity($course, $viewfullnames, $timestart) {
198
//    echo "(The recent activity of this modul)>";
199
//    return true;
200
//}
201
//
202
//The functions xxx_user_outline() and xxx_user_complete() have been removed from the majority of core modules (see MDL-41286),
203
//except for those that require unique functionality. These functions are used by the outline report, but now if they no longer
204
//exist, the default behaviour is chosen, which supports the legacy and standard log storages introduced in 2.7 (see MDL-41266).
205
//It is highly recommended you remove these functions from your module if they are simply performing the default behaviour.
206
//
207
///**
208
// *  Print a detailed representation of what a user has done with
209
// *  a given particular instance of this module, for user activity reports.
210
// *
211
// * @param $course
212
// * @param $user
213
// * @param $mod
214
// * @param stdClass $adleradaptivity database record of the module instance
215
// * @return void
216
// */
217
//function adleradaptivity_user_complete($course, $user, $mod, $adleradaptivity) {}
218
//
219
///**
220
// * Return a small object with summary information about what a
221
// * user has done with a given particular instance of this module
222
// * Used for user activity reports.
223
// * $return->time = the time they did it
224
// * $return->info = a short text description
225
// *
226
// * @param stdClass $course
227
// * @param stdClass $user
228
// * @param cm_info|stdClass $mod
229
// * @param stdClass $feedback
230
// * @return stdClass
231
// */
232
//function feedback_user_outline($course, $user, $mod, $feedback) {}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc