<?php
/******************************************************************************
 *  SiteBar 3 - The Bookmark Server for Personal and Team Use.                *
 *  Copyright (C) 2003,2004  Ondrej Brablc <http://brablc.com/mailto?o>       *
 *                                                                            *
 *  This program is free software; you can redistribute it and/or modify      *
 *  it under the terms of the GNU General Public License as published by      *
 *  the Free Software Foundation; either version 2 of the License, or         *
 *  (at your option) any later version.                                       *
 *                                                                            *
 *  This program is distributed in the hope that it will be useful,           *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of            *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *  GNU General Public License for more details.                              *
 *                                                                            *
 *  You should have received a copy of the GNU General Public License         *
 *  along with this program; if not, write to the Free Software               *
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA *
 ******************************************************************************/

require_once('./inc/errorhandler.inc.php');
require_once('./inc/page.inc.php');
require_once('./inc/usermanager.inc.php');
require_once('./inc/tree.inc.php');

/**
* Change the line below to command.cgi, if you have backend problems with
* importing bookmarks or other security related problems or your server
* runs PHP in a 'safe mode' :
*/
define( "FORM_ACTION_EXECUTOR", "command.php");
/*
* Example of command.cgi (create manually, save as "command.cgi" and
* upload to the same directory as this file is :
* --- BEGIN ---
* #!/usr/bin/php
* <?php include("command.php"); ?>
* --- END ---
*/

/******************************************************************************/

class CommandWindow extends ErrorHandler
{
    var $command;
    var $um;
    var $tree;

    var $reload = false;
    var $close = false;

    var $fields = array();
    var $message = '';
    var $nobuttons = false;
    var $bookmarklet = false;
    var $onLoad = 'initCommander()';
    var $showWithErrors = false;
    var $skipBuild = false;
    var $forward = false;
    var $getInfo = false;

    function CommandWindow()
    {
        $this->command = reqVal('command');

        if (!$this->command)
        {
            $this->error('Missing command!');
        }

        if (is_array($this->command))
        {
            $this->command=key($this->command);
        }

        if (reqChk('weblinks'))
        {
            $this->bookmarklet = true;
        }

        $this->um =& UserManager::staticInstance();
        $this->tree =& Tree::staticInstance();

        $this->handleCommand();
    }

    function handleCommand()
    {
        if (!$this->um->isAuthorized($this->command,
            in_array($this->command, array("Log In", "Log Out", "Sign Up")),
            reqVal('command_gid'), reqVal('nid_acl'), reqVal('lid_acl')))
        {
            $this->error('Access denied!');
            return;
        }

        // For logout we do not build the form and just execute
        // Do is set on build forms (if not set another form is opened)
        if (!$this->forward && (reqVal('do') ||
           in_array($this->command,array('Log Out','Verify Email Code'))))
        {
            $this->reload = !$this->um->getParam('user','extern_commander');
            $this->close = $this->um->getParam('user','auto_close');

            $execute = 'command' . $this->shortName();
            $this->forward = false;

            if (method_exists($this, $execute))
            {
                $this->$execute();
            }

            foreach ($this->um->plugins as $plugin)
            {
                if (in_array($this->command, $plugin['command']))
                {
                    $authorized = true;
                    if (in_array($this->command, $plugin['authorization']))
                    {
                        $execute = $plugin['prefix'].'IsAuthorized';
                        $authorized = $execute($this->um, $this->command);
                    }

                    if ($authorized)
                    {
                        $execute = $plugin['prefix'].'Command'.$this->shortName();
                        $execute($this);
                    }
                }
            }

            if ($this->forward)
            {
                $this->handleCommand();
            }
        }
        else
        {
            $built = false;

            $execute = 'build' . $this->shortName();

            if (method_exists($this, $execute))
            {
                $fields = $this->$execute();
                $built = true;
            }

            foreach ($this->um->plugins as $plugin)
            {
                if (in_array($this->command, $plugin['build']))
                {
                    $execute = $plugin['prefix'].'Build'.$this->shortName();
                    $execute($this, $fields);
                    $built = true;
                }
            }

            if (!$this->skipBuild)
            {
                if (!$built || !count($fields))
                {
                    if (!$this->hasErrors())
                    {
                        $this->error('Unknown command.');
                    }
                }
                else
                {
                    $this->fields = $fields;
                }
            }
        }
    }

    function shortName()
    {
        return str_replace(' ','',$this->command);
    }

    function forwardCommand($command)
    {
        if (!$this->hasErrors() && !$this->message)
        {
            $this->fields  = array();
            $this->command = $command;
            $this->forward = true;
        }
    }

    function goBack()
    {
        // We cannot repair error in this case because we would
        // lost additional infomation.
        if (reqChk('bookmarklet') && $this->command="Log In")
        {
            $this->bookmarklet = true;
            return;
        }

        $this->showWithErrors = true;
        $execute = 'build' . $this->shortName();

        if (method_exists($this, $execute))
        {
            $fields = $this->$execute();
        }

        foreach ($this->um->plugins as $plugin)
        {
            if (in_array($this->command, $plugin['build']))
            {
                $execute = $plugin['prefix'].'Build'.$this->shortName();
                $execute($this, $fields);
            }
        }

        foreach ($fields as $name => $params)
        {
            if (isset($fields[$name]['name']) && !strstr($name,'-raw') )
            {
                $fields[$name]['value'] = reqVal($fields[$name]['name']);
            }
        }

        $this->fields = $fields;
    }

    function inPlace()
    {
        return !$this->bookmarklet &&
            (in_array($this->command, $this->um->inPlaceCommands()) ||
             !$this->um->getParam('user','extern_commander'));
    }

    function getParams($html=true)
    {
        $params = array();
        if (isset($_REQUEST['target'])) $params[] = 'target='.$_REQUEST['target'];
        if (isset($_REQUEST['mode']))   $params[] = 'mode='.$_REQUEST['mode'];

        $params[] = 'uniq=' . time();

        $paramstr = '';
        if (count($params))
        {
            $paramstr = '?' . implode($html?'&amp;':'&',$params);
        }

        return $paramstr;
    }

    function getFieldParams($params)
    {
        static $tabindex = 1;

        if (!isset($params['maxlength']) && isset($params['name']))
        {
            if ($params['name'] == 'name' || $params['name'] == 'email')
            {
                $params['maxlength'] = 50;
            }
        }

        $txt = '';

        if (!array_key_exists('disabled', $params)
        &&  !array_key_exists('hidden', $params)
        &&   isset($params['type'])
        &&   $params['type']=="text")
        {
            if ($tabindex==1)
            {
                $txt .= 'id="focused" ';
            }
            $tabindex++;
        }

        foreach ($params as $param => $value)
        {
            if ($value!=='' && $param{0}!='_')
            {
                if ($param=='type' && $value=='textarea')
                {
                    continue;
                }

                if ($param=='value')
                {
                    $value = Page::quoteValue($value);
                }
                $txt .= $param . ($value?'="' . $value . '" ':' ');
            }
        }
        return $txt;
    }

    function checkFile($name)
    {
        if (isset($_FILES[$name]['name']) && !$_FILES[$name]['name'])
        {
            // We cannot do this directly because it would be always missing
            $this->checkMandatoryFields(array($name));
            $this->goBack();
            return false;
        }

        if (!is_uploaded_file($_FILES[$name]['tmp_name']) || !$_FILES[$name]['size'])
        {
            $this->error('Invalid filename or other upload related problem: %s!',
                array( safeVal($_FILES[$name],'error')));
            $this->goBack();
            return false;
        }

        return true;
    }

    function _getAuthMethod()
    {
        $auths = array();
        $dirName = './plugins/auth';

        if (is_dir($dirName) && $dir = opendir($dirName))
        {
            $auths[] = '';

            while (($fileName = readdir($dir)) !== false)
            {
                if (is_file($dirName.'/'.$fileName))
                {
                    $auths[] = str_replace('.inc.php', '', $fileName);
                }
            }
            closedir($dir);
        }

        return $auths;
    }

    function _buildAuthList($select=null)
    {
        foreach ($this->_getAuthMethod() as $auth)
        {
            echo '<option '. ($select==$auth?'selected':'') .
                 ' value="' . $auth . '">' . (strlen($auth)?$auth:'SiteBar') . "</option>\n";
        }
    }

    function _buildSkinList($select=null)
    {
        if ($dir = opendir("./skins"))
        {
            $skins = array();
            while (($dirname = readdir($dir)) !== false)
            {
                if (!file_exists('./skins/'.$dirname.'/sitebar.css')) continue;
                $skins[] = $dirname;
            }
            closedir($dir);

            sort($skins);
            foreach ($skins as $skin)
            {
                echo '<option '. ($select==$skin?'selected':'') .
                     ' value="' . $skin . '">' . $skin . "</option>\n";
            }
        }
    }

    function _buildLangList($select=null)
    {
        $l =& Localizer::getInstance();

        foreach ($l->getLanguages() as $lang)
        {
            $dir = $lang['dir'] . str_repeat("&nbsp;", 5-strlen($lang['dir']));

            echo '<option class="fixed" '. ($select==$lang['dir']?'selected':'') .
                 ' value="' . $lang['dir'] . '">' . $dir .  " " . $lang['language'] . "</option>\n";
        }
    }

    function _buildUserList($select=null, $exclude=null)
    {
        foreach ($this->um->getUsers() as $uid => $rec)
        {
            if ($uid == $exclude) continue;
            echo '<option '. ($select==$uid?'selected':'') .
                ' value="' . $uid . '">' . $rec['cryptmail'] . "</option>\n";
        }
    }

    function _buildUserCheck($params)
    {
        $id = 'l_'.$params['name'];
        $attr = ' name="' .$params['name'].'" '.
            (isset($params['checked'])?' checked':'').
            (isset($params['disabled'])?' disabled':'');
?>
        <tr>
            <td class='check'>
                <input id="<?php echo $id?>" type="checkbox" value="1" <?php echo $attr?>>
            </td>
            <td>
                <label for="<?php echo $id?>"><?php echo $params['email']?></label>
            </td>
        </tr>
        <tr>
            <td>&nbsp;</td>
            <td>
                <label for="<?php echo $id?>">"<?php echo $params['realname']?>"</label>
            </td>
        </tr>
<?php

    }

    function _buildGroupList()
    {
        $groups = $this->um->getModeratedGroups($this->um->uid);

        foreach ($this->um->getGroups() as $gid => $rec)
        {
            if (!$this->um->isAdmin()
            && !in_array($gid, array_keys($groups))) continue;
            echo '<option  value="' . $gid . '">' .
                $rec['name'] . "</option>\n";
        }
    }

    function _buildFolderOrder($params)
    {
?>
        <tr>
            <td>
                <input class='order' value='<?php echo $params['order']?>'
                    name='id<?php echo $params['id']?>' maxlength='5'>
            </td>
            <td>
                <?php echo $params['name']?>
            </td>
        </tr>
<?php
    }

    function _buildFavicon($lid, $favicon)
    {
        $wrong = Skin::imgsrc('link_wrong_favicon');
        $txt = '';

        if ($this->um->getParam('config', 'use_favicon_cache'))
        {
            $cached = 'favicon.php?' . md5($favicon) . '=' . $lid . "&refresh=" . StopWatch::getMicroTime();
            $txt .= T("Cached: ") . "<img alt='' class='favicon' src='$cached' onerror='this.src=\"$wrong\"'>";
            $txt .= "&nbsp;";
        }

        $txt .= T("Original: ") . "<img alt='' src='$favicon' onerror='this.src=\"$wrong\"'>";

        return "<div>$txt</div>\n";
    }

    function _buildSendEmail($label=null, $checkRCPT=false)
    {
        $fields = array();
        $fields[$label?$label:'Message'] = array('name'=>'message', 'type'=>'textarea', 'rows'=>5);

        if ($checkRCPT)
        {
            $fields['-hidden000-'] = array('name'=>'checkrcpt', 'value'=>1);
            $fields['Respect Allow Info Mail'] =
                array('name'=>'respect', 'type'=>'checkbox', 'checked'=>1);
            $fields['Only to Verified Emails'] =
                array('name'=>'verified', 'type'=>'checkbox', 'checked'=>1);
        }

        return $fields;
    }

    function _commandSendEmail($to, $subject, $group=null)
    {
        // Prefetch to have it in our language
        $okStr    = T('%s - ok.');
        $errorStr = T('%s - error!');

        $message  = stripslashes(reqVal('message'));

        foreach ($to as $uid => $user)
        {
            $this->um->explodeParams($user['params'], 'tmp');

            if (reqVal("checkrcpt"))
            {
                if (reqChk('respect') && !$this->um->getParam('tmp','allow_info_mails'))
                {
                    continue;
                }

                if (reqChk('verified') && !$user['verified'])
                {
                    continue;
                }
            }

            SetLanguage($this->um->getParam('tmp','lang'));

            $body = '';
            if ($group)
            {
                $body = P('command::contact_group',array($group, $message, Page::baseurl()));
            }
            else
            {
                $body = P('command::contact',array($message, Page::baseurl()));
            }

            if (!strstr($this->um->email,'@'))
            {
                $this->error('The e-mail %s does not look correct!', $this->um->email);
                continue;
            }

            $ret = $this->um->sendMail($user, T($subject), $body, $this->um->name, $this->um->email);

            // No translation here
            if ($ret)
            {
                $this->warn('%s', sprintf($okStr, $user['cryptmail']));
            }
            else
            {
                $this->error('%s', sprintf($errorStr, $user['cryptmail']));
            }
        }

        SetLanguage($this->um->getParam('user','lang'));
    }

    function writeForm()
    {
        $customButton = false;
?>
<form method="post" enctype="multipart/form-data" action="<?php echo FORM_ACTION_EXECUTOR ?>">
    <input type="hidden" name="command" value="<?php echo $this->command?>">
<?php
        $target = '';
        if (isset($_REQUEST['target'])) $target = $_REQUEST['target'];
        if ($target)
        {
?>
    <input type="hidden" name="target" value='<?php echo $target?>'>
<?php
        }

        $mode = '';
        if (isset($_REQUEST['mode'])) $mode = $_REQUEST['mode'];
        if ($mode)
        {
?>
    <input type="hidden" name="mode" value='<?php echo $mode?>'>
<?php
        }

        $enabled = false;
        foreach ($this->fields as $name => $params)
        {
            if (!is_array($params))
            {
                if (substr($name,'-raw'))
                {
                    echo $params;
                }
                else
                {
?>
    <div class="label" <?php echo ($params?'title="'.$params.'"':'')?>><?php echo $name?><?php echo $params?'*':''?></div>
<?php
                }
                continue;
            }

            if (!isset($params['type']))
            {
                $params['type'] = 'text';
            }

            $disabled = !$params || array_key_exists('disabled', $params);

            // Is at least one field enabled
            $enabled = ($name{0} != '-' && !$disabled) || $enabled;

            // If we have disabled field then keep the value that would
            // be otherwise lost. Needed to go back.
            if ($disabled && $params['type'] == 'text')
            {
?>
    <input type="hidden" name="<?php echo $params['name']?>" value="<?php echo $params['value']?>">
<?php
                $params['name'] = ''; // Don't use name with disabled fields.
            }

            if ($name{0} == '-')
            {
?>
    <input type="hidden" name="<?php echo $params['name']?>" value="<?php echo $params['value']?>">
<?php
            }
            elseif (isset($params['type']) &&  $params['type'] == 'checkbox')
            {
                $id = 'l_'.$params['name'];
                $params['id'] = $id;
                if (!isset($params['value']))
                {
                    $params['value'] = 1;
                }
?>
    <div class="check">
        <input <?php echo $this->getFieldParams($params)?>>
        <label for="<?php echo $id?>"><?php echo isset($params['_raw'])?$name:T($name)?></label>
    </div>
<?php
            }
            elseif (isset($params['type']) && $params['type'] == 'select')
            {
                unset($params['type']);
?>
    <div class="label"><?php echo T($name)?></div>
    <div class="data">
        <select <?php echo $this->getFieldParams($params)?>>
<?php
            $this->$params['_options'](
                isset($params['_select'])?$params['_select']:null,
                isset($params['_exclude'])?$params['_exclude']:null
                );
?>
        </select>
    </div>
<?php
            }
            elseif (isset($params['type']) &&  $params['type'] == 'callback')
            {
                $this->$params['function'](isset($params['params'])?$params['params']:null);
            }
            elseif (isset($params['type']) &&  $params['type'] == 'callbackextern')
            {
                $params['function'](isset($params['params'])?$params['params']:null);
            }
            elseif (isset($params['type']) &&  ($params['type'] == 'button') || ($params['type'] == 'addbutton'))
            {
                if (!$this->um->isAuthorized($name,false,null,reqVal('nid_acl'),reqVal('lid_acl'))) continue;

                if ($params['type'] == 'button')
                {
                    $customButton = true;
                }

                $params['name'] = 'command['.$name.']';
?>
    <div>
        <input class="button customButton" type="submit" name="<?php echo $params['name']?>" value="<?php echo T($name)?>">
    </div>
<?php
            }
            elseif (isset($params['type']) &&  $params['type'] == 'textarea')
            {
                unset($params['type']);
?>
    <div class="label"><?php echo T($name)?></div>
    <div class="data">
        <textarea <?php echo $this->getFieldParams($params)?>><?php echo isset($params['value'])?$params['value']:''?></textarea>
    </div>
<?php
            }
            else
            {
?>
    <div class="label"><?php echo T($name)?></div>
    <div class="data">
        <input <?php echo $this->getFieldParams($params)?>>
        <input type="hidden" name="label_<?php echo $params['name']?>" value="<?php echo $name?>">
    </div>
<?php
            }
        }

        if (!$customButton)
        {
?>
    <div class="buttons">
        <input class="button" type="submit" name="do" value="<?php echo T("Submit")?>">
<?php
            if ($enabled) :
?>
        <input class="button" type="reset" value="<?php echo T("Reset")?>">
<?php
            endif;
?>
    </div>
<?php
        }
?>
</form>
<?php
    }

    function checkMandatoryFields($fields)
    {
        $ok = true;

        foreach ($fields as $field)
        {
            if (!reqVal($field))
            {
                $this->error('Field %s must be filled!', array(T(reqVal('label_'.$field))));
                $ok = false;
            }
        }

        return $ok;
    }

/******************************************************************************/

    function buildSiteBarSettings()
    {
        $fields = array();

        $lang = reqChk('lang')?reqVal('lang'):$this->um->getParam('config','lang');

        $fields['Default Skin'] = array('name'=>'skin','type'=>'select',
            '_options'=>'_buildSkinList', '_select'=>$this->um->getParam('config','skin'));
        $fields['Default Language'] = array('name'=>'lang','type'=>'select', 'class'=>'fixed',
            '_options'=>'_buildLangList', '_select'=>$lang);

        if (count($this->_getAuthMethod()))
        {
            $fields['Authorization Method'] = array('name'=>'auth','type'=>'select',
                '_options'=>'_buildAuthList', '_select'=>$this->um->getParam('config','auth'));
        }

        $fields['Link Description Length'] = array('name'=>'comment_limit',
            'value'=>$this->um->getParam('config','comment_limit'));
        $fields['Sender E-mail'] = array('name'=>'sender_email',
            'value'=>$this->um->getParam('config','sender_email'));

        $fields['Allow Anonymous Contact'] = array('name'=>'allow_contact', 'type'=>'checkbox',
            'checked'=>$this->um->getParamCheck('config','allow_contact'));
        $fields['Allow Noninteractive Mode'] = array('name'=>'backend_server', 'type'=>'checkbox',
            'checked'=>$this->um->getParamCheck('config','backend_server'));
        $fields['Allow Sign Up'] = array('name'=>'allow_sign_up', 'type'=>'checkbox',
            'checked'=>$this->um->getParamCheck('config','allow_sign_up'));

        $fields['Description Import/Export'] = array('name'=>'comment_impex', 'type'=>'checkbox',
            'checked'=>$this->um->getParamCheck('config','comment_impex'));

        $fields['Personal Mode'] = array('name'=>'personal_mode', 'type'=>'checkbox',
            'checked'=>$this->um->getParamCheck('config','personal_mode'));
        $fields['Users Can Create Trees'] = array('name'=>'allow_user_trees', 'type'=>'checkbox',
            'checked'=>$this->um->getParamCheck('config','allow_user_trees'));
        $fields['Users Can Delete Trees'] = array('name'=>'allow_user_tree_deletion', 'type'=>'checkbox',
            'checked'=>$this->um->getParamCheck('config','allow_user_tree_deletion'));

        $fields['Use Conversion Engine'] = array('name'=>'use_conv_engine', 'type'=>'checkbox',
            'checked'=>$this->um->getParamCheck('config','use_conv_engine'));
        $fields['Use Compression'] = array('name'=>'use_compression', 'type'=>'checkbox',
            'checked'=>$this->um->getParamCheck('config','use_compression'));
        $fields['Use E-mail Features'] = array('name'=>'use_mail_features', 'type'=>'checkbox',
            'checked'=>$this->um->getParamCheck('config','use_mail_features'));
        $fields['Use Outbound Connection'] = array('name'=>'use_outbound_connection', 'type'=>'checkbox',
            'checked'=>$this->um->getParamCheck('config','use_outbound_connection'));
        $fields['Use Hit Counter'] = array('name'=>'use_hit_counter', 'type'=>'checkbox',
            'checked'=>$this->um->getParamCheck('config','use_hit_counter'));

        $fields['Show Logo'] = array('name'=>'show_logo', 'type'=>'checkbox',
            'checked'=>$this->um->getParamCheck('config','show_logo'));
        $fields['Show Sponsor'] = array('name'=>'show_sponsor', 'type'=>'checkbox',
            'checked'=>$this->um->getParamCheck('config','show_sponsor'));
        $fields['Show Statistics'] = array('name'=>'show_statistics', 'type'=>'checkbox',
            'checked'=>$this->um->getParamCheck('config','show_statistics'));

        $fields['Favicon Management'] = array('type'=>'addbutton');

        return $fields;
    }

    function commandSiteBarSettings()
    {
        $checks = array
        (
            'allow_contact',
            'allow_sign_up',
            'allow_user_trees',
            'allow_user_tree_deletion',
            'backend_server',
            'comment_impex',
            'personal_mode',
            'show_logo',
            'show_sponsor',
            'show_statistics',
            'use_compression',
            'use_conv_engine',
            'use_hit_counter',
            'use_mail_features',
            'use_outbound_connection',
        );

        $values = array
        (
            'auth',
            'skin',
            'lang',
            'comment_limit',
            'sender_email',
        );

        foreach ($checks as $check)
        {
            $this->um->setParam('config',$check, reqVal($check)?1:0);
        }
        foreach ($values as $check)
        {
            $this->um->setParam('config',$check, reqVal($check));
        }

        $this->um->saveConfiguration();
    }

/******************************************************************************/

    function buildFaviconManagement()
    {
        $fields = array();

        $fields['Use the Favicon Cache'] = array('name'=>'use_favicon_cache',
            'type'=>'checkbox',
            'checked'=>$this->um->getParamCheck('config','use_favicon_cache'));
        /* TODO: This is not used at the moment, there is missing
         * backend function for this.
         *
        $fields['Maximal Icons per User'] = array('name'=>'max_icon_user',
            'value'=>$this->um->getParam('config','max_icon_user'));
         */
        $fields['Maximal Icons Total'] = array('name'=>'max_icon_cache',
            'value'=>$this->um->getParam('config','max_icon_cache'));
        $fields['Maximal Icon Size'] = array('name'=>'max_icon_size',
            'value'=>$this->um->getParam('config','max_icon_size'));
        $fields['Maximal Icon Age'] = array('name'=>'max_icon_age',
            'value'=>$this->um->getParam('config','max_icon_age'));

        $fields['Purge Cache'] = array('type'=>'addbutton');
        $fields['Show All Icons'] = array('type'=>'addbutton');

        return $fields;
    }

    function commandFaviconManagement()
    {
        $values = array
        (
            // 'max_icon_user', // TODO: see build comment
            'max_icon_cache',
            'max_icon_size',
            'max_icon_age',
        );

        foreach ($values as $value)
        {
            $this->um->setParam('config',$value, reqVal($value));
        }

        $this->um->setParam('config','use_favicon_cache', reqVal('use_favicon_cache'));

        $this->um->saveConfiguration();

    }

    function buildPurgeCache()
    {
        $fields = array();

        $fields['-raw1-'] = P('command::purge_cache');

        return $fields;
    }

    function commandPurgeCache()
    {
        require_once('./inc/faviconcache.inc.php');
        $fc = & FaviconCache::staticInstance();
        $fc->purge();
    }

    function buildShowAllIcons()
    {
        $fields = array();

        $carpet = '';

        require_once('./inc/faviconcache.inc.php');

        $fc = & FaviconCache::staticInstance();

        $cacheItems = $fc->faviconGetAll();

        foreach ($cacheItems as $item)
        {
            $favicon = './favicon.php?' . $item['favicon_md5'];

            $carpet .= '<img class="favicon" alt="" src="'.$favicon.'"'.
                ' onerror="this.src=\''.Skin::imgsrc("link_wrong_favicon").'\'">'."\n";
        }

        $fields['-raw1-'] = $carpet;

        return $fields;
    }

/******************************************************************************/

    function _buildTreeList()
    {
        foreach ($this->tree->loadRoots(true, true) as $root)
        {
            echo '<option value="' . $root->id . '">'. $root->name . "</option>\n";
        }
    }

    function buildMaintainTrees()
    {
        $fields = array();
        $fields['Create Tree'] = array('type'=>'button');
        $fields['Unhide Trees'] = array('type'=>'button');
        $fields['Order of Trees'] = array('type'=>'button');

        if ($this->um->getParam('config','allow_user_tree_deletion') || $this->um->isAdmin())
        {
            $fields['Select Tree'] = array('name'=>'nid_acl','type'=>'select', '_options'=>'_buildTreeList');
            $fields['Delete Tree'] = array('type'=>'button');
        }

        // Dirty, to allow forward back
        $_REQUEST['nid_acl'] = null;

        return $fields;
    }

    function buildCreateTree()
    {
        $fields = array();
        if ($this->um->isAdmin())
        {
            $fields['Owner'] = array('name'=>'uid','type'=>'select',
                '_options'=>'_buildUserList','_select'=>$this->um->uid);
        }
        $fields['Tree Name'] = array('name'=>'treename');
        $fields['Description'] = array('name'=>'comment');
        return $fields;
    }

    function commandCreateTree()
    {
        $this->checkMandatoryFields(array('treename'));
        if ($this->hasErrors())
        {
            $this->goBack();
            return;
        }

        $uid = reqVal('uid');

        if (!$this->um->isAdmin())
        {
            $uid = $this->um->uid;
        }

        $this->tree->addRoot($uid, reqVal('treename'), reqVal('comment'));

        $this->forwardCommand("Maintain Trees");
    }

    function buildUnhideTrees()
    {
        $fields['-raw1-'] = "<table cellpadding='0'>";
        $count = 0;

        foreach ($this->tree->loadRoots(true) as $root)
        {
            if ($root->hidden)
            {
                $fields[$root->name] = array('name'=>'nid_'.$root->id,'type'=>'checkbox');
                $count++;
            }
        }

        if (!$count)
        {
            $this->warn("There are no hidden trees!");
        }

        $fields['-raw2-'] = "</table>";

        return $fields;
    }

    function commandUnhideTrees()
    {
        foreach ($this->tree->loadRoots(true) as $root)
        {
            if ($root->hidden && reqVal('nid_'.$root->id))
            {
                unset($this->um->hiddenFolders[$root->id]);
            }
        }

        $this->um->setParam('user','hidden_folders', implode(':',array_keys($this->um->hiddenFolders)));
        $this->um->saveUserParams();

        $this->forwardCommand("Maintain Trees");
    }

    function buildOrderOfTrees()
    {
        $fields['-raw1-'] = "<table cellpadding='0'>";

        foreach ($this->tree->loadRoots() as $root)
        {
            $label = $root->name;
            $fields[$label] = array
            (
                'type'=>'callback',
                'function'=>'_buildFolderOrder',
                'params'=>array('name'=>$root->name,'id'=>$root->id,'order'=>$root->order),
            );
        }

        $fields['-raw2-'] = "</table>";

        return $fields;
    }

    function commandOrderOfTrees()
    {
        $order = array();

        foreach ($this->tree->loadRoots() as $root)
        {
            $order[] = $root->id.'~'.intval(reqVal('id'.$root->id));
        }

        $this->um->setParam('user', 'root_order', implode(':',$order));
        $this->um->saveUserParams();
        $this->forwardCommand("Maintain Trees");
    }

    function buildDeleteTree()
    {
        $node = $this->tree->getNode(reqVal('nid_acl',true));
        if (!$node) return null;

        $fields['Folder Name'] = array('name'=>'name','value'=>$node->name, 'disabled'=>null);
        $fields['Description'] = array('name'=>'comment', 'type'=>'textarea',
            'value'=>$node->comment, 'disabled'=>null);
        $fields['-hidden1-'] = array('name'=>'nid_acl','value'=>$node->id);

        return $fields;
    }

    function commandDeleteTree()
    {
        $this->tree->removeNode(reqVal('nid_acl'), false);
        $this->tree->purgeNode(reqVal('nid_acl'));

        $this->forwardCommand("Maintain Trees");
    }

/******************************************************************************/

    function buildSetUp()
    {
        $fields['E-mail'] = array('name'=>'email');
        $fields['Admin Password'] = array('name'=>'pass','type'=>'password');
        $fields['Repeat Admin Password'] = array('name'=>'pass_repeat','type'=>'password');
        $fields['Real Name'] = array('name'=>'realname');

        return array_merge($fields, $this->buildSiteBarSettings());
    }

    function commandSetUp()
    {
        SetLanguage(reqVal('lang'));

        if (reqVal('pass') != reqVal('pass_repeat'))
        {
            $this->error('The password was not repeated correctly!');
        }

        $this->checkMandatoryFields(array('pass','realname','email'));
        if ($this->hasErrors())
        {
            $this->goBack();
            return;
        }

        if ($this->um->setUp(reqVal('email'),reqVal('pass'),reqVal('realname')))
        {
            $this->um->setParam('user','lang', reqVal('lang'));
            $this->um->saveUserParams();
            $this->commandSiteBarSettings();

            $this->reload = true;
            $this->close = false;
            $this->message =
                T('%s, welcome to the SiteBar!<p>You have been already logged in.',
                array(reqVal('realname')));
        }
    }

/******************************************************************************/

    function buildLogIn()
    {
        $fields = array();
        $fields['E-mail'] = array('name'=>'email');
        $fields['Password'] = array('name'=>'pass','type'=>'password');
        $fields['Remember Me'] = array('name'=>'expires','type'=>'select',
            '_options'=>'_buildExpirationList');

        return $fields;
    }

    function _buildExpirationList()
    {
        $expiration = array
        (
            'Until I close browser' =>0,
            '12 hours' =>60*60*12,
            '6 days'   =>60*60*24*6,
            '1 month'  =>60*60*24*30,
        );

        foreach ($expiration as $label => $value)
        {
            echo '<option value="' . $value. '">' . T($label). "</option>\n";
        }
    }

    function commandLogIn()
    {
        $this->checkMandatoryFields(array('email','pass'));
        if ($this->hasErrors())
        {
            $this->goBack();
            return;
        }

        if (!$this->um->login(reqVal('email'), reqVal('pass'), reqVal('expires')))
        {
            $this->goBack();
            return;
        }

        if (reqChk('forward'))
        {
            header('Location: '.reqVal('forward'));
            exit;
        }

        if (reqChk('bookmarklet'))
        {
            $this->command = 'Add Link';
            $this->fields = $this->buildAddLink();
        }
        else
        {
            $this->reload = true;
            $this->close = true;
        }
    }

    function commandLogOut()
    {
        $this->um->logout();
        $this->reload = true;
        $this->close = true;
    }

/******************************************************************************/

    function buildHelp()
    {
        $fields = array();
        $topics = GetHelpTopics();

        $fields['Help Topic'] = array('class'=>'help', 'name'=>'topic','type'=>'select',
            'size'=> (reqChk('topic')?1:count($topics)),
            '_options'=>'_buildHelpTopicList', '_select'=>reqVal('topic'));
        $fields['Display Topic'] = array('type'=>'button','value'=>'Help');

        if (reqChk('topic'))
        {
            $fields['-raw1-'] = '<h3>' . $topics[reqVal('topic')] . '</h3>';
            $fields['Topic'] = array('type'=>'callbackextern',
                'function'=>'GetHelp', 'params'=>array('topic'=>reqVal('topic')));
        }
        return $fields;
    }

    function buildDisplayTopic()
    {
        $this->command = 'Help';
        return $this->buildHelp();
    }

    function _buildHelpTopicList($select=null)
    {
        foreach (GetHelpTopics() as $value => $label)
        {
            if (intval($value) % 100)
            {
                $label = '&nbsp;-&nbsp;' . $label;
            }

            echo '<option '.($select==$value?'selected':'').
                 ' value="' . $value. '">' . $label. "</option>\n";
        }
    }

/******************************************************************************/

    function buildContactAdmin()
    {
        $fields = array();
        if ($this->um->isAnonymous())
        {
            $fields['Your E-mail'] = array('name'=>'email');
        }
        return array_merge($fields,$this->_buildSendEmail('Feedback or Other Comment'));
    }

    function commandContactAdmin()
    {
        $name = $this->um->name;
        $email = $this->um->email;

        if (reqChk('email'))
        {
            if (reqVal('email'))
            {
                $name  = T('Guest');
                $email = reqVal('email');
            }
            else
            {
                $name  = T('Anonymous Guest');
                $email = 'noname@no.where';
            }
        }

        $comment = reqVal('message');

        if (!$comment)
        {
            return;
        }

        $admins = $this->um->getMembers($this->um->config['gid_admins']);

        foreach ($admins as $uid => $user)
        {
            $this->um->explodeParams($user['params'], 'tmp');
            SetLanguage($this->um->getParam('tmp','lang'));
            $msg = P('command::contact',array($comment,Page::baseurl()));
            $this->um->sendMail($user, T('SiteBar: Contact Admin'), $msg, $name, $email);
        }

        SetLanguage($this->um->getParam('user','lang'));
    }

/******************************************************************************/

    function buildSignUp()
    {
        $fields = array();

        $lang = reqChk('lang')?reqVal('lang'):$this->um->getParam('user','lang');

        $fields['Language'] = array('name'=>'lang','type'=>'select', 'class'=>'fixed',
            '_options'=>'_buildLangList', '_select'=>$lang);
        $fields['E-mail'] = array('name'=>'email');
        $fields['Password'] = array('name'=>'pass','type'=>'password');
        $fields['Repeat Password'] = array('name'=>'pass_repeat','type'=>'password');
        $fields['Real Name'] = array('name'=>'realname');
        $fields['Comment'] = array('name'=>'comment');
        return $fields;
    }

    function commandSignUp($autoLogin = true)
    {
        SetLanguage(reqVal('lang'));

        if (reqVal('pass') != reqVal('pass_repeat'))
        {
            $this->error('The password was not repeated correctly!');
        }

        if (!$this->checkMandatoryFields(array('email','pass','realname')))
        {
            $this->goBack();
        }

        if ($this->hasErrors())
        {
            $this->goBack();
            return;
        }

        $uid = $this->um->signUp(
            reqVal('email'),
            reqVal('pass'),
            reqVal('realname'),
            reqVal('comment'),
            !$autoLogin,
            reqVal('verified'),
            reqVal('demo'),
            reqVal('lang'));

        if ($uid)
        {
            $this->tree->addRoot($uid, T('%s&#39;s Bookmarks', reqVal('realname')));

            if ($autoLogin)
            {
                $this->um->login(reqVal('email'), reqVal('pass'));
                $this->reload = true;
                $this->close = false;
                $this->message =
                    T('%s, welcome to the SiteBar!<p>You have been already logged in.',
                    array(reqVal('realname')));
            }
        }

        if ($this->hasErrors())
        {
            $this->goBack();
            return;
        }
    }

/******************************************************************************/

    function buildVerifyEmail()
    {
        $subject = T('SiteBar: Email Verification');
        $msg = P('command::verify_email',array($this->um->getVerificationUrl()));
        // Verify email
        $this->um->sendMail($this->um->user, $subject, $msg);
        $this->warn('Verification e-mail has been sent to your e-mail address!');
        return array();
    }

    function commandVerifyEmailCode()
    {
        $email = reqVal('email', true);
        $code = reqVal('code', true);

        $this->um->verifyEmail($email, $code);

        if (!$this->hasErrors())
        {
            $this->nobuttons = true;
            $this->reload = false;
            $this->close = false;
            $this->message =
                T('E-mail %s verified! Any pending memberships were approved.',
                array($email));
        }
    }

/******************************************************************************/

    function buildMaintainUsers()
    {
        $fields = array();
        $fields['Create User'] = array('type'=>'button');
        $fields['Send Email to All'] = array('type'=>'button');
        $fields['Select User'] = array('name'=>'uid','type'=>'select',
            '_options'=>'_buildUserList');
        $fields['Modify User'] = array('type'=>'button');
        $fields['Delete User'] = array('type'=>'button');
        $fields['Send Email to User'] = array('type'=>'button');
        return $fields;
    }

    function buildCreateUser()
    {
        $fields = $this->buildSignUp();
        $fields['E-mail Verified'] = array('name'=>'verified', 'type'=>'checkbox', 'checked'=>null);
        $fields['Demo Account'] = array('name'=>'demo', 'type'=>'checkbox');
        return $fields;
    }

    function commandCreateUser()
    {
        $this->commandSignUp(false);

        $this->forwardCommand("Maintain Users");
    }

    function buildModifyUser()
    {
        if (!reqChk('uid'))
        {
            $this->error('No user was selected!');
            return null;
        }
        $fields = array();
        $user = $this->um->getUser(reqVal('uid'));
        $fields['E-mail'] = array('name'=>'email', 'value'=>$user['email'], 'disabled' => null);
        $fields['Real Name'] = array('name'=>'realname', 'value'=>$user['name']);
        $fields['Comment'] = array('name'=>'comment', 'value'=>$user['comment']);
        $fields['Password'] = array('name'=>'pass','type'=>'password');
        $fields['Repeat Password'] = array('name'=>'pass_repeat','type'=>'password');
        $fields['E-mail Verified'] = array('name'=>'verified', 'type'=>'checkbox');
        $fields['Demo Account'] = array('name'=>'demo', 'type'=>'checkbox');

        if ($user['verified'])
        {
            $fields['E-mail Verified']['checked'] = null;
        }

        if ($user['demo'])
        {
            $fields['Demo Account']['checked'] = null;
        }

        $fields['-hidden1-'] = array('name'=>'uid', 'value'=>reqVal('uid'));
        return $fields;
    }

    function commandModifyUser()
    {
        if (reqChk('pass') && reqVal('pass') != reqVal('pass_repeat'))
        {
            $this->error('The password was not repeated correctly!');
        }

        if ($this->hasErrors())
        {
            $this->goBack();
            return;
        }
        $this->um->modifyUser(reqVal('uid',true), reqVal('pass'),
            reqVal('realname'), reqVal('comment'),
            reqVal('verified'), reqVal('demo'));

        $this->forwardCommand("Maintain Users");
    }

    function buildDeleteUser()
    {
        if (!reqChk('uid'))
        {
            $this->error('No user was selected!');
            return null;
        }

        if ($this->um->uid == reqVal('uid',true))
        {
            $this->error('Use "%s" command to delete own account!', T('Delete Account'));
            return null;
        }

        $fields = array();
        $user = $this->um->getUser(reqVal('uid'));
        $fields['E-mail'] = array('name'=>'email', 'value'=>$user['email'], 'disabled' => null);
        $fields['Real Name'] = array('name'=>'realname', 'value'=>$user['name'], 'disabled' => null);
        $fields['-hidden1-'] = array('name'=>'uid', 'value'=>reqVal('uid'));

        if (count($this->tree->getUserRoots(reqVal('uid'))))
        {
            $fields['New Tree Owner'] = array('name'=>'owner','type'=>'select',
                '_options'=>'_buildUserList', '_exclude'=>reqVal('uid'));
        }
        return $fields;
    }

    function commandDeleteUser()
    {
        if (!$this->um->removeUser(reqVal('uid',true)))
        {
            return;
        }
        if (reqChk('owner'))
        {
            $this->tree->changeOwner(reqVal('uid'), reqVal('owner'));
        }

        $this->forwardCommand("Maintain Users");
    }

    function buildDeleteAccount()
    {
        $fields = array();
        $fields['-raw1-'] = P('command::delete_account');
        $fields['Password'] = array('name'=>'pass','type'=>'password');
        return $fields;
    }

    function commandDeleteAccount()
    {
        $this->checkMandatoryFields(array('pass'));

        if (reqChk('pass') && reqVal('pass') && !$this->um->checkPassword($this->um->uid,reqVal('pass')))
        {
            $this->error('Invalid password!');
        }

        if ($this->hasErrors())
        {
            $this->goBack();
            return;
        }

        if ($this->um->deleteAccount())
        {
            $this->commandLogOut();
        }
    }

    function buildUserSettings()
    {
        $fields = array();

        $fields['Personal Data'] = array('type'=>'addbutton');
        $fields['Delete Account'] = array('type'=>'addbutton');

        if ($this->um->demo)
        {
            foreach ($fields as $name => $field)
            {
                $fields[$name]['disabled'] = null;
            }
        }

        $fields['Language'] = array('name'=>'lang','type'=>'select', 'class'=>'fixed',
            '_options'=>'_buildLangList', '_select'=>$this->um->getParam('user','lang'));
        $fields['Default Search In'] = array('name'=>'default_search','type'=>'select',
            '_options'=>'_buildSearchPrefix', '_select'=>$this->um->getParam('user','default_search'));
        $fields['Default Link Sort Mode'] = array('name'=>'link_sort_mode','type'=>'select',
            '_options'=>'_buildLinkSortMode', '_select'=>$this->um->getParam('user','link_sort_mode'));
        $fields['Keep In Cache'] = array('name'=>'keep_in_cache','type'=>'select',
            '_options'=>'_buildKeepInCache', '_select'=>$this->um->getParam('user','keep_in_cache'));
        $fields['Order of Folders v. Links'] = array('name'=>'mix_mode','type'=>'select',
            '_options'=>'_buildMixMode', '_select'=>$this->um->getParam('user','mix_mode'));
        $fields['Paste Mode'] = array('name'=>'paste_mode','type'=>'select',
            '_options'=>'_buildPasteModeSetting', '_select'=>$this->um->getParam('user','paste_mode'));
        $fields['Skin'] = array('name'=>'skin','type'=>'select',
            '_options'=>'_buildSkinList', '_select'=>$this->um->getParam('user','skin'));

        if (!$this->um->pmode)
        {
            $fields['Allow Given Membership'] = array('name'=>'allow_given_membership',
                'type'=>'checkbox',
                'checked'=>$this->um->getParamCheck('user','allow_given_membership'),
                'title'=>'Allow moderators to add me to their groups.');
            $fields['Allow Info Mail'] = array('name'=>'allow_info_mails',
                'type'=>'checkbox',
                'checked'=>$this->um->getParamCheck('user','allow_info_mails'),
                'title'=>'Allow admins and moderators of group that I belong to, to send me info emails.');
        }

        if ($this->um->getParam('config','use_outbound_connection'))
        {
            $fields['Auto Retrieve Favicon'] = array('name'=>'auto_retrieve_favicon',
                'type'=>'checkbox',
                'checked'=>$this->um->getParamCheck('user','auto_retrieve_favicon'),
                'title'=>'Retrieve favicon automatically when it is missing and link is being added.');
        }

        $name = ($this->um->pmode?'Decorate Published Folders':'Decorate ACL Folders');

        $fields[$name] = array('name'=>'show_acl',
            'type'=>'checkbox',
            'checked'=>$this->um->getParamCheck('user','show_acl'),
            'title'=>'Decorate folders with security specification - performance penalty.');

        $fields['Extern Commander'] = array('name'=>'extern_commander',
            'type'=>'checkbox',
            'checked'=>$this->um->getParamCheck('user','extern_commander'),
            'title'=>'Execute commands using external window - without reloads after every command.');

        $fields['Use Favicons'] = array('name'=>'use_favicons', 'type'=>'checkbox',
            'checked'=>$this->um->getParamCheck('user','use_favicons'));

        $fields['Use Folder Hiding'] = array('name'=>'use_hiding', 'type'=>'checkbox',
            'checked'=>$this->um->getParamCheck('user','use_hiding'));

        $fields['Show Menu Icon'] = array('name'=>'menu_icon', 'type'=>'checkbox',
            'checked'=>$this->um->getParamCheck('user','menu_icon'));

        $fields['Skip Execution Messages'] = array('name'=>'auto_close',
            'type'=>'checkbox',
            'checked'=>$this->um->getParamCheck('user','auto_close'),
            'title'=>'Do not display command execution status in case of success.');

        if ($this->um->pmode)
        {
            $fields['Show Public Bookmarks'] = array('name'=>'show_public',
                'type'=>'checkbox',
                'checked'=>($this->um->showPublic()?null:''),
                'title'=>'Shows bookmarks published by other users.');
        }

        return $fields;
    }

    function _buildKeepInCache($select=null)
    {
        $expiration = array
        (
            'No caching' =>0,
            '12 hours' =>60*60*12,
            '6 days'   =>60*60*24*6,
            '1 month'  =>60*60*24*30,
        );

        foreach ($expiration as $label => $value)
        {
            echo '<option '. ($select==$value?'selected':'') .
                 ' value="' . $value. '">' . T($label). "</option>\n";
        }
    }

    function _buildPasteModeSetting($select=null)
    {
        $modes = array
        (
            'ask'  => T('Ask'),
            'copy' => T('Copy'),
            'move' => T('Move or Copy'),
        );

        foreach ($modes as $mode => $label)
        {
            echo '<option '. ($select==$mode?'selected':'') .
                 ' value="' . $mode . '">' . $label . "</option>\n";
        }
    }

    function _buildSearchPrefix($select=null)
    {
        $modes = array
        (
            'name' => T('Name'),
            'url'  => T('URL'),
            'desc' => T('Description'),
            'all'  => T('All'),
        );

        foreach ($modes as $mode => $label)
        {
            echo '<option '. ($select==$mode?'selected':'') .
                 ' value="' . $mode . '">' . $label . "</option>\n";
        }
    }

    function _buildMixMode($select=null)
    {
        $modes = array
        (
            'nodes' => T('Folders first'),
            'links' => T('Links first'),
        );

        foreach ($modes as $mode => $label)
        {
            echo '<option '. ($select==$mode?'selected':'') .
                 ' value="' . $mode . '">' . $label . "</option>\n";
        }
    }

    function _buildLinkSortMode($select=null)
    {
        $modes = array
        (
            'abc'     => T('Alphabetically'),
            'changed' => T('New/changed links first'),
        );

        if ($this->um->getParam('config','use_hit_counter'))
        {
            $modes['hits']  = T('Most clicked first');
            $modes['visit'] = T('Waiting for visit');
        }

        foreach ($modes as $mode => $label)
        {
            echo '<option '. ($select==$mode?'selected':'') .
                 ' value="' . $mode . '">' . $label . "</option>\n";
        }
    }

    function commandUserSettings()
    {

        $checks = array
        (
            'auto_close',
            'extern_commander',
            'menu_icon',
            'show_acl',
            'use_favicons',
            'use_hiding',
        );

        if ($this->um->getParam('config','use_outbound_connection'))
        {
            $checks[] = 'auto_retrieve_favicon';
        }

        $values = array
        (
            'default_search',
            'link_sort_mode',
            'mix_mode',
            'lang',
            'paste_mode',
            'skin',
            'keep_in_cache',
        );

        foreach ($checks as $check)
        {
            $this->um->setParam('user',$check, reqVal($check)?1:0);
        }
        foreach ($values as $check)
        {
            $this->um->setParam('user',$check, reqVal($check));
        }

        if (!$this->um->pmode)
        {
            $this->um->setParam('user','allow_given_membership', reqVal('allow_given_membership'));
            $this->um->setParam('user','allow_info_mails', reqVal('allow_info_mails'));
        }
        else
        {
            $gid = $this->um->config['gid_everyone'];

            if (reqVal('show_public') && !$this->um->showPublic())
            {
                // Add to Everyone group
                $this->um->addMember($gid,$this->um->uid);
            }
            else if (!reqVal('show_public') && $this->um->showPublic())
            {
                // Remove from Everyone group
                $this->um->removeMember($gid,$this->um->uid);
            }
        }

        if ($this->hasErrors())
        {
            $this->goBack();
            return;
        }

        // Have the behaviour immediately
        $this->reload = !$this->um->getParam('user','extern_commander');
        $this->close = $this->um->getParam('user','auto_close');

        $this->um->saveUserParams();
    }

    function buildPersonalData()
    {
        $fields = array();

        $fields['E-mail'] = array('name'=>'email', 'value'=>$this->um->email);
        $fields['Old Password'] = array('name'=>'pass','type'=>'password');
        $fields['Password'] = array('name'=>'pass1','type'=>'password');
        $fields['Repeat Password'] = array('name'=>'pass2','type'=>'password');
        $fields['Real Name'] = array('name'=>'realname','value'=>$this->um->name);
        $fields['Comment'] = array('name'=>'comment','value'=>$this->um->comment);

        if ($this->um->demo)
        {
            foreach ($fields as $name => $field)
            {
                $fields[$name]['disabled'] = null;
            }
        }

        return $fields;
    }

    function commandPersonalData()
    {
        if (reqVal('pass1') != reqVal('pass2'))
        {
            $this->error('The password was not repeated correctly!');
        }

        $mfields = array('email','realname');

        // When changing password or email, old password must be specified
        if (reqVal('pass1') || (reqVal('email') != $this->um->email))
        {
            $mfields[] = 'pass';

            if (strlen(reqVal('pass')) && !$this->um->checkPassword($this->um->uid,reqVal('pass')))
            {
                $this->error('Old password is invalid!');
            }
        }

        $this->checkMandatoryFields($mfields);

        if ($this->hasErrors())
        {
            $this->goBack();
            return;
        }

        $this->um->personalData(
            reqVal('email'),
            ($this->um->demo?null:reqVal('pass1')),
            reqVal('realname'),
            reqVal('comment'));

        $this->forwardCommand("User Settings");
    }

/******************************************************************************/

    function buildMaintainGroups()
    {
        $fields = array();
        $fields['Create Group'] = array('type'=>'button');
        $fields['Select Group'] = array('name'=>'command_gid','type'=>'select','_options'=>'_buildGroupList');
        $fields['Group Properties'] = array('type'=>'button');
        $fields['Group Members'] = array('type'=>'button');
        $fields['Group Moderators'] = array('type'=>'button');
        $fields['Delete Group'] = array('type'=>'button');
        $fields['Send Email to Members'] = array('type'=>'button');
        $fields['Send Email to Moderators'] = array('type'=>'button');
        return $fields;
    }

    function buildGroupProperties()
    {
        $fields = $this->buildCreateGroup();

        $group = $this->um->getGroup(reqVal('command_gid'));
        foreach ($fields as $name => $params)
        {
            if ($params['name']
            &&  $params['name']!='allow_addself'
            &&  $params['name']!='allow_contact'
            &&  isset($group[$params['name']]))
            {
                $fields[$name]['value'] = $group[$params['name']];
            }
        }
        if ($group['allow_addself'])
        {
            $fields['Allow Self Add']['checked'] = null;
        }
        if ($group['allow_contact'])
        {
            $fields['Allow Contact']['checked'] = null;
        }
        $fields['-hidden1-'] = array('name'=>'command_gid','value'=>$group['gid']);

        return $fields;
    }

    function commandGroupProperties()
    {
        // Regexp check
        if (reqChk('auto_join'))
        {
            $autoJoin = reqVal('auto_join');
            if ($autoJoin{0} != '/')
            {
                $autoJoin = '/'.$autoJoin.'/i';
            }

            // Would be caugth by error handler
            preg_match($autoJoin, '');

            if ($this->hasErrors())
            {
                $this->error("Invalid regular expresssion!");
                $this->goBack();
                return;
            }
        }

        $this->um->updateGroup(reqVal('command_gid'), reqVal('name'), reqVal('comment'),
            reqVal('allow_addself')?1:0, reqVal('allow_contact')?1:0, reqVal('auto_join'));

        $this->forwardCommand("Maintain Groups");
    }

    function buildGroupMembers()
    {
        $fields = array();
        $group = $this->um->getGroup(reqVal('command_gid'));
        $members    = $this->um->getMembers(reqVal('command_gid'));
        $moderators = $this->um->getMembers(reqVal('command_gid'), true);

        $fields['Group Name'] = array('name'=>'name','value'=>$group['name'],'disabled'=>null);
        $fields['-hidden1-'] = array('name'=>'command_gid','value'=>$group['gid']);

        $fields['-raw1-'] = "<table cellpadding='0'>";

        foreach ($this->um->getUsers() as $uid => $rec)
        {
            $label = $rec['email'];
            $fields[$label] = array
            (
                'type'=>'callback',
                'function'=>'_buildUserCheck',
                'params'=>array('name'=>$uid,'email'=>$rec['cryptmail'],'realname'=>$rec['name']),
            );

            if (in_array($uid, array_keys($members)))
            {
                $fields[$label]['params']['checked'] = true;

                if (in_array($uid, array_keys($moderators)))
                {
                    $fields[$label]['params']['disabled'] = true;
                }
            }
            else
            {
                // Member is not in the group and does not want to be added
                if (strstr($rec['params'],'allow_given_membership=;'))
                {
                    array_pop($fields); // Hidden value
                }
            }
        }

        $fields['-raw2-'] = "</table>";
        return $fields;
    }

    function commandGroupMembers()
    {
        $group = $this->um->getGroup(reqVal('command_gid'));
        $members    = $this->um->getMembers(reqVal('command_gid'));
        $moderators = $this->um->getMembers(reqVal('command_gid'), true);

        foreach ($this->um->getUsers() as $uid => $rec)
        {
            if (!reqChk($uid))
            {
                // Skip moderators - they cannot be unchecked
                if (in_array($uid, array_keys($moderators)))
                {
                    continue;
                }

                if (in_array($uid, array_keys($members)))
                {
                    $this->um->removeMember(reqVal('command_gid'),$uid);
                }
            }
            else
            {
                // Member already in
                if (in_array($uid, array_keys($members)))
                {
                    continue;
                }
                // Member is not in the group and does not want to be added
                if (strstr($rec['params'],'allow_given_membership=;'))
                {
                    $this->error('Access denied!');
                    return;
                }

                $this->um->addMember(reqVal('command_gid'),$uid);
            }
        }

        $this->forwardCommand("Maintain Groups");
    }

    function buildGroupModerators()
    {
        $fields = array();
        $group = $this->um->getGroup(reqVal('command_gid'));
        $members    = $this->um->getMembers(reqVal('command_gid'));
        $moderators = $this->um->getMembers(reqVal('command_gid'),true);

        $fields['Group Name'] = array('name'=>'name','value'=>$group['name'],'disabled'=>null);
        $fields['-hidden1-'] = array('name'=>'command_gid','value'=>$group['gid']);
        $fields['-raw1-'] = "<table cellpadding='0'>";

        foreach ($members as $uid => $rec)
        {
            $label = $rec['email'];
            $fields[$label] = array
            (
                'name'=>$uid,
                'type'=>'callback',
                'function'=>'_buildUserCheck',
                'params'=>array('name'=>$uid,'email'=>$rec['cryptmail'],'realname'=>$rec['name']),
            );

            if (in_array($uid, array_keys($moderators)))
            {
                $fields[$label]['params']['checked'] = true;
            }
        }

        $fields['-raw2-'] = "</table>";
        return $fields;
    }

    function commandGroupModerators()
    {
        $group = $this->um->getGroup(reqVal('command_gid'));
        $members    = $this->um->getMembers(reqVal('command_gid'));
        $moderators = $this->um->getMembers(reqVal('command_gid'), true);

        foreach ($members as $uid => $rec)
        {
            if (!reqChk($uid) && in_array($uid, array_keys($moderators)))
            {
                $this->um->updateMember(reqVal('command_gid'),$uid,false);
            }
            else if (reqChk($uid))
            {
                $this->um->updateMember(reqVal('command_gid'),$uid,true);
            }
        }

        // We might have deleted all moderators what would be fatal and
        // require manual change in database, therefore we make this one
        // as the only moderator.
        $moderators = $this->um->getMembers(reqVal('command_gid'),true);
        if (!count($moderators))
        {
            $this->um->updateMember(reqVal('command_gid'),$this->um->uid,true);
            $this->error('Cannot remove all moderators from a group!');
            $this->error('You have been left as group moderator.');
        }

        $this->forwardCommand("Maintain Groups");
    }

    function buildDeleteGroup()
    {
        $fields = array();
        if (reqVal('command_gid') == $this->um->config['gid_admins'])
        {
            $this->error("Cannot delete built in group!");
            return $fields;
        }

        $group = $this->um->getGroup(reqVal('command_gid'));
        $fields['Group Name'] = array('name'=>'name','value'=>$group['name'],"disabled"=>null);
        $fields['Comment'] = array('name'=>'comment','value'=>$group['comment'],"disabled"=>null);
        $fields['-hidden1-'] = array('name'=>'command_gid','value'=>$group['gid']);
        return $fields;
    }

    function commandDeleteGroup()
    {
        $this->um->removeGroup(reqVal('command_gid'));

        $this->forwardCommand("Maintain Groups");
    }

    function buildCreateGroup()
    {
        $fields = array();
        $fields['Group Name'] = array('name'=>'name');
        $fields['Comment'] = array('name'=>'comment');
        $fields['Auto Join E-Mail RegExp'] = array('name'=>'auto_join');
        if ($this->command == 'Create Group')
        {
            $fields['Moderator'] = array('name'=>'uid','type'=>'select',
                '_options'=>'_buildUserList');
        }
        $fields['Allow Self Add'] = array('name'=>'allow_addself','type'=>'checkbox');
        $fields['Allow Contact'] = array('name'=>'allow_contact','type'=>'checkbox');
        return $fields;
    }

    function commandCreateGroup()
    {
        $this->um->addGroup(reqVal('name'), reqVal('comment'), reqVal('uid'),
            reqVal('allow_addself')?1:0, reqVal('allow_contact')?1:0, reqVal('auto_join'));

        $this->forwardCommand("Maintain Groups");
    }

/******************************************************************************/

    function buildMembership()
    {
        $fields = array();
        $mygroups = $this->um->getUserGroups($this->um->uid);

        foreach ($this->um->getGroups() as $gid => $rec)
        {
            $isMyGroup = in_array($gid, array_keys($mygroups));
            $canJoin = $this->um->canJoinGroup($rec);

            if ($isMyGroup || $canJoin || $rec['allow_contact'])
            {
                $name = $rec['name'];

                if (!$isMyGroup && $rec['allow_contact'])
                {
                    $name .= ' [<a href="?command=Contact%20Moderator&amp;gid=' . $gid . '">Contact</a>]';
                }

                $fields[$name] =  array('name'=>'gid_'.$gid,'type'=>'checkbox','_raw'=>1);

                if ($isMyGroup)
                {
                    $fields[$name]['checked'] = null;
                }

                $isModerator = isset($mygroups[$gid]) && $mygroups[$gid]['moderator'];
                if ((!$canJoin && !$isMyGroup) || $isModerator)
                {
                    $fields[$name]['disabled'] = null;
                }
            }
        }
        return $fields;
    }

    function commandMembership()
    {
        $mygroups = $this->um->getUserGroups($this->um->uid);

        foreach ($this->um->getGroups() as $gid => $rec)
        {
            $isMyGroup = in_array($gid, array_keys($mygroups));
            $canJoin = $this->um->canJoinGroup($rec);
            $checked = reqVal('gid_'.$gid)==1;
            $isModerator = isset($mygroups[$gid]) && $mygroups[$gid]['moderator'];

            if ($isMyGroup && !$checked && !$isModerator)
            {
                $this->um->removeMember($gid,$this->um->uid);
            }
            else if (!$isMyGroup && $checked)
            {
                if ($canJoin)
                {
                    $this->um->addMember($gid,$this->um->uid);
                }
                else
                {
                    $this->error('Access denied!');
                }
            }
        }
    }

    function buildContactModerator()
    {
        return $this->_buildSendEmail();
    }

    function commandContactModerator()
    {
        $group = $this->um->getGroup(reqVal('gid'));
        $comment = reqVal('message');

        if (!$comment || !$group)
        {
            return;
        }

        if (!$group['allow_contact'])
        {
            $this->error('Access denied!');
            return;
        }

        $moderators = $this->um->getMembers(reqVal('gid'), true);
        foreach ($moderators as $uid => $user)
        {
            $this->um->explodeParams($user['params'], 'tmp');
            SetLanguage($this->um->getParam('tmp','lang'));
            $msg = P('command::contact_group',array($group['name'],$comment,Page::baseurl()));
            $this->um->sendMail($user, T("SiteBar: Contact Group Moderator"), $msg,
                $this->um->name, $this->um->email);
        }
        SetLanguage($this->um->getParam('user','lang'));
    }

    function buildSendEmailtoMembers()
    {
        $group = $this->um->getGroup(reqVal('command_gid'));
        $fields['Group Name'] = array('name'=>'name','value'=>$group['name'],'disabled'=>null);
        $fields['-hidden1-'] = array('name'=>'command_gid','value'=>$group['gid']);
        return array_merge($fields,$this->_buildSendEmail(null, true));
    }

    function buildSendEmailtoModerators()
    {
        $group = $this->um->getGroup(reqVal('command_gid'));
        $fields['Group Name'] = array('name'=>'name','value'=>$group['name'],'disabled'=>null);
        $fields['-hidden1-'] = array('name'=>'command_gid','value'=>$group['gid']);
        return array_merge($fields,$this->_buildSendEmail(null, true));
    }

    function buildSendEmailtoUser()
    {
        if (!reqChk('uid'))
        {
            $this->error('No user was selected!');
            return null;
        }

        $fields = array();
        $user = $this->um->getUser(reqVal('uid'));

        $fields['E-mail'] = array('name'=>'email', 'value'=>$user['email'], 'disabled' => null);
        $fields['Real Name'] = array('name'=>'realname', 'value'=>$user['name'], 'disabled' => null);

        $fields['-hidden1-'] = array('name'=>'uid', 'value'=>reqVal('uid'));
        $fields = array_merge($fields,$this->_buildSendEmail());

        return $fields;
    }

    function buildSendEmailtoAll()
    {
        return $this->_buildSendEmail(null, true);
    }

    function commandSendEmailtoMembers()
    {
        $group = $this->um->getGroup(reqVal('command_gid'));
        $to = $this->um->getMembers($group['gid']);
        $this->_commandSendEmail($to, 'SiteBar: Email to Group Members', $group['name']);
    }

    function commandSendEmailtoModerators()
    {
        $group = $this->um->getGroup(reqVal('command_gid'));
        $to = $this->um->getMembers($group['gid'], true);
        $this->_commandSendEmail($to, 'SiteBar: Email to Group Moderators', $group['name']);
    }

    function commandSendEmailtoUser()
    {
        $to = array($this->um->getUser(reqVal('uid', true)));
        $this->_commandSendEmail($to, 'SiteBar: Email to SiteBar User');
    }

    function commandSendEmailtoAll()
    {
        $to = $this->um->getUsers();
        $this->_commandSendEmail($to, 'SiteBar: Email to all SiteBar Users');
    }

/******************************************************************************/

    function _buildFolderSortMode($select=null)
    {
        $modes = array
        (
            'user'    => T('User default'),
            'custom'  => T('Custom order'),
            'abc'     => T('Alphabetically'),
            'changed' => T('New/changed links first'),
        );

        if ($this->um->getParam('config','use_hit_counter'))
        {
            $modes['hits']  = T('Most clicked first');
            $modes['visit'] = T('Waiting for visit');
        }

        foreach ($modes as $mode => $label)
        {
            echo '<option '. ($select==$mode?'selected':'') .
                 ' value="' . $mode . '">' . $label . "</option>\n";
        }
    }

    function buildAddFolder()
    {
        $fields = array();
        $node = $this->tree->getNode(reqVal('nid_acl',true));
        if (!$node) return null;

        if ($this->command == 'Add Folder')
        {
            $fields['Parent Folder'] = array('name'=>'parent','value'=>$node->name, 'disabled'=>null);
        }

        $fields['Folder Name'] = array('name'=>'name','maxlength'=>255);
        $fields['Sort Mode'] = array('name'=>'sort_mode','type'=>'select',
                '_options'=>'_buildFolderSortMode', '_select'=>$node->sort_mode);

        $fields['Description'] = array('name'=>'comment', 'type'=>'textarea');
        $fields['-hidden1-'] = array('name'=>'nid_acl','value'=>$node->id);

        if ($this->command == 'Folder Properties'
        && $node->id_parent==0
        && $this->um->isAdmin()
        && !$this->um->pmode)
        {
            $uid = $this->tree->getRootOwner($node->id);
            $fields['Tree Owner'] = array('name'=>'uid','type'=>'select',
                '_options'=>'_buildUserList', '_select'=> $uid);
        }

        if ($this->command != 'Add Folder')
        {
            $fields['Folder Name']['value'] = $node->name;
            $fields['Description']['value'] = $node->comment;
        }

        if ($this->um->pmode)
        {
            $inherited = false;
            $acl = false;

            if ($this->command == 'Folder Properties')
            {
                $inherited = $node->isPublishedByParent();
                $acl = $node->getGroupACL($this->um->config['gid_everyone']);
            }

            $fields['Publish Folder'] = array
            (
                'name'=>'publish',
                'type'=>'checkbox',
                'checked'=>((($acl&&$acl['allow_select'])||(!$acl&&$inherited))?null:''),
            );

            if ($inherited)
            {
                $fields['Publish Folder']['disabled'] = null;
            }
        }

        return $fields;
    }

    function commandAddFolder()
    {
        $nid = $this->tree->addNode(reqVal('nid_acl'),reqVal('name'),
            reqVal('comment'), reqVal('sort_mode'));

        if ($this->um->pmode && !$this->hasErrors())
        {
            $node = $this->tree->getNode($nid);
            $node->publishFolder(reqVal('publish'));
        }
    }

/******************************************************************************/

    function buildHideFolder()
    {
        $this->skipBuild = true;
        $this->reload = !$this->um->getParam('user','extern_commander');
        $this->close = $this->um->getParam('user','auto_close');
        $this->um->hiddenFolders[reqVal('nid_acl')] = 1;
        $this->um->setParam('user','hidden_folders', implode(':',array_keys($this->um->hiddenFolders)));
        $this->um->saveUserParams();
    }

    function buildUnhideSubfolders()
    {
        $this->skipBuild = true;
        $this->reload = !$this->um->getParam('user','extern_commander');
        $this->close = $this->um->getParam('user','auto_close');

        $parent = $this->tree->getNode(reqVal('nid_acl'));

        $this->tree->loadNodes($parent, false, 'select', true);

        foreach ($parent->getNodes() as $node)
        {
            if (isset($this->um->hiddenFolders[$node->id]))
            {
                unset($this->um->hiddenFolders[$node->id]);
            }
        }

        $this->um->setParam('user','hidden_folders', implode(':',array_keys($this->um->hiddenFolders)));
        $this->um->saveUserParams();
    }

/******************************************************************************/

    function buildFolderProperties()
    {
        $fields = $this->buildAddFolder();
        $fields['Custom Order'] = array('type'=>'addbutton');
        $fields['Import Bookmarks'] = array('type'=>'addbutton');
        $fields['Export Bookmarks'] = array('type'=>'addbutton');
        $fields['Validate Links'] = array('type'=>'addbutton');
        $fields['Security'] = array('type'=>'addbutton');
        return $fields;
    }

    function commandFolderProperties()
    {
        $nid = reqVal('nid_acl');

        $columns = array
        (
            'name' => reqVal('name'),
            'sort_mode' => reqVal('sort_mode'),
            'comment'=> reqVal('comment'),
        );

        $this->tree->updateNode( $nid, $columns);

        if (reqVal('uid'))
        {
            $this->tree->updateNodeOwner( $nid, reqVal('uid'));
        }

        if ($this->um->pmode && !$this->hasErrors())
        {
            $node = $this->tree->getNode($nid);
            $node->publishFolder(reqVal('publish'));
        }
    }

    function buildCustomOrder()
    {
        $node = $this->tree->getNode(reqVal('nid_acl', true));
        $this->tree->loadNodes($node);

        $fields['-raw1-'] = "<table cellpadding='0'>";

        foreach ($node->getChildren() as $child)
        {
            $label = $child->name;
            $fields[$label] = array
            (
                'type'=>'callback',
                'function'=>'_buildFolderOrder',
                'params'=>array('name'=>$child->name,'id'=>$child->type_flag.$child->id,'order'=>$child->order),
            );
        }

        $fields['-raw2-'] = "</table>";

        $fields['-hidden1-'] = array('name'=>'nid_acl','value'=>$node->id);

        return $fields;
    }

    function commandCustomOrder()
    {
        $node = $this->tree->getNode(reqVal('nid_acl', true));
        $this->tree->loadNodes($node);

        $order = array();

        foreach ($node->getChildren() as $child)
        {
            $id = $child->type_flag.$child->id;
            $order[] = $id.'~'.intval(reqVal('id'.$id));
        }

        $columns = array
        (
            'custom_order' => implode(':',$order),
            'sort_mode' => 'custom',
        );

        $this->tree->updateNode($node->id, $columns);
        $this->forwardCommand('Folder Properties');
    }

/******************************************************************************/

    function buildDeleteFolder()
    {
        $fields = $this->buildDeleteTree();
        $fields['Delete Content Only'] = array('name'=>'content','type'=>'checkbox');
        return $fields;
    }

    function commandDeleteFolder()
    {
        $this->tree->removeNode(reqVal('nid_acl'), reqVal('content'));
    }

/******************************************************************************/

    function buildPurgeFolder()
    {
        return $this->buildDeleteTree();
    }

    function commandPurgeFolder()
    {
        $this->tree->purgeNode(reqVal('nid_acl'));
    }

/******************************************************************************/

    function buildUndelete()
    {
        return $this->buildDeleteTree();
    }

    function commandUndelete()
    {
        $this->tree->undeleteNode(reqVal('nid_acl'));
    }

/******************************************************************************/

    function _buildPasteMode($params)
    {
?>
    <div class='label'><?php echo T('Paste Mode')?></div>
    <input value='Copy' type='radio' name='mode' <?php echo $params['canMove']?'':'checked'?>><?php echo T('Copy (Keep Source)')?><br>
    <input value='Move' type='radio' name='mode' <?php echo $params['canMove']?'checked':'disabled'?>><?php echo T('Move (Delete Source)')?><br>
<?php
    }

    function buildPaste()
    {
        $fields = array();
        $sourceId   = reqVal('sid',true);
        $sourceIsNode = reqVal('stype',true);
        $sourceObj  = null;
        $targetID = reqVal('nid_acl',true);
        $targetNode = $this->tree->getNode($targetID);
        $sourceNodeId = $sourceId;

        if ($sourceIsNode)
        {
            $sourceObj = $this->tree->getNode($sourceId);
            if (!$this->um->isAuthorized('Copy', false, null, $sourceId))
            {
                $this->error('Access denied!');
                return;
            }

            $parents = $this->tree->getParentNodes($targetID);

            if (in_array($sourceId, $parents))
            {
                $this->warn('Cannot move folder to its subfolder!');
                return;
            }
        }
        else
        {
            $sourceObj = $this->tree->getLink($sourceId);
            $sourceNodeId = $sourceObj->id_parent;

            if (!$this->um->isAuthorized('Copy Link', false, null, null, $sourceId))
            {
                $this->error('Access denied!');
                return;
            }

            if ($sourceObj->id_parent == $targetNode->id)
            {
                $this->warn('Link already is in the target folder!');
                return;
            }
        }

        $canMove = $this->um->canMove($sourceNodeId,$targetNode->id,$sourceIsNode);

        if ($this->um->getParam('user','paste_mode')!='ask')
        {
            $this->skipBuild = true;
            $this->reload = !$this->um->getParam('user','extern_commander');
            $this->close = $this->um->getParam('user','auto_close');

            $move = $canMove && $this->um->getParam('user','paste_mode')=='move';
            $this->executePaste($targetNode->id, $sourceId, $sourceIsNode, $move);
            return;
        }

        $fields[$sourceIsNode?T('Source Folder Name'):T('Source Link Name')] =
            array('name'=>'sidname', 'value'=>$sourceObj->name, 'disabled' => null);
        $fields['Target Folder Name'] =
            array('name'=>'tidname', 'value'=>$targetNode->name, 'disabled' => null);

        if ($sourceIsNode)
        {
            $fields['Content Only'] = array('name'=>'content','type'=>'checkbox');
        }

        $fields['Mode'] = array('type'=>'callback', 'function'=>'_buildPasteMode',
            'params'=>array('canMove'=>$canMove));

        $fields['-hidden1-'] = array('name'=>'nid_acl','value'=>$targetNode->id);
        $fields['-hidden2-'] = array('name'=>'sid','value'=>$sourceId);
        $fields['-hidden3-'] = array('name'=>'stype','value'=>$sourceIsNode);

        return $fields;
    }

    function commandPaste()
    {
        $targetID = reqVal('nid_acl');
        $sourceId   = reqVal('sid',true);
        $sourceIsNode = reqVal('stype',true);
        $move = reqVal('mode',true)=='Move';

        $this->executePaste($targetID, $sourceId, $sourceIsNode, $move, reqVal('content'));
    }

    function executePaste($targetID, $sourceId, $sourceIsNode, $move, $contentOnly=false)
    {
        $targetNode = $this->tree->getNode($targetID);
        $sourceObj  = null;

        if ($sourceIsNode)
        {
            $sourceObj = $this->tree->getNode($sourceId);
            if (!$this->um->isAuthorized('Copy', false, null, $sourceId) ||
                ($move && !$this->um->canMove($sourceId, $targetNode->id, true)))
            {
                $this->error('Access denied!');
                return;
            }

            if ($move)
            {
                $this->tree->moveNode( $sourceId, $targetNode->id, $contentOnly);
            }
            else
            {
                $this->tree->copyNode( $sourceId, $targetNode->id, $contentOnly);
            }
        }
        else
        {
            $sourceObj = $this->tree->getLink($sourceId);
            if (!$this->um->isAuthorized('Copy Link', false, null, null, $sourceId) ||
                ($move && !$this->um->canMove($sourceObj->id_parent, $targetNode->id, false)))
            {
                $this->error('Access denied!');
                return;
            }

            if ($move)
            {
                $this->tree->moveLink( $sourceId, $targetNode->id);
            }
            else
            {
                $this->tree->copyLink( $sourceId, $targetNode->id);
            }
        }
    }


/******************************************************************************/

    function buildEmailLink()
    {
        $fields = array();
        $link = $this->tree->getLink(reqVal('lid_acl'));
        if (!$link) return null;

        if ($this->um->canUseMail())
        {
            $fields['From'] = array('name'=>'from',
                'value'=> $this->um->email, 'disabled' => null);
            $fields['To'] =
                array('name'=>'to');

            $fields['Link Name'] = array('name'=>'name','value'=>$link->name,'disabled'=>null);
            $fields['URL']       = array('name'=>'url','value'=>$link->url,'disabled'=>null);
            $fields['Description'] = array('name'=>'comment','type'=>'textarea','value'=>$link->comment);
            $fields['-hidden1-'] = array('name'=>'lid_acl','value'=>$link->id);
        }

        $fields['-raw1-'] = P('command::email_link_href',
            array(htmlspecialchars($link->name),htmlspecialchars($link->url),Page::baseurl()));
        return $fields;
    }

    function commandEmailLink()
    {
        $link = $this->tree->getLink(reqVal('lid_acl'));
        if (!$link) return null;

        $this->checkMandatoryFields(array('to'));

        if ($this->hasErrors())
        {
            $this->goBack();
            return;
        }

        $subject = T('SiteBar: Web site') . ' ' . $link->name;

        $msg = P('command::email_link',array($link->name, $link->url, reqChk('comment'), Page::baseurl()));
        $this->um->sendMail(array('email'=>reqVal('to')), $subject, $msg, $this->um->name, $this->um->email);
    }

/******************************************************************************/

    function _buildAddLinkNode($node, $level)
    {
        foreach ($node->getChildren() as $childNode)
        {
            if ($childNode->type_flag!='n')
            {
                continue;
            }
            echo '<option class="fldList'.$level.'" '.(!$childNode->hasRight('insert')?'class="noinsert"':'').
                 ($childNode->id==$this->um->getParam('user','default_folder')?' selected ':'').
                 ' value='.$childNode->id.'>'.
                 str_repeat('&nbsp;&nbsp;&nbsp;',$level) . $childNode->name,
                 '</option>';
            $this->_buildAddLinkNode($childNode, $level+1);
        }
    }

    function _buildAddLink($params)
    {
?>
        <select class="fldList" name='nid_acl'>
<?php
        foreach ($this->tree->loadRoots($this->um->uid) as $root)
        {
            echo '<option class="'. ($root->hasRight('insert')?'fldList':'noinsert') .'" '.
                 ($root->id==$this->um->getParam('user','default_folder')?' selected ':'').
                 ' value='.$root->id.'>['.$root->name.']</option>';

            // Load just folders
            $this->tree->loadNodes($root, false, 'insert', true);
            $this->_buildAddLinkNode($root, 1);
        }
?>
        </select>
<?php
    }

    /* Retrieve available link information.
     * Currently this only retrieves the
     * favicon.
     */
    function buildRetrieveLinkInformation()
    {
        $this->command = reqVal('origin');
        $execute = 'build'.str_replace(' ','',$this->command);
        $this->getInfo = true;
        $fields = $this->$execute();

        if ($this->hasErrors(E_WARNING) && !$this->hasErrors(E_ERROR))
        {
            $this->showWithErrors = true;
        }

        return $fields;
    }

    function buildAddLink()
    {
        $fields = array();
        $node = null;
        $favicon = '';
        $name = reqVal('name');

        if (reqChk('nid_acl') && reqVal('bookmarklet')!=1)
        {
            $node = $this->tree->getNode(reqVal('nid_acl'));
            $fields['-hidden0-'] = array('name'=>'nid_acl','value'=>$node->id);
            $fields['Parent Folder'] = array('name'=>'parent',
                'value'=>$node->name,'disabled'=>null);
            if (!$node) return null;
        }
        else
        {
            $this->bookmarklet = true;

            if (reqVal('bookmarklet')!=1)
            {
                $cp = reqVal('cp');

                // If we have empty cp or undefined use iso
                if (!strlen($cp) || $cp=='undefined')
                {
                    $cp = "iso-8859-1";
                }

                require_once('./inc/converter.inc.php');
                $c = new Converter($this->um->getParam('config','use_conv_engine'),$cp);
                $name = $c->utf8RawUrlDecode($name);
            }

            if ($this->um->isAnonymous())
            {
                $this->command = 'Log In';
                $fields = $this->buildLogIn();
                $fields['-hidden0-'] = array('name'=>'bookmarklet','value'=>1);
                $fields['-hidden1-'] = array('name'=>'name','value'=>$name);
                $fields['-hidden2-'] = array('name'=>'url','value'=>reqVal('url'));
                $fields['-hidden3-'] = array('name'=>'cp','value'=>reqVal('cp'));
                return $fields;
            }

            $fields['Parent Folder'] = array('type'=>'callback','function'=>'_buildAddLink');
            $fields['Create New Sub Folder'] = array('name'=>'newfolder');
            $fields['Remember as Default'] = array('name'=>'default_folder','type'=>'checkbox');

            if (strlen($this->um->getParam('user','default_folder')))
            {
                $fields['Remember as Default']['checked'] = null;
            }

            $fields['-hidden0-'] = array('name'=>'bookmarklet','value'=>1);
            $this->nobuttons = true;

            // If we want to get favicon on submit, then do it better now
            // we will get more information.
            if ($this->um->getParam('user','auto_retrieve_favicon'))
            {
                $this->getInfo = true;
            }
        }

        $fields['URL'] =
            array('name'=>'url','value'=>(reqChk('url')?reqVal('url'):'http://'));

        if (!$this->getInfo)
        {
            if ($this->um->getParam('config','use_outbound_connection'))
            {
                /* Show the "Retrieve Info" button only in case it has not been yet
                 * performed
                 */
                $fields['Retrieve Link Information'] = array('type'=>'addbutton');
            }

            $fields['-hidden4-'] = array('name'=>'origin','value'=>$this->command);

            $fields['Link Name'] = array('name'=>'name','value'=>$name,'maxlength'=>255);

            if ($this->um->getParam('user','use_favicons'))
            {
                $fields['Favicon'] = array('name'=>'favicon', 'value'=>$favicon);
            }
        }
        else
        {
            /* Try to get the title and favicon */
            require_once('./inc/pageparser.inc.php');
            $page = new PageParser( reqVal('url'));
            $page->getInformation(array('CHARSET', 'TITLE', 'FAVURL'));

            $cp = 'iso-8859-1';

            if (isset($page->info['CHARSET']))
            {
                $cp = $page->info['CHARSET'];
            }

            $fields['Link Name'] = array('name'=>'name','maxlength'=>255);

            if (!$name && isset($page->info['TITLE']))
            {
                require_once('./inc/converter.inc.php');
                $c = new Converter($this->um->getParam('config','use_conv_engine'),$cp);
                $name = $c->utf8RawUrlDecode($page->info['TITLE']);
            }

            $fields['Link Name']['value'] = $name;

            if ($this->um->getParam('user','use_favicons'))
            {
                $fields['Favicon'] = array('name'=>'favicon');

                if (isset($page->info['FAVURL']))
                {
                    $favicon = $page->info['FAVURL'];

                    /* Show the retrieved favicon. */
                    $wrong = Skin::imgsrc('link_wrong_favicon');
                    $fields['-raw2-'] = "<div><img class='favicon' alt='' src='$favicon' onerror='this.src=\"$wrong\"'></div>";
                }

                $fields['Favicon']['value'] = $favicon;
            }
        }

        $fields['Description'] = array('name'=>'comment', 'type'=>'textarea');

        // It is allowed to create private item in others tree
        $fields['Private'] = array('name'=>'private','type'=>'checkbox');

        if ($this->um->getParam('config','use_outbound_connection'))
        {
            $fields['Exclude From Validation'] = array('name'=>'novalidate','type'=>'checkbox');
        }

        if ($this->hasErrors(E_WARNING) && !$this->hasErrors(E_ERROR))
        {
            $this->showWithErrors = true;
        }

        return $fields;
    }

    function commandAddLink()
    {
        $nid = reqVal('nid_acl',true);
        $node = $this->tree->getNode($nid);
        if (!$node) return;

        if (reqChk('bookmarklet'))
        {
            if (strlen(reqVal('newfolder'))>0)
            {
                $newnode = $this->tree->addNode($nid, reqVal('newfolder'));
                if ($this->hasErrors())
                {
                    return;
                }
                $nid = $newnode;
            }
        }

        // Get values entered by the user
        $url = reqVal('url');
        $favicon = reqVal('favicon');
        $name = reqVal('name');

        // If we have bookmarklet we have already received the icon
        if (!reqChk('bookmarklet') && !$favicon && $this->um->getParam('user','auto_retrieve_favicon'))
        {
            require_once('./inc/pageparser.inc.php');
            $page = new PageParser( $url, array('FAVURL'));
            $page->getInformation(array('FAVURL'));

            if ($page->errorCode['FAVURL']<PP_ERR)
            {
                $favicon = $page->info['FAVURL'];
                $fiurl   = './favicon.php?' . md5($favicon) . '=' . reqVal('lid_acl');
                $this->message = T('Favicon <img src="%s"> found at url %s.', array($fiurl, $url));
            }
            else
            {
                $this->message = T('Favicon not found!');
            }
        }

        $insert = array
        (
            'name'=>$name,
            'url'=>$url,
            'favicon'=>$favicon,
            'private'=>reqVal('private'),
            'comment'=>reqVal('comment'),
            'validate'=>reqVal('novalidate')?0:1,
        );

        $this->tree->addLink($nid, $insert);

        if (reqChk('bookmarklet'))
        {
            $this->um->setParam('user','default_folder', reqChk('default_folder')?$nid:'');
            $this->um->saveUserParams();
            $this->bookmarklet = true;
            $this->nobuttons = true;
            $this->message =
                T("Link has been added.<p>You must reload your SiteBar in order to see added link!");
        }
    }

/******************************************************************************/

    function buildProperties()
    {
        $fields = array();
        $link = $this->tree->getLink(reqVal('lid_acl'));
        if (!$link) return null;

        $fields['URL'] = array('name'=>'url', 'value'=>$link->url);

        if (!$this->getInfo)
        {
            /* Show the "Retrieve Info" button only in case it has not been yet
             * performed
             */
            if ($this->command!="Delete Link")
            {
                $fields['Retrieve Link Information'] = array('type'=>'addbutton');
                $fields['-hidden4-'] = array('name'=>'origin','value'=>$this->command);
            }

            $fields['Link Name'] = array('name'=>'name','value'=>$link->name,'maxlength'=>255);

            if ($this->um->getParam('user','use_favicons'))
            {
                $fields['Favicon'] = array('name'=>'favicon', 'value'=>$link->favicon);

                if ($link->favicon)
                {
                    $fields['-raw2-'] = $this->_buildFavicon($link->id, $link->favicon);
                }
            }
        }
        else
        {
            $name = reqVal('name');
            $url = reqVal('url');
            $favicon = reqVal('favicon');

            /* Try to get the title and favicon */
            require_once('./inc/pageparser.inc.php');
            $page = new PageParser( $url );
            $page->getInformation(array('CHARSET', 'TITLE', 'FAVURL'));

            $cp = 'iso-8859-1';

            if ($page->errorCode['CHARSET']<PP_ERR)
            {
                $cp = $page->info['CHARSET'];
            }

            $fields['Link Name'] = array('name'=>'name', 'maxlength'=>255);

            if (!$name && isset($page->info['TITLE']))
            {
                require_once('./inc/converter.inc.php');
                $c = new Converter($this->um->getParam('config','use_conv_engine'),$cp);
                $name = $c->utf8RawUrlDecode($page->info['TITLE']);
            }

            if ($this->um->getParam('user','use_favicons'))
            {
                $fields['Favicon'] = array('name'=>'favicon');

                if (!$favicon && isset($page->info['FAVURL']))
                {
                    $favicon = $page->info['FAVURL'];
                    /* Show the retrieved favicon. */
                    $fields['-raw2-'] = $this->_buildFavicon(reqVal('lid_acl'), $favicon);
                }
            }

            $fields['URL']['value'] = $url;
            $fields['Link Name']['value'] = $name;
            $fields['Favicon']['value'] = $favicon;
        }

        $size = strlen($link->comment);
        $MAXSIZETOEDIT = 4096;

        if ($size<=$MAXSIZETOEDIT)
        {
            $fields['Description'] = array('name'=>'comment',
                'type'=>'textarea','value'=>$link->comment);
        }
        else
        {
            $fields['-raw1'] = T("Description too long for inplace editing, please use export feature!");
        }
        $fields['-hidden1-'] = array('name'=>'lid_acl','value'=>$link->id);

        if ($this->tree->inMyTree($link->id_parent))
        {
            $fields['Private'] = array('name'=>'private','type'=>'checkbox');
            if ($link->private)
            {
                $fields['Private']['checked'] = null;
            }
        }

        if ($link->is_dead)
        {
            $fields['Dead Link'] = array('name'=>'is_dead_check','type'=>'checkbox','checked'=>null);
            $fields['-hidden2-'] = array('name'=>'is_dead','type'=>'hidden','value'=>1);
        }

        if ($this->um->getParam('config','use_outbound_connection'))
        {
            $fields['Exclude From Validation'] = array('name'=>'novalidate','type'=>'checkbox');
            if (!$link->validate)
            {
                $fields['Exclude From Validation']['checked'] = null;
            }
        }

        if ($this->command!="Delete Link")
        {
            if (($this->um->getParam('config','comment_impex') && strlen($link->comment)>0)
            ||  strlen($link->comment)>=$MAXSIZETOEDIT)
            {
                $fields['Export Description'] = array('name'=>'command','type'=>'addbutton');
            }

            if ($this->um->getParam('config','comment_impex'))
            {
                $fields['Import Description'] = array('name'=>'command','type'=>'addbutton');
            }
        }

        return $fields;
    }

    function commandProperties()
    {
        if (reqVal('private'))
        {
            $link = $this->tree->getLink(reqVal('lid_acl'));
            if (!$link) return;
            if (!$this->tree->inMyTree($link->id_parent))
            {
                $this->error('Access denied!');
                return;
            }
        }

        $limit = $this->um->getParam('config','comment_limit');

        if ($limit && $limit<strlen(reqVal('comment')))
        {
            $this->error('The description length exceeds maximum length of %s bytes!', array($limit));
            return;
        }

        $update = array
        (
            'name'=>reqVal('name'),
            'url'=>reqVal('url'),
            'favicon'=>reqVal('favicon'),
            'private'=>reqVal('private')?1:0,
            'comment'=>reqVal('comment'),
            'validate'=>reqVal('novalidate')?0:1,
        );

        if (reqVal('is_dead') && !reqVal('is_dead_check'))
        {
            $update['is_dead'] = 0;
        }

        $this->tree->updateLink(reqVal('lid_acl', true), $update);

        if ($this->um->getParam('config','use_favicon_cache'))
        {
            require_once('./inc/faviconcache.inc.php');
            $fc = & FaviconCache::staticInstance();
            // Delete favicon from cache on update to allow new version
            $fc->purge(reqVal('lid_acl'));
        }
    }

    function buildExportDescription()
    {
        $fields['Decode Using'] = array('type'=>'callback', 'function'=>'_buildDecodeUsing');
        $fields['-hidden1-'] = array('name'=>'lid_acl','value'=>reqVal('lid_acl'));

        return $fields;
    }

    function _buildDecodeUsing($params)
    {
?>
        <div class='label'>Decode Using</div>
        <input value='base64' type='radio' name='type' checked>MIME Base64<br>
        <input value='text' type='radio' name='type'>No decoding<br>
<?php
    }

    function commandExportDescription()
    {
        $link = $this->tree->getLink(reqVal('lid_acl'));
        if (!strlen($link->comment))
        {
            $this->error("Cannot export empty description!");
        }

        if ($this->hasErrors())
        {
            return;
        }

        switch (reqVal('type'))
        {
            case 'base64':
                header("Content-type: application/octet-stream\n");
                header("Content-disposition: attachment; filename=\"" . $link->name . "\"\n");
                header("Content-transfer-encoding: binary\n");
                echo base64_decode($link->comment);
                break;

            case 'text':
                header("Content-type: text/plain\n");
                header("Content-disposition: attachment; filename=\"" . $link->name . "\"\n");
                header("Content-transfer-encoding: binary\n");
                echo $link->comment;
                break;
        }

        exit; // Really break program here
    }

    function buildImportDescription()
    {
        $fields['Description File'] = array('type'=>'file','name'=>'file');
        $fields['Encode Using'] = array('type'=>'callback', 'function'=>'_buildEncodeUsing');
        $fields['-hidden1-'] = array('name'=>'lid_acl','value'=>reqVal('lid_acl'));
        return $fields;
    }

    function _buildEncodeUsing($params)
    {
?>
        <div class='label'>Encode Using</div>
        <input value='base64' type='radio' name='type' checked>MIME Base64<br>
        <input value='text' type='radio' name='type'>No encoding<br>
<?php
    }

    function commandImportDescription()
    {
        if (!$this->checkFile('file'))
        {
            return;
        }
        $filename = $_FILES['file']['tmp_name'];
        $link = $this->tree->getLink(reqVal('lid_acl'));

        if ($this->hasErrors())
        {
            return;
        }

        $limit = $this->um->getParam('config','comment_limit');

        if ($limit && $limit<filesize($filename))
        {
            $this->error('The description length exceeds maximum length of %s bytes!', array($limit));
            return;
        }

        $size = filesize($filename);
        $handle = fopen($filename, 'rb');
        $file_content = fread($handle,$size);
        fclose($handle);

        // File might not exist when closed
        $this->useHandler(false);
        @unlink($filename);
        $this->useHandler(true);

        $comment = '';

        switch (reqVal('type'))
        {
            case 'base64':
                $comment = base64_encode($file_content);
                break;

            case 'text':
                $comment = $file_content;
                break;
        }

        $this->tree->updateLink($link->id, array( 'comment'=>$comment ));
    }

/******************************************************************************/

    function buildDeleteLink()
    {
        $fields = $this->buildProperties();

        foreach ($fields as $name => $value)
        {
            if (isset($fields[$name]['type']) && $fields[$name]['type']=='hidden')
            {
                continue;
            }

            if (isset($fields[$name]['name']) && !strstr($name,'-raw'))
            {
                $fields[$name]['disabled'] = null;
            }
        }

        return $fields;
    }

    function commandDeleteLink()
    {
        $this->tree->removeLink(reqVal('lid_acl'));
    }

/******************************************************************************/

    function buildSecurity()
    {
        $fields = array();
        $node = $this->tree->getNode(reqVal('nid_acl',true));

        $fields['Folder Name'] = array('name'=>'name','value'=>$node->name,'disabled'=>null);
        $fields['Security'] = array('type'=>'callback',
            'function'=>'_buildSecurityList', 'params'=>array('node'=>$node));
        $fields['-hidden1-'] = array('name'=>'nid_acl','value'=>$node->id);
        return $fields;
    }

    function _buildSecurityList($params)
    {
        $groups = $this->um->getGroups();
        $myGroups = $this->um->getUserGroups();
        $node = $params['node'];
?>
    <table cellpadding='0'>
        <tr>
            <th class='group'><?php echo T('Group')?></th>
            <th class='right'><?php echo T('R')?></th>
            <th class='right'><?php echo T('A')?></th>
            <th class='right'><?php echo T('M')?></th>
            <th class='right'><?php echo T('D')?></th>
            <th class='right'><?php echo T('P')?></th>
            <th class='right'><?php echo T('G')?></th>
        </tr>
<?php
        foreach ($groups as $gid => $rec)
        {
            $acl = $node->getGroupACL($gid);
            $parentACL = $node->getParentACL($gid);
            $isMyGroup = isset($myGroups[$gid]) || $this->um->canJoinGroup($rec);
            $isModerator = isset($myGroups[$gid]) && isset($myGroups[$gid]) && $myGroups[$gid]['moderator'];

            if (!$acl)
            {
                $acl = $parentACL;
            }

            $aclSum = 0;
            foreach ($this->tree->rights as $right)
            {
                $aclSum += $acl['allow_'.$right];
            }

            if (!$isMyGroup // It is not my group and I cannot join it freely
            &&  !($node->hasRight('grant') && $aclSum)) // I cannot modify it
            {
                continue;
            }

            $sumChange = 0;
            $sumDiff = 0;

            // Check whether we have direct right or right thanks to the
            // fact that some right is enabled and we are moderator of
            // the group.
            foreach ($this->tree->rights as $right)
            {
                $value = $acl && $acl['allow_'.$right];
                $canChange = $node->hasRight('grant') || ($value && $isModerator);

                if ($canChange)
                {
                    $sumChange++;
                }

                // Count differences between parent and this ACL.
                if (($parentACL && $parentACL['allow_'.$right] != $value)
                ||  ($acl && !$parentACL)
                ||  (!$acl && $parentACL))
                {
                    $sumDiff++;
                }
            }

            // We cannot change and there is no right set on this node for this group.
            // Therefore we do not show it.
            if (!$sumChange && !$sumDiff)
            {
                continue;
            }

/* Debugging:

            if ($parentACL || $acl)
            {
                echo $rec['name']." ".$sumChange.":".$sumDiff."<br>";
                dump($parentACL);
                dump($acl);
            }
*/

?>
        <tr class='group'>
            <td rowspan='2' class='group'><?php echo $rec['name']?></td>
<?php
            foreach ($this->tree->rights as $right)
            {
?>
            <td class='right'>
                <input type='checkbox' disabled <?php echo $parentACL && $parentACL['allow_'.$right]?'checked':''?>>
            </td>
<?php
            }
?>
        </tr><tr>
<?php
            foreach ($this->tree->rights as $right)
            {
                $value = $acl && $acl['allow_'.$right];
                $canChange = ($node->hasRight('grant') && ($isMyGroup||$value))
                    || ($value && $isModerator);
?>
            <td class='right'>
                <input type='checkbox' value='1' <?php echo $canChange?'':'disabled'?>
                    name='<?php echo $right.'_'.$gid?>' <?php echo $value?'checked':''?>>
            </td>
<?php
            }
?>
        </tr>
<?php
        }
?>
    </table>
    <div class='legend'><?php echo P('command::security_legend')?></div>
<?php
    }

    function commandSecurity()
    {
        $groups = $this->um->getGroups();
        $myGroups = $this->um->getUserGroups();
        $node = $this->tree->getNode(reqVal('nid_acl',true));
        $sameACL = true;
        $updated = 0;

        foreach ($groups as $gid => $rec)
        {
            $isMyGroup = isset($myGroups[$gid]) || $this->um->canJoinGroup($rec);
            $isModerator = isset($myGroups[$gid]) && $myGroups[$gid]['moderator'];

            if (!$node->hasRight('grant')  // We have no grant right to node
            &&  !$isModerator)             // And we are not moderator
            {
                continue;
            }

            $parentACL = $node->getParentACL($gid);
            $oldacl = $node->getGroupACL($gid);
            $newacl = array();
            $newsum = 0;
            $same = true;

            foreach ($this->tree->rights as $right)
            {
                $name = $right.'_'.$gid;
                $value = reqVal($name)?1:0;
                $parentValue = $parentACL?$parentACL['allow_'.$right]:0;
                $same = $same && $value==$parentValue;
                $newacl['allow_'.$right] = $value?1:0;
                $newsum += $value;
            }

            // We had right on the node before and we do not have right
            // to grant right but have right to remove it then check
            // that we are not cheating.
            if ($oldacl && (!$node->hasRight('grant') || !$isMyGroup))
            {
                foreach ($this->tree->rights as $right)
                {
                    if ($newacl['allow_'.$right]>$oldacl['allow_'.$right])
                    {
                        $this->error('Access denied!');
                        return;
                    }
                }
            }

            // Remove empty acl
            if (!$newsum && $same)
            {
                $node->removeACL($gid);
            }
            else
            {
                $node->updateACL($gid, $newacl);
            }

            $updated++;
            $sameACL = $sameACL && $same;
        }

        // If complete group ACL is the same as parent then we can remove it
        if ($updated && $sameACL)
        {
            $node->removeACL();
        }
    }

/******************************************************************************/

    function buildValidateLinks()
    {
        $fields = array();
        $node = $this->tree->getNode(reqVal('nid_acl',true));
        if (!$node) return null;

        $fields['Folder Name'] = array('name'=>'name','maxlength'=>255,
            'disabled'=>null, 'value'=>$node->name);
        $fields['-hidden1-'] = array('name'=>'nid_acl','value'=>$node->id);
        $fields['Include Subfolders'] = array('name'=>'subfolders', 'type'=>'checkbox', 'checked'=>null);
        $fields['Ignore Recently Tested'] = array('name'=>'ignore_recently', 'type'=>'checkbox', 'checked'=>null);
        $fields['Recent Time Expressed in Seconds'] = array('name'=>'recent_time', 'value'=>60*60);

        if ($this->um->getParam('user', 'use_favicons'))
        {
            $fields['Discover Missing Favicons'] = array('name'=>'discover_favicons', 'type'=>'checkbox', 'checked'=>null);

            if ($this->um->getParam('config', 'use_favicon_cache'))
            {
                $fields['Delete Invalid Favicons'] = array('name'=>'delete_favicons', 'type'=>'checkbox', 'checked'=>null);
            }
        }

        return $fields;
    }

    function commandValidateLinks()
    {
        $this->forwardCommand("Validation");
    }

    function buildValidation()
    {
        $fields = array();
        $node = $this->tree->getNode(reqVal('nid_acl',true));
        if (!$node) return null;

        require_once('./inc/validator.inc.php');
        $validator = new Validator();

        if (reqVal('ignore_recently'))
        {
            $this->tree->loadLinkFilter =
                'UNIX_TIMESTAMP(tested) < ' . (mktime() - reqVal('recent_time'));
        }

        if (reqVal('subfolders'))
        {
            $this->tree->loadNodes($node);
        }
        else
        {
            $this->tree->loadLinks($node);
        }

        $validator->buildValidate($node, $fields,
            reqVal('discover_favicons'),
            reqVal('delete_favicons'));

        if (!$validator->linkCount)
        {
            if (reqVal('ignore_recently'))
            {
                $this->warn('All links recently validated!');
            }
            else
            {
                $this->warn('No links in the folder!');
            }
        }

        return $fields;
    }

/******************************************************************************/

    function buildImportBookmarks()
    {
        require_once('./inc/bookmarkmanager.inc.php');

        $fields = array();
        $node = $this->tree->getNode(reqVal('nid_acl',true));
        $fields['Target Folder Name'] = array('value'=>$node->name,'disabled'=>null);
        $fields['Bookmark File'] = array('type'=>'file','name'=>'file');
        $fields['Rename Duplicate Links'] = array('name'=>'rename', 'type'=>'checkbox');
        $fields['Codepage'] = array('type'=>'callback', 'function'=>'_buildCodepage');
        $fields['File Type'] = array('type'=>'callback', 'function'=>'_buildFileType',
            'params'=>array('import'=>true));
        $fields['-hidden1-'] = array('name'=>'nid_acl','value'=>$node->id);

        if (Page::isMSIE())
        {
            $fields['-raw1-'] = P('command::import_bk');
        }
        return $fields;
    }

    function buildExportBookmarks()
    {
        require_once('./inc/bookmarkmanager.inc.php');

        $fields = array();
        $node = $this->tree->getNode(reqVal('nid_acl',true));
        $fields['Folder Name'] = array('value'=>$node->name,'disabled'=>null);
        $fields['Codepage'] = array('type'=>'callback', 'function'=>'_buildCodepage');
        $fields['File Type'] = array('type'=>'callback', 'function'=>'_buildFileType');
        $fields['-hidden1-'] = array('name'=>'nid_acl','value'=>$node->id);

        if (Page::isMSIE())
        {
            $fields['-raw1-'] = P('command::export_bk');
        }
        return $fields;
    }

    function _buildCodepage()
    {
        if (!$this->um->getParam('config','use_conv_engine'))
        {
            return;
        }

        $bm = new BookmarkManager();

        if ($bm->getEngine()==CHARSET_IGNORE)
        {
            echo P('command::noiconv');
            return;
        }

        $default = $bm->langDetect();

        function _cmdlangCmp(&$a, $b)
        {
            return (strcmp($a[1], $b[1]));
        }

        uasort($bm->languages, '_cmdlangCmp');
        reset($bm->languages);
?>
    <div class='label'><?php echo T('Codepage')?></div>
    <select class="language" name="codepage">
<?php
        foreach ($bm->languages as $key => $value)
        {
            $lang_name = ucfirst(substr(strstr($value[0], '|'), 1));
            echo "\t\t" . '<option value="' . $key . '" ' .
                ($key==$default?'selected':'') . '>' .
                $lang_name .' (' . $key . ')</option>' . "\n";
        }
?>
    </select>
<?php
    }

    function _buildFileType($params)
    {
?>
    <div class='label'><?php echo T('File Type')?></div>
<?php
        $checked = 'checked';
        $import = isset($params['import']) && $params['import'];

        if ($import)
        {
?>
    <input value='auto' type='radio' name='type' <?php echo $checked?>><?php echo T("Auto detection")?><br>
<?php
            $checked = '';
        }
?>
<?php  if ($import) : ?>
    <input value='<?php echo BM_T_NETSCAPE?>' type='radio' name='type' <?php echo $checked?>><?php echo T('Netscape/Mozilla/MS IE')?><br>
<?php  else: ?>
    <input value='<?php echo BM_T_NETSCAPE?>' type='radio' name='type' <?php if (!Page::isMSIE()) echo $checked?>><?php echo T('Netscape/Mozilla')?><br>
    <input value='<?php echo BM_T_IE?>' type='radio' name='type' <?php if (Page::isMSIE()) echo $checked?>><?php echo T('MS Internet Explorer')?><br>
<?php  endif; ?>
    <input value='<?php echo BM_T_OPERA?>' type='radio' name='type'><?php echo T('Opera Hotlist version 2.0')?><br>
<?php
    }

    function commandImportBookmarks()
    {
        require_once('./inc/bookmarkmanager.inc.php');

        if (!$this->checkFile('file'))
        {
            return;
        }

        $filename = $_FILES['file']['tmp_name'];

        $bm = new BookmarkManager($this->um->getParam('config','use_conv_engine'),reqVal('codepage'));
        $type = reqVal('type');
        $bm->import($filename, ($type=='auto'?null:$type));

        // If not loaded message will be recorded and we go out
        if (!$bm->success)
        {
            return;
        }

        $this->message = T(
            'Imported %s link(s) into %s folder(s) from the bookmark file.',
            array($bm->importedLinks, $bm->importedFolders));

        $this->tree->importTree(reqVal('nid_acl'), $bm->tree, reqChk('rename'));
    }

    function commandExportBookmarks()
    {
        require_once('./inc/bookmarkmanager.inc.php');

        $base = $this->tree->getNode(reqVal('nid_acl',true));
        $this->tree->loadNodes($base, true, 'select', true);

        if ($this->hasErrors())
        {
            return;
        }

        $type = reqVal('type');

        if (reqVal('do')=='direct')
        {
            $type = BM_T_NETSCAPE;
        }

        $ext = $type==BM_T_OPERA?".adr":".html";

        header("Content-type: application/octet-stream\n");
        header("Content-disposition: attachment; filename=\"" . $base->name . $ext ."\"\n");
        header("Content-transfer-encoding: binary\n");

        $bm = new BookmarkManager($this->um->getParam('config','use_conv_engine'), reqVal('codepage'));
        $bm->export($base, $type);
        exit; // Really break program here
    }
}

/******************************************************************************/
/******************************************************************************/
/******************************************************************************/

$cw = new CommandWindow();
Skin::set($cw->um->getParam('user','skin'));

// On error no reloading and no closing
if ($cw->hasErrors())
{
    $cw->reload = false;
    $cw->close = false;
}

$isIIS = strstr($_SERVER['SERVER_SOFTWARE'],'IIS');
$metaClose = false;
$metaTag = null;

if ($cw->close && $isIIS && in_array($cw->command, $cw->um->inPlaceCommands()))
{
    $metaClose = true;
    $metaTag =
        '<meta http-equiv="refresh" content="0;url=sitebar.php'.
        $cw->getParams()."\">\n";
}

// On command success when auto close is required and we do not use IIS with
// in place commands.
if ($cw->close && !$cw->fields && !$metaClose)
{
    // When in place just reload
    if ($cw->inPlace())
    {
        header("Location: sitebar.php".$cw->getParams(false));
        exit;
    }
    // When not in place then close
    else
    {
        $cw->onLoad = 'window.close()';
    }
}

/**
 * I do not need instance, I just need to call static functions.
 * As of PHP 4.3.1 it will generate strange warning in case
 * bookmarkmanager issued an error() on import(). I cannot see
 * any relevance because Page does not inherit from ErrorHandler.
 * But it is indeed related to ErrorHandler (when removing & from
 * declaration of getErrors() it works, but errors cannot be
 * reported then. Too curious for reporting and PHP 5 adds
 * static members what should solve the problem in future.
 */
$page = new Page();
$page->head('Commander', 'cmdWin', null, $cw->onLoad, $metaTag);

$errId = ($cw->hasErrors() && $cw->hasErrors(E_ERROR))?'error':'warn';

?>

<div id="<?php echo ($cw->hasErrors()?$errId:'command').'Head'?>"><?php echo T($cw->command)?></div>
<div id="<?php echo ($cw->hasErrors()?$errId:'command').'Body'?>">
<?php
    if ($cw->hasErrors())
    {
        $cw->writeErrors(false);
        echo "<p>";
    }

    // If we have no errors or ignore them
    if (!$cw->hasErrors() || $cw->showWithErrors || $cw->hasHandledErrors()==$cw->hasErrors())
    {
        if ($cw->fields)
        {
            $cw->writeForm();
        }
        else
        {
            echo (strlen($cw->message)?$cw->message:T("Successful execution!"));
        }
    }

    if ($cw->inPlace()) : ?>
    <div class='buttons'>
        <input class='button' type='button' onclick="reloadPage(true)" value='<?php echo T('Return')?>'>
    </div>
<?php
    endif;
?>
</div>
<?php
    if (!$cw->nobuttons) : ?>
<div id='foot'>
<?php   if (!$cw->inPlace()) : ?>
<?php       if (!$cw->bookmarklet) : ?>
    [<a href="javascript:window.opener.location.reload();window.close()"><?php echo T('Reload SiteBar')?></a>]
<?php       endif; ?>
    [<a href="javascript:window.close()"><?php echo T('Close')?></a>]
    <div class='hidden'>
        <div><a href='sitebar.php'><?php echo T('Backup Return to SiteBar')?></a></div>
    </div>
<?php   endif; ?>
</div>
<?php
    endif;
$page->foot();
?>
