/**
 * Handles the upload and import of the user CSV file.
 * @param Object $page The page object to show messages.
 */
function WPCW_users_importUsersFromFile($page)
{
    set_time_limit(0);
    $page->showMessage(__('Import started...', 'wp_courseware'));
    flush();
    if (isset($_FILES['import_course_csv']['name'])) {
        // See what type of file we're tring to upload
        $type = strtolower($_FILES['import_course_csv']['type']);
        $fileTypes = array('text/csv', 'text/plain', 'application/csv', 'text/comma-separated-values', 'application/excel', 'application/vnd.ms-excel', 'application/vnd.msexcel', 'text/anytext', 'application/octet-stream', 'application/txt');
        if (!in_array($type, $fileTypes)) {
            $page->showMessage(__('Unfortunately, you tried to upload a file that isn\'t a CSV file.', 'wp_courseware'), true);
            return false;
        }
        // Filetype is fine, carry on
        $errornum = $_FILES['import_course_csv']['error'] + 0;
        $tempfile = $_FILES['import_course_csv']['tmp_name'];
        // File uploaded successfully?
        if ($errornum == 0) {
            // Try the import, return error/success here
            if (($csvHandle = fopen($tempfile, "r")) !== FALSE) {
                $assocData = array();
                $rowCounter = 0;
                // Extract the user details from the CSV file into an array for importing.
                while (($rowData = fgetcsv($csvHandle, 0, ",")) !== FALSE) {
                    if (0 === $rowCounter) {
                        $headerRecord = $rowData;
                    } else {
                        foreach ($rowData as $key => $value) {
                            $assocData[$rowCounter - 1][$headerRecord[$key]] = $value;
                        }
                        $assocData[$rowCounter - 1]['row_num'] = $rowCounter + 1;
                    }
                    $rowCounter++;
                }
                // Check we have users to process before continuing.
                if (count($assocData) < 1) {
                    $page->showMessage(__('No data was found in the CSV file, so there is nothing to do.', 'wp_courseware'), true);
                    return;
                }
                // Get a list of all courses that we can add a user too.
                $courseList = WPCW_courses_getCourseList(false);
                // Statistics for update.
                $count_newUser = 0;
                $count_skippedButUpdated = 0;
                $count_aborted = 0;
                // By now, $assocData contains a list of user details in an array.
                // So now we try to insert all of these users into the system, and validate them all.
                $skippedList = array();
                foreach ($assocData as $userRowData) {
                    // #### 1 - See if we have a username that we can use. If not, abort.
                    $firstName = trim($userRowData['first_name']);
                    $lastName = trim($userRowData['last_name']);
                    $userNameToCreate = $firstName . $lastName;
                    if (!$userNameToCreate) {
                        $skippedList[] = array('id' => $userRowData, 'row_num' => $userRowData['row_num'], 'aborted' => true, 'reason' => __('Cannot create a user with no name.', 'wp_courseware'));
                        $count_aborted++;
                        continue;
                    }
                    // username check
                    // // #### 2 - Email address of user already exists.
                    if ($userID = email_exists($userRowData['email_address'])) {
                        $skippedList[] = array('id' => $userRowData, 'row_num' => $userRowData['row_num'], 'aborted' => false, 'reason' => __('Email address already exists.', 'wp_courseware'));
                        $count_skippedButUpdated++;
                    } else {
                        // #### 3A - Try and create a unique Username
                        $userlogin = $userNameToCreate;
                        while (username_exists($userlogin)) {
                            $userlogin = $userNameToCreate . rand(10, 999);
                        }
                        // #### 3B - Create a new password
                        $newPassword = wp_generate_password(15);
                        // #### 3C - Try to create the new user
                        $userDetailsToAdd = array('user_login' => $userlogin, 'user_email' => $userRowData['email_address'], 'first_name' => $firstName, 'last_name' => $lastName, 'display_name' => trim($firstName . ' ' . $lastName), 'user_pass' => $newPassword);
                        // #### 3D - Check for error when creating
                        $result = wp_insert_user($userDetailsToAdd);
                        if (is_wp_error($result)) {
                            $skippedList[] = array('id' => $userRowData, 'row_num' => $userRowData['row_num'], 'aborted' => true, 'reason' => $result->get_error_message());
                            $count_aborted++;
                            continue;
                        }
                        // #### 3E - User now exists at this point, copy ID
                        // to user ID variable.
                        $userID = $result;
                        // #### 3F - Notify user of their new password.
                        wp_new_user_notification($userID, $newPassword);
                        flush();
                        $message = sprintf(__('Username: %s'), $user->user_login) . "\r\n\r\n";
                        $message .= __('To set your password, visit the following address:') . "\r\n\r\n";
                        $message .= '<' . network_site_url("wp-login.php?action=rp&key={$key}&login="******">\r\n\r\n";
                        $message .= wp_login_url() . "\r\n\r\n";
                        $message .= sprintf(__('If you have any problems, please contact us at %s.'), get_option('admin_email')) . "\r\n\r\n";
                        $message .= __('Adios!') . "\r\n\r\n";
                        wp_mail($user->user_email, sprintf(__('[%s] Your username and password info'), $blogname), $message);
                        $count_newUser++;
                    }
                    // #### 4 - Break list of courses into an array, and then add that user to those courses
                    $coursesToAdd = explode(',', $userRowData['courses_to_add_to']);
                    if ($coursesToAdd && count($coursesToAdd) > 0) {
                        WPCW_courses_syncUserAccess($userID, $coursesToAdd);
                    }
                }
                // Summary import.
                $page->showMessage(__('Import complete!', 'wp_courseware') . ' ' . sprintf(__('%d users were registered, %d users were updated, and %d user entries could not be processed.', 'wp_courseware'), $count_newUser, $count_skippedButUpdated, $count_aborted));
                // Show any skipped users
                if (!empty($skippedList)) {
                    printf('<div id="wpcw_user_import_skipped">');
                    printf('<b>' . __('The following %d users were not imported:', 'wp_courseware') . '</b>', count($skippedList));
                    printf('<table class="widefat">');
                    printf('<thead>');
                    printf('<tr>');
                    printf('<th>%s</th>', __('Line #', 'wp_courseware'));
                    printf('<th>%s</th>', __('User Email Address', 'wp_courseware'));
                    printf('<th>%s</th>', __('Reason why not imported', 'wp_courseware'));
                    printf('<th>%s</th>', __('Updated Anyway?', 'wp_courseware'));
                    printf('</tr>');
                    printf('</thead>');
                    $odd = false;
                    foreach ($skippedList as $skipItem) {
                        printf('<tr class="%s %s">', $odd ? 'alternate' : '', $skipItem['aborted'] ? 'wpcw_error' : 'wpcw_ok');
                        printf('<td>%s</td>', $skipItem['row_num']);
                        printf('<td>%s</td>', $skipItem['id']['email_address']);
                        printf('<td>%s</td>', $skipItem['reason']);
                        printf('<td>%s</td>', $skipItem['aborted'] ? __('No, Aborted', 'wp_courseware') : __('Yes', 'wp_courseware'));
                        printf('</tr>');
                        $odd = !$odd;
                    }
                    printf('</table>');
                    printf('</div>');
                }
                // All done
                fclose($csvHandle);
            } else {
                $page->showMessage(__('Unfortunately, the temporary CSV file could not be opened for processing.', 'wp_courseware'), true);
                return;
            }
        } else {
            switch ($errornum) {
                case UPLOAD_ERR_FORM_SIZE:
                case UPLOAD_ERR_INI_SIZE:
                    $page->showMessage(__("Unfortunately the file you've uploaded is too large for the system.", 'wp_courseware'), true);
                    break;
                case UPLOAD_ERR_PARTIAL:
                case UPLOAD_ERR_NO_FILE:
                    $page->showMessage(__("For some reason, the file you've uploaded didn't transfer correctly to the server. Please try again.", 'wp_courseware'), true);
                    break;
                case UPLOAD_ERR_NO_TMP_DIR:
                case UPLOAD_ERR_CANT_WRITE:
                    $page->showMessage(__("There appears to be an issue with your server, as the import file could not be stored in the temporary directory.", 'wp_courseware'), true);
                    break;
                case UPLOAD_ERR_EXTENSION:
                    $page->showMessage(__('Unfortunately, you tried to upload a file that isn\'t a CSV file.', 'wp_courseware'), true);
                    break;
            }
        }
    }
    // end of if (isset($_FILES['import_course_csv']['name']))
}
    /**
     * Shows the configuration form for the widget.
     */
    function form($instance)
    {
        // Create a default title if there is one.
        if ($instance) {
            $title = esc_attr($instance['title']);
        } else {
            $title = __('Current User Progress', 'wp_courseware');
        }
        $option_course = esc_attr(WPCW_arrays_getValue($instance, 'option_course'));
        $option_module = esc_attr(WPCW_arrays_getValue($instance, 'option_module'));
        $option_show_course_title = WPCW_arrays_getValue($instance, 'option_show_course_title') == 'on' ? 'checked="checked"' : '';
        $option_show_course_desc = WPCW_arrays_getValue($instance, 'option_show_course_desc') == 'on' ? 'checked="checked"' : '';
        $option_show_module_desc = WPCW_arrays_getValue($instance, 'option_show_module_desc') == 'on' ? 'checked="checked"' : '';
        $option_show_only_on_units = WPCW_arrays_getValue($instance, 'option_show_only_on_units') == 'on' ? 'checked="checked"' : '';
        // Module visibility Toggling
        $option_toggle_modules = esc_attr(WPCW_arrays_getValue($instance, 'option_toggle_modules'));
        $option_show_modules_previous = esc_attr(WPCW_arrays_getValue($instance, 'option_show_modules_previous'));
        $option_show_modules_next = esc_attr(WPCW_arrays_getValue($instance, 'option_show_modules_next'));
        // Generate dropdowns for the previous/next options
        $optionsList_previous = array('all' => __('All previous modules', 'wp_courseware'), 'none' => __('None', 'wp_courseware'));
        for ($i = 1; $i <= 20; $i++) {
            $optionsList_previous[$i] = sprintf(_n('Show just 1 previous module', 'Show %d previous modules', $i, 'wp_courseware'), $i);
        }
        $optionsList_next = array('all' => __('All subsequent modules', 'wp_courseware'), 'none' => __('None', 'wp_courseware'));
        for ($i = 1; $i <= 20; $i++) {
            $optionsList_next[$i] = sprintf(_n('Show just 1 subsequent module', 'Show %d subsequent modules', $i, 'wp_courseware'), $i);
        }
        ?>
		<p>
			<b><label for="<?php 
        echo $this->get_field_id('title');
        ?>
"><?php 
        _e('Title:', 'wp_courseware');
        ?>
</label></b> 
			<input class="widefat" id="<?php 
        echo $this->get_field_id('title');
        ?>
" name="<?php 
        echo $this->get_field_name('title');
        ?>
" type="text" value="<?php 
        echo $title;
        ?>
" />
			<small><?php 
        _e('(Optional) Leave blank for no title.', 'wp_courseware');
        ?>
</small>
		</p>
		
		<p>
			<b style="display: block; padding-bottom: 3px;"><label for="<?php 
        echo $this->get_field_id('option_course');
        ?>
"><?php 
        _e('Course To Show:', 'wp_courseware');
        ?>
</label></b>
			<?php 
        $courseList = array('' => __('-- Select a Training Course --', 'wp_courseware'), 'current' => __("Show User's Current Course", 'wp_courseware'));
        // Blend lists together
        $mainCourseList = WPCW_courses_getCourseList();
        if ($mainCourseList) {
            $courseList = $courseList + $mainCourseList;
        }
        echo WPCW_CourseProgress::createDropdown($this->get_field_name('option_course'), $courseList, $option_course, $this->get_field_id('option_course'));
        ?>
			<br/><small><?php 
        _e('(Required) Choose whether to display a specific course to the user or to display the course associated with the unit that the user is currently viewing.', 'wp_courseware');
        ?>
</small>
		</p>
		
		<p>
			<b style="display: block; padding-bottom: 3px;"><label><?php 
        _e('Show/Hide Modules:', 'wp_courseware');
        ?>
</label></b>
			<small><?php 
        _e('Here you can control how many modules to show before and after the current module to save space.', 'wp_courseware');
        ?>
</small>
		</p>
		
		<table>
			<tr>
				<td><label for="<?php 
        echo $this->get_field_id('option_show_modules_previous');
        ?>
"><?php 
        _e('Previous modules to display:', 'wp_courseware');
        ?>
</label></td>
				<td><label for="<?php 
        echo $this->get_field_id('option_show_modules_next');
        ?>
"><?php 
        _e('Subsequent modules to display:', 'wp_courseware');
        ?>
</label></td>
			</tr>
			
			<tr>
				<td>
					<?php 
        echo WPCW_CourseProgress::createDropdown($this->get_field_name('option_show_modules_previous'), $optionsList_previous, $option_show_modules_previous, $this->get_field_id('option_show_modules_previous'));
        ?>
				</td>
				
				<td>
					<?php 
        echo WPCW_CourseProgress::createDropdown($this->get_field_name('option_show_modules_next'), $optionsList_next, $option_show_modules_next, $this->get_field_id('option_show_modules_next'));
        ?>
				</td>
			</tr>
		</table><br/>
		
		<p>
			<b style="display: block; padding-bottom: 3px;"><label for="<?php 
        echo $this->get_field_id('option_toggle_modules');
        ?>
"><?php 
        _e('Expand/Contract Modules:', 'wp_courseware');
        ?>
</label></b>
			<?php 
        echo WPCW_CourseProgress::createDropdown($this->get_field_name('option_toggle_modules'), array('expand_all' => __('Expand all modules', 'wp_courseware'), 'contract_all_but_current' => __('Contract all except current module', 'wp_courseware'), 'contract_all' => __('Contract all modules', 'wp_courseware')), $option_toggle_modules, $this->get_field_id('option_toggle_modules'));
        ?>
			<br/><small><?php 
        _e('You can save sidebar space by contracting  modules in the widget to just show the module title.', 'wp_courseware');
        ?>
</small>
		</p>
		
		<?php 
        /*
        // Likely to be deprecated.
        <p>
        	<b><label for="<?php echo $this->get_field_id('option_module'); ?>"><?php _e('Module:', 'wp_courseware'); ?></label></b> 
        	<input class="widefat" id="<?php echo $this->get_field_id('option_module'); ?>" name="<?php echo $this->get_field_name('option_module'); ?>" type="text" value="<?php echo $option_module; ?>" />
        	<small><?php _e('(Optional) The module number of a module in this course to show specifically (rather than all modules in the course).', 'wp_courseware'); ?></small>
        </p>
        */
        ?>
		
		<p>
			<b style="display: block; padding-bottom: 3px;"><label for="<?php 
        echo $this->get_field_id('option_show_module_desc');
        ?>
"><?php 
        _e('More Options:', 'wp_courseware');
        ?>
</label></b>
			<input id="<?php 
        echo $this->get_field_id('option_show_course_title');
        ?>
" name="<?php 
        echo $this->get_field_name('option_show_course_title');
        ?>
" type="checkbox" <?php 
        echo $option_show_course_title;
        ?>
 /> <?php 
        _e('Show Course Title', 'wp_courseware');
        ?>
<br/>
			<input id="<?php 
        echo $this->get_field_id('option_show_course_desc');
        ?>
" name="<?php 
        echo $this->get_field_name('option_show_course_desc');
        ?>
" type="checkbox" <?php 
        echo $option_show_course_desc;
        ?>
 /> <?php 
        _e('Show Course Description', 'wp_courseware');
        ?>
<br/>
			<input id="<?php 
        echo $this->get_field_id('option_show_module_desc');
        ?>
" name="<?php 
        echo $this->get_field_name('option_show_module_desc');
        ?>
" type="checkbox" <?php 
        echo $option_show_module_desc;
        ?>
 /> <?php 
        _e('Show Module Descriptions', 'wp_courseware');
        ?>
<br/>
			
			<input id="<?php 
        echo $this->get_field_id('option_show_only_on_units');
        ?>
" name="<?php 
        echo $this->get_field_name('option_show_only_on_units');
        ?>
" type="checkbox" <?php 
        echo $option_show_only_on_units;
        ?>
 /> <?php 
        _e('Only display this widget when showing a course unit', 'wp_courseware');
        ?>
		</p>
		<?php 
    }
/**
 * Function that allows a module to be created or edited.
 */
function WPCW_showPage_ModifyModule_load()
{
    $page = new PageBuilder(true);
    $moduleDetails = false;
    $moduleID = false;
    $adding = false;
    // Trying to edit a course
    if (isset($_GET['module_id'])) {
        $moduleID = $_GET['module_id'] + 0;
        $moduleDetails = WPCW_modules_getModuleDetails($moduleID);
        // Abort if module not found.
        if (!$moduleDetails) {
            $page->showPageHeader(__('Edit Module', 'wp_courseware'), '75%', WPCW_icon_getPageIconURL());
            $page->showMessage(__('Sorry, but that module could not be found.', 'wp_courseware'), true);
            $page->showPageFooter();
            return;
        } else {
            $page->showPageHeader(__('Edit Module', 'wp_courseware'), '75%', WPCW_icon_getPageIconURL());
        }
    } else {
        $page->showPageHeader(__('Add Module', 'wp_courseware'), '75%', WPCW_icon_getPageIconURL());
        $adding = true;
    }
    global $wpcwdb;
    $formDetails = array('module_title' => array('label' => __('Module Title', 'wp_courseware'), 'type' => 'text', 'required' => true, 'cssclass' => 'wpcw_module_title', 'desc' => __('The title of your module. You <b>do not need to number the modules</b> - this is done automatically based on the order that they are arranged.', 'wp_courseware'), 'validate' => array('type' => 'string', 'maxlen' => 150, 'minlen' => 1, 'regexp' => '/^[^<>]+$/', 'error' => __('Please specify a name for your module, up to a maximum of 150 characters, just no angled brackets (&lt; or &gt;). Your trainees will be able to see this module title.', 'wp_courseware'))), 'parent_course_id' => array('label' => __('Associated Course', 'wp_courseware'), 'type' => 'select', 'required' => true, 'cssclass' => 'wpcw_associated_course', 'desc' => __('The associated training course that this module belongs to.', 'wp_courseware'), 'data' => WPCW_courses_getCourseList(__('-- Select a Training Course --', 'wp_courseware'))), 'module_desc' => array('label' => __('Module Description', 'wp_courseware'), 'type' => 'textarea', 'required' => true, 'cssclass' => 'wpcw_module_desc', 'desc' => __('The description of this module. Your trainees will be able to see this module description.', 'wp_courseware'), 'validate' => array('type' => 'string', 'maxlen' => 5000, 'minlen' => 1, 'error' => __('Please limit the description of your module to 5000 characters.', 'wp_courseware'))));
    $form = new RecordsForm($formDetails, $wpcwdb->modules, 'module_id');
    $form->customFormErrorMsg = __('Sorry, but unfortunately there were some errors saving the module details. Please fix the errors and try again.', 'wp_courseware');
    $form->setAllTranslationStrings(WPCW_forms_getTranslationStrings());
    // Useful place to go
    $directionMsg = '<br/></br>' . sprintf(__('Do you want to return to the <a href="%s">course summary page</a>?', 'wp_courseware'), admin_url('admin.php?page=WPCW_wp_courseware'));
    // Override success messages
    $form->msg_record_created = __('Module details successfully created.', 'wp_courseware') . $directionMsg;
    $form->msg_record_updated = __('Module details successfully updated.', 'wp_courseware') . $directionMsg;
    $form->setPrimaryKeyValue($moduleID);
    $form->setSaveButtonLabel(__('Save ALL Details', 'wp_courseware'));
    // See if we have a course ID to pre-set.
    if ($adding && ($courseID = WPCW_arrays_getValue($_GET, 'course_id'))) {
        $form->loadDefaults(array('parent_course_id' => $courseID));
    }
    // Call to re-order modules once they've been created
    $form->afterSaveFunction = 'WPCW_actions_modules_afterModuleSaved_formHook';
    $form->show();
    $page->showPageMiddle('20%');
    // Editing a module?
    if ($moduleDetails) {
        // ### Include a link to delete the module
        $page->openPane('wpcw-deletion-module', __('Delete Module?', 'wp_courseware'));
        printf('<a href="%s&action=delete_module&module_id=%d" class="wpcw_delete_item" title="%s">%s</a>', admin_url('admin.php?page=WPCW_wp_courseware'), $moduleID, __("Are you sure you want to delete the this module?\n\nThis CANNOT be undone!", 'wp_courseware'), __('Delete this Module', 'wp_courseware'));
        printf('<p>%s</p>', __('Units will <b>not</b> be deleted, they will <b>just be disassociated</b> from this module.', 'wp_courseware'));
        $page->closePane();
        // #### Show a list of all sub-units
        $page->openPane('wpcw-units-module', __('Units in this Module', 'wp_courseware'));
        $unitList = WPCW_units_getListOfUnits($moduleID);
        if ($unitList) {
            printf('<ul class="wpcw_unit_list">');
            foreach ($unitList as $unitID => $unitObj) {
                printf('<li>%s %d - %s</li>', __('Unit', 'wp_courseware'), $unitObj->unit_meta->unit_number, $unitObj->post_title);
            }
            printf('</ul>');
        } else {
            printf('<p>%s</p>', __('There are currently no units in this module.', 'wp_courseware'));
        }
    }
    $page->showPageFooter();
}
 /**
  * Page that shows the overview of mapping for the levels to courses.
  * @param PageBuilder $page The current page object.
  */
 private function showMembershipMappingLevels_overview($page)
 {
     // Handle the detection of the membership plugin before doing anything else.
     if (!$this->found_membershipTool()) {
         $page->showPageFooter();
         return;
     }
     // Try to show the level data
     $levelData = $this->getMembershipLevels_cached();
     if ($levelData) {
         // Create the table to show the data
         $table = new TableBuilder();
         $table->attributes = array('class' => 'wpcw_tbl widefat', 'id' => 'wpcw_members_tbl');
         $col = new TableColumn(__('Level ID', 'wp_courseware'), 'wpcw_members_id');
         $table->addColumn($col);
         $col = new TableColumn(__('Level Name', 'wp_courseware'), 'wpcw_members_name');
         $table->addColumn($col);
         $col = new TableColumn(__('Users at this level can access:', 'wp_courseware'), 'wpcw_members_levels');
         $table->addColumn($col);
         $col = new TableColumn(__('Actions', 'wp_courseware'), 'wpcw_members_actions');
         $table->addColumn($col);
         $odd = false;
         // Work out the base URL for the overview page
         $baseURL = admin_url('admin.php?page=' . $this->extensionID);
         // The list of courses that are currently on the system.
         $courses = WPCW_courses_getCourseList(false);
         // Add actual level data
         foreach ($levelData as $id => $levelDatum) {
             $data = array();
             $data['wpcw_members_id'] = $levelDatum['id'];
             $data['wpcw_members_name'] = $levelDatum['name'];
             // Get list of courses already associated with level.
             $courseListInDB = $this->getCourseAccessListForLevel($levelDatum['id']);
             if ($courses) {
                 $data['wpcw_members_levels'] = '<ul class="wpcw_tickitems">';
                 // Show which courses will be added to users created at this level.
                 foreach ($courses as $courseID => $courseName) {
                     $data['wpcw_members_levels'] .= sprintf('<li class="wpcw_%s">%s</li>', isset($courseListInDB[$courseID]) ? 'enabled' : 'disabled', $courseName);
                 }
                 $data['wpcw_members_levels'] .= '</ul>';
             } else {
                 $data['wpcw_members_levels'] = __('There are no courses yet.', 'wp_courseware');
             }
             // Buttons to edit the permissions
             $data['wpcw_members_actions'] = sprintf('<a href="%s&level_id=%s" class="button-secondary">%s</a>', $baseURL, $levelDatum['id'], __('Edit Course Access Settings', 'wp_courseware'));
             $odd = !$odd;
             $table->addRow($data, $odd ? 'alternate' : '');
         }
         echo $table->toString();
     } else {
         $page->showMessage(sprintf(__('No membership levels were found for %s.', 'wp_courseware'), $this->extensionName), true);
     }
 }
/**
 * Generate a course list for resetting the progress for a user.
 * 
 * @param $fieldName String The name to use for the name attribute for this dropdown.
 * @param $courseIDList Array If specified, this is a list of IDs to determine which courses to use in the reset box.
 * @param $blankMessage String the message to show if there are no courses.
 * @param $addBlank String Use this string as the first item in the dropdown.
 * @param $cssID String The CSS ID to use for the select box.
 * @param $cssClass String The CSS class to use for the select box.
 *  
 * @return String The course reset dropdown box.
 */
function WPCW_courses_getCourseResetDropdown($fieldName, $courseIDList = false, $blankMessage, $addBlank, $cssID, $cssClass)
{
    $selectDetails = array('' => $addBlank);
    // Need all courses
    $courseList = WPCW_courses_getCourseList();
    if (!empty($courseList)) {
        $blankCount = 2;
        foreach ($courseList as $courseID => $aCourse) {
            // Filter out unwanted courses.
            if (is_array($courseIDList) && !in_array($courseID, $courseIDList)) {
                continue;
            }
            // Have sentinel of course_ to identify a course.
            $selectDetails['course_' . $courseID] = $aCourse;
            // Now we add the modules for this course
            $moduleList = WPCW_courses_getModuleDetailsList($courseID);
            if (!empty($moduleList)) {
                foreach ($moduleList as $moduleID => $moduleDetails) {
                    // Now we add the units for this course
                    $units = WPCW_units_getListOfUnits($moduleID);
                    if (!empty($units)) {
                        // Only add a module if it has units, to make resetting easier.
                        $selectDetails['module_' . $moduleID] = sprintf('&nbsp;&nbsp;- %s %d: %s', __('Module', 'wp_courseware'), $moduleDetails->module_number, $moduleDetails->module_title);
                        foreach ($units as $unitID => $unitDetails) {
                            $selectDetails['unit_' . $unitID] = sprintf('&nbsp;&nbsp;-- %s %d: %s', __('Unit', 'wp_courseware'), $unitDetails->unit_meta->unit_number, $unitDetails->post_title);
                        }
                    }
                    // end of unit list check
                }
                // end of foreach module
            }
            // end of module list check
            // Add a blank sentinel to space out courses.
            $paddingKey = str_pad(false, $blankCount++, ' ');
            $selectDetails[$paddingKey] = '&nbsp';
        }
    }
    // No courses... show meaningful message to the trainer.
    if (count($selectDetails) == 1) {
        $selectDetails[' '] = $blankMessage;
    }
    // Generate the select box. Use the $cssID as the name of the field too.
    return WPCW_forms_createDropdown($fieldName, $selectDetails, false, $cssID, $cssClass);
}