function _upgradeAccounts()
{
    // add new upload fields
    $schema = loadSchema('accounts');
    // make schema and menu visible
    if (@$schema['tableHidden']) {
        $schema['tableHidden'] = 0;
    }
    if (@$schema['menuHidden']) {
        $schema['menuHidden'] = 0;
    }
    // add new fields
    if (!@$schema['createdDate']) {
        $schema['createdDate'] = array('type' => 'none', 'label' => "Created", 'isSystemField' => '1');
    }
    if (!@$schema['createdByUserNum']) {
        $schema['createdByUserNum'] = array('type' => 'none', 'label' => "Created By", 'isSystemField' => '1');
    }
    if (!@$schema['updatedDate']) {
        $schema['updatedDate'] = array('type' => 'none', 'label' => "Last Updated", 'isSystemField' => '1');
    }
    if (!@$schema['updatedByUserNum']) {
        $schema['updatedByUserNum'] = array('type' => 'none', 'label' => "Last Updated By", 'isSystemField' => '1');
    }
    if (!@$schema['accessList']) {
        $schema['accessList'] = array('type' => 'accessList', 'label' => "Section Access", 'isSystemField' => '1', 'order' => time());
    }
    if (!@$schema['lastLoginDate']) {
        // added in v2.08
        $schema['lastLoginDate'] = array('type' => 'date', 'label' => "Last Login", 'defaultDate' => 'none', 'order' => time(), 'showTime' => '1', 'use24HourFormat' => '0', 'showSeconds' => '1', 'yearRangeStart' => '2010', 'yearRangeEnd' => '2020');
    }
    // remove fields
    foreach (array_keys($schema) as $fieldname) {
        $fieldSchema =& $schema[$fieldname];
        if (!is_array($fieldSchema)) {
            continue;
        }
        // fields are stored as arrays, other entries are table metadata, skip metadata
        // remove old "show tablenames" field for old access settings
        if (@$fieldSchema['type'] == 'separator' && preg_match("/listTableNames\\(\\)'>MySQL Tablenames/", @$fieldSchema['separatorHTML'])) {
            unset($schema[$fieldname]);
        }
    }
    ### update order
    // increase field order for all fields
    foreach (array_keys($schema) as $fieldname) {
        $fieldSchema =& $schema[$fieldname];
        if (!is_array($fieldSchema)) {
            continue;
        }
        // fields are stored as arrays, other entries are table metadata, skip metadata
        $fieldSchema['order'] += 10;
    }
    // hard code field order
    if (@$schema['num']) {
        $schema['num']['order'] = '1';
    }
    if (@$schema['createdDate']) {
        $schema['createdDate']['order'] = '2';
    }
    if (@$schema['createdByUserNum']) {
        $schema['createdByUserNum']['order'] = '3';
    }
    if (@$schema['updatedDate']) {
        $schema['updatedDate']['order'] = '4';
    }
    if (@$schema['updatedByUserNum']) {
        $schema['updatedByUserNum']['order'] = '5';
    }
    ### change fields
    // Set checked/unchecked values for 'isAdmin' field
    if (@$schema['isAdmin']) {
        if (@$schema['isAdmin']['checkedValue'] == '') {
            $schema['isAdmin']['checkedValue'] = 'Yes';
        }
        if (@$schema['isAdmin']['uncheckedValue'] == '') {
            $schema['isAdmin']['uncheckedValue'] = '-';
        }
        $schema['isAdmin']['adminOnly'] = "2";
    }
    // Set accessList to be a system field
    if (@$schema['accessList']) {
        $schema['accessList']['isSystemField'] = 1;
    }
    // v1.32 - add "My Account" fields
    $myAccountFields = array('fullname', 'username', 'email', 'password');
    foreach ($myAccountFields as $field) {
        if (!is_array(@$schema[$field])) {
            continue;
        }
        if (array_key_exists('myAccountField', $schema[$field])) {
            continue;
        }
        // ignore if already set
        $schema[$field]['myAccountField'] = 1;
    }
    // save changes
    saveSchema('accounts', $schema);
    // add to schema
    createMissingSchemaTablesAndFields();
    // add to database
    clearAlertsAndNotices();
    // don't show "created table/field" alerts
}
function addTable()
{
    global $TABLE_PREFIX, $APP;
    $menuType = @$_REQUEST['menuType'];
    $presetTableName = @$_REQUEST['presetName'];
    $advancedType = @$_REQUEST['advancedType'];
    //
    security_dieUnlessPostForm();
    security_dieUnlessInternalReferer();
    security_dieOnInvalidCsrfToken();
    //
    disableInDemoMode('', 'ajax');
    // error checking
    $errors = '';
    if (!$menuType) {
        $errors .= "No menu type selected!\n";
    }
    if (!@$_REQUEST['menuName']) {
        $errors .= "No menu name specified!\n";
    }
    $errors .= getTablenameErrors(@$_REQUEST['tableName']);
    $newSchema = null;
    if ($menuType == 'copy') {
        if ($errors) {
            die($errors);
        }
        $sourceSchemaName = @$_REQUEST['copy'];
        if (!in_array($sourceSchemaName, getSchemaTables())) {
            die("Couldn't load source schema");
        }
        $newSchema = loadSchema($sourceSchemaName) or die("Couldn't load source schema");
    } else {
        if ($menuType == 'preset') {
            $schemaPresets = getSchemaPresets();
            $presetFound = array_key_exists(@$_REQUEST['preset'], $schemaPresets);
            if (!@$_REQUEST['preset']) {
                $errors .= "You must select a preset from the pulldown!\n";
            } elseif (!$presetFound) {
                $errors .= "No schema preset file found for '" . htmlencode($presetTableName) . "'\n";
            }
        }
        if ($errors) {
            die($errors);
        }
        // create new schema data
        if ($menuType == 'single') {
            $presetTableName = "customSingle";
        } elseif ($menuType == 'multi') {
            $presetTableName = "customMulti";
        } elseif ($menuType == 'preset') {
            $presetTableName = @$_REQUEST['preset'];
        } elseif ($menuType == 'advanced' && $advancedType == 'category') {
            $presetTableName = "customCategory";
        } elseif ($menuType == 'advanced' && $advancedType == 'textlink') {
            $presetTableName = "customTextLink";
        } elseif ($menuType == 'advanced' && $advancedType == 'menugroup') {
            $presetTableName = "customMenuGroup";
        } else {
            die("Unable to determine preset table name to load!");
        }
        $schemaPresetDir = DATA_DIR . "/schemaPresets/";
        $newSchema = loadSchema($presetTableName, $schemaPresetDir) or die("Couldn't load preset schema");
    }
    $newSchema['menuName'] = @$_REQUEST['menuName'];
    // change menu name
    $newSchema['menuOrder'] = time();
    // use time to sort to bottom
    // create mysql table
    // (this isn't required but done here so we catch get mysql errors creating the table)
    // createMissingSchemaTablesAndFields() creates if this doesn't.
    $tableNameWithPrefix = $TABLE_PREFIX . @$_REQUEST['tableName'];
    $result = mysql_query("CREATE TABLE `" . mysql_escape($tableNameWithPrefix) . "` (\n                                          num int(10) unsigned NOT NULL auto_increment,\n                                          PRIMARY KEY (num)\n                                        ) ENGINE=MyISAM DEFAULT CHARSET=utf8;");
    if (!$result) {
        print "Error creating MySQL table.\n\nMySQL error was: " . htmlencode(mysql_error()) . "\n";
        exit;
    }
    // save new schema
    saveSchema(@$_REQUEST['tableName'], $newSchema);
    // Create schema table and fields in MySQL
    createMissingSchemaTablesAndFields();
    clearAlertsAndNotices();
    // don't display alerts about adding new fields
    exit;
    // this is called with ajax so returning nothing means success - see: addTable_functions.js - initSubmitFormWithAjax
}
function installIfNeeded()
{
    global $SETTINGS, $APP, $TABLE_PREFIX;
    if (isInstalled()) {
        return;
    }
    // skip if already installed
    // rename default files
    renameOrRemoveDefaultFiles();
    // error checking
    if ($SETTINGS['uploadDir'] && !is_dir($SETTINGS['uploadDir'])) {
        print "Upload directory doesn't exist, please update 'uploadDir' in /data/" . SETTINGS_FILENAME . "<br/>\n";
        print "Current uploadDir value: " . htmlencode($SETTINGS['uploadDir']) . "<br/>\n";
        print "Suggested uploadDir value: uploads/ or ../uploads/<br/>\n";
        exit;
    }
    // error checking
    checkFilePermissions();
    // display license
    if (@$_REQUEST['menu'] == 'license') {
        showInterface('license.php');
    }
    // save
    if (@$_REQUEST['save']) {
        // error checking
        if (!$_REQUEST['licenseCompanyName']) {
            alert("Please enter your 'Company Name'<br/>\n");
        }
        if (!$_REQUEST['licenseDomainName']) {
            alert("Please enter your 'Domain Name'<br/>\n");
        }
        if (!$_REQUEST['licenseProductId']) {
            alert("Please enter your 'Product Id'<br/>\n");
        } else {
            if (!isValidProductId($_REQUEST['licenseProductId'])) {
                alert("Invalid Product Id!<br/>\n");
            }
        }
        if (!$_REQUEST['agreeToOneInstall']) {
            alert("Please check 'I agree not to use this 'Product Id' for multiple installs'<br/>\n");
        }
        if (!$_REQUEST['understandTermination']) {
            alert("Please check 'I understand doing so may cause be to lose my right to use this software'<br/>\n");
        }
        if (!$_REQUEST['agreeToLicense']) {
            alert("Please check 'I accept the terms of the License Agreement'<br/>\n");
        }
        if (!$_REQUEST['mysqlHostname']) {
            alert("Please enter your 'MySQL Hostname'<br/>\n");
        }
        if (!$_REQUEST['mysqlDatabase']) {
            alert("Please enter your 'MySQL Database'<br/>\n");
        }
        if (!$_REQUEST['mysqlUsername']) {
            alert("Please enter your 'MySQL Username'<br/>\n");
        }
        if (!$_REQUEST['mysqlTablePrefix']) {
            alert("Please enter your 'MySQL Table Prefix'<br/>\n");
        } elseif (preg_match("/[A-Z]/", $_REQUEST['mysqlTablePrefix'])) {
            alert("Value for 'MySQL Table Prefix' must be lowercase.<br/>\n");
        } elseif (!preg_match("/^[a-z]/i", $_REQUEST['mysqlTablePrefix'])) {
            alert("Value for 'MySQL Table Prefix' must start with a letter.<br/>\n");
        } elseif (!preg_match("/_\$/", $_REQUEST['mysqlTablePrefix'])) {
            alert("Value for 'MySQL Table Prefix' must end in underscore.<br/>\n");
        }
        // New Installation
        if (!@$_REQUEST['restoreFromBackup']) {
            if (!$_REQUEST['adminFullname']) {
                alert("Please enter 'Admin Full Name'<br/>\n");
            }
            if (!$_REQUEST['adminEmail']) {
                alert("Please enter 'Admin Email'<br/>\n");
            } elseif (!isValidEmail($_REQUEST['adminEmail'])) {
                alert("Please enter a valid email for 'Admin Email' (Example: user@example.com)<br/>\n");
            }
            if (!$_REQUEST['adminUsername']) {
                alert("Please enter 'Admin Username'<br/>\n");
            }
            $passwordErrors = getNewPasswordErrors($_REQUEST['adminPassword1'], $_REQUEST['adminPassword2'], $_REQUEST['adminUsername']);
            // v2.52
            if ($passwordErrors) {
                alert(nl2br(htmlencode($passwordErrors)));
            }
        }
        // Restore from Backup
        if (@$_REQUEST['restoreFromBackup']) {
            if (!$_REQUEST['restore']) {
                alert("Please select a backup file to restore<br/>\n");
            }
        }
        // Advanced - v2.53
        if (!@$_REQUEST['useCustomSettingsFile']) {
            if (is_file(SETTINGS_DEV_FILEPATH)) {
                alert(t("You must select 'Use Custom Settings File' since a custom settings file for this domain already exists!") . "<br/>\n");
            } elseif (isDevServer()) {
                alert("This is a development server, you must select 'Use Custom Settings File'." . "<br/>\n");
            }
        }
        if (@$_REQUEST['webPrefixUrl'] != '') {
            if (!preg_match("|^(\\w+:/)?/|", $_REQUEST['webPrefixUrl'])) {
                alert(t("Website Prefix URL must start with /") . "<br/>\n");
            }
            if (preg_match("|/\$|", $_REQUEST['webPrefixUrl'])) {
                alert(t("Website Prefix URL cannot end with /") . "<br/>\n");
            }
        }
        // update settings (not saved unless there are no errors)
        $SETTINGS['cookiePrefix'] = substr(md5(mt_rand()), 0, 5) . '_';
        //v2.51 shortened prefix so it's easy to see full cookie names in browser cookie list
        $SETTINGS['adminEmail'] = @$SETTINGS['adminEmail'] ? $SETTINGS['adminEmail'] : $_REQUEST['adminEmail'];
        $SETTINGS['licenseCompanyName'] = $_REQUEST['licenseCompanyName'];
        $SETTINGS['licenseDomainName'] = $_REQUEST['licenseDomainName'];
        $SETTINGS['licenseProductId'] = $_REQUEST['licenseProductId'];
        $SETTINGS['webRootDir'] = @$SETTINGS['webRootDir'] ? $SETTINGS['webRootDir'] : @$_SERVER['DOCUMENT_ROOT'];
        $SETTINGS['mysql']['hostname'] = $_REQUEST['mysqlHostname'];
        $SETTINGS['mysql']['database'] = $_REQUEST['mysqlDatabase'];
        $SETTINGS['mysql']['username'] = $_REQUEST['mysqlUsername'];
        $SETTINGS['mysql']['password'] = $_REQUEST['mysqlPassword'];
        $SETTINGS['mysql']['tablePrefix'] = $_REQUEST['mysqlTablePrefix'];
        $TABLE_PREFIX = $_REQUEST['mysqlTablePrefix'];
        // update TABLE_PREFIX global as well.
        $SETTINGS['webPrefixUrl'] = $_REQUEST['webPrefixUrl'];
        // display errors
        if (alert()) {
            require "lib/menus/install.php";
            exit;
        }
        // connect to mysql
        $errors = connectToMySQL('returnErrors');
        if ($errors) {
            alert($errors);
            require "lib/menus/install.php";
            exit;
        } else {
            connectToMySQL();
        }
        // create schema tables
        createMissingSchemaTablesAndFields();
        clearAlertsAndNotices();
        // don't show "created table/field" alerts
        // New Installation: check if admin user already exists
        if (!@$_REQUEST['restoreFromBackup']) {
            $passwordHash = getPasswordDigest($_REQUEST['adminPassword1']);
            $identicalUserExists = mysql_count('accounts', array('username' => $_REQUEST['adminUsername'], 'password' => $passwordHash, 'isAdmin' => '1'));
            if (!$identicalUserExists) {
                // if the don't exist, check if a user with the same username exists and show an error if they do
                $count = mysql_count('accounts', array('username' => $_REQUEST['adminUsername']));
                if (!$identicalUserExists && $count > 0) {
                    alert("Admin username already exists, please choose another.<br/>\n");
                }
            }
            // create admin user
            if (!$identicalUserExists && !alert()) {
                mysqlStrictMode(false);
                // disable Mysql strict errors for when a field isn't defined below (can be caused when fields are added later)
                mysql_query("INSERT INTO `{$TABLE_PREFIX}accounts` SET\n                          createdDate      = NOW(),\n                          createdByUserNum = '0',\n                          updatedDate      = NOW(),\n                          updatedByUserNum = '0',\n                          fullname         = '" . mysql_escape($_REQUEST['adminFullname']) . "', email    = '" . mysql_escape($_REQUEST['adminEmail']) . "',\n                          username         = '******'adminUsername']) . "', password = '******',\n                          disabled         = '0',\n                          isAdmin          = '1',\n                          expiresDate      = '0000-00-00 00:00:00',\n                          neverExpires     = '1'") or alert("MySQL Error Creating Admin User:<br/>\n" . htmlencode(mysql_error()) . "\n");
                // create accesslist entry
                mysql_query("INSERT INTO `{$TABLE_PREFIX}_accesslist` (userNum, tableName, accessLevel, maxRecords, randomSaveId)\n                          VALUES (LAST_INSERT_ID(), 'all', '9', NULL, '1234567890')") or alert("MySQL Error Creating Admin Access List:<br/>\n" . htmlencode(mysql_error()) . "\n");
            }
        }
        // Restore from Backup: Restore backup file
        if (@$_REQUEST['restoreFromBackup']) {
            $userCount = mysql_count('accounts');
            if ($userCount) {
                $userTable = $TABLE_PREFIX . 'accounts';
                $errorMessage = sprintf("Can't restore from backup because it would overwrite the %s existing user accounts in the specified database location.<br/>\n", $userCount);
                $errorMessage .= sprintf("Try changing the MySQL Database or Table Prefix to restore to a different location, or remove existing users from '%s'.<br/>\n", $userTable);
                alert($errorMessage);
            } else {
                // restore database
                $filename = @$_REQUEST['restore'];
                mysqlStrictMode(false);
                // disable Mysql strict errors
                restoreDatabase(DATA_DIR . '/backups/' . $filename);
                notice("Restored backup file /data/backups/{$filename}");
                makeAllUploadRecordsRelative();
            }
        }
        // save settings
        if (!alert()) {
            saveSettings(@$_REQUEST['useCustomSettingsFile']);
            isInstalled(true);
            // save installed status
            redirectBrowserToURL('?menu=home', true);
            // refresh page
            exitl;
        }
    }
    // set defaults
    if (!array_key_exists('licenseDomainName', $_REQUEST)) {
        $_REQUEST['licenseDomainName'] = $_SERVER['HTTP_HOST'];
    }
    if (!array_key_exists('mysqlHostname', $_REQUEST)) {
        $_REQUEST['mysqlHostname'] = $SETTINGS['mysql']['hostname'];
    }
    if (!array_key_exists('mysqlDatabase', $_REQUEST)) {
        $_REQUEST['mysqlDatabase'] = $SETTINGS['mysql']['database'];
    }
    if (!array_key_exists('mysqlUsername', $_REQUEST)) {
        $_REQUEST['mysqlUsername'] = $SETTINGS['mysql']['username'];
    }
    if (!array_key_exists('mysqlTablePrefix', $_REQUEST)) {
        $_REQUEST['mysqlTablePrefix'] = $SETTINGS['mysql']['tablePrefix'];
    }
    // show form
    require "lib/menus/install.php";
    exit;
}
function admin_saveSettings($savePagePath)
{
    global $SETTINGS, $APP;
    // error checking
    clearAlertsAndNotices();
    // so previous alerts won't prevent saving of admin options
    // security checks
    security_dieUnlessPostForm();
    security_dieUnlessInternalReferer();
    security_dieOnInvalidCsrfToken();
    //
    disableInDemoMode('settings', $savePagePath);
    # license error checking
    if (array_key_exists('licenseProductId', $_REQUEST)) {
        if (!isValidProductId($_REQUEST['licenseProductId'])) {
            alert("Invalid Product License ID!");
        } else {
            if ($SETTINGS['licenseProductId'] != $_REQUEST['licenseProductId']) {
                $SETTINGS['licenseCompanyName'] = $_REQUEST['licenseCompanyName'];
                // update settings
                $SETTINGS['licenseDomainName'] = $_REQUEST['licenseDomainName'];
                // ...
                $SETTINGS['licenseProductId'] = $_REQUEST['licenseProductId'];
                // ...
                $isValid = register();
                // validate productId (and save new settings)
                if (!$isValid) {
                    redirectBrowserToURL('?menu=admin', true);
                    exit;
                }
            }
        }
    }
    # program url / adminUrl
    if (array_key_exists('adminUrl', $_REQUEST)) {
        if (!preg_match('/^http/i', $_REQUEST['adminUrl'])) {
            alert("Program URL must start with http:// or https://<br/>\n");
        }
        if (preg_match('/\\?/i', $_REQUEST['adminUrl'])) {
            alert("Program URL can not contain a ?<br/>\n");
        }
    }
    # webPrefixUrl - v2.53
    if (@$_REQUEST['webPrefixUrl'] != '') {
        if (!preg_match("|^(\\w+:/)?/|", $_REQUEST['webPrefixUrl'])) {
            alert(t("Website Prefix URL must start with /") . "<br/>\n");
        }
        if (preg_match("|/\$|", $_REQUEST['webPrefixUrl'])) {
            alert(t("Website Prefix URL cannot end with /") . "<br/>\n");
        }
    }
    # upload url/dir
    if (array_key_exists('uploadDir', $_REQUEST)) {
        #    if      (!preg_match('/\/$/',      $_REQUEST['uploadDir'])) { alert("Upload Directory must end with a slash! (eg: /www/htdocs/uploads/)<br/>\n"); }
    }
    if (array_key_exists('uploadUrl', $_REQUEST)) {
        #    if      (preg_match('/^\w+:\/\//', $_REQUEST['uploadUrl'])) { alert("Upload Folder Url must be the web path only without a domain (eg: /uploads/)<br/>\n"); }
        #    else if (!preg_match('/^\//',      $_REQUEST['uploadUrl'])) { alert("Upload Folder Url must start with a slash! (eg: /uploads/)<br/>\n"); }
        #    if      (!preg_match('/\/$/',      $_REQUEST['uploadUrl'])) { alert("Upload Folder Url must end with a slash! (eg: /uploads/)<br/>\n"); }
        $_REQUEST['uploadUrl'] = chop($_REQUEST['uploadUrl'], '\\\\/');
        // remove trailing slashes
    }
    # admin email
    if (array_key_exists('adminEmail', $_REQUEST) && !isValidEmail($_REQUEST['adminEmail'])) {
        alert("Admin Email must be a valid email (example: user@example.com)<br/>\n");
    }
    // error checking - require HTTPS
    if (@$_REQUEST['requireHTTPS'] && !isHTTPS()) {
        alert("Require HTTPS: You must be logged in with a secure HTTPS url to set this option!<br/>\n");
    }
    // error checking - require HTTPS
    if (@$_REQUEST['restrictByIP'] && !isIpAllowed(true, @$_REQUEST['restrictByIP_allowed'])) {
        alert(t("Restrict IP Access: You current IP address must be in the allowed IP list!") . "<br/>\n");
    }
    // error checking - session values
    $sessionErrors = getCustomSessionErrors(@$_REQUEST['session_cookie_domain'], @$_REQUEST['session_save_path']);
    if ($sessionErrors) {
        alert($sessionErrors);
    }
    # show errors
    if (alert()) {
        showInterface('admin/general.php');
        exit;
    }
    ### update global settings
    $globalSettings =& $SETTINGS;
    foreach (array_keys($globalSettings) as $key) {
        if (array_key_exists($key, $_REQUEST)) {
            $globalSettings[$key] = $_REQUEST[$key];
        }
    }
    # update subsection settings
    $subsections = array('advanced', 'wysiwyg');
    foreach ($subsections as $subsection) {
        $sectionSettings =& $SETTINGS[$subsection];
        foreach (array_keys($sectionSettings) as $key) {
            if (array_key_exists($key, $_REQUEST)) {
                $sectionSettings[$key] = $_REQUEST[$key];
            }
        }
    }
    # save to file
    saveSettings();
    # return to admin home
    notice('Settings have been saved.');
    showInterface($savePagePath);
}