Location: PHPKode > projects > SASHA > SASHA/inc/lib/lib.base.php
<?php

/**
 * SASHA :: inc/lib/lib.base.php
 *
 * This contains the functions common to all child classes of SASHA.
 *
 * @package SASHA
 * @copyright (C) 2006-2010 Gordon P. Hemsley
 * @license docs/LICENSE BSD License
 * @version $Id: lib.base.php 82 2010-01-20 03:06:18Z gphemsley $
 */

/**
 * Base
 *
 * Base class for SASHA
 *
 * @todo Consolidate Base::_create_*_type_selector() functions
 *
 * @package Base
 */
class Base
{
	/**
	 * Base->semester
	 *
	 * @var int $semester Current semester (format: YYYYMM)
	 */
	var $semester;
	/**
	 * Base->institution
	 *
	 * @var string $institution Current institution
	 */
	var $institution;

	const M_bit = 64;
	const T_bit = 32;
	const W_bit = 16;
	const R_bit = 8;
	const F_bit = 4;
	const S_bit = 2;
	const U_bit = 1;

	const M_num = 0;
	const T_num = 1;
	const W_num = 2;
	const R_num = 3;
	const F_num = 4;
	const S_num = 5;
	const U_num = 6;

	/**
	 * Base::__construct()
	 *
	 * Sets up SASHA class for use.
	 *
	 * @access public
	 * @param int $user User id
	 * @param bool|string $semester Semester to use (format: YYYYMM; default: FALSE)
	 * @param bool|string $institution Institution to use (default: FALSE)
	 * @return void Initializes SASHA class
	 */
	public function __construct( $semester = FALSE, $institution = FALSE )
	{
		global $Database, $User;
		global $config;

		$this->instructor_types = array(
			IT_OTHER	=>	'Other',

			IT_LECTURER				=>	'Lecturer',
			IT_INSTRUCTOR			=>	'Instructor',
			IT_ASSISTANT_PROFESSOR	=>	'Assistant Professor',
			IT_ASSOCIATE_PROFESSOR	=>	'Associate Professor',
			IT_PROFESSOR			=>	'Professor',
			IT_SENIOR_LECTURER		=>	'Senior Lecturer',

			IT_CLINICAL_LECTURER			=>	'Clinical Lecturer',
			IT_CLINICAL_INSTRUCTOR			=>	'Clinical Instructor',
			IT_CLINICAL_ASSISTANT_PROFESSOR	=>	'Clinical Assistant Professor',
			IT_CLINICAL_ASSOCIATE_PROFESSOR	=>	'Clinical Associate Professor',
			IT_CLINICAL_PROFESSOR			=>	'Clinical Professor',

			IT_RESEARCH_LECTURER			=>	'Research Lecturer',
			IT_RESEARCH_INSTRUCTOR			=>	'Research Instructor',
			IT_RESEARCH_ASSISTANT_PROFESSOR	=>	'Research Assistant Professor',
			IT_RESEARCH_ASSOCIATE_PROFESSOR	=>	'Research Associate Professor',
			IT_RESEARCH_PROFESSOR			=>	'Research Professor',

			IT_ADJUNCT_LECTURER				=>	'Adjunct Lecturer',
			IT_ADJUNCT_INSTRUCTOR			=>	'Adjunct Instructor',
			IT_ADJUNCT_ASSISTANT_PROFESSOR	=>	'Adjunct Assistant Professor',
			IT_ADJUNCT_ASSOCIATE_PROFESSOR	=>	'Adjunct Associate Professor',
			IT_ADJUNCT_PROFESSOR			=>	'Adjunct Professor',

			IT_VISITING_LECTURER			=>	'Visiting Lecturer',
			IT_VISITING_INSTRUCTOR			=>	'Visiting Instructor',
			IT_VISITING_ASSISTANT_PROFESSOR	=>	'Visiting Assistant Professor',
			IT_VISITING_ASSOCIATE_PROFESSOR	=>	'Visiting Associate Professor',
			IT_VISITING_PROFESSOR			=>	'Visiting Professor',
			IT_VISITING_SENIOR_LECTURER		=>	'Visiting Senior Lecturer',

			IT_EXTENSION_LECTURER				=>	'Extension Lecturer',
			IT_EXTENSION_INSTRUCTOR				=>	'Extension Instructor',
			IT_EXTENSION_ASSISTANT_PROFESSOR	=>	'Extension Assistant Professor',
			IT_EXTENSION_ASSOCIATE_PROFESSOR	=>	'Extension Associate Professor',
			IT_EXTENSION_PROFESSOR				=>	'Extension Professor',

			IT_LIBRARY_LECTURER				=>	'Library Lecturer',
			IT_LIBRARY_INSTRUCTOR			=>	'Library Instructor',
			IT_LIBRARY_ASSISTANT_PROFESSOR	=>	'Library Assistant Professor',
			IT_LIBRARY_ASSOCIATE_PROFESSOR	=>	'Library Associate Professor',
			IT_LIBRARY_PROFESSOR			=>	'Library Professor'
		);

		$this->schedule_types = array(
			ST_LECTURE		=>	'Lecture',
			ST_LABORATORY	=>	'Laboratory',
			ST_DISCUSSION	=>	'Discussion',
			ST_SEMINAR		=>	'Seminar',
			ST_TESTING		=>	'Testing',
			ST_LECTDISC		=>	'Lecture/Discussion'
		);

		$this->assignment_statuses = array(
			AT_TODO		=>	'To Do',
			AT_STARTED	=>	'Started',
			AT_MIDWAY	=>	'Mid-way Through',
			AT_ALMOST	=>	'Almost Done',
			AT_DONE		=>	'Done'
		);

		$this->test_types = array(
			TT_QUIZ		=>	'Quiz',
			TT_EXAM		=>	'Exam',
			TT_MIDTERM	=>	'Midterm',
			TT_FINAL	=>	'Final'
		);

		$this->days = array(
			'M'	=>	array( self::M_num, self::M_bit ),
			'T'	=>	array( self::T_num, self::T_bit ),
			'W'	=>	array( self::W_num, self::W_bit ),
			'R'	=>	array( self::R_num, self::R_bit ),
			'F'	=>	array( self::F_num, self::F_bit ),
			'S'	=>	array( self::S_num, self::S_bit ),
			'U'	=>	array( self::U_num, self::U_bit )
		);

		$this->default_institution = ( exists( $User->user_info['default_institution'] ) ) ? $User->user_info['default_institution'] : $config['default_institution'];

		$this->institution = ( $institution && $this->validate_institution( $institution ) ) ? $institution : $this->get_current_institution();

		$this->default_semester = $this->get_default_semester();

		$this->semester = ( $semester && $this->validate_semester( $semester ) ) ? $semester : $this->get_current_semester();

		// Ensure that forms can be properly generated.
		require_once( ROOT . 'inc/lib/lib.forms.php' );
	}

	/**
	 * Base::convert_newlines()
	 *
	 * Convert newline characters to XHTML line breaks.
	 *
	 * @access public
	 * @param string $text Text with newline characters
	 * @return string Text with XHTML line breaks
	 */
	public function convert_newlines( $text )
	{
		$text = str_replace( array( "\r\n", "\n\r", "\r" ), "\n", $text );
		$text = str_replace( "\n", "<br />", $text );

		return $text;
	}

	/**
	 * Base::convert_hour()
	 *
	 * Convert between 12-hour and 24-hour format.
	 *
	 * @access public
	 * @param bool $to_24 Convert to/from 24-hour time (to: TRUE; from: FALSE)
	 * @param int $hour Hour to be converted
	 * @param array $time_parts Array containing time information (default: array())
	 * @return array Array containing time information
	 */
	final public function convert_hour( $to_24, $hour, $time_parts = array() )
	{
		if( $to_24 )
		{
			if( isset( $time_parts['meridiem'] ) && ( $time_parts['meridiem'] == 'PM' ) )
			{
				if( $hour == 12 )
				{
					$time_parts['hour'] = '12';
				}
				else
				{
					$time_parts['hour'] = (string) ( $hour + 12 );
				}

				$time_parts['meridiem'] = FALSE;
			}
			elseif( isset( $time_parts['meridiem'] ) && ( $time_parts['meridiem'] == 'AM' ) )
			{
				if( $hour == 12 )
				{
					$time_parts['hour'] = '00';
				}
				else
				{
					$time_parts['hour'] = str_pad( $hour, 2, '0', STR_PAD_LEFT );
				}

				$time_parts['meridiem'] = FALSE;
			}
			else
			{
				// Assume $hour is 24-hour format
				$time_parts['hour'] = str_pad( $hour, 2, '0', STR_PAD_LEFT );
				$time_parts['meridiem'] = FALSE;
			}
		}
		else
		{
			if( isset( $time_parts['meridiem'] ) && ( $time_parts['meridiem'] != FALSE ) )
			{
				// $hour is already in 12-hour format
				$time_parts['hour'] = str_pad( $hour, 2, '0', STR_PAD_LEFT );
			}
			else
			{
				if( ( $hour == 0 ) || ( $hour == 24 ) )
				{
					$time_parts['hour'] = '12';
					$time_parts['meridiem'] = 'AM';
				}
				elseif( $hour == 12 )
				{
					$time_parts['hour'] = '12';
					$time_parts['meridiem'] = 'PM';
				}
				elseif( $hour > 12 )
				{
					$time_parts['hour'] = (string) ( $hour - 12 );
					$time_parts['meridiem'] = 'PM';
				}
				else
				{
					$time_parts['hour'] = (string) $hour;
					$time_parts['meridiem'] = 'AM';
				}
			}
		}

		return $time_parts;
	}

	/**
	 * Base::get_current_semester()
	 *
	 * Get currently-specified semester.
	 *
	 * @access public
	 * @return string|bool Semester
	 */
	final public function get_current_semester()
	{
		$semester = FALSE;

		if( exists( $_REQUEST['semester'] ) )
		{
			if( is_array( $_REQUEST['semester'] ) )
			{
				$semester = (string) ( $_REQUEST['semester']['year'] . $_REQUEST['semester']['season'] );
			}
			else
			{
				$semester = (string) $_REQUEST['semester'];
			}
		}

		$semester = ( $this->validate_semester( $semester ) ) ? $semester : FALSE;

		return $semester;
	}

	/**
	 * Base::get_default_semester()
	 *
	 * Get default semester.
	 *
	 * @todo Get custom data from database for semester breakdown (e.g. Winterim).
	 *
	 * @access public
	 * @return string|bool Semester
	 */
	final public function get_default_semester()
	{
		$year = date( 'Y' );

		switch( date( 'm' ) )
		{
			case '01':
			case '02':
			case '03':
			case '04':
			case '05':
				$month = '01';
			break;

			case '06':
			case '07':
			case '08':
				$month = '06';
			break;

			case '09':
			case '10':
			case '11':
			case '12':
				$month = '09';
			break;
		}

		return $year . $month;
	}

	/**
	 * Base::get_current_institution()
	 *
	 * Get currently-specified institution.
	 *
	 * @access public
	 * @return string|bool Institution
	 */
	final public function get_current_institution()
	{
		if( exists( $_REQUEST['institution'] ) && $this->validate_institution( $_REQUEST['institution'] ) )
		{
			$institution = (string) $_REQUEST['institution'];
		}
		else
		{
			$institution = FALSE;
		}

		return $institution;
	}

	/**
	 * Base::get_course_title()
	 *
	 * Get title of given course.
	 *
	 * @access public
	 * @param string $institution Course institution
	 * @param string $subject Course subject
	 * @param int $course Course number
	 * @return string Course title
	 */
	final public function get_course_title( $institution, $subject, $course )
	{
		global $Database;

		$institution = ( $institution && $this->validate_institution( $institution ) ) ? (string) $institution : $this->institution;

		$sql = "SELECT c.course_title
			FROM courses c
			WHERE institution = '$institution'
				AND subject = '$subject'
				AND course = '$course'";

		$result = $Database->query( $sql );
		$course = $Database->fetch_assoc( $result );
		$Database->free_result( $result );

		if( $course )
		{
			return $course['course_title'];
		}
		else
		{
			return FALSE;
		}
	}

	/**
	 * Base::validate_institution()
	 *
	 * Used to ensure that the given institution is valid.
	 *
	 * @access public
	 * @param string $institution Institution to validate
	 * @return bool Validity of institution
	 */
	final public function validate_institution( $institution )
	{
		return preg_match( '/^[a-z]([a-z0-9.-]*[a-z0-9]+)?$/', $institution );
	}

	/**
	 * Base::validate_semester()
	 *
	 * Used to ensure that the given semester is valid.
	 *
	 * @access public
	 * @param string $semester Semester to validate
	 * @return bool Validity of semester
	 */
	final public function validate_semester( $semester )
	{
		return preg_match( '/^[0-9]{6}$/', $semester );
	}

	/**
	 * Base::validate_instructor_key()
	 *
	 * Used to ensure that the given instructor key is valid.
	 *
	 * @access public
	 * @param string $instructor_key Instructor key to validate
	 * @return bool Validity of instructor key
	 */
	final public function validate_instructor_key( $instructor_key )
	{
		return preg_match( '/^[a-z0-9]{40}$/', $instructor_key );
	}

	/**
	 * Base::validate_color()
	 *
	 * Used to ensure that color is valid.
	 *
	 * @todo Implement validation of CSS color keywords
	 *
	 * @access public
	 * @param string $color Color to validate
	 * @param bool $allow_keywords Allow named colors to validate (default: FALSE)
	 * @return bool Validity of color
	 */
	final public function validate_color( $color, $allow_keywords = FALSE )
	{
		if( $allow_keywords )
		{
			// Allow color keywords
		}
		else
		{
			return preg_match( '/^#?([A-F0-9]{3}){1,2}$/i', $color );
		}
	}

	/**
	 * Base::format_date()
	 *
	 * Format date. Mostly just a surrogate for PHP's date().
	 *
	 * @access public
	 * @param int $date UNIX timestamp
	 * @param string $format Date format to use (default: 'F j, Y')
	 * @return string Formatted date
	 */
	final public function format_date( $date, $format = 'F j, Y' )
	{
		return date( $format, $date );
	}

	/**
	 * Base::prepare_instructors()
	 *
	 * Create comma-separated list of instructor keys.
	 *
	 * @access public
	 * @param array $instructors Raw list of instructors
	 * @return string Prepared list of instructors
	 */
	final public function prepare_instructors( $instructors )
	{
		global $Database;

		foreach( $instructors as $id => $instructor )
		{
			if( !empty( $instructor ) && ( (int) $instructor ) )
			{
				$sql = "SELECT instructor_key
					FROM instructors
					WHERE instructor_id = '$instructor'";

				$result = $Database->query( $sql );
				$instructor = $Database->fetch_assoc( $result );
				$Database->free_result( $result );

				$instructors[$id] = @$instructor['instructor_key'];
			}
		}

		return rtrim( implode( ',', $instructors ), ',' );
	}

	/**
	 * Base::format_time()
	 *
	 * Format time according to given input and output settings.
	 *
	 * @access public
	 * @param mixed $time Time to format
	 * @param bool $twelve_hour Use AM/PM format
	 * @param bool|string $input Input type (possible values: array, timestamp, 24-hour)
	 * @param string $output Output type (possible values: hour, 24-hour, text, html; default: html)
	 * @return string Formatted time
	 */
	final public function format_time( $time, $twelve_hour = TRUE, $input = FALSE, $output = 'html' )
	{
		$time_parts = array();

		if( !$input )
		{
			if( is_array( $time ) && ( count( $time ) == 3 ) )
			{
				$input = 'array';
			}
			elseif( (int) $time )
			{
				if( @strlen( $time ) == 4 )
				{
					$input = '24-hour';
				}
				else
				{
					$input = 'timestamp';
				}
			}
		}

		switch( $input )
		{
			case 'array':
				$time_parts['hour'] = str_pad( $time['hour'], 2, '0', STR_PAD_LEFT );
				$time_parts['minute'] = str_pad( $time['minute'], 2, '0', STR_PAD_LEFT );
				$time_parts['meridiem'] = $time['meridiem'];
			break;

			case 'timestamp':
				$time_parts['hour'] = ( ( $twelve_hour ) ? date( 'h', $time ) : date( 'H', $time ) );
				$time_parts['minute'] = date( 'i', $time );
				$time_parts['meridiem'] = ( ( $twelve_hour ) ? date( 'A', $time ) : FALSE );
			break;

			case '24-hour':
			default:
				$time_split = str_split( str_pad( $time, 4, '0', STR_PAD_LEFT ), 2 );

				$time_parts['hour'] = $time_split[0];
				$time_parts['minute'] = $time_split[1];
				$time_parts['meridiem'] = FALSE;

				if( $twelve_hour )
				{
					$time_parts = $this->convert_hour( FALSE, (int) $time_split[0], $time_parts );
				}
			break;
		}

		switch( $output )
		{
			case 'hour':
				return $this->convert_hour( TRUE, $time_parts['hour'], $time_parts );
			break;

			case '24-hour':
				if( $time_parts['meridiem'] )
				{
					if( $time_parts['meridiem'] == 'PM' )
					{
						$time_parts['meridiem'] = FALSE;

						if( $time_parts['hour'] < 12 )
						{
							$time_parts['hour'] += 12;
						}
					}
					elseif( $time_parts['hour'] == 12 )
					{
						$time_parts['hour'] = '00';
					}
				}

				return $time_parts['hour'] . $time_parts['minute'];
			break;

			case 'text':
				if( $time_parts['meridiem'] )
				{
					return $time_parts['hour'] . ':' . $time_parts['minute'] . ' ' . $time_parts['meridiem'];
				}
				else
				{
					return $time_parts['hour'] . ':' . $time_parts['minute'];
				}
			break;

			case 'html':
			default:
				if( $time_parts['meridiem'] )
				{
					return $time_parts['hour'] . ':' . $time_parts['minute'] . '&nbsp;' . $time_parts['meridiem'];
				}
				else
				{
					return $time_parts['hour'] . ':' . $time_parts['minute'];
				}
			break;
		}
	}

	/**
	 * Base::format_days()
	 *
	 * Format schedule days according to given input and output settings.
	 *
	 * @access public
	 * @param mixed $days Days to format
	 * @param bool|string $input Input type (possible values: binary, array; default: array)
	 * @param string $output Output type (possible values: array, binary, decimal, text, html; default: html)
	 * @return string Formatted days
	 */
	final public function format_days( $days, $input = 'array', $output = 'html' )
	{
		$days_array = array();

		if( ( $input === TRUE ) || is_array( $days ) )
		{
			$input = 'array';
		}
		elseif( ( in_array( $input, array( 'array', NULL ) ) ) && !is_array( $days ) )
		{
			// MySQL 5.0 returns bit as binary, while MySQL 5.1 returns decimal
			if( $days == chr( ord( $days ) ) )
			{
				$input = 'binary';
			}
			else
			{
				$input = 'decimal';
			}
		}

		switch( $input )
		{
			case 'decimal':
			case 'binary':
				$days_dec = ( $input == 'binary' ) ? hexdec( bin2hex( $days ) ) : $days;

				$days_array['M'] = (bool) ( ( $days_dec & self::M_bit ) == self::M_bit );
				$days_array['T'] = (bool) ( ( $days_dec & self::T_bit ) == self::T_bit );
				$days_array['W'] = (bool) ( ( $days_dec & self::W_bit ) == self::W_bit );
				$days_array['R'] = (bool) ( ( $days_dec & self::R_bit ) == self::R_bit );
				$days_array['F'] = (bool) ( ( $days_dec & self::F_bit ) == self::F_bit );
				$days_array['S'] = (bool) ( ( $days_dec & self::S_bit ) == self::S_bit );
				$days_array['U'] = (bool) ( ( $days_dec & self::U_bit ) == self::U_bit );
			break;

			case 'array':
			default:
				$days_array['M'] = ( isset( $days['M'] ) ) ? (bool) $days['M'] : is_int( array_search( 'M', $days ) );
				$days_array['T'] = ( isset( $days['T'] ) ) ? (bool) $days['T'] : is_int( array_search( 'T', $days ) );
				$days_array['W'] = ( isset( $days['W'] ) ) ? (bool) $days['W'] : is_int( array_search( 'W', $days ) );
				$days_array['R'] = ( isset( $days['R'] ) ) ? (bool) $days['R'] : is_int( array_search( 'R', $days ) );
				$days_array['F'] = ( isset( $days['F'] ) ) ? (bool) $days['F'] : is_int( array_search( 'F', $days ) );
				$days_array['S'] = ( isset( $days['S'] ) ) ? (bool) $days['S'] : is_int( array_search( 'S', $days ) );
				$days_array['U'] = ( isset( $days['U'] ) ) ? (bool) $days['U'] : is_int( array_search( 'U', $days ) );
			break;
		}

		switch( $output )
		{
			case 'array':
				return $days_array;
			break;

			case 'binary':
			case 'decimal':
				$formatted_days = 0;

				$formatted_days = ( (bool) $days_array['M'] ) ? $formatted_days | self::M_bit : $formatted_days;
				$formatted_days = ( (bool) $days_array['T'] ) ? $formatted_days | self::T_bit : $formatted_days;
				$formatted_days = ( (bool) $days_array['W'] ) ? $formatted_days | self::W_bit : $formatted_days;
				$formatted_days = ( (bool) $days_array['R'] ) ? $formatted_days | self::R_bit : $formatted_days;
				$formatted_days = ( (bool) $days_array['F'] ) ? $formatted_days | self::F_bit : $formatted_days;
				$formatted_days = ( (bool) $days_array['S'] ) ? $formatted_days | self::S_bit : $formatted_days;
				$formatted_days = ( (bool) $days_array['U'] ) ? $formatted_days | self::U_bit : $formatted_days;

				$formatted_days = ( $output == 'binary' ) ? decbin( $formatted_days ) : $formatted_days;
			break;

			case 'text':
			case 'html':
			default:
				$dash = ( $output == 'html' ) ? '&ndash;' : "\x2013";
				$formatted_days = '';

				$formatted_days .= ( (bool) $days_array['M'] ) ? 'M' : $dash;
				$formatted_days .= ( (bool) $days_array['T'] ) ? 'T' : $dash;
				$formatted_days .= ( (bool) $days_array['W'] ) ? 'W' : $dash;
				$formatted_days .= ( (bool) $days_array['R'] ) ? 'R' : $dash;
				$formatted_days .= ( (bool) $days_array['F'] ) ? 'F' : $dash;
				$formatted_days .= ( (bool) $days_array['S'] ) ? 'S' : $dash;
				$formatted_days .= ( (bool) $days_array['U'] ) ? 'U' : $dash;
			break;
		}

		return $formatted_days;
	}

	/**
	 * Base::format_instructor_type()
	 *
	 * Format instructor type using given id.
	 *
	 * @access public
	 * @param int $instructor_type Instructor type id
	 * @param bool|string $instructor_type_description Instructor type description (default: FALSE)
	 * @return string Formatted instructor type
	 */
	final public function format_instructor_type( $instructor_type, $instructor_type_description = FALSE )
	{
		if( ( $instructor_type == IT_OTHER ) && $instructor_type_description )
		{
			return $instructor_type_description;
		}
		else
		{
			return ( ( $instructor_type >= 100 ) ? $this->instructor_types[$instructor_type - 100] . ' Emeritus' : $this->instructor_types[$instructor_type] );
		}
	}

	/**
	 * Base::format_schedule_type()
	 *
	 * Format schedule type using given id.
	 *
	 * @access public
	 * @param int $schedule_type Schedule type id
	 * @return string Formatted schedule type
	 */
	final public function format_schedule_type( $schedule_type )
	{
		return $this->schedule_types[$schedule_type];
	}

	/**
	 * Base::format_assignment_status()
	 *
	 * Format assignment status using given id.
	 *
	 * @access public
	 * @param int $assignment_status Assignment status id
	 * @return string Formatted assignment status
	 */
	final public function format_assignment_status( $assignment_status )
	{
		return $this->assignment_statuses[$assignment_status];
	}

	/**
	 * Base::format_test_type()
	 *
	 * Format test type using given id.
	 *
	 * @access public
	 * @param int $test_type Test type id
	 * @return string Formatted test type
	 */
	final public function format_test_type( $test_type )
	{
		return $this->test_types[$test_type];
	}

	/**
	 * Base::format_institution()
	 *
	 * Format institution name, using long or short form.
	 *
	 * @access public
	 * @param string $institution Institution
	 * @param bool $full Force long form of institution name
	 * @return string Formatted institution name
	 */
	final public function format_institution( $institution, $full = FALSE )
	{
		global $Database;

		$sql = "SELECT i.name, i.short_name
			FROM institutions i
			WHERE i.institution = '$institution'";

		$result = $Database->query( $sql );
		$names = $Database->fetch_assoc( $result );
		$Database->free_result( $result );

		return ( ( $names['short_name'] && !$full ) ? $names['short_name'] : $names['name'] );
	}

	/**
	 * Base::format_semester()
	 *
	 * Format semester, using seasons.
	 *
	 * @access public
	 * @param int|string $semester Semester (format: YYYYMM)
	 * @return string Formatted semester
	 */
	final public function format_semester( $semester )
	{
		$data = preg_split( '/(\d{4})(\d{2})/', $semester, -1, PREG_SPLIT_DELIM_CAPTURE );

		if( !$data || ( count( $data ) != 4 ) )
		{
			return FALSE;
		}

		$year = $data[1];

		switch( $data[2] )
		{
			case '01':
			case '02':
			case '03':
			case '04':
			case '05':
				$season = 'Spring';
			break;

			case '06':
			case '07':
			case '08':
				$season = 'Summer';
			break;

			case '09':
			case '10':
			case '11':
			case '12':
				$season = 'Fall';
			break;

			default:
				return FALSE;
			break;
		}

		return "$season $year";
	}

	/**
	 * Base::format_instructors()
	 *
	 * Format list of instructors.
	 *
	 * @todo Remove obsolete code.
	 *
	 * @access public
	 * @param string $instructors Unformatted list of intructors
	 * @param string $glue Glue used to connect instructors (default: "\n")
	 * @return string Formatted list of instructors
	 */
	final public function format_instructors( $instructors, $glue = "\n" )
	{
		global $Database;

		if( is_array( $instructors ) )
		{
			$instructors = $this->prepare_instructors( $instructors );
		}

		$instructors = explode( ',', $instructors );

		foreach( $instructors as $id => $instructor )
		{
			$instructors[$id] = $instructor = trim( $instructor );

			if( $this->validate_instructor_key( $instructor ) )
			{
				$sql = "SELECT *
					FROM instructors
					WHERE instructor_key = '$instructor'";

				$result = $Database->query( $sql );

				while( $row = $Database->fetch_assoc( $result ) )
				{
					$instructors[$id] = $row['last_name'] . ', ' . $row['first_name'];

					if( trim( $row['middle_name'] ) )
					{
						$instructors[$id] .= ' ';

						if( strpos( $row['middle_name'], '.' ) )
						{
							$instructors[$id] .= $row['middle_name'];
						}
						else
						{
							$instructors[$id] .= substr( $row['middle_name'], 0, 1 ) . '.';
						}
					}
				}

				$Database->free_result( $result );
			}
			else
			{
				$last_name = substr( $instructor, 0, strrpos( $instructor, ' ' ) );
				$first_initial = substr( $instructor, strrpos( $instructor, ' ' ) + 1, strlen( $instructor ) );

				if( $last_name && $first_initial )
				{
					$instructors[$id] = "$last_name, $first_initial.";
				}
			}
		}

		return implode( $glue, $instructors );
	}

	/**
	 * Base::format_course()
	 *
	 * Format course code.
	 *
	 * @access public
	 * @param string $subject Subject code
	 * @param string|int $course Course number
	 * @param bool|string $institution Institution
	 * @param string $glue Glue to assemble subject and course (default: '&nbsp;')
	 * @return string Formatted course number
	 */
	final public function format_course( $subject, $course, $institution = FALSE, $glue = '&nbsp;' )
	{
		global $Database;

		$institution = ( $institution && $this->validate_institution( $institution ) ) ? (string) $institution : $this->institution;

		$sql = "SELECT cf.*
			FROM course_format cf
			WHERE cf.institution = '$institution'";

		$result = $Database->query( $sql );

		if( $Database->has_result( $result ) )
		{
			$course_format = $Database->fetch_assoc( $result );

			$course_max_length = $course_format['course_max_length'];

			// Don't force the lengths; use them only as a guide
			$course = ltrim( $course, '0' );
			$course = ( strlen( $course ) > $course_max_length ) ? $course : str_pad( $course, $course_max_length, '0', STR_PAD_LEFT );

//			$subject = substr( $subject, 0, $course_format['subject_max_length'] );
//			$course = substr( $course, -$course_max_length, $course_max_length );
		}

		$Database->free_result( $result );

		if( $glue === FALSE )
		{
			return array(
				'subject'	=>	$subject,
				'course'	=>	$course
			);
		}

		return $subject . $glue . $course;
	}

	/**
	 * Base::print_sub_navigation()
	 *
	 * Print page-specific navigation.
	 *
	 * @access public
	 * @param array $sub_nav Navigation link data.
	 * @return bool Status of printing sub-navigation.
	 */
	public function print_sub_navigation( $sub_nav )
	{
		if( !is_array( $sub_nav ) )
		{
			return FALSE;
		}

		print "\t\t" . '<div id="sub-nav">' . "\n";
		print "\t\t\t" . '<ul>' . "\n";

		foreach( $sub_nav as $link_id => $link )
		{
			print "\t\t\t\t" . '<li><a href="' . htmlentities( $link['url'], ENT_QUOTES, 'UTF-8' ) . '">' . htmlentities( $link['title'], ENT_QUOTES, 'UTF-8' ) . '</a></li>' . "\n";
		}

		print "\t\t\t" . '</ul>' . "\n";
		print "\t\t" . '</div>' . "\n";

		return TRUE;
	}

	/**
	 * Base::print_semester_navigation()
	 *
	 * Print semester navigation for previous and next semesters.
	 *
	 * @access public
	 * @param int|string $semester Current semester (format: YYYYMM)
	 * @param string $file File/path requiring navigation
	 * @param bool|string $institution Limit selection to given institution
	 * @return bool Status of printing navigation links
	 */
	public function print_semester_navigation( $semester, $file, $institution = FALSE )
	{
		if( !$this->validate_semester( $semester ) || ( $institution && !$this->validate_institution( $institution ) ) )
		{
			return FALSE;
		}

		$data = preg_split( '/^(\d{4})(\d{2})$/', $semester, -1, PREG_SPLIT_DELIM_CAPTURE );

		$year = $data[1];

		switch( $data[2] )
		{
			case '01':
			case '02':
			case '03':
			case '04':
			case '05':
				$previous_month = '09';
				$previous_year = $year - 1;
				$next_month = '06';
				$next_year = $year;
			break;

			case '06':
			case '07':
			case '08':
				$previous_month = '01';
				$previous_year = $year;
				$next_month = '09';
				$next_year = $year;
			break;

			case '09':
			case '10':
			case '11':
			case '12':
				$previous_month = '06';
				$previous_year = $year;
				$next_month = '01';
				$next_year = $year + 1;
			break;

			default:
				return FALSE;
			break;
		}

		$previous = $previous_year . $previous_month;
		$next = $next_year . $next_month;
		$only_institution = ( $institution ) ? 'institution=' . $institution . '&amp;' : '';

		print "\t\t" . '<div class="semester-nav">' . "\n";
		print "\t\t\t" . '<div class="left-column"><a href="' . $file . '?' . $only_institution . 'semester=' . $previous . '">&larr;&nbsp;' . $this->format_semester( $previous ) . '</a></div>' . "\n";
		print "\t\t\t" . '<div class="center-column">';

		if( $institution )
		{
			print $this->format_institution( $institution, TRUE ) . ' &ndash; <a href="' . $file . '?semester=' . $semester . '">';
		}

		print $this->format_semester( $semester ) . ( ( $institution ) ? '</a>' : '' ) . '</div>' . "\n";
		print "\t\t\t" . '<div class="right-column"><a href="' .$file . '?' . $only_institution . 'semester=' . $next . '">' . $this->format_semester( $next ) . '&nbsp;&rarr;</a></div>' . "\n";
		print "\t\t" . '</div>' . "\n";

		return TRUE;
	}

	/**
	 * Base::print_list_table()
	 *
	 * Print table for lists.
	 *
	 * @access public
	 * @param string $nav_file Path to use for navigation
	 * @param array $columns Data for column structure
	 * @param array $headers Data for headers and footers
	 * @param array $rows Data for rows and cells
	 * @return bool Status of printing list table
	 */
	public function print_list_table( $nav_file, $columns, $headers, $rows )
	{
		$semester = ( $this->semester ) ? $this->semester : $this->default_semester;

		$this->print_semester_navigation( $semester, $nav_file, $this->institution );

		print "\t\t" . '<table class="list">' . "\n";

		print "\t\t\t" . '<colgroup>' . "\n";

		foreach( $columns as $col )
		{
			print "\t\t\t\t" . '<col';

			if( exists( $col['span'] ) )
			{
				print ' span="' . $col['span'] . '"';
			}

			if( exists( $col['class'] ) )
			{
				print ' class="' . $col['class'] . '"';
			}

			if( exists( $col['style'] ) )
			{
				print ' style="' . $col['style'] . '"';
			}

			print ' />' . "\n";
		}

		print "\t\t\t" . '</colgroup>' . "\n";

		for( $top = 1; $top >= 0; $top-- )
		{
			if( $top )
			{
				print "\t\t\t" . '<thead style="border-bottom: 3px solid black;">' . "\n";
			}
			else
			{
				print "\t\t\t" . '<tfoot style="border-top: 3px solid black;">' . "\n";
			}

			print "\t\t\t\t" . '<tr>' . "\n";

			foreach( $headers as $head )
			{
				if( does_not_exist( $head['content'] ) )
				{
					$head['content'] = '&nbsp;';
				}

				print "\t\t\t\t\t" . '<th';

				if( exists( $head['colspan'] ) )
				{
					print ' colspan="' . $head['colspan'] . '"';
				}

				if( exists( $head['class'] ) )
				{
					print ' class="' . $head['class'] . '"';
				}

				if( exists( $head['style'] ) )
				{
					print ' style="' . $head['style'] . '"';
				}

				print '>' . $head['content'] . '</th>' . "\n";
			}

			print "\t\t\t\t" . '</tr>' . "\n";

			if( $top )
			{
				print "\t\t\t" . '</thead>' . "\n";
			}
			else
			{
				print "\t\t\t" . '</tfoot>' . "\n";
			}
		}

		print "\t\t\t" . '<tbody>' . "\n";

		foreach( $rows as $row_id => $row )
		{
			print "\t\t\t\t" . '<tr';

			if( exists( $row['class'] ) )
			{
				print ' class="' . $row['class'] . '"';
			}

			if( exists( $row['style'] ) )
			{
				print ' style="' . $row['style'] . '"';
			}

			print '>' . "\n";

			foreach( $row['content'] as $cell )
			{
				if( empty( $cell ) )
				{
					continue;
				}

				if( does_not_exist( $cell['content'] ) )
				{
					$cell['content'] = '&nbsp;';
				}

				print "\t\t\t\t\t" . '<td';

				if( exists( $cell['colspan'] ) )
				{
					print ' colspan="' . $cell['colspan'] . '"';
				}

				if( exists( $cell['class'] ) )
				{
					print ' class="' . $cell['class'] . '"';
				}

				if( exists( $cell['style'] ) )
				{
					print ' style="' . $cell['style'] . '"';
				}

				print '>' . $cell['content'] . '</td>' . "\n";
			}

			print "\t\t\t\t" . '</tr>' . "\n";
		}
		print "\t\t\t" . '</tbody>' . "\n";

		print "\t\t" . '</table>' . "\n";

		$this->print_semester_navigation( $semester, $nav_file, $this->institution );

		return TRUE;
	}

	/**
	 * Base::create_form()
	 *
	 * Create a form using Forms::create_form().
	 *
	 * @access public
	 * @param string $form_name Form name
	 * @param string $form_action URL to send form data
	 * @param array $form_data Parameters used to propagate form
	 * @return void Prints a complete HTML form
	 */
	public function create_form( $form_name, $form_action, $form_data )
	{
		return Forms::create_form( $form_name, $form_action, $form_data );
	}
}

?>
Return current item: SASHA