Proyectos de Subversion Moodle

Rev

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