├── README.md ├── auth.php ├── composer.json ├── db └── upgrade.php ├── icon.svg ├── lang └── en │ └── auth_wp2moodle.php ├── locallib.php ├── login.php ├── settings.php ├── version.php ├── wp2m.js ├── wp2m.php └── wp2m_settings_page.php /README.md: -------------------------------------------------------------------------------- 1 | wp2moodle 2 | ========= 3 | 4 | WordPress to Moodle (wp2moodle) is a plugin that allows users in WordPress to open Moodle courses without getting a logon box in between. It will also (optionally) enrol the user into cohorts, courses and groups. 5 | 6 | It uses an encrypted link and **doesn’t rely on SSL / https** (though it’s recommended you use SSL where possible). Your WordPress and Moodle servers might be on the same host, or can be on different networks or server technologies. Since it only uses hyperlinks to communicate, there’s no special setup. 7 | 8 | The plugin has these limitations by design: 9 | 10 | - The users that created through this plugin can’t sign in to Moodle using their WordPress username – they must sign in from the link this plugin generates. 11 | - You can’t go in reverse; i.e. log onto Moodle and be signed back into WordPress (using those users – other auth plugins still work) 12 | - WordPress is not notified of any course results 13 | - WordPress is not notified of any changes to the user profile done by Moodle (though the plugin normally disables the password) 14 | - WordPress has no way of knowing if the values being linked to exist within Moodle (e.g. it doesn't check your work) 15 | 16 | Data is encrypted (using aes-256-cbc via openssl) at the Wordpress end and handed over a standard http GET request. Only the minimum required information is sent in order to create a Moodle user record. The user is automatically created if not present at the Moodle end, and then authenticated, and (optionally) enrolled in a Cohort, a Group, or both. 17 | 18 | How it works 19 | ------------ 20 | 21 | This plugin allows you to place a shortcode in a post that passes encrypted logon information to Moodle (requires this plugin to be also installed into Moodle). The user will be added to Moodle and optionally enrolled in the specified Cohort(s), Course(s) and/or Group(s). If a user is deleted or suspended in Moodle, they will redirect to an url (either the Moodle login page, or a URL you specify). 22 | 23 | Use the Moodle button on the rich editor to insert the shortcode, or enter the details manually using the examples below as a guide. 24 | 25 | Example: `[wp2moodle class='css-classname' group='group1' cohort='class1' target='_blank' authtext='Please log on']launch the course[/wp2moodle]` 26 | 27 | 28 | | Attribute | Kind | Purpose | Example | 29 | | --- | --- | --- | --- | 30 | | `class` | optional | defaults to 'wp2moodle'; CSS class attribute of link | `[wp2moodle course='abc1' class='wp2m-link']Open[/wp2moodle]` | 31 | | `cohort` | optional, csv | idnumber of one or more cohorts in which to enrol a user | `[wp2moodle cohort='business_cert3']enrol in Cert 3 Business[/wp2moodle]` | 32 | | `group` | optional, csv | idnumber of one or more groups in which to enrol | `[wp2moodle group='eng14_a,math14_b,hist13_c']Math, English & History[/wp2moodle]` | 33 | | `course` | optional, csv | idnumber of one or more courses in which to enrol | `[wp2moodle course='abc1,abc2,def1']Enrol in 3 courses[/wp2moodle]` | 34 | | `target` | optional | defaults to '_self'; href target attribute of link | `[wp2moodle course='abc1' target='_blank']Open[/wp2moodle]` | 35 | | `authtext` | optional | string to display if not yet logged on, can be a shortcode | `[wp2moodle authtext='Please log on first' course='abc1']Open the course[/wp2moodle]` | 36 | | `activity` | optional, depreciated | 1-based index (count) of visible activites | `[wp2moodle course='abc1' activity='2']Open course page[/wp2moodle]` | 37 | | `cmid` |optional | Activity ID to open (e.g. /mod/plugin/view.php?id=XXX) | `[wp2moodle course='abc1' cmid='4683']Open course blog[/wp2moodle]` | 38 | | `url` | optional | Url to open after logon (overrides everything else) | `/mod/customplugin/index.php?id=123` | 39 | 40 | *Note* **csv** means comma separated values, no extra spaces 41 | 42 | Requirements 43 | ------------ 44 | PHP 5.6+ (Reccomended: 7.3 or higher) 45 | Moodle 3.1 or above (Reccomended: 3.6.4 or higher, last checked in 3.10.1+) 46 | Wordpress 4 or above (Reccomended: 5.2.2 or higher, last checked in 5.7) 47 | openssl extension on your php (you probably have this) 48 | 49 | How to install this plugin 50 | --------------------- 51 | 52 | 1. download the plugin into a zip file named wp2moodle.zip 53 | 2. in wordpress choose `Plugins > Add New > Upload Plugin` and upload and active the plugin in the normal way 54 | 3. in moodle choose `Site Administration > Plugins > Install plugins` and upload and activate thie plugin in the normal way 55 | 56 | 57 | Usage: 58 | ------ 59 | You can not use this plugin directly; it is launched by wp2moodle from within Wordpress. 60 | 61 | *IMPORTANT*: when linking to things by their `id` make sure you use the moodle field `id number`. This is often blank by default - you need to set it. 62 | 63 | 64 | Problems? 65 | --------- 66 | If you are having problems, try these first. If you raise an issue, let me know ALL the version numbers of your installations, what server platform they are running on, and any relevent error messages, otherwise I won't be able to help. 67 | 68 | 1. Confirm that you have the requirement met to run the plugin (e.g. openssl must be installed and show up in phpinfo) 69 | 2. Confirm that your course has the appropriate enrolment providers set up already (e.g. cohort based enrolment or manual enrolment) 70 | 3. Confirm that your shortcode is working in Wordpress 71 | 4. Confirm that you are using the text/string version of an identifier and NOT the numerical id of a course or cohort. the Id Number field is NOT set by default in moodle- you have to add something. 72 | 5. Look in your sites php error log to see if you can see if the plugin is silently throwing an error that you are not seeing on the page. In Moodle you can turn on DEVELOPER DEBUGGING to reveal crashes or error messages. 73 | 6. If you're trying one lookup type (e.g. group) then try switching to a different type (e.g. cohort). This may help me narrow down if it's a particular lookup type that is affected. 74 | 7. If you are using IDNUMBER matching on the user, ensure you have a prefix set and the value isn't clashing with existing user records (e.g. mdlw8_user_mneuse_uix error) 75 | 76 | Licence: 77 | -------- 78 | GPL3, as per Moodle. -------------------------------------------------------------------------------- /auth.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * @author Tim St.Clair 19 | * @author Mike Uding - Only changes from 2015-01. 20 | * @license http://www.gnu.org/copyleft/gpl.html GNU Public License 21 | * @package moodle / wordpress single sign on (wp2moodle) 22 | * 23 | * source https://github.com/frumbert/wp2moodle 24 | * Authentication Plugin: Wordpress 2 Moodle Single Sign On 25 | * 26 | * 2012-05-28 File created. 27 | * 2015-01-06 Added support for courses to be added via the login hook 28 | */ 29 | 30 | if (!defined('MOODLE_INTERNAL')) { 31 | die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page 32 | } 33 | 34 | require_once($CFG->libdir.'/authlib.php'); 35 | 36 | /** 37 | * Plugin for no authentication. 38 | */ 39 | class auth_plugin_wp2moodle extends auth_plugin_base { 40 | 41 | /** 42 | * Constructor. 43 | */ 44 | public function auth_plugin_wp2moodle() { 45 | debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER); 46 | self::__construct(); 47 | } 48 | function __construct() { 49 | $this->authtype = 'wp2moodle'; 50 | $this->config = get_config('auth_wp2moodle'); 51 | } 52 | 53 | /** 54 | * Returns true if the username and password work or don't exist and false 55 | * if the user exists and the password is wrong. 56 | * 57 | * @param string $username The username 58 | * @param string $password The password 59 | * @return bool Authentication success or failure. 60 | */ 61 | function user_login ($username, $password = null) { 62 | global $CFG, $DB; 63 | if ($password == null || $password == '') { return false; } 64 | if ($user = $DB->get_record('user', array('username'=>$username, 'password'=>$password, 'mnethostid'=>$CFG->mnet_localhost_id))) { 65 | return true; 66 | } 67 | return false; 68 | } 69 | 70 | function prevent_local_passwords() { 71 | return true; 72 | } 73 | 74 | /** 75 | * Returns true if this authentication plugin is 'internal'. 76 | * 77 | * @return bool 78 | */ 79 | function is_internal() { 80 | return false; 81 | } 82 | 83 | /** 84 | * Returns true if this authentication plugin can change the user's 85 | * password. 86 | * 87 | * @return bool 88 | */ 89 | function can_change_password() { 90 | return false; 91 | } 92 | 93 | /** 94 | * Returns the URL for changing the user's pw, or empty if the default can 95 | * be used. 96 | * 97 | * @return moodle_url 98 | */ 99 | function change_password_url() { 100 | return null; 101 | } 102 | 103 | /** 104 | * Returns true if plugin allows resetting of internal password. 105 | * 106 | * @return bool 107 | */ 108 | function can_reset_password() { 109 | return false; 110 | } 111 | 112 | function logoutpage_hook() { 113 | global $SESSION; 114 | set_moodle_cookie('nobody'); 115 | require_logout(); 116 | if (isset($this->config->logoffurl)) { 117 | if (ob_get_level() !== 0) ob_end_clean(); // in case we are inside a buffer 118 | // 301: move permanently 119 | // 302: found 120 | // 303: see other 121 | // 307: temporary redirect 122 | header("Location: " . $this->config->logoffurl, true, 301); 123 | exit; // flush header 124 | } 125 | } 126 | 127 | 128 | /** 129 | * Prints a form for configuring this authentication plugin. 130 | * 131 | * This function is called from admin/auth.php, and outputs a full page with 132 | * a form for configuring this plugin. 133 | * 134 | * @param array $page An object containing all the data for this page. 135 | */ 136 | function config_form($config, $err, $user_fields) { 137 | include "config.html"; 138 | } 139 | 140 | /** 141 | * Processes and stores configuration data for this authentication plugin. 142 | */ 143 | function process_config($config) { 144 | // set to defaults if undefined 145 | if (!isset($config->sharedsecret)) { 146 | $config->sharedsecret = 'this is not a secure key, change it'; 147 | } 148 | if (!isset($config->timeout)) { 149 | $config->timeout = '5'; 150 | } 151 | if (!isset($config->logoffurl)) { 152 | $config->logoffurl = ''; 153 | } 154 | if (!isset($config->autoopen)) { 155 | $config->autoopen = 'no'; 156 | } 157 | if (!isset($config->updateuser)) { 158 | $config->updateuser = 'yes'; 159 | } 160 | if (!isset($config->redirectnoenrol)) { 161 | $config->redirectnoenrol = 'no'; 162 | } 163 | if (!isset($config->idprefix)) { 164 | $config->idprefix = ''; 165 | } 166 | if (!isset($config->updateuser)) { 167 | $config->updateuser = 'no'; 168 | } 169 | 170 | // save settings 171 | set_config('sharedsecret', $config->sharedsecret, 'auth_wp2moodle'); 172 | set_config('logoffurl', $config->logoffurl, 'auth_wp2moodle'); 173 | set_config('timeout', $config->timeout, 'auth_wp2moodle'); 174 | set_config('autoopen', $config->autoopen, 'auth_wp2moodle'); 175 | set_config('updateuser', $config->updateuser, 'auth_wp2moodle'); 176 | set_config('updateuser', $config->updateuser, 'auth_wp2moodle'); 177 | set_config('redirectnoenrol', $config->redirectnoenrol, 'auth_wp2moodle'); 178 | set_config('idprefix', $config->idprefix, 'auth_wp2moodle'); 179 | 180 | return true; 181 | } 182 | 183 | } 184 | 185 | 186 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frumbert/wp2moodle", 3 | "description": "combination moodle and wordpress plugin for single sign on from wordpress to moodle", 4 | "type": "wordpress-plugin", 5 | "keywords": [ 6 | "moodle", 7 | "wordpress" 8 | ], 9 | "homepage": "http://wordpress.frumbert.org", 10 | "license": "GPL2", 11 | "support": { 12 | "issues": "https://github.com/frumbert/wp2moodle" 13 | }, 14 | "require": { 15 | "composer/installers": "~1.0", 16 | "php": ">=5.6" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /db/upgrade.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * CAS authentication plugin upgrade code 19 | * 20 | * @package auth_cas 21 | * @subpackage wp2moodle 22 | * @copyright 2021 Tim St.Clair (tim.stclair@gmail.com) 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | /** 29 | * Function to upgrade auth_cas. 30 | * @param int $oldversion the version we are upgrading from 31 | * @return bool result 32 | */ 33 | function xmldb_auth_wp2moodle_upgrade($oldversion) { 34 | global $CFG; 35 | 36 | if ($oldversion < 2020111500) { 37 | set_config('settings_matchfield', 'idnumber', 'auth_wp2moodle'); 38 | upgrade_plugin_savepoint(true, 2020111500, 'auth', 'wp2moodle'); 39 | } 40 | 41 | return true; 42 | } 43 | 44 | -------------------------------------------------------------------------------- /icon.svg: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /lang/en/auth_wp2moodle.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * @package auth_wp2moodle 20 | * @copyright 2012 onwards Tim St.Clair 21 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 22 | */ 23 | 24 | $string['pluginname'] = 'Wordpress 2 Moodle'; 25 | 26 | $string['settings_heading'] = 'Wordpress 2 Moodle'; 27 | $string['settings_description'] = 'Uses Wordpress user details to create user & log onto Moodle (one way Single Sign On)'; 28 | 29 | $string['settings_sharedsecret'] = 'Shared secret'; 30 | $string['settings_sharedsecret_desc'] = 'Encryption key which matches Wordpress'; 31 | 32 | $string['settings_timeout'] = 'Link timeout'; 33 | $string['settings_timeout_desc'] = 'Minutes before incoming link is considered invalid (use 0 for no expiry)'; 34 | 35 | $string['settings_logoffurl'] = 'Logoff Url'; 36 | $string['settings_logoffurl_desc'] = 'Url to redirect to if the user presses Logoff (optional)'; 37 | 38 | $string['settings_invalidloginurl'] = 'Invalid Login Url'; 39 | $string['settings_invalidloginurl_desc'] = 'Url to redirect to if the user is suspended or deleted'; 40 | 41 | $string['settings_autoopen'] = 'Auto open course'; 42 | $string['settings_autoopen_desc'] = 'Automatically open the course after successful authentication'; 43 | 44 | $string['settings_updateuser'] = 'Update user profile fields using Wordpress values?'; 45 | $string['settings_updateuser_desc'] = 'When YES, user profile fields (firstname, lastname, email, idnumber) are updated to use the supplied values. Turn this off if you want to let the user manage their profile fields independantly.'; 46 | 47 | $string['settings_redirectnoenrol'] = 'Only redirect user to course?'; 48 | $string['settings_redirectnoenrol_desc'] = 'When YES, course enrolment is bypassed. The user will still be redirected to the course homepage (if not otherwise overridden).'; 49 | 50 | $string['settings_firstname'] = 'First name (if empty)'; 51 | $string['settings_firstname_desc'] = 'If no first name is specified by Wordpress, use this value'; 52 | 53 | $string['settings_lastname'] = 'Last name (if empty)'; 54 | $string['settings_lastname_desc'] = 'If no last name is specified by Wordpress, use this value'; 55 | 56 | $string['settings_matchfield'] = 'Field used to match'; 57 | $string['settings_matchfield_desc'] = 'When creating or matching users, use this database field to match records (default: idnumber).'; 58 | 59 | $string['settings_idprefix'] = 'Prefix for user idnumber'; 60 | $string['settings_idprefix_desc'] = 'Optional string value to store in front of of the idnumber to avoid clashes (default: wp2m).'; 61 | 62 | $string['notloggedindebug'] = 'The login attempt failed. Reason: {$a}'; 63 | $string['loginerror_invaliddomain'] = 'The email address is not allowed at this site.'; 64 | 65 | $string['settings_usernameemail'] = 'Set username to email'; 66 | $string['settings_usernameemail_desc'] = 'Prefer email to username in username field when creating or updating records.'; -------------------------------------------------------------------------------- /locallib.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * decode a string encrypted with openssl 19 | */ 20 | function wp2m_base64_decode($b64) { 21 | return base64_decode(str_replace(array('-','_'),array('+','/'),$b64)); 22 | } 23 | function wp2m_is_base64($string) { 24 | $decoded = base64_decode($string, true); 25 | // Check if there is no invalid character in string 26 | if (!preg_match('/^[a-zA-Z0-9\/\r\n+]*={0,2}$/', $string)) return false; 27 | // Decode the string in strict mode and send the response 28 | if (!base64_decode($string, true)) return false; 29 | // Encode and compare it to original one 30 | if (base64_encode($decoded) != $string) return false; 31 | return true; 32 | } 33 | 34 | function decrypt_string($data, $key) { 35 | if ( wp2m_is_base64($key)) { 36 | $encryption_key = base64_decode($key); 37 | } else { 38 | $encryption_key = $key; 39 | } 40 | list($encrypted_data, $iv) = explode('::', wp2m_base64_decode($data), 2); 41 | return openssl_decrypt($encrypted_data, 'aes-256-cbc', $encryption_key, 0, $iv); 42 | } 43 | 44 | /** 45 | * querystring helper, returns the value of a key in a string formatted in key=value&key=value&key=value pairs, e.g. saved querystrings 46 | */ 47 | function get_key_value($string, $key) { 48 | $list = explode( '&', str_replace( '&', '&', $string)); 49 | foreach ($list as $pair) { 50 | $item = explode( '=', $pair); 51 | if (strtolower($key) == strtolower($item[0])) { 52 | return urldecode($item[1]); // not for use in $_GET etc, which is already decoded, however our encoder uses http_build_query() before encrypting 53 | } 54 | } 55 | return ""; 56 | } 57 | 58 | // truncate_userinfo requires and returns an array 59 | // but we want to send in and return a user object 60 | function truncate_user($userobj) { 61 | $user_array = truncate_userinfo((array) $userobj); 62 | $obj = new stdClass(); 63 | foreach($user_array as $key=>$value) { 64 | $obj->{$key} = $value; 65 | } 66 | return $obj; 67 | } 68 | 69 | 70 | /* 71 | Issue: https://github.com/frumbert/wp2moodle--wordpress-/issues/10 72 | Author: catasoft 73 | Purpose, enrols everyone as student using the manual enrolment plugin 74 | Todo: do we trigger \core\event\user_enrolment_created::create() ?? 75 | */ 76 | function enrol_into_course($courseid, $userid, $roleid = 5) { 77 | global $DB; 78 | $manualenrol = enrol_get_plugin('manual'); // get the enrolment plugin 79 | $enrolinstance = $DB->get_record('enrol', 80 | array('courseid'=>$courseid, 81 | 'status'=>ENROL_INSTANCE_ENABLED, 82 | 'enrol'=>'manual' 83 | ), 84 | '*', 85 | MUST_EXIST 86 | ); 87 | // retrieve enrolment instance associated with your course 88 | return $manualenrol->enrol_user($enrolinstance, $userid, $roleid); // enrol the user 89 | } 90 | 91 | function check_user_email($email) { 92 | global $SESSION; 93 | if (email_is_not_allowed($email)) { 94 | 95 | $failurereason = AUTH_LOGIN_FAILED; 96 | $event = \core\event\user_login_failed::create(['other' => ['username' => $username, 97 | 'reason' => $failurereason]]); 98 | $event->trigger(); 99 | // The username exists but the emails don't match. Refuse to continue. 100 | $reason = get_string('loginerror_invaliddomain', 'auth_wp2moodle'); 101 | $errormsg = get_string('notloggedindebug', 'auth_wp2moodle', $reason); 102 | $SESSION->loginerrormsg = $errormsg; 103 | redirect(new moodle_url('/login/index.php')); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /login.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * @author Tim St.Clair - timst.clair@gmail.com 19 | * @license http://www.gnu.org/copyleft/gpl.html GNU Public License 20 | * @package local/wp2moodle 21 | * @version 1.0 22 | * 23 | * Moodle-end component of the wpMoodle Wordpress plugin. 24 | * Accepts user details passed across from Wordpress, creates a user in Moodle, authenticates them, and enrols them in the specified Cohort(s) or Group(s) 25 | * 26 | * 2012-05 Created 27 | * 2014-04 Added option to bypass updating user record for existing users 28 | * Added option to enrol user into multiple cohorts or groups by specifying comma-separated list of identifiers 29 | **/ 30 | 31 | 32 | global $CFG, $USER, $SESSION, $DB; 33 | 34 | require('../../config.php'); 35 | require_once('locallib.php'); 36 | require_once($CFG->libdir.'/moodlelib.php'); 37 | require_once($CFG->dirroot.'/cohort/lib.php'); 38 | require_once($CFG->dirroot.'/group/lib.php'); 39 | require_once($CFG->dirroot.'/course/lib.php'); 40 | require_once($CFG->dirroot."/lib/enrollib.php"); 41 | 42 | $SESSION->wantsurl = $CFG->wwwroot.'/'; 43 | 44 | $PASSTHROUGH_KEY = get_config('auth_wp2moodle', 'sharedsecret'); 45 | if (!isset($PASSTHROUGH_KEY)) { 46 | die("Sorry, this plugin has not yet been configured. Please contact the Moodle administrator for details."); 47 | } 48 | if (!is_enabled_auth('wp2moodle')) { 49 | die('Sorry this plugin is not enabled. Please contact the Moodle administrator for details.'); 50 | } 51 | 52 | $rawdata = $_GET['data']; 53 | if (!empty($_GET)) { 54 | 55 | $auth = 'wp2moodle'; 56 | 57 | // get the data that was passed in 58 | $userdata = decrypt_string($rawdata, $PASSTHROUGH_KEY); 59 | 60 | // time (in minutes) before incoming link is considered invalid 61 | $timeout = (integer) get_config('auth_wp2moodle', 'timeout'); 62 | 63 | // determine field name to match on 64 | $mf = intval(get_config('auth_wp2moodle', 'matchfield') ?: 0); 65 | $matchvalue = ""; 66 | $courseId = 0; 67 | 68 | switch ($mf) { 69 | case 1: $matchfield = "email"; break; 70 | case 2: $matchfield = "username"; break; 71 | default: $matchfield = "idnumber"; 72 | } 73 | 74 | // default field values for fields required by moodle 75 | $default_firstname = get_config('auth_wp2moodle', 'firstname') ?: "no-firstname"; 76 | $default_lastname = get_config('auth_wp2moodle', 'lastname') ?: "no-lastname"; 77 | 78 | // set a default idnumber prefix to help prevent index clashes on mdlw8_user_mneuse_uix 79 | $idnumber_prefix = get_config('auth_wp2moodle', 'idprefix') ?: "wp2m"; 80 | $redirectnoenrol = get_config('auth_wp2moodle', 'redirectnoenrol'); 81 | 82 | $invalidloginurl = get_config('auth_wp2moodle', 'invalidloginurl'); 83 | if (empty($invalidloginurl)) $invalidloginurl = '/login/index.php'; 84 | $invalidloginurl = new moodle_url($invalidloginurl); 85 | 86 | // if userdata didn't decrypt, then timestamp will = 0, so following code will be bypassed anyway (e.g. bad data) 87 | $timestamp = (integer) get_key_value($userdata, "stamp"); // remote site should have set this to new DateTime("now").getTimestamp(); which is a unix timestamp (utc) 88 | $theirs = new DateTime("@$timestamp"); // @ format here: http://www.gnu.org/software/tar/manual/html_node/Seconds-since-the-Epoch.html#SEC127 89 | $diff = floatval(date_diff(date_create("now"), $theirs)->format("%i")); // http://www.php.net/manual/en/dateinterval.format.php 90 | 91 | // check the timestamp to make sure that the request is still within a few minutes of this servers time 92 | if ($timestamp > 0 && ($timeout == 0 || $diff <= $timeout)) { // less than N minutes passed since this link was created or timeout=0, so it's still ok 93 | 94 | $username = trim(strtolower(get_key_value($userdata, "username"))); // php's tolower, not moodle's 95 | $hashedpassword = get_key_value($userdata, "passwordhash"); 96 | $firstname = get_key_value($userdata, "firstname") ?: $default_firstname; 97 | $lastname = get_key_value($userdata, "lastname") ?: $default_lastname; 98 | $email = get_key_value($userdata, "email"); 99 | $idnumber = $idnumber_prefix . get_key_value($userdata, "idnumber"); // the users id in the wordpress database, stored here for possible user-matching, prefixed to avoid clashes 100 | $cohort_idnumbers = get_key_value($userdata, "cohort"); // the cohort to map the user user; these can be set as enrolment options on one or more courses, if it doesn't exist then skip this step 101 | $group_idnumbers = get_key_value($userdata, "group"); 102 | $course_idnumbers = get_key_value($userdata, "course"); 103 | 104 | // specifify either the activity order (topmost being 1, counting down the page); 105 | // or specify the activity id number - the XXX in moodle when your url is /mod/activitymame/view.php?id=XXX 106 | // specify one, not both. 107 | $activity = (integer) get_key_value($userdata, "activity"); // activity number to start at, > 0 108 | $cmid = (integer) get_key_value($userdata, "cmid"); // activity cmid 109 | 110 | // $updatefields = (get_key_value($userdata, "updatable") != "false"); // if true or not set, update fields like email, username, etc. 111 | $updatefields = (get_config('auth_wp2moodle', 'updateuser') === '1'); 112 | $usernameemail = (get_config('auth_wp2moodle', 'usernameemail') === '1'); // 113 | $wantsurl = get_key_value($userdata, "url"); 114 | 115 | // set DB record lookup match value; default = idnumber 116 | switch ($matchfield) { 117 | case "username": $matchvalue = $username; break; 118 | case "email": $matchvalue = $email; break; 119 | default: $matchvalue = $idnumber; $matchfield = "idnumber"; 120 | } 121 | 122 | // if idnumber matches a user but the matchfield no longer does, assume that field was updated and switch mathing methods so the record updates 123 | $sql = "SELECT id 124 | FROM {user} 125 | WHERE idnumber = ? 126 | AND {$matchfield} <> ? 127 | "; 128 | $rs = $DB->get_record_sql($sql, array($idnumber, $matchvalue)); 129 | if ($rs) { 130 | $matchfield = "idnumber"; $matchvalue = $idnumber; 131 | } 132 | 133 | // find the user record (if it exists) and update if required 134 | if ($DB->record_exists('user', [$matchfield => $matchvalue])) { 135 | $updateuser = get_complete_user_data($matchfield, $matchvalue); 136 | if ($updateuser->suspended == 1 || $updateuser->deleted == 1) { 137 | $SESSION->loginerrormsg = get_string('invalidlogin'); 138 | redirect($invalidloginurl); 139 | } 140 | 141 | if ($updatefields) { 142 | check_user_email($email); 143 | $updateuser->username = ($usernameemail) ? $email : $username; 144 | $updateuser->email = $email; 145 | $updateuser->idnumber = $idnumber; 146 | $updateuser->firstname = $firstname; 147 | $updateuser->lastname = $lastname; 148 | $updateuser = truncate_user($updateuser); // typecast obj to array, works just as well 149 | $updateuser->timemodified = time(); // record that we changed the record 150 | 151 | $DB->update_record('user', $updateuser); 152 | \core\event\user_updated::create_from_userid($updateuser->id)->trigger(); 153 | } 154 | $user = get_complete_user_data('id', $updateuser->id); 155 | } else { 156 | $authplugin = get_auth_plugin($auth); 157 | check_user_email($email); 158 | $updateuser = new stdClass(); 159 | if ($newinfo = $authplugin->get_userinfo($username)) { 160 | $newinfo = truncate_user($newinfo); 161 | foreach ($newinfo as $key => $value){ 162 | $updateuser->$key = $value; 163 | } 164 | } 165 | $updateuser->city = ''; 166 | $updateuser->auth = $auth; 167 | $updateuser->policyagreed = 1; 168 | $updateuser->idnumber = $idnumber; 169 | $updateuser->username = ($usernameemail) ? $email : $username; 170 | $updateuser->password = md5($hashedpassword); // manual auth checks password validity, so we need to set a valid password 171 | $updateuser->firstname = $firstname; 172 | $updateuser->lastname = $lastname; 173 | $updateuser->email = $email; 174 | $updateuser->lang = $CFG->lang; 175 | $updateuser->confirmed = 1; // don't want an email going out about this user 176 | $updateuser->lastip = getremoteaddr(); 177 | $updateuser->timecreated = time(); 178 | $updateuser->timemodified = $updateuser->timecreated; 179 | $updateuser->mnethostid = $CFG->mnet_localhost_id; 180 | $updateuser = truncate_user($updateuser); 181 | $updateuser->id = $DB->insert_record('user', $updateuser); 182 | \core\event\user_created::create_from_userid($updateuser->id)->trigger(); 183 | $user = get_complete_user_data('id', $updateuser->id); 184 | } 185 | 186 | // Entrol users to matched COHORTS 187 | if (!empty($cohort_idnumbers)) { 188 | $ids = array_map('trim', explode(',', $cohort_idnumbers)); 189 | foreach ($ids as $cohort) { 190 | if ($DB->record_exists('cohort', array('idnumber'=>$cohort))) { 191 | $cohortrow = $DB->get_record('cohort', array('idnumber'=>$cohort)); 192 | if (!$DB->record_exists('cohort_members', array('cohortid'=>$cohortrow->id, 'userid'=>$user->id))) { 193 | // internally triggers cohort_member_added event 194 | cohort_add_member($cohortrow->id, $user->id); 195 | } 196 | 197 | // if the plugin auto-opens the course, then find the course this cohort enrols for and set it as the opener link 198 | if (get_config('auth_wp2moodle', 'autoopen') === '1') { 199 | $courseId = $DB->get_field('enrol','courseid',['enrol'=>'cohort','customint1'=>$cohortrow->id,'status'=>0], IGNORE_MULTIPLE); 200 | } 201 | } 202 | } 203 | } 204 | 205 | // Enrol users to matched GROUPS 206 | if (!empty($group_idnumbers) && $redirectnoenrol === '0') { 207 | $ids = array_map('trim', explode(',', $group_idnumbers)); 208 | foreach ($ids as $group) { 209 | if ($DB->record_exists('groups', array('idnumber'=>$group))) { 210 | $grouprow = $DB->get_record('groups', array('idnumber'=>$group)); 211 | $courseId = $grouprow->courseid; 212 | enrol_into_course($courseId, $user->id); 213 | if (!$DB->record_exists('groups_members', array('groupid'=>$grouprow->id, 'userid'=>$user->id))) { 214 | // internally triggers groups_member_added event 215 | groups_add_member($grouprow->id, $user->id); 216 | } 217 | } 218 | } 219 | } 220 | 221 | // Enrol users to matched COURSES 222 | if (!empty($course_idnumbers)) { 223 | $studentrow = $DB->get_record('role', array('shortname'=>'student')); 224 | $ids = array_map('trim', explode(',', $course_idnumbers)); 225 | foreach ($ids as $course) { 226 | if ($DB->record_exists('course', array('idnumber'=>$course))) { 227 | $courserow = $DB->get_record('course', array('idnumber'=>$course)); 228 | if ($redirectnoenrol === '0') { // 0 = try enrol; 1 = skip enrol 229 | if (!enrol_try_internal_enrol($courserow->id, $user->id, $studentrow->id)) { 230 | continue; 231 | } 232 | } 233 | $courseId = $courserow->id; 234 | } 235 | } 236 | } 237 | 238 | $courseId = intval($courseId); 239 | 240 | // Work out STARTING URL 241 | if (get_config('auth_wp2moodle', 'autoopen') === '1') { 242 | if ($courseId > 0) { 243 | $SESSION->wantsurl = new moodle_url('/course/view.php', array('id'=>$courseId)); 244 | } 245 | // course might still be zero (or false), can we calculate it? 246 | if ($courseId == 0 && $cmid > 0) $courseId = $DB->get_field('course_modules','course', array('id' => $cmid)); 247 | 248 | // if an activity is specified, or a cmid has been specified, then work out its url. 249 | if ($courseId != 0 && ($activity > 0 || $cmid > 0)) { 250 | $course = get_course($courseId); 251 | $modinfo = get_fast_modinfo($course); 252 | $index = 0; 253 | foreach ($modinfo->get_cms() as $cmindex => $cm) { 254 | if ($cm->uservisible && $cm->available) { 255 | // TODO fix this loop to account for contextual permissions to cm objects .. one day 256 | if (($index === $activity && $cmid === 0) || ($activity === 0 && $cmid === $cmindex)) { 257 | $SESSION->wantsurl = new moodle_url("/mod/" . $cm->modname . "/view.php", array("id" => $cmindex)); 258 | break; 259 | } 260 | $index += 1; 261 | } 262 | } 263 | } 264 | } 265 | 266 | // Do we need to override the STARTING URL ? 267 | if (!empty($wantsurl)) { 268 | $SESSION->wantsurl = new moodle_url(rawurldecode($wantsurl)); 269 | } 270 | 271 | // LOG IN 272 | $authplugin = get_auth_plugin($auth); 273 | if ($authplugin->user_login($user->username, $user->password)) { 274 | $user->loggedin = true; 275 | $user->site = $CFG->wwwroot; 276 | complete_user_login($user); 277 | } 278 | } 279 | } 280 | 281 | // Finished - REDIRECT 282 | redirect($SESSION->wantsurl); 283 | -------------------------------------------------------------------------------- /settings.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Admin settings and defaults 20 | * 21 | * @package auth_wp2moodle 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die; 26 | 27 | if ($ADMIN->fulltree) { 28 | 29 | $wp2myesno = array( 30 | new lang_string('no'), 31 | new lang_string('yes'), 32 | ); 33 | 34 | $wp2m_fields = ['idnumber','email','username']; 35 | 36 | $settings->add(new admin_setting_heading('auth_wp2moodle/pluginname', 37 | '', 38 | new lang_string('settings_description', 'auth_wp2moodle'))); 39 | 40 | $settings->add(new admin_setting_configtext('auth_wp2moodle/sharedsecret', 41 | new lang_string('settings_sharedsecret','auth_wp2moodle'), 42 | new lang_string('settings_sharedsecret_desc', 'auth_wp2moodle'), 43 | 'this is not a secure key, change it' 44 | )); 45 | 46 | $settings->add(new admin_setting_configtext_with_maxlength('auth_wp2moodle/timeout', 47 | new lang_string('settings_timeout','auth_wp2moodle'), 48 | new lang_string('settings_timeout_desc', 'auth_wp2moodle'), 49 | '5', 50 | PARAM_RAW, 4, 3 51 | )); 52 | 53 | $settings->add(new admin_setting_configtext('auth_wp2moodle/logoffurl', 54 | new lang_string('settings_logoffurl','auth_wp2moodle'), 55 | new lang_string('settings_logoffurl_desc', 'auth_wp2moodle'), 56 | '' 57 | )); 58 | 59 | $settings->add(new admin_setting_configtext('auth_wp2moodle/invalidloginurl', 60 | new lang_string('settings_invalidloginurl','auth_wp2moodle'), 61 | new lang_string('settings_invalidloginurl_desc', 'auth_wp2moodle'), 62 | '' 63 | )); 64 | 65 | $settings->add(new admin_setting_configselect('auth_wp2moodle/matchfield', 66 | new lang_string('settings_matchfield', 'auth_wp2moodle'), 67 | new lang_string('settings_matchfield_desc', 'auth_wp2moodle'), 68 | 'idnumber', 69 | $wp2m_fields 70 | )); 71 | 72 | $settings->add(new admin_setting_configselect('auth_wp2moodle/autoopen', 73 | new lang_string('settings_autoopen', 'auth_wp2moodle'), 74 | new lang_string('settings_autoopen_desc', 'auth_wp2moodle'), 75 | 1, 76 | $wp2myesno 77 | )); 78 | 79 | $settings->add(new admin_setting_configselect('auth_wp2moodle/updateuser', 80 | new lang_string('settings_updateuser', 'auth_wp2moodle'), 81 | new lang_string('settings_updateuser_desc', 'auth_wp2moodle'), 82 | 1, 83 | $wp2myesno 84 | )); 85 | 86 | $settings->add(new admin_setting_configselect('auth_wp2moodle/usernameemail', 87 | new lang_string('settings_usernameemail', 'auth_wp2moodle'), 88 | new lang_string('settings_usernameemail_desc', 'auth_wp2moodle'), 89 | 0, 90 | $wp2myesno 91 | )); 92 | 93 | $settings->add(new admin_setting_configselect('auth_wp2moodle/redirectnoenrol', 94 | new lang_string('settings_redirectnoenrol', 'auth_wp2moodle'), 95 | new lang_string('settings_redirectnoenrol_desc', 'auth_wp2moodle'), 96 | 0, 97 | $wp2myesno 98 | )); 99 | 100 | $settings->add(new admin_setting_configtext('auth_wp2moodle/firstname', 101 | new lang_string('settings_firstname','auth_wp2moodle'), 102 | new lang_string('settings_firstname_desc', 'auth_wp2moodle'), 103 | 'empty-firstname' 104 | )); 105 | 106 | $settings->add(new admin_setting_configtext('auth_wp2moodle/lastname', 107 | new lang_string('settings_lastname','auth_wp2moodle'), 108 | new lang_string('settings_lastname_desc', 'auth_wp2moodle'), 109 | 'empty-lastname' 110 | )); 111 | 112 | $settings->add(new admin_setting_configtext('auth_wp2moodle/idprefix', 113 | new lang_string('settings_idprefix','auth_wp2moodle'), 114 | new lang_string('settings_idprefix_desc', 'auth_wp2moodle'), 115 | 'wp2m' 116 | )); 117 | 118 | // Display locking / mapping of profile fields. 119 | // $authplugin = get_auth_plugin('wp2moodle'); 120 | // display_auth_lock_options($settings, $authplugin->authtype, 121 | // $authplugin->userfields, get_string('auth_fieldlocks_help', 'auth'), false, false); 122 | 123 | } -------------------------------------------------------------------------------- /version.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Version information 19 | * 20 | * @package auth 21 | * @subpackage wp2moodle 22 | * @copyright 2014 Tim St.Clair (tim.stclair@gmail.com) 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | $plugin->version = 2021121600; // The current plugin version (Date: YYYYMMDDXX) 29 | $plugin->requires = 2011112900; // Requires this Moodle version 30 | $plugin->component = 'auth_wp2moodle'; // Full name of the plugin (used for diagnostics) 31 | $plugin->maturity = MATURITY_STABLE; 32 | $plugin->release = '1.1'; -------------------------------------------------------------------------------- /wp2m.js: -------------------------------------------------------------------------------- 1 | // purpose: register a wp2moodle button in the rich editor in the admin interface 2 | (function() { 3 | tinymce.create('tinymce.plugins.wp2m', { 4 | init : function(ed, url) { 5 | ed.addButton('wp2m', { 6 | title : 'Wordpress 2 Moodle', 7 | image : url+'/icon.svg', 8 | onclick : function() { 9 | ed.selection.setContent('[wp2moodle cohort="" group="" course="" class="wp2moodle" target="_self" authtext="" activity="0"]' + ed.selection.getContent() + '[/wp2moodle]'); 10 | } 11 | }); 12 | }, 13 | createControl : function(n, cm) { 14 | return null; 15 | }, 16 | }); 17 | tinymce.PluginManager.add('wp2m', tinymce.plugins.wp2m); 18 | })(); 19 | -------------------------------------------------------------------------------- /wp2m.php: -------------------------------------------------------------------------------- 1 | css class to put on link we build 166 | // $cohort => text id of the moodle cohort in which to enrol this user 167 | // $group => text id of the moodle group in which to enrol this user 168 | // $course => text id of the course, if you just want to enrol a user directly to a course 169 | // $authtext => string containing text content to display when not logged on (defaults to content between tags when empty / missing) 170 | // $activity => index of the first activity to open, if autoopen is enabled in moodle 171 | // $cmid => id of the coursemodule (/mod/pluginname/view.php?id=XXX) to open after authentication 172 | // $url => the url to open after login 173 | extract(shortcode_atts(array( 174 | "cohort" => '', 175 | "group" => '', 176 | "course" => '', 177 | "class" => 'wp2moodle', 178 | "target" => '_self', 179 | "authtext" => '', 180 | "activity" => 0, 181 | "cmid" => 0, 182 | "url" => '', 183 | ), $atts)); 184 | 185 | if ($content == null || !is_user_logged_in() ) { 186 | if (trim($authtext) == "") { 187 | $url = '' . do_shortcode($content) . ''; // return content text linked to registration page (value between start and end tag) 188 | } else { 189 | $url = '' . do_shortcode($authtext) . ''; // return authtext linked to registration page (value of attribute, if set) 190 | } 191 | } else { 192 | // url = moodle_url + "?data=" + 193 | $url = ''.do_shortcode($content).''; // hyperlinked content 194 | } 195 | return $url; 196 | } 197 | 198 | // wp2m filters are set at higher priority so they execute first 199 | 200 | // over-ride the url for Marketpress *if* the download is a file named something-wp2moodle.txt 201 | add_filter('mp_download_url', 'wp2m_download_url', 10, 3); 202 | 203 | // over-ride the url for WooCommerce (has various download filters we have to try) 204 | add_filter('woocommerce_download_file_redirect','woo_wp2m_download_url', 5, 2); 205 | add_filter('woocommerce_download_file_force','woo_wp2m_download_url', 5, 2); 206 | add_filter('woocommerce_download_file_xsendfile','woo_wp2m_download_url', 5, 2); 207 | 208 | // woo shim to handle different arguments 209 | function woo_wp2m_download_url($filepath, $filename) { 210 | wp2m_download_url($filepath, "", ""); 211 | } 212 | 213 | // the download file is actually a text file containing the shortcode values 214 | function wp2m_download_url($url, $order, $download) { 215 | 216 | if (strpos($url, 'wp2moodle.txt') !== false) { 217 | // mp url is full url = including http:// and so on... we want the file url 218 | $path = $_SERVER['DOCUMENT_ROOT'] . parse_url($url)["path"]; 219 | $cohort = ""; 220 | $group = ""; 221 | $urllog = ""; 222 | $activity = 0; 223 | $cmid = 0; 224 | $data = file($path); // now it's an array! 225 | foreach ($data as $row) { 226 | $pair = explode("=",$row); 227 | switch (strtolower(trim($pair[0]))) { 228 | case "group": 229 | $group = trim(str_replace(array('\'','"'), '', $pair[1])); 230 | break; 231 | case "cohort": 232 | $cohort = trim(str_replace(array('\'','"'), '', $pair[1])); 233 | break; 234 | case "course": 235 | $course = trim(str_replace(array('\'','"'), '', $pair[1])); 236 | break; 237 | case "activity": 238 | $activity = trim(str_replace(array('\'','"'), '', $pair[1])); 239 | break; 240 | case "cmid": 241 | $cmid = trim(str_replace(array('\'','"'), '', $pair[1])); 242 | break; 243 | case "url": 244 | $urllog = trim(str_replace(array('\'','"'), '', $pair[1])); 245 | break; 246 | } 247 | } 248 | $url = wp2moodle_generate_hyperlink($cohort,$group,$course,$activity,$urllog,$cmid); 249 | if (ob_get_contents()) { ob_clean(); } 250 | header('Location: ' . $url, true, 301); // redirect to this url 251 | exit(); 252 | } 253 | return $url; 254 | } 255 | 256 | /* 257 | If you are only using wp2moodle and not offering other marketpress products, you can hide the shipping info like this: 258 | add_action('add_meta_boxes_product','remove_unwanted_mp_meta_boxes',999); 259 | function remove_unwanted_mp_meta_boxes() { 260 | // shipping meta box 261 | remove_meta_box('mp-meta-shipping','product','normal'); 262 | // download file box 263 | // remove_meta_box('mp-meta-download','product','normal'); 264 | } 265 | */ 266 | 267 | /* 268 | * Function to build the encrypted hyperlink 269 | */ 270 | function wp2moodle_generate_hyperlink($cohort,$group,$course,$activity = 0, $url = null, $cmid = 0) { 271 | 272 | // needs authentication; ensure userinfo globals are populated 273 | global $current_user; 274 | wp_get_current_user(); 275 | 276 | $update = get_option('wp2m_update_details') ?: "true"; 277 | 278 | $enc = array( 279 | "offset" => rand(1234,5678), // just some junk data to mix into the encryption 280 | "stamp" => time(), // unix timestamp so we can check that the link isn't expired 281 | "firstname" => $current_user->user_firstname, // first name 282 | "lastname" => $current_user->user_lastname, // last name 283 | "email" => $current_user->user_email, // email 284 | "username" => $current_user->user_login, // username 285 | "passwordhash" => $current_user->user_pass, // hash of password (we don't know/care about the raw password) 286 | "idnumber" => $current_user->ID, // int id of user in this db (for user matching on services, etc) 287 | "cohort" => $cohort, // string containing cohort to enrol this user into 288 | "group" => $group, // string containing group to enrol this user into 289 | "course" => $course, // string containing course id, optional 290 | "activity" => $activity, // index of first [visible] activity to go to, if auto-open is enabled in moodle 291 | "cmid" => $cmid, // coursemodule id 292 | "url" => rawurlencode($url) // url to open after logon 293 | ); 294 | 295 | // encode array as querystring 296 | $details = http_build_query($enc); 297 | 298 | // encryption = 3des using shared_secret 299 | return rtrim(get_option('wp2m_moodle_url'),"/").WP2M_MOODLE_PLUGIN_URL.encrypt_string($details, get_option('wp2m_shared_secret')); 300 | //return get_option('wp2m_moodle_url').WP2M_MOODLE_PLUGIN_URL.'=>'.$details; 301 | } 302 | 303 | /** 304 | * initialiser for registering scripts to the rich editor 305 | */ 306 | function wp2m_register_addbutton() { 307 | if ( current_user_can('edit_posts') && current_user_can('edit_pages') ) { 308 | add_filter('mce_external_plugins', 'wp2m_add_plugin'); 309 | add_filter('mce_buttons', 'wp2m_register_button'); 310 | } 311 | } 312 | function wp2m_register_button($buttons) { 313 | array_push($buttons,"|","wp2m"); // pipe = break on toolbar 314 | return $buttons; 315 | } 316 | function wp2m_add_plugin($plugin_array) { 317 | $plugin_array['wp2m'] = plugin_dir_url(__FILE__).'wp2m.js'; 318 | return $plugin_array; 319 | } 320 | 321 | ?> 322 | -------------------------------------------------------------------------------- /wp2m_settings_page.php: -------------------------------------------------------------------------------- 1 | 2 | 8 |
9 | 10 | 11 | 12 |
13 |

Warning!

The openssl php extension has not been detected. You'll need to fix your PHP configuration before this plugin will operate.

14 |
15 | 16 | 17 |

18 |

This plugin allows you to place a shortcode in a post that passes encrypted logon information to Moodle (requires this plugin to be also installed into Moodle). The user will be added to Moodle and optionally enrolled in the specified Cohort(s), Course(s) and/or Group(s).

19 |

Use the Moodle button on the rich editor to insert the shortcode, or enter the details manually using the examples below as a guide.

20 |

Example: [wp2moodle class='css-classname' group='group1' cohort='class1' target='_blank' authtext='Please log on']launch the course[/wp2moodle].

21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
AttributePurposeExample
class optional, defaults to 'wp2moodle'; CSS class attribute of link
cohort optional, idnumber of the cohort in which to enrol a user at the moodle end. You can specify multiple values using comma seperated strings.[wp2moodle cohort='business_cert3']enrol in Cert 3 Business[/wp2moodle]
group optional, idnumber of the group in which to enrol a user at the moodle end (typically you use group or cohort). You can specify multiple values using comma seperated strings.[wp2moodle group='eng14_a,math14_b,hist13_c']Math, English & History[/wp2moodle]
course optional, idnumber of the course in which to enrol a user at the moodle end. You can use multiple ids(as above)
target optional, defaults to '_self'; href target attribute of linktarget="_blank"
authtext optional, defaults to content between shortcode tags; string to display if not yet logged on
activity optional, numerical index of the first activity to open (> 0) if auto-open is enabled in the Moodle plugin
cmidoptional, numerical id of the activity view page (e.g. /mod/plugin/view.php?id=XXX)
url optional, overrides other lookups, relative moodle url to open after logon (e.g. /mod/page/index.php?id=123)
35 |

The idnumber mentioned above is not the same as the course id (which is a number); moodle has a special field called "idnumber" which is an alphanumeric value. If you mix them up, it won't work!

36 |

The link that is generated is timestamped and will expire, so it cannot be bookmarked or hijacked. You must set the expiry time in the Moodle plugin. You should allow reading time of the page when considering a timeout value, since the link is generated when the page is loaded, not when the link is clicked.

37 | 38 | 39 |

MarketPress or WooCommerce?

40 |

Selling with MarketPress or WooCommerce? Create a text file called "yourcourse-wp2moodle.txt" (actually, the name only has to end with -wp2moodle.txt; the name before that can be whatever you like) and write ay attributes (shown above) on their own lines into it, like this:

41 | 42 | 46 |
43 | group=maths102_sem2
44 | cohort=2015allCourses 45 |
47 | 48 |

Upload this file as your digital download for the product. Then, after a purchase instead of a download, they will redirect to your Moodle site with an authentication token just like a shortcode link.

49 | 50 |

Settings

51 |
52 | 55 | 56 | 57 | 58 | 61 | 62 | 63 | 64 | 65 | 68 | 69 | 70 |
Moodle Root URL 59 |
Plugin will append the url .
60 |
Encryption secret
Must match Moodle
66 |
Here is a freshly generated secure key: .
67 |
71 | 72 |

73 | 74 |

75 | 76 |
77 |

Moodle icon credits 78 |

79 | --------------------------------------------------------------------------------