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
 * Paypal enrolment plugin.
19
 *
20
 * This plugin allows you to set up paid courses.
21
 *
22
 * @package    enrol_paypal
23
 * @copyright  2010 Eugene Venter
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;
1 efrain 29
 
30
/**
31
 * Paypal enrolment plugin implementation.
32
 * @author  Eugene Venter - based on code by Martin Dougiamas and others
33
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
34
 */
35
class enrol_paypal_plugin extends enrol_plugin {
36
 
37
    public function get_currencies() {
38
        // See https://www.paypal.com/cgi-bin/webscr?cmd=p/sell/mc/mc_intro-outside,
39
        // 3-character ISO-4217: https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_currency_codes
40
        $codes = array(
41
            'AUD', 'BRL', 'CAD', 'CHF', 'CZK', 'DKK', 'EUR', 'GBP', 'HKD', 'HUF', 'ILS', 'INR', 'JPY',
42
            'MXN', 'MYR', 'NOK', 'NZD', 'PHP', 'PLN', 'RUB', 'SEK', 'SGD', 'THB', 'TRY', 'TWD', 'USD');
43
        $currencies = array();
44
        foreach ($codes as $c) {
45
            $currencies[$c] = new lang_string($c, 'core_currencies');
46
        }
47
 
48
        return $currencies;
49
    }
50
 
51
    /**
52
     * Returns optional enrolment information icons.
53
     *
54
     * This is used in course list for quick overview of enrolment options.
55
     *
56
     * We are not using single instance parameter because sometimes
57
     * we might want to prevent icon repetition when multiple instances
58
     * of one type exist. One instance may also produce several icons.
59
     *
60
     * @param array $instances all enrol instances of this type in one course
61
     * @return array of pix_icon
62
     */
63
    public function get_info_icons(array $instances) {
64
        $found = false;
65
        foreach ($instances as $instance) {
66
            if ($instance->enrolstartdate != 0 && $instance->enrolstartdate > time()) {
67
                continue;
68
            }
69
            if ($instance->enrolenddate != 0 && $instance->enrolenddate < time()) {
70
                continue;
71
            }
72
            $found = true;
73
            break;
74
        }
75
        if ($found) {
76
            return array(new pix_icon('icon', get_string('pluginname', 'enrol_paypal'), 'enrol_paypal'));
77
        }
78
        return array();
79
    }
80
 
81
    public function roles_protected() {
82
        // users with role assign cap may tweak the roles later
83
        return false;
84
    }
85
 
86
    public function allow_unenrol(stdClass $instance) {
87
        // users with unenrol cap may unenrol other users manually - requires enrol/paypal:unenrol
88
        return true;
89
    }
90
 
91
    public function allow_manage(stdClass $instance) {
92
        // users with manage cap may tweak period and status - requires enrol/paypal:manage
93
        return true;
94
    }
95
 
96
    public function show_enrolme_link(stdClass $instance) {
97
        return ($instance->status == ENROL_INSTANCE_ENABLED);
98
    }
99
 
100
    /**
101
     * Returns true if the user can add a new instance in this course.
102
     * @param int $courseid
103
     * @return boolean
104
     */
105
    public function can_add_instance($courseid) {
106
        $context = context_course::instance($courseid, MUST_EXIST);
107
 
108
        if (!has_capability('moodle/course:enrolconfig', $context) or !has_capability('enrol/paypal:config', $context)) {
109
            return false;
110
        }
111
 
112
        // multiple instances supported - different cost for different roles
113
        return true;
114
    }
115
 
116
    /**
117
     * We are a good plugin and don't invent our own UI/validation code path.
118
     *
119
     * @return boolean
120
     */
121
    public function use_standard_editing_ui() {
122
        return true;
123
    }
124
 
125
    /**
126
     * Add new instance of enrol plugin.
127
     * @param object $course
128
     * @param array $fields instance fields
129
     * @return int id of new instance, null if can not be created
130
     */
1441 ariadna 131
    public function add_instance($course, ?array $fields = null) {
1 efrain 132
        if ($fields && !empty($fields['cost'])) {
133
            $fields['cost'] = unformat_float($fields['cost']);
134
        }
135
        return parent::add_instance($course, $fields);
136
    }
137
 
138
    /**
139
     * Update instance of enrol plugin.
140
     * @param stdClass $instance
141
     * @param stdClass $data modified instance fields
142
     * @return boolean
143
     */
144
    public function update_instance($instance, $data) {
145
        if ($data) {
146
            $data->cost = unformat_float($data->cost);
147
        }
148
        return parent::update_instance($instance, $data);
149
    }
150
 
1441 ariadna 151
    #[\Override]
152
    public function enrol_page_hook(stdClass $instance) {
1 efrain 153
        global $CFG, $USER, $OUTPUT, $PAGE, $DB;
154
 
1441 ariadna 155
        if ($DB->record_exists('user_enrolments', ['userid' => $USER->id, 'enrolid' => $instance->id])) {
156
            return '';
1 efrain 157
        }
158
 
159
        if ($instance->enrolstartdate != 0 && $instance->enrolstartdate > time()) {
1441 ariadna 160
            return '';
1 efrain 161
        }
162
 
163
        if ($instance->enrolenddate != 0 && $instance->enrolenddate < time()) {
1441 ariadna 164
            return '';
1 efrain 165
        }
166
 
1441 ariadna 167
        $course = $DB->get_record('course', ['id' => $instance->courseid]);
1 efrain 168
        $context = context_course::instance($course->id);
169
 
170
        if ( (float) $instance->cost <= 0 ) {
171
            $cost = (float) $this->get_config('cost');
172
        } else {
173
            $cost = (float) $instance->cost;
174
        }
175
 
1441 ariadna 176
        $name = $this->get_instance_name($instance);
177
 
178
        if (abs($cost) < 0.01) {
179
            // No cost, other enrolment methods (instances) should be used.
180
            $notification = new \core\output\notification(get_string('nocost', 'enrol_paypal'), 'error', false);
181
            $notification->set_extra_classes(['mb-0']);
182
            $enrolpage = new enrol_page(
183
                instance: $instance,
184
                header: $name,
185
                body: $OUTPUT->render($notification));
186
            return $OUTPUT->render($enrolpage);
1 efrain 187
        } else {
188
            // Calculate localised and "." cost, make sure we send PayPal the same value,
189
            // please note PayPal expects amount with 2 decimal places and "." separator.
190
            $localisedcost = format_float($cost, 2, true);
191
            $cost = format_float($cost, 2, false);
192
 
1441 ariadna 193
            $body = $OUTPUT->render_from_template('enrol_paypal/enrol_page',
194
                ['currency' => $instance->currency, 'cost' => $localisedcost]);
195
            if (isguestuser() || !isloggedin()) {
196
                $button = new single_button(new moodle_url(get_login_url()), get_string('loginsite'), 'get',
197
                    single_button::BUTTON_PRIMARY);
1 efrain 198
            } else {
1441 ariadna 199
                // Sanitise some fields before building the PayPal form.
200
                $coursefullname  = format_string($course->fullname, true, ['context' => $context]);
201
                $courseshortname = format_string($course->shortname, true, ['context' => $context]);
1 efrain 202
                $userfullname    = fullname($USER);
203
                $userfirstname   = $USER->firstname;
204
                $userlastname    = $USER->lastname;
205
                $useraddress     = $USER->address;
206
                $usercity        = $USER->city;
1441 ariadna 207
                $buttonurl = new moodle_url(empty($CFG->usepaypalsandbox) ?
208
                        'https://www.paypal.com/cgi-bin/webscr' :
209
                        'https://www.sandbox.paypal.com/cgi-bin/webscr',
210
                    [
211
                        'cmd' => '_xclick',
212
                        'charset' => 'utf-8',
213
                        'business' => $this->get_config('paypalbusiness'),
214
                        'item_name' => $coursefullname,
215
                        'item_number' => $courseshortname,
216
                        'quantity' => 1,
217
                        'on0' => get_string("user"),
218
                        'os0' => $userfullname,
219
                        'custom' => "{$USER->id}-{$course->id}-{$instance->id}",
220
                        'currency_code' => $instance->currency,
221
                        'amount' => $cost,
222
                        'for_auction' => 'false',
223
                        'no_note' => 1,
224
                        'no_shipping' => 1,
225
                        'notify_url' => "$CFG->wwwroot/enrol/paypal/ipn.php",
226
                        'return' => "$CFG->wwwroot/enrol/paypal/return.php?id={$course->id}",
227
                        'cancel_return' => $CFG->wwwroot,
228
                        'rm' => 2,
229
                        'cbt' => get_string("continuetocourse"),
230
                        'first_name' => $userfirstname,
231
                        'last_name' => $userlastname,
232
                        'address' => $useraddress,
233
                        'city' => $usercity,
234
                        'email' => $USER->email,
235
                        'country' => $USER->country,
236
                    ]);
237
                $button = new single_button($buttonurl, get_string("sendpaymentbutton", "enrol_paypal"),
238
                    'get', single_button::BUTTON_PRIMARY);
1 efrain 239
            }
240
 
1441 ariadna 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
     * @return bool
328
     */
329
    public function edit_instance_form($instance, MoodleQuickForm $mform, $context) {
330
 
331
        $mform->addElement('text', 'name', get_string('custominstancename', 'enrol'));
332
        $mform->setType('name', PARAM_TEXT);
333
 
334
        $options = $this->get_status_options();
335
        $mform->addElement('select', 'status', get_string('status', 'enrol_paypal'), $options);
336
        $mform->setDefault('status', $this->get_config('status'));
337
 
338
        $mform->addElement('text', 'cost', get_string('cost', 'enrol_paypal'), array('size' => 4));
339
        $mform->setType('cost', PARAM_RAW);
340
        $mform->setDefault('cost', format_float($this->get_config('cost'), 2, true));
341
 
342
        $paypalcurrencies = $this->get_currencies();
343
        $mform->addElement('select', 'currency', get_string('currency', 'enrol_paypal'), $paypalcurrencies);
344
        $mform->setDefault('currency', $this->get_config('currency'));
345
 
346
        $roles = $this->get_roleid_options($instance, $context);
347
        $mform->addElement('select', 'roleid', get_string('assignrole', 'enrol_paypal'), $roles);
348
        $mform->setDefault('roleid', $this->get_config('roleid'));
349
 
350
        $options = array('optional' => true, 'defaultunit' => 86400);
351
        $mform->addElement('duration', 'enrolperiod', get_string('enrolperiod', 'enrol_paypal'), $options);
352
        $mform->setDefault('enrolperiod', $this->get_config('enrolperiod'));
353
        $mform->addHelpButton('enrolperiod', 'enrolperiod', 'enrol_paypal');
354
 
355
        $options = array('optional' => true);
356
        $mform->addElement('date_time_selector', 'enrolstartdate', get_string('enrolstartdate', 'enrol_paypal'), $options);
357
        $mform->setDefault('enrolstartdate', 0);
358
        $mform->addHelpButton('enrolstartdate', 'enrolstartdate', 'enrol_paypal');
359
 
360
        $options = array('optional' => true);
361
        $mform->addElement('date_time_selector', 'enrolenddate', get_string('enrolenddate', 'enrol_paypal'), $options);
362
        $mform->setDefault('enrolenddate', 0);
363
        $mform->addHelpButton('enrolenddate', 'enrolenddate', 'enrol_paypal');
364
 
365
        if (enrol_accessing_via_instance($instance)) {
366
            $warningtext = get_string('instanceeditselfwarningtext', 'core_enrol');
367
            $mform->addElement('static', 'selfwarn', get_string('instanceeditselfwarning', 'core_enrol'), $warningtext);
368
        }
369
    }
370
 
371
    /**
372
     * Perform custom validation of the data used to edit the instance.
373
     *
374
     * @param array $data array of ("fieldname"=>value) of submitted data
375
     * @param array $files array of uploaded files "element_name"=>tmp_file_path
376
     * @param object $instance The instance loaded from the DB
377
     * @param context $context The context of the instance we are editing
378
     * @return array of "element_name"=>"error_description" if there are errors,
379
     *         or an empty array if everything is OK.
380
     * @return void
381
     */
382
    public function edit_instance_validation($data, $files, $instance, $context) {
383
        $errors = array();
384
 
385
        if (!empty($data['enrolenddate']) and $data['enrolenddate'] < $data['enrolstartdate']) {
386
            $errors['enrolenddate'] = get_string('enrolenddaterror', 'enrol_paypal');
387
        }
388
 
389
        $cost = str_replace(get_string('decsep', 'langconfig'), '.', $data['cost']);
390
        if (!is_numeric($cost)) {
391
            $errors['cost'] = get_string('costerror', 'enrol_paypal');
392
        }
393
 
394
        $validstatus = array_keys($this->get_status_options());
395
        $validcurrency = array_keys($this->get_currencies());
396
        $validroles = array_keys($this->get_roleid_options($instance, $context));
397
        $tovalidate = array(
398
            'name' => PARAM_TEXT,
399
            'status' => $validstatus,
400
            'currency' => $validcurrency,
401
            'roleid' => $validroles,
402
            'enrolperiod' => PARAM_INT,
403
            'enrolstartdate' => PARAM_INT,
404
            'enrolenddate' => PARAM_INT
405
        );
406
 
407
        $typeerrors = $this->validate_param_types($data, $tovalidate);
408
        $errors = array_merge($errors, $typeerrors);
409
 
410
        return $errors;
411
    }
412
 
413
    /**
414
     * Execute synchronisation.
415
     * @param progress_trace $trace
416
     * @return int exit code, 0 means ok
417
     */
418
    public function sync(progress_trace $trace) {
419
        $this->process_expirations($trace);
420
        return 0;
421
    }
422
 
423
    /**
424
     * Is it possible to delete enrol instance via standard UI?
425
     *
426
     * @param stdClass $instance
427
     * @return bool
428
     */
429
    public function can_delete_instance($instance) {
430
        $context = context_course::instance($instance->courseid);
431
        return has_capability('enrol/paypal:config', $context);
432
    }
433
 
434
    /**
435
     * Is it possible to hide/show enrol instance via standard UI?
436
     *
437
     * @param stdClass $instance
438
     * @return bool
439
     */
440
    public function can_hide_show_instance($instance) {
441
        $context = context_course::instance($instance->courseid);
442
        return has_capability('enrol/paypal:config', $context);
443
    }
444
}