Alex Trofimov 12 лет назад
Родитель
Сommit
08cd8edfb7

+ 21 - 4
install/classes/BxDolInstallController.php

@@ -19,7 +19,7 @@ class BxDolInstallController
 
     function run ($sAction = '') 
     {
-        $sMethod = 'action' . ucfirst($sAction);
+        $sMethod = 'action' . str_replace(' ', '', ucwords(str_replace('_', ' ', $sAction)));
         if ($sAction && method_exists($this, $sMethod))
             $this->$sMethod ();
         else
@@ -35,7 +35,7 @@ class BxDolInstallController
 
         $this->_oView->out('audit.php', compact('sAuditOutput'));
 
-        $this->_oView->pageEnd('Dolphin 8.0.0 Installation');
+        $this->_oView->pageEnd($this->_getTitle());
     }
 
     function actionInitial () 
@@ -55,8 +55,9 @@ class BxDolInstallController
         else
             $this->_oView->out('initial_fail.php', compact('aLangs', 'aErrors'));
 
-        $this->_oView->pageEnd('Dolphin 8.0.0 Installation');
+        $this->_oView->pageEnd($this->_getTitle());
     }
+
     function actionPermissions () 
     {
         $this->_oView->pageStart();
@@ -71,7 +72,23 @@ class BxDolInstallController
 
         $this->_oView->out('permissions.php', compact('sPermissionsStyles', 'sPermissionsTable', 'bPermissionsOk'));
 
-        $this->_oView->pageEnd('Dolphin 8.0.0 Installation');
+        $this->_oView->pageEnd($this->_getTitle());
+    }
+
+    function actionSiteConfig () 
+    {
+        $this->_oView->pageStart();
+
+        $oSiteConfig = new BxDolInstallSiteConfig();
+        $sForm = $oSiteConfig->getFormHtml();
+
+        $this->_oView->out('site_config.php', compact('sForm'));
+
+        $this->_oView->pageEnd($this->_getTitle());
+    }
+
+    protected function _getTitle() {
+        return _t('_sys_inst_title', BX_DOL_VER, BX_DOL_BUILD);
     }
 }
 

+ 327 - 0
install/classes/BxDolInstallSiteConfig.php

@@ -0,0 +1,327 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinInstall Dolphin Install
+ * @{
+ */
+
+class BxDolInstallSiteConfig
+{
+    protected $_aConfig;
+
+    public function __construct()
+    {
+        $this->_aConfig = array (
+
+            // path config
+
+            'section_site_paths_open' => array(
+                'name' => _t('_sys_inst_conf_section_paths'),
+                'func' => 'rowSectionOpen',
+            ),
+
+            'site_url' => array(
+                'name' => _t('_sys_inst_conf_field_site_url'),
+                'ex' => 'http://www.mydomain.com/path/',
+                'desc' => _t('_sys_inst_conf_desc_site_url'),
+                'def' => 'http://',
+                'def_exp' => array('defUrl', ''),
+                'check' => array('checkLength', 10),
+            ),
+            'dir_root' => array(
+                'name' => _t('_sys_inst_conf_field_root_dir'),
+                'ex' => '/home/mydomain/public_html/',
+                'desc' => _t('_sys_inst_conf_desc_root_dir'),
+                'def_exp' => array('defPath', ''),
+                'check' => array('checkLength', 1),
+            ),
+            'dir_convert' => array(
+                'name' => _t('_sys_inst_conf_field_path_to_binary', 'convert'),
+                'ex' => '/usr/local/bin/convert',
+                'desc' => _t('_sys_inst_conf_desc_path_to_binary', 'convert'),
+                'def' => '/usr/local/bin/convert',
+                'def_exp' => array('defImageMagickBin', 'convert'),
+                'check' => array('checkLength', 7),
+            ),
+            'dir_composite' => array(
+                'name' => _t('_sys_inst_conf_field_path_to_binary', 'composite'),
+                'ex' => '/usr/local/bin/composite',
+                'desc' => _t('_sys_inst_conf_desc_path_to_binary', 'composite'),
+                'def' => '/usr/local/bin/composite',
+                'def_exp' => array('defImageMagickBin', 'composite'),
+                'check' => array('checkLength', 7),
+            ),
+
+            'section_site_paths_close' => array(
+                'func' => 'rowSectionClose',
+            ),
+
+            // db config
+
+            'section_db_config_open' => array(
+                'name' => _t('_sys_inst_conf_section_db_config'),
+                'func' => 'rowSectionOpen',
+            ),
+
+            'db_host' => array(
+                'name' => _t('_sys_inst_conf_field_db_host'),
+                'ex' => 'localhost',
+                'desc' => _t('_sys_inst_conf_desc_db_host'),
+                'def' => 'localhost',
+                'check' => array('checkLength', 1),
+            ),
+            'db_port' => array(
+                'name' => _t('_sys_inst_conf_field_db_port'),
+                'ex' => '5506',
+                'desc' => _t('_sys_inst_conf_desc_db_port'),
+            ),
+            'db_sock' => array(
+                'name' => _t('_sys_inst_conf_field_db_sock'),
+                'ex' => '/tmp/mysql.sock',
+                'desc' => _t('_sys_inst_conf_desc_db_sock'),
+            ),
+            'db_name' => array(
+                'name' => _t('_sys_inst_conf_field_db_name'),
+                'ex' => 'mydomian_dolphin',
+                'desc' => _t('_sys_inst_conf_desc_db_name'),
+                'check' => array('checkLength', 1),
+            ),
+            'db_user' => array(
+                'name' => _t('_sys_inst_conf_field_db_user'),
+                'ex' => 'mydomian_dolphin',
+                'desc' => _t('_sys_inst_conf_desc_db_user'),
+                'check' => array('checkLength', 1),
+            ),
+            'db_password' => array(
+                'name' => _t('_sys_inst_conf_field_db_pwd'),
+                'ex' => 'Super*Secret#Word_1234',
+                'desc' => _t('_sys_inst_conf_desc_db_pwd'),
+            ),
+
+            'section_db_config_close' => array(
+                'func' => 'rowSectionClose',
+            ),
+
+            // site config
+
+            'section_site_info_open' => array(
+                'name' => _t('_sys_inst_conf_section_site_info'),
+                'func' => 'rowSectionOpen',
+            ),
+
+            'site_title' => array(
+                'name' => _t('_sys_inst_conf_field_site_title'),
+                'ex' => 'The Best Community',
+                'desc' => _t('_sys_inst_conf_desc_site_title'),
+                'check' => array('checkLength', 1),
+            ),
+            'site_desc' => array(
+                'name' => _t('_sys_inst_conf_field_site_desc'),
+                'ex' => 'The place to find new friends, communicate and have fun.',
+                'desc' => _t('_sys_inst_conf_desc_site_desc'),
+                'check' => array('checkLength', 1),
+            ),
+            'site_email' => array(
+                'name' => _t('_sys_inst_conf_field_site_email'),
+                'ex' => 'no-reply@youdomain.here',
+                'desc' => _t('_sys_inst_conf_desc_site_email'),
+                'check' => array('checkEmail', 3),
+            ),
+            'admin_email' => array(
+                'name' => _t('_sys_inst_conf_field_admin_email'),
+                'ex' => 'admin@email.here',
+                'desc' => _t('_sys_inst_conf_desc_admin_email'),
+                'check' => array('checkEmail', 3),
+            ),
+            'admin_username' => array(
+                'name' => _t('_sys_inst_conf_field_admin_username'),
+                'ex' => 'admin',
+                'desc' => _t('_sys_inst_conf_desc_admin_username'),
+                'check' => array('checkLength', 1),
+            ),
+            'admin_password' => array(
+                'name' => _t('_sys_inst_conf_field_admin_pwd'),
+                'ex' => 'Super*Secret#Word_1234',
+                'desc' => _t('_sys_inst_conf_desc_admin_pwd'),
+                'check' => array('checkLength', 1),
+            ),
+
+            'section_site_info_close' => array(
+                'func' => 'rowSectionClose',
+            ),
+        );
+    }
+
+    public function getFormHtml() 
+    {
+        $aErrorFields = array();
+        if (isset($_POST['site_config'])) {
+            $aErrorFields = $this->checkConfig();
+            if (empty($aErrorFields)) {
+                die('TODO: everything is correct - save data and redirect to the next step');
+            }
+        }
+
+        $sRows = $this->getFormFields($aErrorFields);
+        $sSubmitTitle = _t('_Submit');
+        return <<<EOF
+            <form method="post">
+                <div class="bx-form-advanced-wrapper sys_account_wrapper">
+
+                    {$sRows}
+
+                    <div class="bx-form-element-wrapper bx-def-margin-top">
+                        <div class="bx-form-value">
+                            <div class="bx-form-input-wrapper bx-form-input-wrapper-submit">
+                                <button class="bx-def-font-inputs bx-form-input-submit bx-btn bx-btn-primary" type="submit" name="site_config">
+                                    {$sSubmitTitle}
+                                </button>
+                            </div>
+                        </div>
+                    </div>
+
+                </div>
+            <form>
+EOF;
+    }
+
+    public function checkConfig() 
+    {
+        $aErrorFields = array();
+        foreach ($this->_aConfig as $sKey => $a) {
+            if (!$this->check ($sKey, isset($_POST[$sKey]) ? $_POST[$sKey] : '', $a)) {
+                $aErrorFields[$sKey] = true;
+                unset($_POST[$sKey]);
+            }
+        }
+        return $aErrorFields;
+    }
+
+    protected function getFormFields($aErrorFields) 
+    {
+        $s = '';
+        foreach($this->_aConfig as $sKey => $a) {
+            $sFunc = isset($a['func']) ? $a['func'] : 'rowInput';
+            $s .= $this->$sFunc($sKey, $a, isset($aErrorFields[$sKey]) ? $aErrorFields[$sKey] : false);
+        }
+
+        return $s;
+    }
+    
+    protected function rowInput ($sKey, $a, $isError = false) 
+    {
+        $sAutoMessage = "";
+        $sValue = bx_html_attribute($this->def ($sKey, $a, $sAutoMessage));
+
+        $sDesc = _t('_sys_inst_conf_desc', $sAutoMessage, $a['desc'], $a['ex']);
+
+        $sError = '';
+        if ($isError)
+            $sError = '<div class="bx-form-warn">' . _t('_sys_inst_conf_error') . '</div>';
+
+        $sRequired = '';
+        if (isset($a['check']) && $a['check'])
+            $sRequired = '<span class="bx-form-required">*</span>';
+
+        return <<<EOF
+            <div class="bx-form-element-wrapper bx-def-margin-top-auto">
+                <div class="bx-form-caption">
+                    {$a['name']}
+                    {$sRequired}
+                </div>
+                <div class="bx-form-value">
+                    <div class="bx-form-input-wrapper bx-form-input-wrapper-text">
+                        <input type="text" name="{$sKey}" value="{$sValue}" class="bx-def-font-inputs bx-form-input-text" />
+                    </div>
+                    $sError
+                    <div class="bx-form-info bx-def-font-grayed bx-def-font-small">
+                        {$sDesc}
+                    </div>                    
+                </div></div>
+EOF;
+    }
+
+    protected function rowSectionOpen ($sKey, $a) 
+    {
+        return <<<EOF
+                    <div class="bx-form-section-wrapper bx-def-margin-top">
+                        <fieldset class="bx-form-section bx-def-padding-sec-top bx-def-border-top bx-form-section-header">
+                            <legend class="bx-def-padding-sec-right bx-def-font-grayed bx-def-font-h3">{$a['name']}</legend>
+                            <div class="bx-form-section-content bx-def-padding-top bx-def-padding-bottom">
+EOF;
+    }
+
+    protected function rowSectionClose ($sKey, $a) 
+    {
+        return <<<EOF
+                            </div>
+                        </fieldset>
+                    </div>
+EOF;
+    }
+
+    protected function check ($sKey, $sValue, $a) 
+    {
+        if (empty($a['check']))
+            return true;
+        return $this->{$a['check'][0]}($sValue, $a['check'][1]);
+    }
+
+    protected function checkLength ($s, $i) 
+    {
+        return mb_strlen($s) >= $i ? true : false;
+    }
+
+    protected function checkEmail ($s, $i) 
+    {
+        return mb_strlen($s) > $i && false !== strpos($s, '@') ? true : false;
+    }
+
+    protected function def ($sKey, $a, &$sAutoMessage) 
+    {
+        if (isset($_POST[$sKey]))
+            return bx_process_pass($_POST[$sKey]);
+        if (empty($a['def_exp']))
+            return '';
+        $s = $this->{$a['def_exp'][0]}($a['def_exp'][1]);
+        if ($s) {
+            $sAutoMessage = _t('_sys_inst_conf_found') . '<br />';
+            return $s;
+        } else {
+            $sAutoMessage = _t('_sys_inst_conf_not_found') . '<br />';
+        }
+        return isset($a['def']) ? $a['def'] : '';
+    }
+
+    protected function defUrl ($foo) 
+    {
+        $s = "http://" . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
+        return preg_replace("/install\/(index\.php$)/", '', $s);
+    }
+
+    protected function defPath ($foo) 
+    {
+        $s = rtrim($_SERVER['DOCUMENT_ROOT'], '/') . $_SERVER['PHP_SELF'];
+        return preg_replace("/install\/(index\.php$)/", '', $s);
+    }
+
+    protected function defImageMagickBin ($sBin) 
+    {
+        $a = array(
+            '/usr/X11R6/bin/', 
+            '/usr/local/bin/',
+            '/usr/bin/',
+            '/usr/local/X11R6/bin/',
+            '/usr/bin/X11/',
+            '/opt/local/bin/',
+        );
+        foreach ($a as $sPath)
+            if (file_exists($sPath . $sBin))
+                return $sPath . $sBin;
+        return '';
+    }
+}
+
+/** @} */

+ 3 - 0
install/classes/BxDolInstallView.php

@@ -22,6 +22,7 @@ class BxDolInstallView
         'general.css',
         'icons.css',
         'colors.css',
+        'forms.css',
         'media-desktop.css',
         'media-tablet.css',
         'media-phone.css',
@@ -30,7 +31,9 @@ class BxDolInstallView
 
     protected $_aFilesJs = array (
         'plugins/jquery/jquery.js',
+        'plugins/jquery/jquery.ui.position.min.js',
         'inc/js/jquery.dolPopup.js',
+        'inc/js/jquery.webForms.js',
     );
 
     function __construct()

+ 10 - 11
install/css/styles.css

@@ -12,6 +12,13 @@ html, body {
     float:none;
 }
 
+.bx-install-initial,
+.bx-install-initial-langs,
+.bx-install-permissions-buttons,
+.bx-install-site-config .bx-form-input-wrapper-submit {
+    text-align:center;
+}
+
 .bx-install-page {
 	min-height: 100%;
 	height: auto !important;
@@ -32,10 +39,6 @@ html, body {
     float:right;
 }
 
-.bx-install-initial {
-    text-align:center;
-}
-
 .bx-install-initial img.bx-install-dolphin {
     display:block;
     margin-left:auto;
@@ -44,15 +47,11 @@ html, body {
     max-width:90%;
 }
 
-.bx-install-initial-langs {
-    text-align:center;
-}
-
 .bx-install-initial-langs img {
     width:32px;
     height:32px;
 }
 
-.bx-install-permissions-buttons {
-    text-align:center;
-}
+.bx-install-site-config .bx-form-input-wrapper-submit button {
+    display:inline-block;
+} 

+ 4 - 0
install/index.php

@@ -10,6 +10,9 @@
 define('BX_DOL', 1);
 define('BX_DOL_INSTALL', 1);
 
+define('BX_DOL_VER', '8.0');
+define('BX_DOL_BUILD', '0');
+
 define ('BX_INSTALL_DEFAULT_LANGUAGE', 'en');
 
 define ('BX_INSTALL_URL_ROOT', '../');
@@ -38,6 +41,7 @@ require_once('../studio/classes/BxDolStudioToolsAudit.php');
 require_once(BX_INSTALL_DIR_CLASSES . 'BxDolInstallController.php');
 require_once(BX_INSTALL_DIR_CLASSES . 'BxDolInstallView.php');
 require_once(BX_INSTALL_DIR_CLASSES . 'BxDolInstallLang.php');
+require_once(BX_INSTALL_DIR_CLASSES . 'BxDolInstallSiteConfig.php');
 
 $oController = new BxDolInstallController ();
 $oController->run(isset($_REQUEST['action']) ? $_REQUEST['action'] : '');

+ 1 - 1
install/templates/permissions.php

@@ -13,7 +13,7 @@
             </div>
             <a href="javascript:void(0);" class="bx-btn bx-btn-primary bx-btn-disabled"><? echo _t('_sys_inst_continue'); ?></a>
         <?php endif; if ($bPermissionsOk): ?>
-            <a href="?action=info" class="bx-btn bx-btn-primary"><? echo _t('_sys_inst_continue'); ?></a>
+            <a href="?action=site_config" class="bx-btn bx-btn-primary"><? echo _t('_sys_inst_continue'); ?></a>
         <?php endif; ?>
 
         <a href="javascript:void(0);" onclick="location.reload(false);" class="bx-btn"><? echo _t('_sys_inst_refresh'); ?></a>

+ 7 - 0
install/templates/site_config.php

@@ -0,0 +1,7 @@
+<div class="bx-install-site-config">
+
+    <h1><?php echo _t('_adm_admtools_Site_config'); ?></h1>
+
+   <?=$sForm; ?>
+
+</div>

+ 41 - 0
modules/boonex/english/data/langs/system.xml

@@ -3044,6 +3044,7 @@ Please click "Continue" below to navigate to the home page of the site.]]></stri
 	<string name="_adm_admtools_Executable"><![CDATA[Executable]]></string>
 	<string name="_adm_admtools_Non_Executable"><![CDATA[Not executable]]></string>
 	<string name="_adm_admtools_Permissions"><![CDATA[Permissions]]></string>
+	<string name="_adm_admtools_Site_config"><![CDATA[Site configuration]]></string>
 	<string name="_sys_invitation_no_users_selected"><![CDATA[Please check at least one user or write at least one email]]></string>
 	<string name="_sys_invitation_step_select_users"><![CDATA[Select Users]]></string>
 	<string name="_sys_invitation_step_select_users_info"><![CDATA[Users are from your contacts, friends and favorites]]></string>
@@ -3679,8 +3680,48 @@ If you don't know how to do it by yourself you can submit <a target="_blank" hre
     <string name="_sys_audit_msg_mail_send_failed"><![CDATA[mail send failed]]></string>
     <string name="_sys_audit_msg_mail_sent"><![CDATA[test mail was send, please check {0} mailbox]]></string>
 
+    <string name="_sys_inst_conf_error"><![CDATA[Please, input valid data here.]]></string>
+    <string name="_sys_inst_conf_desc"><![CDATA[{0}{1}<br />Example: {2}]]></string>
+    <string name="_sys_inst_conf_found"><![CDATA[<span style="color:green">Found</span>]]></string>
+    <string name="_sys_inst_conf_not_found"><![CDATA[<span style="color:red">Not found</span>]]></string>
+    <string name="_sys_inst_conf_section_paths"><![CDATA[Site paths]]></string>
+    <string name="_sys_inst_conf_section_db_config"><![CDATA[DB config]]></string>
+    <string name="_sys_inst_conf_section_site_info"><![CDATA[Site info]]></string>
+
+    <string name="_sys_inst_conf_field_site_url"><![CDATA[Site URL]]></string>
+    <string name="_sys_inst_conf_desc_site_url"><![CDATA[Your site URL here (slash at the end is required).]]></string>
+    <string name="_sys_inst_conf_field_root_dir"><![CDATA[Site directory]]></string>
+    <string name="_sys_inst_conf_desc_root_dir"><![CDATA[Path to directory where Dolphin files are stored.]]></string>
+    <string name="_sys_inst_conf_field_path_to_binary"><![CDATA[Path to '{0}' binary]]></string>
+    <string name="_sys_inst_conf_desc_path_to_binary"><![CDATA[If '{0}' binary doesn\'t exist, you need to install <a target="_blank" href="http://www.imagemagick.org/">ImageMagick</a>.]]></string>
+    <string name="_sys_inst_conf_field_db_host"><![CDATA[Database host name]]></string>
+    <string name="_sys_inst_conf_desc_db_host"><![CDATA[Your MySQL database host name here.]]></string>
+    <string name="_sys_inst_conf_field_db_port"><![CDATA[Database host port number]]></string>
+    <string name="_sys_inst_conf_desc_db_port"><![CDATA[Leave blank or specify custom MySQL database host port number.]]></string>
+    <string name="_sys_inst_conf_field_db_sock"><![CDATA[Database socket path]]></string>
+    <string name="_sys_inst_conf_desc_db_sock"><![CDATA[Leave blank or specify MySQL database socket path.]]></string>
+    <string name="_sys_inst_conf_field_db_name"><![CDATA[Database name]]></string>
+    <string name="_sys_inst_conf_desc_db_name"><![CDATA[Your MySQL database name here.]]></string>
+    <string name="_sys_inst_conf_field_db_user"><![CDATA[Database user]]></string>
+    <string name="_sys_inst_conf_desc_db_user"><![CDATA[Your MySQL database user name with read/write access.]]></string>
+    <string name="_sys_inst_conf_field_db_pwd"><![CDATA[Database password]]></string>
+    <string name="_sys_inst_conf_desc_db_pwd"><![CDATA[Your MySQL database password here.]]></string>
+    <string name="_sys_inst_conf_field_site_title"><![CDATA[Site title]]></string>
+    <string name="_sys_inst_conf_desc_site_title"><![CDATA[The name of your site.]]></string>
+    <string name="_sys_inst_conf_field_site_desc"><![CDATA[Site description]]></string>
+    <string name="_sys_inst_conf_desc_site_desc"><![CDATA[Meta description of your site.]]></string>
+    <string name="_sys_inst_conf_field_site_email"><![CDATA[Site e-mail]]></string>
+    <string name="_sys_inst_conf_desc_site_email"><![CDATA[Email to send site messages from.]]></string>
+    <string name="_sys_inst_conf_field_admin_email"><![CDATA[Admin e-mail]]></string>
+    <string name="_sys_inst_conf_desc_admin_email"><![CDATA[Admin user e-mail address.]]></string>
+    <string name="_sys_inst_conf_field_admin_username"><![CDATA[Admin user name]]></string>
+    <string name="_sys_inst_conf_desc_admin_username"><![CDATA[Admin user name here.]]></string>
+    <string name="_sys_inst_conf_field_admin_pwd"><![CDATA[Admin password]]></string>
+    <string name="_sys_inst_conf_desc_admin_pwd"><![CDATA[Admin user password here.]]></string>
+
     <string name="_sys_inst_msg_permissions_wrong"><![CDATA[Some files or folders rights aren't correct, please set necessary permissions then click "Continue".]]></string>
 
+    <string name="_sys_inst_title"><![CDATA[Dolphin {0}.{1} Installation]]></string>
     <string name="_sys_inst_start_installation"><![CDATA[Start installation]]></string>
     <string name="_sys_inst_server_audit"><![CDATA[Server audit]]></string>
     <string name="_sys_inst_hosting_warnings"><![CDATA[Your hosting setup has some warnings, for the complete report go to <a href="?action=audit">server audit page</a>.]]></string>

+ 6 - 0
modules/boonex/russian/data/langs/system.xml

@@ -3436,6 +3436,12 @@ More info about URI DNSBL: <br />
     <string name="_sys_audit_msg_should_be"><![CDATA[хорошо бы {0} {1}]]></string>
     <string name="_sys_audit_msg_must_be"><![CDATA[нужно {0} {1}]]></string>
 
+    <string name="_sys_inst_conf_error"><![CDATA[Тут нужно ввести верные данные]]></string>
+    <string name="_sys_inst_conf_desc"><![CDATA[{0}{1}<br />Пример: {2}]]></string>
+    <string name="_sys_inst_conf_found"><![CDATA[<span style="color:green">Найдено</span>]]></string>
+    <string name="_sys_inst_conf_not_found"><![CDATA[<span style="color:red">Не найдено</span>]]></string>
+
+    <string name="_sys_inst_title"><![CDATA[Инсталляция Дельфина {0}.{1}]]></string>
     <string name="_sys_inst_start_installation"><![CDATA[Начать инсталляцию]]></string>
     <string name="_sys_inst_server_audit"><![CDATA[Аудит сервера]]></string>
     <string name="_sys_inst_hosting_warnings"><![CDATA[Есть некоторые замечания к настройкам сервера, можно перейти к <a href="?action=audit">аудиту сервера</a> для более подробной информации.]]></string>