Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
/**
18
 * Fee enrolment plugin.
19
 *
20
 * This plugin allows you to set up paid courses.
21
 *
22
 * @package    enrol_fee
23
 * @copyright  2019 Shamim Rezaie <shamim@moodle.com>
24
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25
 */
26
 
27
/**
28
 * Fee enrolment plugin implementation.
29
 *
30
 * @copyright  2019 Shamim Rezaie <shamim@moodle.com>
31
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
32
 */
33
class enrol_fee_plugin extends enrol_plugin {
34
 
35
    /**
36
     * Returns the list of currencies that the payment subsystem supports and therefore we can work with.
37
     *
38
     * @return array[currencycode => currencyname]
39
     */
40
    public function get_possible_currencies(): array {
41
        $codes = \core_payment\helper::get_supported_currencies();
42
 
43
        $currencies = [];
44
        foreach ($codes as $c) {
45
            $currencies[$c] = new lang_string($c, 'core_currencies');
46
        }
47
 
48
        uasort($currencies, function($a, $b) {
49
            return strcmp($a, $b);
50
        });
51
 
52
        return $currencies;
53
    }
54
 
55
    /**
56
     * Returns optional enrolment information icons.
57
     *
58
     * This is used in course list for quick overview of enrolment options.
59
     *
60
     * We are not using single instance parameter because sometimes
61
     * we might want to prevent icon repetition when multiple instances
62
     * of one type exist. One instance may also produce several icons.
63
     *
64
     * @param array $instances all enrol instances of this type in one course
65
     * @return array of pix_icon
66
     */
67
    public function get_info_icons(array $instances) {
68
        $found = false;
69
        foreach ($instances as $instance) {
70
            if ($instance->enrolstartdate != 0 && $instance->enrolstartdate > time()) {
71
                continue;
72
            }
73
            if ($instance->enrolenddate != 0 && $instance->enrolenddate < time()) {
74
                continue;
75
            }
76
            $found = true;
77
            break;
78
        }
79
        if ($found) {
80
            return array(new pix_icon('icon', get_string('pluginname', 'enrol_fee'), 'enrol_fee'));
81
        }
82
        return array();
83
    }
84
 
85
    public function roles_protected() {
86
        // Users with role assign cap may tweak the roles later.
87
        return false;
88
    }
89
 
90
    public function allow_unenrol(stdClass $instance) {
91
        // Users with unenrol cap may unenrol other users manually - requires enrol/fee:unenrol.
92
        return true;
93
    }
94
 
95
    public function allow_manage(stdClass $instance) {
96
        // Users with manage cap may tweak period and status - requires enrol/fee:manage.
97
        return true;
98
    }
99
 
100
    public function show_enrolme_link(stdClass $instance) {
101
        return ($instance->status == ENROL_INSTANCE_ENABLED);
102
    }
103
 
104
    /**
105
     * Returns true if the user can add a new instance in this course.
106
     * @param int $courseid
107
     * @return boolean
108
     */
109
    public function can_add_instance($courseid) {
110
        $context = context_course::instance($courseid, MUST_EXIST);
111
 
112
        if (empty(\core_payment\helper::get_supported_currencies())) {
113
            return false;
114
        }
115
 
116
        if (!has_capability('moodle/course:enrolconfig', $context) or !has_capability('enrol/fee:config', $context)) {
117
            return false;
118
        }
119
 
120
        // Multiple instances supported - different cost for different roles.
121
        return true;
122
    }
123
 
124
    /**
125
     * We are a good plugin and don't invent our own UI/validation code path.
126
     *
127
     * @return boolean
128
     */
129
    public function use_standard_editing_ui() {
130
        return true;
131
    }
132
 
133
    /**
134
     * Add new instance of enrol plugin.
135
     * @param object $course
136
     * @param array $fields instance fields
137
     * @return int id of new instance, null if can not be created
138
     */
139
    public function add_instance($course, array $fields = null) {
140
        if ($fields && !empty($fields['cost'])) {
141
            $fields['cost'] = unformat_float($fields['cost']);
142
        }
143
        return parent::add_instance($course, $fields);
144
    }
145
 
146
    /**
147
     * Update instance of enrol plugin.
148
     * @param stdClass $instance
149
     * @param stdClass $data modified instance fields
150
     * @return boolean
151
     */
152
    public function update_instance($instance, $data) {
153
        if ($data) {
154
            $data->cost = unformat_float($data->cost);
155
        }
156
        return parent::update_instance($instance, $data);
157
    }
158
 
159
    /**
160
     * Creates course enrol form, checks if form submitted
161
     * and enrols user if necessary. It can also redirect.
162
     *
163
     * @param stdClass $instance
164
     * @return string html text, usually a form in a text box
165
     */
166
    public function enrol_page_hook(stdClass $instance) {
167
        return $this->show_payment_info($instance);
168
    }
169
 
170
    /**
171
     * Returns optional enrolment instance description text.
172
     *
173
     * This is used in detailed course information.
174
     *
175
     *
176
     * @param object $instance
177
     * @return string short html text
178
     */
179
    public function get_description_text($instance) {
180
        return $this->show_payment_info($instance);
181
    }
182
 
183
    /**
184
     * Generates payment information to display on enrol/info page.
185
     *
186
     * @param stdClass $instance
187
     * @return false|string
188
     * @throws coding_exception
189
     * @throws dml_exception
190
     */
191
    private function show_payment_info(stdClass $instance) {
192
        global $USER, $OUTPUT, $DB;
193
 
194
        ob_start();
195
 
196
        if ($DB->record_exists('user_enrolments', array('userid' => $USER->id, 'enrolid' => $instance->id))) {
197
            return ob_get_clean();
198
        }
199
 
200
        if ($instance->enrolstartdate != 0 && $instance->enrolstartdate > time()) {
201
            return ob_get_clean();
202
        }
203
 
204
        if ($instance->enrolenddate != 0 && $instance->enrolenddate < time()) {
205
            return ob_get_clean();
206
        }
207
 
208
        $course = $DB->get_record('course', array('id' => $instance->courseid));
209
        $context = context_course::instance($course->id);
210
 
211
        if ( (float) $instance->cost <= 0 ) {
212
            $cost = (float) $this->get_config('cost');
213
        } else {
214
            $cost = (float) $instance->cost;
215
        }
216
 
217
        if (abs($cost) < 0.01) { // No cost, other enrolment methods (instances) should be used.
218
            echo '<p>'.get_string('nocost', 'enrol_fee').'</p>';
219
        } else {
220
 
221
            $data = [
222
                'isguestuser' => isguestuser() || !isloggedin(),
223
                'cost' => \core_payment\helper::get_cost_as_string($cost, $instance->currency),
224
                'instanceid' => $instance->id,
225
                'description' => get_string('purchasedescription', 'enrol_fee',
226
                    format_string($course->fullname, true, ['context' => $context])),
227
                'successurl' => \enrol_fee\payment\service_provider::get_success_url('fee', $instance->id)->out(false),
228
            ];
229
            echo $OUTPUT->render_from_template('enrol_fee/payment_region', $data);
230
        }
231
 
232
        return $OUTPUT->box(ob_get_clean());
233
    }
234
 
235
    /**
236
     * Restore instance and map settings.
237
     *
238
     * @param restore_enrolments_structure_step $step
239
     * @param stdClass $data
240
     * @param stdClass $course
241
     * @param int $oldid
242
     */
243
    public function restore_instance(restore_enrolments_structure_step $step, stdClass $data, $course, $oldid) {
244
        global $DB;
245
        if ($step->get_task()->get_target() == backup::TARGET_NEW_COURSE) {
246
            $merge = false;
247
        } else {
248
            $merge = array(
249
                'courseid'   => $data->courseid,
250
                'enrol'      => $this->get_name(),
251
                'roleid'     => $data->roleid,
252
                'cost'       => $data->cost,
253
                'currency'   => $data->currency,
254
            );
255
        }
256
        if ($merge and $instances = $DB->get_records('enrol', $merge, 'id')) {
257
            $instance = reset($instances);
258
            $instanceid = $instance->id;
259
        } else {
260
            $instanceid = $this->add_instance($course, (array) $data);
261
        }
262
        $step->set_mapping('enrol', $oldid, $instanceid);
263
    }
264
 
265
    /**
266
     * Restore user enrolment.
267
     *
268
     * @param restore_enrolments_structure_step $step
269
     * @param stdClass $data
270
     * @param stdClass $instance
271
     * @param int $oldinstancestatus
272
     * @param int $userid
273
     */
274
    public function restore_user_enrolment(restore_enrolments_structure_step $step, $data, $instance, $userid, $oldinstancestatus) {
275
        $this->enrol_user($instance, $userid, null, $data->timestart, $data->timeend, $data->status);
276
    }
277
 
278
    /**
279
     * Return an array of valid options for the status.
280
     *
281
     * @return array
282
     */
283
    protected function get_status_options() {
284
        $options = array(ENROL_INSTANCE_ENABLED  => get_string('yes'),
285
                         ENROL_INSTANCE_DISABLED => get_string('no'));
286
        return $options;
287
    }
288
 
289
    /**
290
     * Return an array of valid options for the roleid.
291
     *
292
     * @param stdClass $instance
293
     * @param context $context
294
     * @return array
295
     */
296
    protected function get_roleid_options($instance, $context) {
297
        if ($instance->id) {
298
            $roles = get_default_enrol_roles($context, $instance->roleid);
299
        } else {
300
            $roles = get_default_enrol_roles($context, $this->get_config('roleid'));
301
        }
302
        return $roles;
303
    }
304
 
305
 
306
    /**
307
     * Add elements to the edit instance form.
308
     *
309
     * @param stdClass $instance
310
     * @param MoodleQuickForm $mform
311
     * @param context $context
312
     * @return bool
313
     */
314
    public function edit_instance_form($instance, MoodleQuickForm $mform, $context) {
315
 
316
        $mform->addElement('text', 'name', get_string('custominstancename', 'enrol'));
317
        $mform->setType('name', PARAM_TEXT);
318
 
319
        $options = $this->get_status_options();
320
        $mform->addElement('select', 'status', get_string('status', 'enrol_fee'), $options);
321
        $mform->setDefault('status', $this->get_config('status'));
322
 
323
        $accounts = \core_payment\helper::get_payment_accounts_menu($context);
324
        if ($accounts) {
325
            $accounts = ((count($accounts) > 1) ? ['' => ''] : []) + $accounts;
326
            $mform->addElement('select', 'customint1', get_string('paymentaccount', 'payment'), $accounts);
327
        } else {
328
            $mform->addElement('static', 'customint1_text', get_string('paymentaccount', 'payment'),
329
                html_writer::span(get_string('noaccountsavilable', 'payment'), 'alert alert-danger'));
330
            $mform->addElement('hidden', 'customint1');
331
            $mform->setType('customint1', PARAM_INT);
332
        }
333
        $mform->addHelpButton('customint1', 'paymentaccount', 'enrol_fee');
334
 
335
        $mform->addElement('text', 'cost', get_string('cost', 'enrol_fee'), array('size' => 4));
336
        $mform->setType('cost', PARAM_RAW);
337
        $mform->setDefault('cost', format_float($this->get_config('cost'), 2, true));
338
 
339
        $supportedcurrencies = $this->get_possible_currencies();
340
        $mform->addElement('select', 'currency', get_string('currency', 'enrol_fee'), $supportedcurrencies);
341
        $mform->setDefault('currency', $this->get_config('currency'));
342
 
343
        $roles = $this->get_roleid_options($instance, $context);
344
        $mform->addElement('select', 'roleid', get_string('assignrole', 'enrol_fee'), $roles);
345
        $mform->setDefault('roleid', $this->get_config('roleid'));
346
 
347
        $options = array('optional' => true, 'defaultunit' => 86400);
348
        $mform->addElement('duration', 'enrolperiod', get_string('enrolperiod', 'enrol_fee'), $options);
349
        $mform->setDefault('enrolperiod', $this->get_config('enrolperiod'));
350
        $mform->addHelpButton('enrolperiod', 'enrolperiod', 'enrol_fee');
351
 
352
        $options = array('optional' => true);
353
        $mform->addElement('date_time_selector', 'enrolstartdate', get_string('enrolstartdate', 'enrol_fee'), $options);
354
        $mform->setDefault('enrolstartdate', 0);
355
        $mform->addHelpButton('enrolstartdate', 'enrolstartdate', 'enrol_fee');
356
 
357
        $options = array('optional' => true);
358
        $mform->addElement('date_time_selector', 'enrolenddate', get_string('enrolenddate', 'enrol_fee'), $options);
359
        $mform->setDefault('enrolenddate', 0);
360
        $mform->addHelpButton('enrolenddate', 'enrolenddate', 'enrol_fee');
361
 
362
        if (enrol_accessing_via_instance($instance)) {
363
            $warningtext = get_string('instanceeditselfwarningtext', 'core_enrol');
364
            $mform->addElement('static', 'selfwarn', get_string('instanceeditselfwarning', 'core_enrol'), $warningtext);
365
        }
366
    }
367
 
368
    /**
369
     * Perform custom validation of the data used to edit the instance.
370
     *
371
     * @param array $data array of ("fieldname"=>value) of submitted data
372
     * @param array $files array of uploaded files "element_name"=>tmp_file_path
373
     * @param object $instance The instance loaded from the DB
374
     * @param context $context The context of the instance we are editing
375
     * @return array of "element_name"=>"error_description" if there are errors,
376
     *         or an empty array if everything is OK.
377
     * @return void
378
     */
379
    public function edit_instance_validation($data, $files, $instance, $context) {
380
        $errors = array();
381
 
382
        if (!empty($data['enrolenddate']) and $data['enrolenddate'] < $data['enrolstartdate']) {
383
            $errors['enrolenddate'] = get_string('enrolenddaterror', 'enrol_fee');
384
        }
385
 
386
        $cost = str_replace(get_string('decsep', 'langconfig'), '.', $data['cost']);
387
        if (!is_numeric($cost)) {
388
            $errors['cost'] = get_string('costerror', 'enrol_fee');
389
        }
390
 
391
        $validstatus = array_keys($this->get_status_options());
392
        $validcurrency = array_keys($this->get_possible_currencies());
393
        $validroles = array_keys($this->get_roleid_options($instance, $context));
394
        $tovalidate = array(
395
            'name' => PARAM_TEXT,
396
            'status' => $validstatus,
397
            'currency' => $validcurrency,
398
            'roleid' => $validroles,
399
            'enrolperiod' => PARAM_INT,
400
            'enrolstartdate' => PARAM_INT,
401
            'enrolenddate' => PARAM_INT
402
        );
403
 
404
        $typeerrors = $this->validate_param_types($data, $tovalidate);
405
        $errors = array_merge($errors, $typeerrors);
406
 
407
        if ($data['status'] == ENROL_INSTANCE_ENABLED &&
408
                (!$data['customint1']
409
                    || !array_key_exists($data['customint1'], \core_payment\helper::get_payment_accounts_menu($context)))) {
410
            $errors['status'] = 'Enrolments can not be enabled without specifying the payment account';
411
        }
412
 
413
        return $errors;
414
    }
415
 
416
    /**
417
     * Execute synchronisation.
418
     * @param progress_trace $trace
419
     * @return int exit code, 0 means ok
420
     */
421
    public function sync(progress_trace $trace) {
422
        $this->process_expirations($trace);
423
        return 0;
424
    }
425
 
426
    /**
427
     * Is it possible to delete enrol instance via standard UI?
428
     *
429
     * @param stdClass $instance
430
     * @return bool
431
     */
432
    public function can_delete_instance($instance) {
433
        $context = context_course::instance($instance->courseid);
434
        return has_capability('enrol/fee:config', $context);
435
    }
436
 
437
    /**
438
     * Is it possible to hide/show enrol instance via standard UI?
439
     *
440
     * @param stdClass $instance
441
     * @return bool
442
     */
443
    public function can_hide_show_instance($instance) {
444
        $context = context_course::instance($instance->courseid);
445
        return has_capability('enrol/fee:config', $context);
446
    }
447
}