<?php

require_once HORDE_BASE . '/lib/Form.php';
require_once HORDE_BASE . '/lib/Form/Renderer.php';
require_once HORDE_BASE . '/lib/Category.php';

/**
 * Horde_Signup:: This class provides an interface to sign up or have
 * new users sign themselves up into the horde installation, depending
 * on how the admin has configured Horde.
 *
 * $Horde: horde/lib/Signup.php,v 1.15 2003/08/08 14:28:32 jwm Exp $
 *
 * Copyright 2002-2003 Marko Djukic <marko@oblo.com>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Marko Djukic <marko@oblo.com>
 * @version $Revision: 1.15 $
 * @since   Horde 3.0
 * @package horde.signup
 */
class Horde_Signup {

    /**
     * Pointer to a category instance to manage/store signups
     *
     * @var object Category $_signup
     */
    var $_signup;

    /**
     */
    function Horde_Signup()
    {
        global $conf;

        if (!isset($conf['category']['driver'])) {
            Horde::fatal(_("You must configure a Signup backend to use Signups."));
        }
        $driver = $conf['category']['driver'];
        $this->_signup = &Category::singleton($driver,
                                              array_merge(Horde::getDriverConfig('category', $driver),
                                                          array('group' => 'horde.signup')));
    }

    /**
     * Attempts to return a reference to a concrete Horde_Signup
     * instance. It will only create a new instance if no Horde_Signup
     * instance currently exists.
     *
     * This method must be invoked as: $var = &Horde_Signup::singleton()
     *
     * @return object Signup  The concrete Signup reference, or false on an
     *                        error.
     */
    function &singleton()
    {
        static $signup;

        if (!isset($signup)) {
            $signup = new Horde_Signup();
        }

        return $signup;
    }

    /**
     * Adds a new user to the system and handles any extra fields
     * that may have been compiled, relying on the hooks.php file.
     *
     * @access public
     *
     * @return mixed  PEAR_Error if any errors, otherwise true.
     */
    function addSignup($info)
    {
        global $auth;

        $success = $auth->addUser($info['user_name'], array('password' => $info['password']));
        if (is_a($success, 'PEAR_Error')) {
            return $success;
        }

        if (!empty($info['extra'])) {
            $addextra = false;
            if (file_exists(HORDE_BASE . '/config/hooks.php')) {
                require_once HORDE_BASE . '/config/hooks.php';
                $function = '_horde_hook_signup_addextra';
                if (function_exists($function)) {
                    $addextra = call_user_func($function, $info['user_name'], $info['extra']);
                }
            }
            if (!$addextra || is_a($addextra, 'PEAR_Error')) {
                Horde::fatal(new PEAR_Error(_("Unable to add extra user information when signing up.")), __FILE__, __LINE__);
            }
        }

        return true;
    }

    /**
     * Queues the user's submitted registration info for later admin
     * approval.
     *
     * @access public
     *
     * @return mixed  PEAR_Error if any errors, otherwise true.
     */
    function &queueSignup($info)
    {
        global $auth;

        if ($auth->exists($info['user_name']) ||
            $this->_signup->exists($info['user_name'])) {
            return PEAR::raiseError(sprintf(_("Username '%s' already exists."), $info['user_name']));
        }
        $signup = $this->newSignup($info['user_name']);
        $signup->data = array_merge($info['extra'],
                                    array('password' => $info['password']),
                                    array('dateReceived' => time()));

        Horde::callHook('_horde_hook_signup_queued',
                        array($info['user_name'], $info));
        return $this->_signup->addCategory($signup);
    }

    /**
     * Get a user's queued signup information.
     * 
     * @access public
     *
     * @param string $username  The username to retrieve the queued info for.
     * @return object CategoryObject_Signup  The Category object for the
     *                                       requested signup.
     */
    function getQueuedSignup($username)
    {
        return $this->_signup->getCategory($username, 'CategoryObject_Signup');
    }

    /**
     * Get the queued information for all pending signups.
     *
     * @access public
     *
     * @return array  An array of CategoryObject_Signup objects, one for
     *                each signup in the queue.
     */
    function getQueuedSignups()
    {
        $signups = array();
        foreach ($this->_signup->get(CATEGORY_FORMAT_FLAT, -1, true) as $username) {
            if ($username != -1) {
                $signups[] = $this->_signup->getCategory($username);
            }
        }
        return $signups;
    }

    /**
     * Remove a queued signup.
     *
     * @access public
     *
     * @param string $username  The user to remove from the signup queue.
     */
    function removeQueuedSignup($username)
    {
        $this->_signup->removeCategory($username);
    }

    /**
     * Return a new signup object.
     *
     * @param string $name The signups's name.
     *
     * @return object CategoryObject_Signup A new signup object.
     */
    function &newSignup($name)
    {
        if (empty($name)) {
            return PEAR::raiseError('Signup names must be non-empty');
        }
        return new CategoryObject_Signup($name);
    }

}

/**
 * Extension of the CategoryObject class for storing Signup information
 * in the Categories driver. If you want to store specialized Signup
 * information, you should extend this class instead of extending
 * CategoryObject directly.
 *
 * @author  Marko Djukic <marko@oblo.com>
 * @version $Revision: 1.15 $
 * @since   Horde 3.0
 * @package horde.signup
 */
class CategoryObject_Signup extends CategoryObject {

    /**
     * We want to see queued signups in descending order of receipt.
     * Insert new categories at position 0 and push the rest down.
     * @var integer $order
     */
    var $order = 0;

    /**
     * The CategoryObject_Signup constructor. Just makes sure to call
     * the parent constructor so that the signup's is is set
     * properly.
     *
     * @param string $id The id of the signup.
     */
    function CategoryObject_Signup($id)
    {
        parent::CategoryObject($id);
        if (is_null($this->data)) {
            $this->data = array();
        }
    }

}

/**
 * Horde Signup Form, extending of Horde_Form::
 *
 * Copyright 2003 Marko Djukic <marko@oblo.com>
 *
 * See the enclosed file COPYING for license information (GPL).  If you
 * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
 *
 * @author  Marko Djukic <marko@oblo.com>
 * @version $Revision: 1.15 $
 * @since   Horde 3.0
 * @package horde.form
 */
class HordeSignupForm extends Horde_Form {

    var $_useFormToken = true;

    function HordeSignupForm(&$vars)
    {
        global $registry;

        parent::Horde_Form($vars, sprintf(_("%s Sign Up"), $registry->getParam('name')));

        $this->_submit = _("Sign up");
        $this->_reset = true;

        $this->addHidden('', 'url', 'text', false);
        $this->addVariable(_("Choose a username"), 'user_name', 'text', true);
        $this->addVariable(_("Choose a password"), 'password', 'passwordconfirm', true, false, _("type the password twice to confirm"));

        /* Use hooks get any extra fields required in signing up. */
        $extra = Horde::callHook('_horde_hook_signup_getextra');
        if (!is_a($extra, 'PEAR_Error') && !empty($extra)) {
            foreach ($extra as $field_name => $field) {
                $readonly = isset($field['readonly']) ? $field['readonly'] : null;
                $desc = isset($field['desc']) ? $field['desc'] : null;
                $field_params = isset($field['params']) ? $field['params'] : array();

                $this->addVariable($field['label'], 'extra[' . $field_name . ']', $field['type'], $field['required'], $readonly, $desc, $field_params);
            }
        }
    }

}
