/** install an additional theme
 *
 * this routine attempts to insert the information from the
 * manifest of theme $theme_key into the database.
 * The routine displays the result (error or success) in a
 * message in $output. Details can be found in the logs.
 *
 * The theme_key is validated by reading all existing module manifests.
 * This is quite expensive, but that is not important because we
 * do not use this routine very often anyway.
 *
 * Note that we assume that the actual thenes are already
 * unpacked into the correct directories.
 * The corresponding manifest should exist in the directory
 * /program/themes/$theme_key.
 *
 * @param object &$output collects the html output
 * @param string $theme_key primary key for theme record in database AND name of the /program/themes subdirectory
 * @return void results are returned as output in $output
 * @uses $CFG
 * @todo we should refactor and combine install_theme() and install_module()
 */
function install_theme(&$output, $theme_key)
{
    global $CFG;
    $retval = TRUE;
    // assume success
    $theme_key = strval($theme_key);
    $progdir_themes = $CFG->progdir . '/themes';
    $manifests = get_manifests($progdir_themes);
    $params = array('{THEME}' => $theme_key);
    // 0 -- sanity check
    if (!isset($manifests[$theme_key])) {
        logger(sprintf('%s(): manifest for theme \'%s\' not found; nothing installed', __FUNCTION__, $theme_key));
        $retval = FALSE;
    } else {
        // 1 -- preliminary creation of a new theme record (we need the pkey in the installer)
        $manifest = $manifests[$theme_key];
        $table = 'themes';
        $fields = array('name' => $theme_key, 'version' => 0, 'manifest' => $manifest['manifest'], 'is_core' => isset($manifest['is_core']) && $manifest['is_core'] ? TRUE : FALSE, 'is_active' => FALSE, 'class' => isset($manifest['class']) ? $manifest['class'] : NULL, 'class_file' => isset($manifest['class_file']) ? $manifest['class_file'] : NULL);
        $key_field = 'theme_id';
        if (($theme_id = db_insert_into_and_get_id($table, $fields, $key_field)) === FALSE) {
            logger(sprintf('%s(): cannot install theme \'%s\': %s', __FUNCTION__, $theme_key, db_errormessage()));
            $retval = FALSE;
        } else {
            // 2A -- maybe insert tables for theme
            if (isset($manifest['tabledefs']) && !empty($manifest['tabledefs'])) {
                $filename = sprintf('%s/%s/%s', $progdir_themes, $theme_key, $manifest['tabledefs']);
                if (file_exists($filename)) {
                    if (!update_create_tables($filename)) {
                        $retval = FALSE;
                    }
                }
            }
            // 2B -- call the installation routine
            $messages = array();
            // collects error messages from call-back routine
            if (isset($manifest['install_script']) && !empty($manifest['install_script'])) {
                $filename = sprintf('%s/%s/%s', $progdir_themes, $theme_key, $manifest['install_script']);
                if (file_exists($filename)) {
                    @(include_once $filename);
                    $theme_install = $theme_key . '_install';
                    if (function_exists($theme_install)) {
                        if ($theme_install($messages, $theme_id)) {
                            logger(sprintf('%s(): %s(): success installing theme \'%s\' (version is %d)', __FUNCTION__, $theme_install, $theme_key, $manifest['version']));
                        } else {
                            $retval = FALSE;
                            logger(sprintf('%s(): %s() returned an error', __FUNCTION__, $theme_install));
                        }
                        foreach ($messages as $message) {
                            // remember messages, either good or bad
                            logger($message);
                        }
                    } else {
                        $retval = FALSE;
                        logger(sprintf('%s(): function %s() does not exist?', __FUNCTION__, $theme_install));
                    }
                } else {
                    $retval = FALSE;
                    logger(sprintf('%s(): file %s does not exist?', __FUNCTION__, $filename));
                }
            } else {
                $retval = FALSE;
                logger(sprintf('%s(): no install script in manifest for theme \'%s\'', __FUNCTION__, $theme_key));
            }
            // 2C -- if all went well, we make the theme active
            if ($retval) {
                $where = array($key_field => $theme_id);
                $fields = array('is_active' => TRUE, 'version' => intval($manifest['version']));
                if (db_update($table, $fields, $where) === FALSE) {
                    logger(sprintf('%s(): cannot activate theme \'%s\': %s', __FUNCTION__, $theme_key, db_errormessage()));
                    $retval = FALSE;
                }
            }
        }
    }
    if ($retval) {
        logger(sprintf('%s(): success installing theme \'%s\'', __FUNCTION__, $theme_key));
        $output->add_message(t('update_subsystem_theme_success', 'admin', $params));
    } else {
        $output->add_message(t('update_subsystem_theme_error', 'admin', $params));
    }
}
Пример #2
0
 /** perform the actual initialisation of the cms
  *
  * this routine initialises the database: creates tables,
  * inserts essential data (first user account, other defaults)
  * and optional demonstration data.
  *
  * The strategy is as follows.
  *
  *  - (1) manufacture a database object in the global $DB
  *  - (2A) create the main tables (from /program/install/tabledefs.php)
  *  - (2B) insert essential data (from /program/install/tabledata.php)
  *  - (2C) store the collected data (website title, etc.),
  *  - (3) if necessary, create the data directory
  *  - (4) record the currently available languages in the database
  *  - (5) create the first useraccount, 
  *
  * Once the main part is done, install modules and themes based on the
  * relevant information that is stored in the corresponding manifest-file
  * by performing the following steps for each module and theme:
  *
  *  - (6A) insert a record in the appropriate table with active = FALSE
  *  - (6B) create the tables (if any tables are necessary according to the manifest)
  *  - (6C) install the item by including a file and executing function <item>_install()
  *  - (6D) flip the active flag in the record from step 5A to indicate success
  *
  * Subsequently the optional demodata is installed.
  *
  *  - (7A) a foundation is created via the function demodata() from /program/install/demodata.php
  *  - (7B) all modules + themes can add to the demo data via the appropriate subroutines
  *
  * If all goes well, this routine ends with an attempt to 
  *
  *  - (8) save the config.php file at the correct location.
  *    (it is not an error if that does not work; it only
  *    means that the  user has to upload the config.php
  *    file manually.
  *
  * @return bool TRUE on success, FALSE otherwise + messages in $this->messages[]
  * @todo should we save the config.php to the datadir if the main dir fails? Mmmm.... security implications?
  * @todo this routine badly needs refactoring
  */
 function perform_installation()
 {
     global $DB;
     $retval = TRUE;
     // assume success
     // 0 -- do we have sane values at this point? If not, send user back to correct data
     if (!$this->check_validation()) {
         return FALSE;
     }
     // 1 -- try to create $DB
     /** Manufacture an object of the database class corresponding with requested db_type */
     include_once dirname(__FILE__) . '/lib/database/databaselib.php';
     $DB = database_factory($_SESSION['INSTALL']['db_prefix'], $_SESSION['INSTALL']['db_type']);
     if ($DB === FALSE) {
         // internal error, shouldn't happen (we already checked the database access a few dialogs ago)
         $this->messages[] = 'Internal error: cannot create database object';
         return FALSE;
     }
     if (!@$DB->connect($_SESSION['INSTALL']['db_server'], $_SESSION['INSTALL']['db_username'], $_SESSION['INSTALL']['db_password'], $_SESSION['INSTALL']['db_name'])) {
         $this->messages[] = $this->t('error_db_cannot_connect') . ' (' . $DB->errno . '/\'' . $DB->error . '\')';
         return FALSE;
     }
     // At this point we have a working database in our hands in the global $DB.
     // 2A -- create the main tables
     if (!$this->create_tables(dirname(__FILE__) . '/install/tabledefs.php')) {
         $retval = FALSE;
     }
     // 2B -- enter essential data in main tables (defaults etc.)
     if (!$this->insert_tabledata(dirname(__FILE__) . '/install/tabledata.php')) {
         $retval = FALSE;
     }
     // 2C -- enter additional configuration data to tables
     $config_updates = array('version' => $_SESSION['INSTALL']['WAS_VERSION'], 'salt' => $this->quasi_random_string(rand(22, 42), 62), 'title' => $_SESSION['INSTALL']['cms_title'], 'website_from_address' => $_SESSION['INSTALL']['cms_website_from_address'], 'website_replyto_address' => $_SESSION['INSTALL']['cms_website_replyto_address'], 'language_key' => $_SESSION['INSTALL']['language_key'], 'friendly_url' => $_SESSION['INSTALL']['friendly_url'] ? '1' : '0', 'clamscan_path' => $_SESSION['INSTALL']['clamscan_path'], 'clamscan_mandatory' => $_SESSION['INSTALL']['clamscan_mandatory'] ? '1' : '0');
     foreach ($config_updates as $name => $value) {
         $where = array('name' => $name);
         $fields = array('value' => strval($value));
         if (db_update('config', $fields, $where) === FALSE) {
             $params = array('{CONFIG}' => $name, '{ERRNO}' => $DB->errno, '{ERROR}' => $DB->error);
             $this->messages[] = $this->t('error_update_config', $params);
             $retval = FALSE;
         }
     }
     // 3A -- maybe create dataroot
     $dataroot = $_SESSION['INSTALL']['cms_dataroot'];
     if (!is_dir($dataroot)) {
         // dataroot does not exist, try to create it
         if (!@mkdir($dataroot, 0700)) {
             $params = array('{FIELD}' => $this->t('cms_datadir_label'), '{DIRECTORY}' => htmlspecialchars($dataroot));
             $this->messages[] = $this->t('error_not_create_dir', $params);
             $retval = FALSE;
         }
         // else { success creating $dataroot }
     }
     // else { nothing to do; we already tested for directory writability
     // 3B -- "protect" the datadirectory with a blank index.html
     @touch($dataroot . '/index.html');
     // 3C -- setup our REAL datadirectory
     $subdirectory = $_SESSION['INSTALL']['cms_datasubdir'];
     if (empty($subdirectory)) {
         $subdirectory = md5($_SESSION['INSTALL']['cms_dir'] . $_SESSION['INSTALL']['cms_www'] . $this->quasi_random_string(13, 62) . $_SESSION['INSTALL']['cms_progdir'] . $_SESSION['INSTALL']['cms_progwww'] . $_SESSION['INSTALL']['cms_title'] . strval(time()));
         $datadir = $dataroot . '/' . $subdirectory;
         $params = array('{FIELD}' => $this->t('cms_datadir_label'), '{DIRECTORY}' => htmlspecialchars($datadir));
         if (is_dir($datadir)) {
             $this->messages[] = $this->t('error_directory_exists', $params);
             $retval = FALSE;
         } elseif (!@mkdir($datadir, 0700)) {
             $this->messages[] = $this->t('error_not_create_dir', $params);
             $retval = FALSE;
         } else {
             $_SESSION['INSTALL']['cms_datasubdir'] = $subdirectory;
         }
     }
     // If the user changed $dataroot after a 1st failed attempt,
     // $subdirectory may not yet exist, we doublecheck and maybe mkdir
     $datadir = $dataroot . '/' . $subdirectory;
     if (!is_dir($datadir)) {
         if (!@mkdir($datadir, 0700)) {
             $params = array('{FIELD}' => $this->t('cms_datadir_label'), '{DIRECTORY}' => htmlspecialchars($datadir));
             $this->messages[] = $this->t('error_not_create_dir', $params);
             $retval = FALSE;
         }
     }
     $_SESSION['INSTALL']['cms_datadir'] = $datadir;
     // construct_config_php() needs this
     @touch($datadir . '/index.html');
     // "protect" directory
     // 3D -- setup essential subdirectories under our REAL datadirectory
     $datadir_subdirectories = array($datadir . '/areas', $datadir . '/users', $datadir . '/groups', $datadir . '/languages');
     foreach ($datadir_subdirectories as $datadir_subdirectory) {
         if (!is_dir($datadir_subdirectory) && !@mkdir($datadir_subdirectory, 0700)) {
             $params = array('{FIELD}' => $this->t('cms_datadir_label'), '{DIRECTORY}' => htmlspecialchars($datadir_subdirectory));
             $this->messages[] = $this->t('error_not_create_dir', $params);
             $retval = FALSE;
         }
         @touch($datadir_subdirectory . '/index.html');
         // "protect" directory
     }
     // 4 -- languages: only add entry to table, no tabledefs or installer here
     $datadir_languages = $datadir . '/languages';
     $languages = $this->get_manifests(dirname(__FILE__) . '/languages');
     foreach ($languages as $language_key => $manifest) {
         $language_key = strval($language_key);
         if (!is_dir($datadir_languages . '/' . $language_key) && !@mkdir($datadir_languages . '/' . $language_key, 0700)) {
             $params = array('{FIELD}' => $this->t('cms_datadir_label'), '{DIRECTORY}' => htmlspecialchars($datadir_languages . '/' . $language_key));
             $this->messages[] = $this->t('error_not_create_dir', $params);
             $retval = FALSE;
         }
         @touch($datadir_languages . '/' . $language_key . '/index.html');
         // "protect" directory
         $table = 'languages';
         $fields = array('language_key' => $language_key, 'language_name' => isset($manifest['language_name']) ? strval($manifest['language_name']) : '(' . $language_key . ')', 'parent_language_key' => isset($manifest['parent_language_key']) ? strval($manifest['parent_language_key']) : '', 'version' => intval($manifest['version']), 'manifest' => $manifest['manifest'], 'is_core' => isset($manifest['is_core']) && $manifest['is_core'] ? TRUE : FALSE, 'is_active' => TRUE, 'dialect_in_database' => FALSE, 'dialect_in_file' => FALSE);
         if (db_insert_into($table, $fields) === FALSE) {
             // This shouldn't happen in a freshly created database. However, try to continue with the next
             $params = array('{TABLENAME}' => $DB->prefix . $table, '{ERRNO}' => $DB->errno, '{ERROR}' => $DB->error);
             $this->messages[] = $this->t('error_insert_into_table', $params);
             $retval = FALSE;
             continue;
         }
     }
     // 5 --  insert the first user account
     // 5A -- construct acl with all privileges for this main (root) user
     $table = 'acls';
     $key_field = 'acl_id';
     $fields = array('permissions_jobs' => -1, 'permissions_intranet' => -1, 'permissions_modules' => -1, 'permissions_nodes' => -1);
     $acl_id = db_insert_into_and_get_id($table, $fields, $key_field);
     if ($acl_id === FALSE) {
         // This shouldn't happen in a freshly created database.
         $params = array('{TABLENAME}' => $DB->prefix . $table, '{ERRNO}' => $DB->errno, '{ERROR}' => $DB->error);
         $this->messages[] = $this->t('error_insert_into_table', $params);
         return FALSE;
     }
     // 5B -- create a personal datadirectory (based on the specified username)
     $username = $_SESSION['INSTALL']['user_username'];
     $userdata_directory = utf8_strtolower($this->sanitise_filename($username));
     // If the username consists of only non-mappable UTF-8 chars, we end up with '_'; make that more readable...
     if ($userdata_directory == '_') {
         $userdata_directory = '_webmaster';
     }
     $datadir_subdirectory = $datadir . '/users/' . $userdata_directory;
     if (!is_dir($datadir_subdirectory) && !@mkdir($datadir_subdirectory, 0700)) {
         $params = array('{FIELD}' => $this->t('cms_datadir_label'), '{DIRECTORY}' => htmlspecialchars($datadir_subdirectory));
         $this->messages[] = $this->t('error_not_create_dir', $params);
         $retval = FALSE;
     }
     @touch($datadir_subdirectory . '/index.html');
     // "protect" directory
     // 5C -- actually create the record in the users table
     $salt = $this->quasi_random_string(12, 62);
     // pick 12 characters from [0-9][A-Z][a-z]
     $table = 'users';
     $key_field = 'user_id';
     $fields = array('username' => $username, 'salt' => $salt, 'password_hash' => md5($salt . $_SESSION['INSTALL']['user_password']), 'full_name' => $_SESSION['INSTALL']['user_full_name'], 'email' => $_SESSION['INSTALL']['user_email'], 'is_active' => TRUE, 'redirect' => '', 'language_key' => $_SESSION['INSTALL']['language_key'], 'path' => $userdata_directory, 'acl_id' => intval($acl_id), 'editor' => 'ckeditor', 'skin' => $_SESSION['INSTALL']['high_visibility'] ? 'textonly' : 'base');
     $user_id = db_insert_into_and_get_id($table, $fields, $key_field);
     if ($user_id === FALSE) {
         $params = array('{TABLENAME}' => $DB->prefix . $table, '{ERRNO}' => $DB->errno, '{ERROR}' => $DB->error);
         $this->messages[] = $this->t('error_insert_into_table', $params);
         $retval = FALSE;
     }
     // 6 -- install modules and themes
     $subsystems = array('modules' => $this->get_manifests(dirname(__FILE__) . '/modules'), 'themes' => $this->get_manifests(dirname(__FILE__) . '/themes'));
     foreach ($subsystems as $subsystem => $manifests) {
         foreach ($manifests as $item => $manifest) {
             if (empty($manifest)) {
                 $this->messages[] = $this->t('warning_no_manifest', array('{ITEM}' => $item));
                 continue;
             }
             // 6A -- prepare entry in the corresponding table
             switch ($subsystem) {
                 case 'modules':
                     $fields = array('name' => strval($item), 'version' => intval($manifest['version']), 'manifest' => $manifest['manifest'], 'is_core' => isset($manifest['is_core']) && $manifest['is_core'] ? TRUE : FALSE, 'is_active' => FALSE, 'has_acls' => isset($manifest['has_acls']) && $manifest['has_acls'] ? TRUE : FALSE, 'view_script' => isset($manifest['view_script']) ? $manifest['view_script'] : NULL, 'admin_script' => isset($manifest['admin_script']) ? $manifest['admin_script'] : NULL, 'search_script' => isset($manifest['search_script']) ? $manifest['search_script'] : NULL, 'cron_script' => isset($manifest['cron_script']) ? $manifest['cron_script'] : NULL, 'cron_interval' => isset($manifest['cron_interval']) ? intval($manifest['cron_interval']) : NULL, 'cron_next' => NULL);
                     $key_field = 'module_id';
                     $table = 'modules';
                     break;
                 case 'themes':
                     $fields = array('name' => strval($item), 'version' => intval($manifest['version']), 'manifest' => $manifest['manifest'], 'is_core' => isset($manifest['is_core']) && $manifest['is_core'] ? TRUE : FALSE, 'is_active' => FALSE, 'class' => isset($manifest['class']) ? $manifest['class'] : NULL, 'class_file' => isset($manifest['class_file']) ? $manifest['class_file'] : NULL);
                     $key_field = 'theme_id';
                     $table = 'themes';
                     break;
                 default:
                     $this->messages[] = 'Internal error: unknown subsystem ' . htmlspecialchars($subsystem);
                     continue;
                     break;
             }
             if (($item_id = db_insert_into_and_get_id($table, $fields, $key_field)) === FALSE) {
                 // This shouldn't happen in a freshly created database. However, try to continue with the next
                 $params = array('{TABLENAME}' => $DB->prefix . $table, '{ERRNO}' => $DB->errno, '{ERROR}' => $DB->error);
                 $this->messages[] = $this->t('error_insert_into_table', $params);
                 $retval = FALSE;
                 continue;
             }
             $is_active = TRUE;
             // assume we can successfully install the item
             // 6B -- maybe insert tables for item
             if (isset($manifest['tabledefs']) && !empty($manifest['tabledefs'])) {
                 $filename = dirname(__FILE__) . '/' . $subsystem . '/' . $item . '/' . $manifest['tabledefs'];
                 if (file_exists($filename)) {
                     if (!$this->create_tables($filename)) {
                         $retval = FALSE;
                         $is_active = FALSE;
                     }
                 }
             }
             // 6C -- maybe install this module or theme
             if (isset($manifest['install_script']) && !empty($manifest['install_script'])) {
                 $filename = dirname(__FILE__) . '/' . $subsystem . '/' . $item . '/' . $manifest['install_script'];
                 if (file_exists($filename)) {
                     @(include_once $filename);
                     $item_install = $item . '_install';
                     if (function_exists($item_install)) {
                         if (!$item_install($this->messages, $item_id)) {
                             $retval = FALSE;
                             $is_active = FALSE;
                         }
                     }
                 }
             }
             // 6D -- indicate everything went well
             if ($is_active) {
                 $where = array($key_field => $item_id);
                 $fields = array('is_active' => $is_active);
                 if (db_update($table, $fields, $where) === FALSE) {
                     $params = array('{CONFIG}' => $item, '{ERRNO}' => $DB->errno, '{ERROR}' => $DB->error);
                     $this->messages[] = $this->t('error_update_config', $params);
                     $retval = FALSE;
                     $is_active = FALSE;
                     // this should not happen (famous last words...)
                 }
             }
             $subsystems[$subsystem][$item]['is_active'] = $is_active;
             $subsystems[$subsystem][$item]['key_field'] = $key_field;
             $subsystems[$subsystem][$item]['item_id'] = $item_id;
         }
         // foreach item
     }
     // foreach subsystem
     // 7 -- Demodata
     if ($_SESSION['INSTALL']['cms_demodata']) {
         // 7A -- prepare essential information for demodata installation
         $demodata_config = array('language_key' => $_SESSION['INSTALL']['language_key'], 'dir' => $_SESSION['INSTALL']['cms_dir'], 'www' => $_SESSION['INSTALL']['cms_www'], 'progdir' => $_SESSION['INSTALL']['cms_progdir'], 'progwww' => $_SESSION['INSTALL']['cms_progwww'], 'datadir' => $_SESSION['INSTALL']['cms_datadir'], 'title' => $_SESSION['INSTALL']['cms_title'], 'replyto' => $_SESSION['INSTALL']['cms_website_replyto_address'], 'user_username' => $_SESSION['INSTALL']['user_username'], 'user_full_name' => $_SESSION['INSTALL']['user_full_name'], 'user_email' => $_SESSION['INSTALL']['user_email'], 'user_id' => $user_id, 'demo_salt' => $_SESSION['INSTALL']['cms_demodata_salt'], 'demo_password' => $_SESSION['INSTALL']['cms_demodata_password'], 'friendly_url' => $_SESSION['INSTALL']['friendly_url'], 'demo_areas' => array(), 'demo_groups' => array(), 'demo_users' => array(), 'demo_nodes' => array());
         $filename = dirname(__FILE__) . '/install/demodata.php';
         include_once $filename;
         if (!demodata($this->messages, $demodata_config)) {
             $this->messages[] = $this->t('error_install_demodata');
             $retval = FALSE;
         }
         // 7B -- insert demodata for all modules and themes
         foreach ($subsystems as $subsystem => $manifests) {
             foreach ($manifests as $item => $manifest) {
                 $item_demodata = $item . '_demodata';
                 if (function_exists($item_demodata)) {
                     if (!$item_demodata($this->messages, intval($manifest['item_id']), $demodata_config, $manifest)) {
                         $this->messages[] = $this->t('error_install_demodata');
                         $retval = FALSE;
                     }
                 }
             }
         }
     }
     // 8 -- Finish with attempt to write config.php
     if ($retval) {
         $_SESSION['INSTALL']['config_php_written'] = $this->write_config_php();
     }
     return $retval;
 }
 /** save the newly added area to the database
  *
  * This saves the essential information of a new area to the database,
  * using sensible defaults for the other fields. Also, a data directory
  * is created and the relative path is stored in the new area record.
  *
  * If something goes wrong, the user can redo the dialog, otherwise we
  * return to the area overview.
  *
  * @return void results are returned as output in $this->output
  * @uses $WAS_SCRIPT_NAME
  * @uses $CFG
  * @uses $USER
  */
 function area_savenew()
 {
     global $WAS_SCRIPT_NAME, $USER, $CFG;
     // 1 -- bail out if user pressed cancel button
     if (isset($_POST['button_cancel'])) {
         $this->output->add_message(t('cancelled', 'admin'));
         $this->area_overview();
         return;
     }
     // 2 -- dow we have permission to add an area?
     if (!$USER->has_site_permissions(PERMISSION_SITE_ADD_AREA)) {
         logger("areamanager: user attempted to add an area without permission");
         $msg = t('task_area_add_access_denied', 'admin');
         $this->output->add_message($msg);
         $this->output->add_popup_bottom($msg);
         $this->area_overview();
         return;
     }
     // 3 -- validate the data
     $invalid = FALSE;
     $dialogdef = $this->get_dialogdef_add_area();
     // 3A -- check for generic errors (string too short, number too small, etc)
     if (!dialog_validate($dialogdef)) {
         $invalid = TRUE;
     }
     // 3B -- additional check: valid datadirectory name entered
     $path = $dialogdef['area_path']['value'];
     $fname = isset($dialogdef['area_path']['label']) ? $dialogdef['area_path']['label'] : 'area_path';
     $params = array('{FIELD}' => str_replace('~', '', $fname));
     $areadata_directory = sanitise_filename($path);
     if ($path != $areadata_directory) {
         // User probably entered a few 'illegal' characters. This is no good
         $dialogdef['area_path']['value'] = $areadata_directory;
         // 'Help' user with a better proposition
         ++$dialogdef['area_path']['errors'];
         $params['{VALUE}'] = htmlspecialchars($path);
         $dialogdef['area_path']['error_messages'][] = t('validate_bad_filename', '', $params);
         $invalid = TRUE;
     }
     // 3C -- additional check: unique datadirectory name entered
     $areadata_directory = strtolower($areadata_directory);
     $where = array('path' => $areadata_directory);
     if (db_select_single_record('areas', 'area_id', $where) !== FALSE) {
         // Oops, a record with that path already exists. Go flag error
         ++$dialogdef['area_path']['errors'];
         $params['{VALUE}'] = $areadata_directory;
         $dialogdef['area_path']['error_messages'][] = t('validate_not_unique', '', $params);
         $invalid = TRUE;
     }
     // 3D -- additional check: can we create said directory?
     $areadata_full_path = $CFG->datadir . '/areas/' . $areadata_directory;
     $areadata_directory_created = @mkdir($areadata_full_path, 0700);
     if ($areadata_directory_created) {
         @touch($areadata_full_path . '/index.html');
         // "protect" the newly created directory from prying eyes
     } else {
         // Mmmm, failed; probably already exists then. Oh well. Go flag error.
         ++$dialogdef['area_path']['errors'];
         $params['{VALUE}'] = '/areas/' . $areadata_directory;
         $dialogdef['area_path']['error_messages'][] = t('validate_already_exists', '', $params);
         $invalid = TRUE;
     }
     // 3E -- if there were any errors go redo dialog while keeping data already entered
     if ($invalid) {
         if ($areadata_directory_created) {
             // Only get rid of the directory _we_ created
             @unlink($areadata_full_path . '/index.html');
             @rmdir($areadata_full_path);
         }
         // there were errors, show them to the user and do it again
         foreach ($dialogdef as $k => $item) {
             if (isset($item['errors']) && $item['errors'] > 0) {
                 $this->output->add_message($item['error_messages']);
             }
         }
         $this->output->add_content('<h2>' . t('areamanager_add_area_header', 'admin') . '</h2>');
         $this->output->add_content(t('areamanager_add_area_explanation', 'admin'));
         $href = href($WAS_SCRIPT_NAME, $this->a_param(AREAMANAGER_CHORE_SAVE_NEW));
         $this->output->add_content(dialog_quickform($href, $dialogdef));
         return;
     }
     // 4 -- go save the new area
     $sort_order = $this->sort_order_new_area();
     $now = strftime('%Y-%m-%d %T');
     $theme_id = intval($dialogdef['area_theme_id']['value']);
     $area_title = $dialogdef['area_title']['value'];
     $fields = array('title' => $area_title, 'is_private' => $dialogdef['area_is_private']['value'] == 1 ? TRUE : FALSE, 'is_active' => TRUE, 'is_default' => FALSE, 'path' => $areadata_directory, 'metadata' => '', 'sort_order' => $sort_order, 'theme_id' => $theme_id, 'ctime' => $now, 'cuser_id' => $USER->user_id, 'mtime' => $now, 'muser_id' => $USER->user_id);
     // 4A -- store area data
     $success = TRUE;
     $new_area_id = db_insert_into_and_get_id('areas', $fields, 'area_id');
     if ($new_area_id === FALSE) {
         if ($areadata_directory_created) {
             // Only get rid of the directory _we_ created
             @unlink($areadata_full_path . '/index.html');
             @rmdir($areadata_full_path);
         }
         logger("areamanager: saving new area failed: " . db_errormessage());
         $success = FALSE;
     }
     // 4B -- handle theme settings for this area
     if ($success) {
         if (!$this->reset_theme_defaults($new_area_id, $theme_id)) {
             logger("areamanager: saving new area-theme properties failed: " . db_errormessage());
             $success = FALSE;
         }
     }
     // 5 -- tell user about results of the operation
     if ($success) {
         $params = array('{AREA}' => $new_area_id, '{AREA_FULL_NAME}' => $area_title);
         $this->output->add_message(t('areamanager_savenew_area_success', 'admin', $params));
         logger(sprintf("areamanager: success saving new area '%d' %s with data directory /areas/%s", $new_area_id, $area_title, $areadata_directory));
     } else {
         $this->output->add_message(t('areamanager_savenew_area_failure', 'admin'));
     }
     $this->area_overview();
 }
 /** save a newly added node to the database
  *
  * this validate and save the (minimal) data for a new node (section or page).
  * First we check which button press brought us here; Cancel means we're done,
  * else we need to validate the user input. This is done by setting up the same
  * dialog structure as we did when presenting the user with a dialog in the first
  * place. This ensures that WE determine which fields we need to look for in the
  * _POST data. (If we simply were to look for fieldnames in the _POST array, we
  * might be tricked in accepting random fieldnames. By starting from the dialog
  * structure we make sure that we only look at fields that are part of the dialog;
  * any other fields are ignored, minimising the risks of the user trying to trick
  * us.)
  *
  * The dialog structure is filled with the data POST'ed by the user and subsequently
  * the data  is validated against the rules in the dialog structure (eg. min length,
  * min/max numerical values, etc). If one or more fields fail the tests, we redo
  * the dialog, using the data from _POST as a new starting point. This makes that
  * the user doesn't lose all other field contents if she makes a minor mistake in
  * entering data for one field.
  *
  * If all data from the dialog appears to be valid, it is copied to an array that
  * will be used to actually insert a new record into the nodes table. This array
  * also holds various other fields (not part of the dialog) with sensible default
  * values. Interesting 'special' fields are 'sort_order' and 'is_hidden' and 'embargo'.
  *
  * 'sort_order' is calculated automatically from other sort orders in the same
  * parent section. There are two ways to do it: always add a node at the end or
  * the exact opposite: always add a node at the beginning. The jury is still out
  * on which of the two is the best choice (see comments in the code below).
  *
  * 'is_hidden' and 'embargo' are calculated from the dialog field 'node_visibility'.
  * The latter gives the user three options: 'visible', 'hidden' and 'embargo'.
  * This translates to the following values for 'is_hidden' and 'embargo' (note
  * that $now is the current time in the form 'yyyy-mm-dd hh:mm:ss'):
  *
  * visible: is_hidden = FALSE, 'embargo' = $now
  * hidden: is_hidden = TRUE, 'embargo' = $now
  * embargo: is_hidden = TRUE, 'embargo' = '9999-12-31 23:59:59'
  *
  * This makes sure that IF the user wants to create a 'secret' node, ie. under embargo
  * until some time in the future, the new node is never visible until the user edits
  * the node to make it visible. However, there is no need to manually add a date/time:
  * we simply plug in the maximum value for a date/time, which effectively means 'forever'.
  *
  * Finally, if the new node is saved, a message about this event is recorded in the logfile (even
  * for new nodes under embargo). Also, if the node is NOT under embargo, an alert message
  * is queued. Note that we do NOT send alerts on a page that is created under embargo.
  * (There is a slight problem with this: once a user edits the node and sets the embargo
  * to a more realistic value, e.g. next week, there is no practical way to inform the
  * 'alert-watchers' about that fact: we cannot send an alert at the time that the
  * embargo date is changed to 'next week' because the node is still under embargo. We don't
  * have a handy opportunity to send alerts because the embargo date will eventually come
  * around and the node will become visible automatically, without anyone being alerted to the
  * fact. Mmmm....
  *
  * @param string $task disinguishes between saving a page or a section
  * @return void results are returned as output in $this->output
  * @todo about 'sort_order': do we insert nodes at the end or the beginning of a parent section?
  * @todo how do we alert users that an embargo date has come around? Do we schedule alerts via cron?
  *
  */
 function task_save_newnode($task)
 {
     global $WAS_SCRIPT_NAME, $USER;
     $is_page = $task == TASK_SAVE_NEWPAGE ? TRUE : FALSE;
     // 1 -- do they want to bail out?
     if (isset($_POST['button_cancel'])) {
         $this->output->add_message(t('cancelled', 'admin'));
         $this->show_tree();
         $this->show_area_menu($this->area_id);
         return;
     }
     // 2 -- validate input
     $dialogdef = $this->get_dialogdef_add_node($is_page);
     if (!dialog_validate($dialogdef)) {
         // there were errors, show them to the user and do it again
         foreach ($dialogdef as $k => $item) {
             if (isset($item['errors']) && $item['errors'] > 0) {
                 $this->output->add_message($item['error_messages']);
             }
         }
         $href = href($WAS_SCRIPT_NAME, array('job' => JOB_PAGEMANAGER, 'task' => $task));
         $this->output->add_content('<h2>' . t($is_page ? 'add_a_page_header' : 'add_a_section_header', 'admin') . '</h2>');
         $this->output->add_content(t($is_page ? 'add_page_explanation' : 'add_section_explanation', 'admin'));
         $this->output->add_content(dialog_quickform($href, $dialogdef));
         return;
     }
     // 3A -- prepare for storing data into database
     $now = strftime('%Y-%m-%d %T');
     $parent_id = intval($dialogdef['node_parent_id']['value']);
     switch (intval($dialogdef['node_visibility']['value'])) {
         case NODE_VISIBILIY_VISIBLE:
             $embargo = FALSE;
             $hidden = FALSE;
             break;
         case NODE_VISIBILIY_HIDDEN:
             $embargo = FALSE;
             $hidden = TRUE;
             break;
         case NODE_VISIBILIY_EMBARGO:
             $embargo = TRUE;
             $hidden = TRUE;
             break;
         default:
             $embargo = FALSE;
             $hidden = FALSE;
             break;
             // same as visible
     }
     $fields = array('area_id' => $this->area_id, 'parent_id' => $parent_id, 'is_page' => $is_page, 'title' => $dialogdef['node_title']['value'], 'link_text' => $dialogdef['node_link_text']['value'], 'is_hidden' => $hidden, 'embargo' => $embargo ? '9999-12-31 23:59:59' : $now, 'sort_order' => $this->calculate_new_sort_order($this->tree, $this->area_id, $parent_id), 'module_id' => $is_page ? intval($dialogdef['node_module_id']['value']) : NULL, 'owner_id' => $USER->user_id, 'ctime' => $now, 'atime' => $now, 'mtime' => $now);
     // 3B -- actually insert the new node
     $errors = 0;
     if (($new_node_id = db_insert_into_and_get_id('nodes', $fields, 'node_id')) === FALSE) {
         ++$errors;
         // error retrieving the new node_id, shouldn't happen
     }
     if ($errors == 0 && $parent_id == 0) {
         if (db_update('nodes', array('parent_id' => $new_node_id), array('node_id' => $new_node_id)) === FALSE) {
             ++$errors;
         }
     }
     if ($errors == 0 && $is_page) {
         if ($this->module_connect($this->area_id, $new_node_id, $fields['module_id']) === FALSE) {
             ++$errors;
         }
     }
     // 3C -- maybe do alerts, too
     if ($errors > 0) {
         // something went wrong, tell user
         $message = t('error_adding_node', 'admin');
         $this->output->add_message($message);
     } else {
         // success! tell the world via an alert if not under embargo
         $nodes = $this->get_node_id_and_ancestors($parent_id);
         $nodes[] = $new_node_id;
         $message = t($is_page ? 'page_added' : 'section_added', 'admin', array('{NODE}' => $new_node_id, '{AREA}' => $this->area_id, '{LINK}' => $fields['link_text'], '{TITLE}' => $fields['title']));
         $this->output->add_message($message);
         $embargo |= is_under_embargo($this->tree, $parent_id);
         // no alerts if new node or ancestors are embargo'ed
         logger(sprintf(__CLASS__ . ': area %d: added new %s %d %s (%s)%s', $this->area_id, $is_page ? 'page' : 'section', $new_node_id, $fields['link_text'], $fields['title'], $embargo ? ' (embargo)' : ''));
         if (!$embargo) {
             $this->queue_area_node_alert($this->area_id, $nodes, $message, $USER->full_name);
         }
         $this->build_cached_tree($this->area_id, TRUE);
         // TRUE means: force reread of tree
         // Finally a dirty trick: close the tree and re-open the path to the new node
         $_SESSION['tree_mode'] = TREE_VIEW_CUSTOM;
         $_SESSION['expanded_nodes'] = array();
         foreach ($nodes as $id) {
             $_SESSION['expanded_nodes'][$id] = TRUE;
         }
     }
     // 4 -- show tree again (maybe freshly read from database)
     $this->show_tree();
     $this->show_area_menu($this->area_id);
 }
Пример #5
0
/** create a few alerts
 *
 *
 * @param array &$messages used to return (error) messages to caller
 * @param array &$config pertinent information about the site
 * @param array &$tr translations of demodata texts
 * @return bool TRUE on success + data entered into database, FALSE on error
 */
function demodata_alerts(&$messages, &$config, &$tr)
{
    $retval = TRUE;
    $now = strftime("%Y-%m-%d %T");
    $email = $config['user_email'];
    $alerts = array('webmaster' => array('full_name' => $config['user_full_name'], 'email' => $email, 'cron_interval' => 1440, 'cron_next' => $now, 'messages' => 1, 'message_buffer' => $now . "\n" . $tr['alerts_initial_load'] . "\n" . $tr['alerts_every_1440_minutes'] . "\n" . $tr['alerts_all_areas'] . "\n" . $tr['alerts_email_address'] . "\n" . $email . " (" . $config['user_full_name'] . ")\n", 'is_active' => TRUE), 'acackl' => array('full_name' => 'Amelia Cackle', 'email' => $email, 'cron_interval' => 60, 'cron_next' => $now, 'messages' => 1, 'message_buffer' => $now . "\n" . $tr['alerts_initial_load'] . "\n" . $tr['alerts_every_60_minutes'] . "\n" . $tr['alerts_private_area'] . "\n" . $tr['alerts_email_address'] . "\n" . $email . " (Amelia Cackle)\n", 'is_active' => TRUE));
    foreach ($alerts as $alert => $fields) {
        if (($alert_id = db_insert_into_and_get_id('alerts', $fields, 'alert_id')) === FALSE) {
            $messages[] = $tr['error'] . ' ' . db_errormessage();
            $retval = FALSE;
        }
        $alerts[$alert]['alert_id'] = intval($alert_id);
    }
    $alerts_areas_nodes = array('webmaster' => array('alert_id' => $alerts['webmaster']['alert_id'], 'area_id' => 0, 'node_id' => 0, 'flag' => TRUE), 'acackl' => array('alert_id' => $alerts['acackl']['alert_id'], 'area_id' => $config['demo_areas']['private']['area_id'], 'node_id' => 0, 'flag' => TRUE));
    foreach ($alerts_areas_nodes as $fields) {
        if (db_insert_into('alerts_areas_nodes', $fields) === FALSE) {
            $messages[] = $tr['error'] . ' ' . db_errormessage();
            $retval = FALSE;
        }
    }
    return $retval;
}
 /** add a group/capacity and corresponding acl to the database
  *
  * @param int $group_id group of interest
  * @param int $capacity_code the capacity
  * @param int $sort_order
  * @return bool TRUE on success, FALSE otherwise
  */
 function add_group_capacity($group_id, $capacity_code, $sort_order)
 {
     static $fields_acl = array('permissions_intranet' => ACL_ROLE_NONE, 'permissions_modules' => ACL_ROLE_NONE, 'permissions_jobs' => ACL_ROLE_NONE, 'permissions_nodes' => ACL_ROLE_NONE);
     //
     // 1 -- create an acl (with no permissions whatsoever) and remember the new acl_id
     //
     $new_acl_id = db_insert_into_and_get_id('acls', $fields_acl, 'acl_id');
     if ($new_acl_id === FALSE) {
         logger(sprintf("%s.%s(): adding new acl for group/capacity '%d/%d' failed: %s", __CLASS__, __FUNCTION__, $group_id, $capacity_code, db_errormessage()));
         return FALSE;
     }
     //
     // 2 -- subsequently add a new group-capacity record pointing to this new acl
     //
     $fields = array('group_id' => intval($group_id), 'capacity_code' => intval($capacity_code), 'sort_order' => intval($sort_order), 'acl_id' => $new_acl_id);
     if (db_insert_into('groups_capacities', $fields) === FALSE) {
         logger(sprintf("%s.%s(): adding new record for group/capacity '%d/%d' failed: %s", __CLASS__, __FUNCTION__, $group_id, $capacity_code, db_errormessage()));
         $retval = db_delete('acls', array('acl_id' => $new_acl_id));
         logger(sprintf("%s.%s(): removing freshly created acl '%d': %s", __CLASS__, __FUNCTION__, $new_acl_id, $retval !== FALSE ? 'success' : 'failure: ' . db_errormessage()));
         return FALSE;
     }
     logger(sprintf("%s.%s(): success adding group/capacity '%d/%d' with acl_id='%d'", __CLASS__, __FUNCTION__, $group_id, $capacity_code, $new_acl_id), WLOG_DEBUG);
     return TRUE;
 }
/** add demonstration data to the system
 *
 * this routine adds to the existing set of demonstration data as specified
 * in $config. Here we add a few nodes as follows:
 *
 * 'snapshots0': a section containing one page connected to the snapshots_module
 * 'snapshots1': a page in section 'snapshots0' acting as an example set of snapshots
 *
 * The section 'snapshots0' is added at the bottom of the demo-section 'news'
 * Maybe not the best place, but at least we don't add yet another top level
 * menu item.
 *
 * Note
 * If the module is installed via the Install Wizard, this routine is
 * called. However, if a module is installed as an additional module
 * after installation, the {$module}_demodata() routine is never called.
 * This is because the only time you know that demodata is installed is
 * when the Install Wizard runs. If we're called from admin.php, the
 * webmaster may have already deleted existing (core) demodata so you
 * never can be sure what to expect. To put it another way: it is hard
 * to re-construct $config when we're NOT the Instal Wizard.
 *
 * The array $config contains the following information.
 *
 * <code>
 * $config['language_key']   => install language code (eg. 'en')
 * $config['dir']            => path to CMS Root Directory (eg. /home/httpd/htdocs)
 * $config['www']            => URL of CMS Root Directory (eg. http://exemplum.eu)
 * $config['progdir']        => path to program directory (eg. /home/httpd/htdocs/program)
 * $config['progwww']        => URL of program directory (eg. http://exemplum.eu/program)
 * $config['datadir']        => path to data directory (eg. /home/httpd/wasdata/a1b2c3d4e5f6)
 * $config['user_username']  => userid of webmaster (eg. wblader)
 * $config['user_full_name'] => full name of webmaster (eg. Wilhelmina Bladergroen)
 * $config['user_email']     => email of webmaster (eg. w.bladergroen@exemplum.eu)
 * $config['user_id']        => numerical user_id (usually 1)
 * $config['demo_salt']      => password salt for all demodata accounts
 * $config['demo_password']  => password for all demodata accounts
 * $config['demo_areas']     => array with demo area data
 * $config['demo_groups']    => array with demo group data
 * $config['demo_users']     => array with demo user data
 * $config['demo_nodes']     => array with demo node data
 * $config['demo_string']    => array with demo strings from /program/install/languages/LL/demodata.php
 * $config['demo_replace']   => array with search/replace pairs to 'jazz up' the demo strings
 * </code>
 *
 * With this information, we can add a demonstration configuration for the public area,
 * which shows off the possibilities. Note that we add our own additions to the array
 * $config so other modules and themes can determine the correct status quo w.r.t. the
 * demodata nodes etc.
 *
 * @param array &$messages collects the (error) messages
 * @param int $module_id the key for this module in the modules table
 * @param array $configuration pertinent data for the new website + demodata foundation
 * @param array $manifest a copy of the manifest for this module
 * @return bool TRUE on success + output via $messages, FALSE otherwise
 */
function snapshots_demodata(&$messages, $module_id, $config, $manifest)
{
    global $DB;
    $retval = TRUE;
    // assume success
    // 0 -- get hold of the module-specific translations in $string[]
    $string = array();
    $language_key = $config['language_key'];
    $filename = dirname(__FILE__) . '/languages/' . $language_key . '/snapshots.php';
    if (!file_exists($filename)) {
        $filename = dirname(__FILE__) . '/languages/en/snapshots.php';
    }
    @(include $filename);
    if (empty($string)) {
        $messages[] = 'Internal error: no translations in ' . $filename;
        return FALSE;
    }
    // 1A -- prepare for addition of a few nodes
    $sort_order = 0;
    $parent_id = $config['demo_nodes']['news']['node_id'];
    foreach ($config['demo_nodes'] as $node => $fields) {
        if ($fields['parent_id'] == $parent_id && $fields['node_id'] != $parent_id) {
            $sort_order = max($sort_order, $fields['sort_order']);
        }
    }
    $sort_order += 10;
    // place the snapshots-section at the end of the parent section
    $nodes = array('snapshots0' => array('parent_id' => $parent_id, 'is_page' => FALSE, 'title' => strtr($string['snapshots0_title'], $config['demo_replace']), 'link_text' => strtr($string['snapshots0_link_text'], $config['demo_replace']), 'sort_order' => $sort_order), 'snapshots1' => array('parent_id' => 0, 'is_page' => TRUE, 'is_default' => TRUE, 'title' => strtr($string['snapshots1_title'], $config['demo_replace']), 'link_text' => strtr($string['snapshots1_link_text'], $config['demo_replace']), 'sort_order' => 10, 'module_id' => $module_id, 'style' => "div.thumbnail_image a:hover img { border: 5px solid #00FF00; }\n"));
    // 1B -- actually add the necessary nodes
    $now = strftime('%Y-%m-%d %T');
    $user_id = $config['user_id'];
    $area_id = $config['demo_areas']['public']['area_id'];
    foreach ($nodes as $node => $fields) {
        $fields['area_id'] = $area_id;
        $fields['ctime'] = $now;
        $fields['mtime'] = $now;
        $fields['atime'] = $now;
        $fields['owner_id'] = $user_id;
        if ($fields['parent_id'] == 0) {
            $fields['parent_id'] = $config['demo_nodes']['snapshots0']['node_id'];
            // plug in real node_id of 'our' section
        }
        if (($node_id = db_insert_into_and_get_id('nodes', $fields, 'node_id')) === FALSE) {
            $messages[] = $config['demo_string']['error'] . db_errormessage();
            $retval = FALSE;
        }
        $node_id = intval($node_id);
        $fields['node_id'] = $node_id;
        $config['demo_nodes'][$node] = $fields;
    }
    // 2 -- Simulate a series of snapshots in the exemplum area data storage
    // 2A -- copy from module demodata directory to exemplum path
    $pictures = array("allium.jpg", "calendula.jpg", "cynara.jpg", "lagos.jpg", "lavandula.jpg", "mentha.jpg", "nepeta.jpg", "ocimum.jpg", "origanum.jpg", "petroselinum.jpg", "salvia.jpg", "thymus.jpg");
    $thumb_prefix = 'zz_thumb_';
    $path_snapshots = '/areas/' . $config['demo_areas']['public']['path'] . '/snapshots';
    // relative to $datadir
    $fullpath_source = dirname(__FILE__) . '/install/demodata';
    $fullpath_target = $config['datadir'] . $path_snapshots;
    if (@mkdir($fullpath_target, 0700) === FALSE) {
        $messages[] = $config['demo_string']['error'] . "mkdir('{$fullpath_target}')";
        $retval = FALSE;
    } else {
        @touch($fullpath_target . '/index.html');
        // try to "protect" directory
        foreach ($pictures as $picture) {
            $filenames = array($picture, $thumb_prefix . $picture);
            foreach ($filenames as $filename) {
                if (!@copy($fullpath_source . '/' . $filename, $fullpath_target . '/' . $filename)) {
                    $messages[] = $config['demo_string']['error'] . "copy('{$path_snapshots}/{$filename}')";
                    $retval = FALSE;
                }
            }
        }
    }
    $fields = array('node_id' => $config['demo_nodes']['snapshots1']['node_id'], 'header' => strtr($string['snapshots1_header'], $config['demo_replace']), 'introduction' => strtr($string['snapshots1_introduction'], $config['demo_replace']), 'snapshots_path' => $path_snapshots, 'variant' => 1, 'dimension' => 512, 'ctime' => $now, 'cuser_id' => $user_id, 'mtime' => $now, 'muser_id' => $user_id);
    if (db_insert_into('snapshots', $fields) === FALSE) {
        $messages[] = $config['demo_string']['error'] . db_errormessage();
        $retval = FALSE;
    }
    return $retval;
}
 /** save a new user to the database
  *
  * this saves a new user to the database. This involves at least two tables:
  * a record in the users table with basic information and also a record with
  * access control in the acls table.
  *
  * @todo maybe we should find a more elegant way to check a field for uniqueness
  * @todo shouldn't we end with the edit-user dialog rather than the users overview?
  *       that might make more sense...
  * @return data saved to the database, directory created, output created via users_overview()
  * @uses $CFG
  * @uses $WAS_SCRIPT_NAME
  */
 function user_savenew()
 {
     global $WAS_SCRIPT_NAME, $CFG;
     //
     // 1 -- bail out if the user pressed cancel button
     //
     if (isset($_POST['button_cancel'])) {
         $this->output->add_message(t('cancelled', 'admin'));
         $this->users_overview();
         return;
     }
     //
     // 2 -- validate the data
     //
     $invalid = FALSE;
     $dialogdef = $this->get_dialogdef_add_user();
     //
     // 2A -- check for generic errors (string too short, number too small, etc)
     if (!dialog_validate($dialogdef)) {
         $invalid = TRUE;
     }
     // 2B -- additional check: unique username
     $fname = $this->get_fname($dialogdef['username']);
     $params = array('{FIELD}' => $fname);
     $username = $dialogdef['username']['value'];
     $record = db_select_single_record('users', 'user_id', array('username' => $username));
     if ($record !== FALSE) {
         // Oops, a record with that username already exists. Go flag error
         ++$dialogdef['username']['errors'];
         $dialogdef['username']['error_messages'][] = t('validate_not_unique', '', $params);
         $invalid = TRUE;
     }
     // 2C -- additional check: unique userdata subdirectory relative to {$CFG->datadir}/users/
     $userdata_directory = strtolower(sanitise_filename($username));
     $userdata_full_path = $CFG->datadir . '/users/' . $userdata_directory;
     $userdata_directory_created = @mkdir($userdata_full_path, 0700);
     if ($userdata_directory_created) {
         @touch($userdata_full_path . '/index.html');
         // "protect" the newly created directory from prying eyes
     } else {
         // Mmmm, failed; probably already exists then. Oh well. Go flag error.
         ++$dialogdef['username']['errors'];
         $params['{VALUE}'] = '/users/' . $userdata_directory;
         $dialogdef['username']['error_messages'][] = t('validate_already_exists', '', $params);
         $invalid = TRUE;
     }
     // 2D -- additional check: valid password
     $password1 = $dialogdef['user_password1']['value'];
     $password2 = $dialogdef['user_password2']['value'];
     if (!empty($password1) || !empty($password2)) {
         if ($password1 != $password2) {
             $params = array('{FIELD1}' => $this->get_fname($dialogdef['user_password1']), '{FIELD2}' => $this->get_fname($dialogdef['user_password2']));
             ++$dialogdef['user_password1']['errors'];
             ++$dialogdef['user_password2']['errors'];
             $dialogdef['user_password1']['error_messages'][] = t('validate_different_passwords', '', $params);
             $dialogdef['user_password1']['value'] = '';
             $dialogdef['user_password2']['value'] = '';
             $invalid = TRUE;
         } elseif (!acceptable_new_password($password1, $password2)) {
             $params = array('{MIN_LENGTH}' => MINIMUM_PASSWORD_LENGTH, '{MIN_LOWER}' => MINIMUM_PASSWORD_LOWERCASE, '{MIN_UPPER}' => MINIMUM_PASSWORD_UPPERCASE, '{MIN_DIGIT}' => MINIMUM_PASSWORD_DIGITS, '{FIELD}' => $this->get_fname($dialogdef['user_password1']));
             ++$dialogdef['user_password1']['errors'];
             $dialogdef['user_password1']['error_messages'][] = t('validate_bad_password', '', $params);
             ++$dialogdef['user_password2']['errors'];
             $dialogdef['user_password1']['value'] = '';
             $dialogdef['user_password2']['value'] = '';
             $invalid = TRUE;
         }
     }
     // 2E -- if there were any errors go redo dialog while keeping data already entered
     if ($invalid) {
         if ($userdata_directory_created) {
             // Get rid of the directory _we_ created
             @unlink($userdata_full_path . '/index.html');
             @rmdir($userdata_full_path);
         }
         // show errors messages
         foreach ($dialogdef as $k => $item) {
             if (isset($item['errors']) && $item['errors'] > 0) {
                 $this->output->add_message($item['error_messages']);
             }
         }
         $this->output->add_content('<h2>' . t('usermanager_add_user_header', 'admin') . '</h2>');
         $this->output->add_content(t('usermanager_add_user_explanation', 'admin'));
         $href = href($WAS_SCRIPT_NAME, $this->a_params(TASK_USER_SAVE_NEW));
         $this->output->add_content(dialog_quickform($href, $dialogdef));
         return;
     }
     //
     // 3 -- store the data
     //
     // At this point we have a validated new user dialog in our hands
     // We now need to convert the data from the dialog to sensible
     // values for the database fields and store the data.
     // Note that the userdata directory already exists at this point
     $fields_acl = array('permissions_intranet' => ACL_ROLE_NONE, 'permissions_modules' => ACL_ROLE_NONE, 'permissions_jobs' => ACL_ROLE_NONE, 'permissions_nodes' => ACL_ROLE_NONE);
     //
     // 3A -- create an acl (with no permissions whatsoever) and remember the new acl_id
     //
     $errors = 0;
     $new_acl_id = db_insert_into_and_get_id('acls', $fields_acl, 'acl_id');
     if ($new_acl_id === FALSE) {
         logger(sprintf("user_savenew(): adding new acl for new user failed: %s", db_errormessage()));
         ++$errors;
     } else {
         //
         // 3B -- subsequently add a new user and remember the new user_id
         //
         $new_username = $dialogdef['username']['value'];
         $new_user_fullname = $dialogdef['user_fullname']['value'];
         $new_salt = password_salt();
         $fields = array('username' => $new_username, 'salt' => $new_salt, 'password_hash' => password_hash($new_salt, $dialogdef['user_password1']['value']), 'bypass_mode' => FALSE, 'bypass_hash' => NULL, 'bypass_expiry' => NULL, 'full_name' => $new_user_fullname, 'email' => $dialogdef['user_email']['value'], 'is_active' => $dialogdef['user_is_active']['value'] == 1 ? TRUE : FALSE, 'redirect' => '', 'language_key' => $CFG->language_key, 'path' => $userdata_directory, 'acl_id' => $new_acl_id, 'editor' => $CFG->editor, 'skin' => 'base');
         $new_user_id = db_insert_into_and_get_id('users', $fields, 'user_id');
         if ($new_user_id === FALSE) {
             logger(sprintf("usermanager: saving new user %s (%s) failed: %s" . $new_username, $new_user_fullname, db_errormessage()));
             ++$errors;
         }
     }
     if ($errors > 0) {
         if ($userdata_directory_created) {
             // Get rid of the directory _we_ created
             @unlink($userdata_full_path . '/index.html');
             @rmdir($userdata_full_path);
         }
         $this->output->add_message(t('usermanager_savenew_user_failure', 'admin'));
     } else {
         $params = array('{USERNAME}' => $new_username, '{FULL_NAME}' => $new_user_fullname);
         $this->output->add_message(t('usermanager_savenew_user_success', 'admin', $params));
         logger(sprintf("usermanager: success saving new user '%d' %s (%s) and datadir /users/%s", $new_user_id, $new_username, $new_user_fullname, $userdata_directory));
     }
     $this->users_overview();
 }